From 5ed4d648e4159c09b2bc9feac76a249f80ad2b15 Mon Sep 17 00:00:00 2001 From: NekoWings Date: Thu, 9 Oct 2025 11:35:13 +0800 Subject: [PATCH 01/25] feat(chat): implement message editing and copying functionality in User component --- .../components/Chat/Messages/User.vue | 145 ++++++++++++++++++ .../sidepanel/components/Chat/index.vue | 49 ++++-- entrypoints/sidepanel/utils/chat/chat.ts | 42 +++++ locales/de.json | 7 +- locales/en.json | 7 +- locales/es.json | 7 +- locales/fr.json | 7 +- locales/id.json | 7 +- locales/ja.json | 7 +- locales/ko.json | 7 +- locales/pt.json | 7 +- locales/ru.json | 7 +- locales/th.json | 7 +- locales/vi.json | 7 +- locales/zh-CN.json | 7 +- locales/zh-TW.json | 7 +- 16 files changed, 291 insertions(+), 36 deletions(-) create mode 100644 entrypoints/sidepanel/components/Chat/Messages/User.vue diff --git a/entrypoints/sidepanel/components/Chat/Messages/User.vue b/entrypoints/sidepanel/components/Chat/Messages/User.vue new file mode 100644 index 00000000..af957096 --- /dev/null +++ b/entrypoints/sidepanel/components/Chat/Messages/User.vue @@ -0,0 +1,145 @@ + + + diff --git a/entrypoints/sidepanel/components/Chat/index.vue b/entrypoints/sidepanel/components/Chat/index.vue index 47cadcdf..9fe12b87 100644 --- a/entrypoints/sidepanel/components/Chat/index.vue +++ b/entrypoints/sidepanel/components/Chat/index.vue @@ -19,16 +19,15 @@ :class="[item.role === 'user' ? 'self-end' : 'self-start', { 'w-full': ['agent-task-group', 'assistant', 'agent'].includes(item.role) ,'mt-2': ['agent-task-group', 'assistant', 'agent'].includes(item.role) }]" class="max-w-full relative flex" > -
-
-
- -
-
-
+ :message="item" + :isEditing="editingMessageId === item.id" + :disabled="chat.isAnswering() || editingInFlight" + @startEdit="onStartEdit(item.id)" + @cancelEdit="() => onCancelEdit(item.id)" + @submitEdit="(value) => onSubmitEdit(item.id, value)" + /> >() const scrollContainerRef = ref>() +const editingMessageId = ref(null) +const editingInFlight = ref(false) +const log = logger.child('chat-sidepanel') defineExpose({ attachmentSelectorRef, @@ -194,6 +197,32 @@ const allowAsk = computed(() => { return !chat.isAnswering() && userInput.value.trim().length > 0 }) +const onStartEdit = (messageId: string) => { + if (chat.isAnswering() || editingInFlight.value) return + editingMessageId.value = messageId +} + +const onCancelEdit = (messageId: string) => { + if (editingMessageId.value === messageId) { + editingMessageId.value = null + } +} + +const onSubmitEdit = async (messageId: string, value: string) => { + if (editingInFlight.value) return + editingMessageId.value = null + editingInFlight.value = true + try { + await chat.editUserMessage(messageId, value) + } + catch (error) { + log.error('Failed to re-send edited message', error) + } + finally { + editingInFlight.value = false + } +} + const cleanUp = chat.historyManager.onMessageAdded(() => { scrollContainerRef.value?.snapToBottom() }) diff --git a/entrypoints/sidepanel/utils/chat/chat.ts b/entrypoints/sidepanel/utils/chat/chat.ts index a52bdf65..7bc2f8f2 100644 --- a/entrypoints/sidepanel/utils/chat/chat.ts +++ b/entrypoints/sidepanel/utils/chat/chat.ts @@ -536,6 +536,48 @@ export class Chat { await this.runWithAgent(baseMessages) } + async editUserMessage(messageId: string, question: string) { + const trimmedQuestion = question.trim() + if (!trimmedQuestion) throw new Error('Question cannot be empty.') + + const messageIndex = this.historyManager.history.value.findIndex((item) => item.id === messageId) + if (messageIndex === -1) throw new Error(`Message with id ${messageId} not found.`) + const message = this.historyManager.history.value[messageIndex] + if (message.role !== 'user') throw new Error(`Message with id ${messageId} is not a user message.`) + + this.stop() + using _s = this.statusScope('pending') + const abortController = new AbortController() + this.abortControllers.push(abortController) + + this.historyManager.chatHistory.value.lastInteractedAt = Date.now() + + if (messageIndex < this.historyManager.history.value.length - 1) { + this.historyManager.history.value.splice(messageIndex + 1) + } + + const contextInfo = this.historyManager.chatHistory.value.contextUpdateInfo + if (contextInfo?.lastFullUpdateMessageId) { + const exists = this.historyManager.history.value.some((item) => item.id === contextInfo.lastFullUpdateMessageId) + if (!exists) { + contextInfo.lastFullUpdateMessageId = undefined + } + } + + const environmentDetails = await this.generateEnvironmentDetails(message.id) + const prompt = await chatWithEnvironment(trimmedQuestion, environmentDetails) + + message.displayContent = trimmedQuestion + message.content = prompt.user.extractText() + message.timestamp = Date.now() + message.done = true + + const baseMessages = this.historyManager.getLLMMessages({ system: prompt.system, lastUser: prompt.user }) + await this.prepareModel() + if (this.contextPDFs.length > 1) log.warn('Multiple PDFs are attached, only the first one will be used for the chat context.') + await this.runWithAgent(baseMessages) + } + private async runWithAgent(baseMessages: CoreMessage[]) { const userConfig = await getUserConfig() const maxIterations = userConfig.chat.agent.maxIterations.get() diff --git a/locales/de.json b/locales/de.json index 9d761d29..05cd640d 100644 --- a/locales/de.json +++ b/locales/de.json @@ -438,7 +438,8 @@ "years": "{year} Jahr | {year} Jahre", "months": "{month} Monat | {month} Monate" }, - "confirm": "Bestätigen" + "confirm": "Bestätigen", + "send": "Senden" }, "ollama": { "sites": { @@ -453,7 +454,9 @@ "close": "Schließen", "chat_history": "Geschichte", "new_chat": "Neuer Chat", - "back": "Zurück" + "back": "Zurück", + "copy_message": "Nachricht kopieren", + "edit_message": "Nachricht bearbeiten" }, "gmail_tools": { "buttons": { diff --git a/locales/en.json b/locales/en.json index 5b52d4c8..6c2975fe 100644 --- a/locales/en.json +++ b/locales/en.json @@ -417,7 +417,8 @@ "years": "{year} year | {year} years", "months": "{month} month | {month} months" }, - "confirm": "Confirm" + "confirm": "Confirm", + "send": "Send" }, "ollama": { "sites": { @@ -453,7 +454,9 @@ "close": "Close", "chat_history": "History", "new_chat": "New Chat", - "back": "Back" + "back": "Back", + "copy_message": "Copy message", + "edit_message": "Edit message" }, "gmail_tools": { "buttons": { diff --git a/locales/es.json b/locales/es.json index f0db0018..ffdb4e95 100644 --- a/locales/es.json +++ b/locales/es.json @@ -417,7 +417,8 @@ "years": "{year} año | {year} años", "months": "{month} mes | {month} meses" }, - "confirm": "Confirmar" + "confirm": "Confirmar", + "send": "Enviar" }, "ollama": { "sites": { @@ -453,7 +454,9 @@ "close": "Cerrar", "chat_history": "Historia", "new_chat": "Nuevo chat", - "back": "Atrás" + "back": "Atrás", + "copy_message": "Copiar mensaje", + "edit_message": "Editar mensaje" }, "gmail_tools": { "buttons": { diff --git a/locales/fr.json b/locales/fr.json index 57dc4615..169e82ca 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -417,7 +417,8 @@ "years": "{year} an | {year} ans", "months": "{month} mois | {month} mois" }, - "confirm": "Confirmer" + "confirm": "Confirmer", + "send": "Envoyer" }, "ollama": { "sites": { @@ -453,7 +454,9 @@ "close": "Fermer", "chat_history": "Histoire", "new_chat": "Nouveau chat", - "back": "Retour" + "back": "Retour", + "copy_message": "Copier le message", + "edit_message": "Modifier le message" }, "gmail_tools": { "buttons": { diff --git a/locales/id.json b/locales/id.json index c74f293f..e4ed9666 100644 --- a/locales/id.json +++ b/locales/id.json @@ -417,7 +417,8 @@ "years": "{year} tahun | {year} tahun", "months": "{month} bulan | {month} bulan" }, - "confirm": "Konfirmasi" + "confirm": "Konfirmasi", + "send": "Kirim" }, "ollama": { "sites": { @@ -453,7 +454,9 @@ "close": "Tutup", "chat_history": "Sejarah", "new_chat": "Obrolan baru", - "back": "Kembali" + "back": "Kembali", + "copy_message": "Salin pesan", + "edit_message": "Edit pesan" }, "gmail_tools": { "buttons": { diff --git a/locales/ja.json b/locales/ja.json index c20c5c26..8c9a0f5f 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -417,7 +417,8 @@ "years": "{year}年 | {year}年", "months": "{month}ヶ月 | {month}ヶ月" }, - "confirm": "確認" + "confirm": "確認", + "send": "送信" }, "ollama": { "sites": { @@ -453,7 +454,9 @@ "close": "閉じる", "chat_history": "歴史", "new_chat": "新しいチャット", - "back": "戻る" + "back": "戻る", + "copy_message": "メッセージをコピー", + "edit_message": "メッセージを編集" }, "gmail_tools": { "buttons": { diff --git a/locales/ko.json b/locales/ko.json index fa2887de..e923d9ff 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -417,7 +417,8 @@ "years": "{year}년 | {year}년", "months": "{month}개월 | {month}개월" }, - "confirm": "확인" + "confirm": "확인", + "send": "보내기" }, "ollama": { "sites": { @@ -453,7 +454,9 @@ "close": "닫기", "chat_history": "역사", "new_chat": "새로운 채팅", - "back": "뒤로" + "back": "뒤로", + "copy_message": "메시지 복사", + "edit_message": "메시지 수정" }, "gmail_tools": { "buttons": { diff --git a/locales/pt.json b/locales/pt.json index 58e50a1d..62643d44 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -417,7 +417,8 @@ "years": "{year} ano | {year} anos", "months": "{month} mês | {month} meses" }, - "confirm": "Confirmar" + "confirm": "Confirmar", + "send": "Enviar" }, "ollama": { "sites": { @@ -453,7 +454,9 @@ "close": "Fechar", "chat_history": "História", "new_chat": "Novo bate -papo", - "back": "Voltar" + "back": "Voltar", + "copy_message": "Copiar mensagem", + "edit_message": "Editar mensagem" }, "gmail_tools": { "buttons": { diff --git a/locales/ru.json b/locales/ru.json index 93beda36..1dbab26c 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -417,7 +417,8 @@ "years": "{year} год | {year} лет", "months": "{month} месяц | {month} месяца | {month} месяцев" }, - "confirm": "Подтвердить" + "confirm": "Подтвердить", + "send": "Отправить" }, "ollama": { "sites": { @@ -453,7 +454,9 @@ "close": "Закрыть", "chat_history": "История", "new_chat": "Новый чат", - "back": "Назад" + "back": "Назад", + "copy_message": "Скопировать сообщение", + "edit_message": "Редактировать сообщение" }, "gmail_tools": { "buttons": { diff --git a/locales/th.json b/locales/th.json index 7587af5d..bd2f74e5 100644 --- a/locales/th.json +++ b/locales/th.json @@ -417,7 +417,8 @@ "years": "{year} ปี | {year} ปี", "months": "{month} เดือน | {month} เดือน" }, - "confirm": "ยืนยัน" + "confirm": "ยืนยัน", + "send": "ส่ง" }, "ollama": { "sites": { @@ -453,7 +454,9 @@ "close": "ปิด", "chat_history": "ประวัติศาสตร์", "new_chat": "แชทใหม่", - "back": "กลับ" + "back": "กลับ", + "copy_message": "คัดลอกข้อความ", + "edit_message": "แก้ไขข้อความ" }, "gmail_tools": { "buttons": { diff --git a/locales/vi.json b/locales/vi.json index e8e4537f..50632761 100644 --- a/locales/vi.json +++ b/locales/vi.json @@ -417,7 +417,8 @@ "years": "{year} năm | {year} năm", "months": "{month} tháng | {month} tháng" }, - "confirm": "Xác nhận" + "confirm": "Xác nhận", + "send": "Gửi" }, "ollama": { "sites": { @@ -453,7 +454,9 @@ "close": "Đóng", "chat_history": "Lịch sử", "new_chat": "Trò chuyện mới", - "back": "Quay lại" + "back": "Quay lại", + "copy_message": "Sao chép tin nhắn", + "edit_message": "Chỉnh sửa tin nhắn" }, "gmail_tools": { "buttons": { diff --git a/locales/zh-CN.json b/locales/zh-CN.json index bee4d513..ec97b663 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -417,7 +417,8 @@ "years": "{year} 年 | {year} 年", "months": "{month} 个月 | {month} 个月" }, - "confirm": "确认" + "confirm": "确认", + "send": "发送" }, "ollama": { "sites": { @@ -453,7 +454,9 @@ "close": "关闭", "chat_history": "历史", "new_chat": "新聊天", - "back": "返回" + "back": "返回", + "copy_message": "复制消息", + "edit_message": "编辑消息" }, "gmail_tools": { "buttons": { diff --git a/locales/zh-TW.json b/locales/zh-TW.json index 47752408..f741c8f5 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -417,7 +417,8 @@ "years": "{year} 年 | {year} 年", "months": "{month} 個月 | {month} 個月" }, - "confirm": "確認" + "confirm": "確認", + "send": "傳送" }, "ollama": { "sites": { @@ -453,7 +454,9 @@ "close": "關閉", "chat_history": "歷史", "new_chat": "新聊天", - "back": "返回" + "back": "返回", + "copy_message": "複製訊息", + "edit_message": "編輯訊息" }, "gmail_tools": { "buttons": { From 88ed48d7fe87665fe6fb08ff734c166efee1b60b Mon Sep 17 00:00:00 2001 From: Neko Liu Date: Fri, 10 Oct 2025 13:26:06 +0800 Subject: [PATCH 02/25] feat(chat): enhance message editing UI with scroll container and improved button functionality --- .../components/Chat/Messages/User.vue | 129 ++++++++++++------ 1 file changed, 87 insertions(+), 42 deletions(-) diff --git a/entrypoints/sidepanel/components/Chat/Messages/User.vue b/entrypoints/sidepanel/components/Chat/Messages/User.vue index af957096..b561b412 100644 --- a/entrypoints/sidepanel/components/Chat/Messages/User.vue +++ b/entrypoints/sidepanel/components/Chat/Messages/User.vue @@ -11,63 +11,93 @@
- -
- - - - - - -
+ + +
- + - + + +
+
+ + + + +
@@ -76,15 +106,17 @@ diff --git a/types/chat.ts b/types/chat.ts index 60835840..b7bd8f44 100644 --- a/types/chat.ts +++ b/types/chat.ts @@ -42,6 +42,14 @@ export type TabAttachment = { value: TabInfo & { id: string } } +export type SelectedTextAttachment = { + type: 'selected-text' + value: { + id: string + text: string + } +} + // this is a placeholder for attachment that is still loading export type LoadingAttachment = { type: 'loading' @@ -52,7 +60,7 @@ export type LoadingAttachment = { } } -export type ContextAttachment = ImageAttachment | PDFAttachment | TabAttachment | LoadingAttachment +export type ContextAttachment = ImageAttachment | PDFAttachment | TabAttachment | SelectedTextAttachment | LoadingAttachment export type ContextAttachmentStorage = { id: string lastInteractedAt?: number // last time user interacted with this context(attach/detach) diff --git a/utils/prompts/chat.ts b/utils/prompts/chat.ts index fb53b250..0caa8187 100644 --- a/utils/prompts/chat.ts +++ b/utils/prompts/chat.ts @@ -1,4 +1,4 @@ -import { ContextAttachment, ContextAttachmentStorage, ImageAttachment, PDFAttachment, TabAttachment } from '@/types/chat' +import { ContextAttachment, ContextAttachmentStorage, ImageAttachment, PDFAttachment, SelectedTextAttachment, TabAttachment } from '@/types/chat' import { Base64ImageData } from '@/types/image' import dayjs from '@/utils/time' @@ -71,6 +71,9 @@ export class EnvironmentDetailsBuilder { } generateFull() { + // Check for selected text + const selectedTextAttachment = this.contextAttachmentStorage.attachments.find((a): a is SelectedTextAttachment => a.type === 'selected-text') + const tabContextBuilder = new TextBuilder('# Available Tabs') const currentTab = this.contextAttachmentStorage.currentTab?.type === 'tab' ? this.contextAttachmentStorage.currentTab : undefined const tabs = this.contextAttachmentStorage.attachments.filter((a): a is TabAttachment => a.type === 'tab' && a.value.tabId !== currentTab?.value.tabId) @@ -117,6 +120,12 @@ ${pdfContextBuilder} ${imageContextBuilder} `.trim()) + // Build the result with optional selected text before environment details + if (selectedTextAttachment) { + const selectedTextTag = new TagBuilder('user_selection').insertContent(selectedTextAttachment.value.text.trim()) + return `${selectedTextTag.build()}\n\n${environmentTagBuilder.build()}` + } + return environmentTagBuilder.build() } } diff --git a/utils/rpc/background-fns.ts b/utils/rpc/background-fns.ts index 31621f0c..449c77b0 100644 --- a/utils/rpc/background-fns.ts +++ b/utils/rpc/background-fns.ts @@ -1075,6 +1075,17 @@ async function forwardGmailAction(action: 'summary' | 'reply' | 'compose', data: } } +async function forwardSelectionText(tabId: number, selectedText: string) { + try { + b2sRpc.emit('selectionChanged', { tabId, selectedText }) + return { success: true } + } + catch (error) { + logger.error('Failed to forward selection text to sidepanel:', error) + return { success: false, error: String(error) } + } +} + export const backgroundFunctions = { emit: (ev: E, ...args: Parameters) => { eventEmitter.emit(ev, ...args) @@ -1146,5 +1157,7 @@ export const backgroundFunctions = { showSettings: showSettingsForBackground, updateSidepanelModelList, forwardGmailAction, + // Selected Text + forwardSelectionText, } ; (self as unknown as { backgroundFunctions: unknown }).backgroundFunctions = backgroundFunctions diff --git a/utils/rpc/content-fns.ts b/utils/rpc/content-fns.ts index ebef8b6b..5d1d0701 100644 --- a/utils/rpc/content-fns.ts +++ b/utils/rpc/content-fns.ts @@ -15,6 +15,7 @@ export type Events = { tabUpdated(opts: { tabId: number, url?: string, faviconUrl?: string, title?: string }): void tabRemoved(opts: { tabId: number } & Browser.tabs.TabRemoveInfo): void contextMenuClicked(opts: { _toTab?: number } & Browser.contextMenus.OnClickData & { menuItemId: ContextMenuId }): void + selectionChanged(opts: { tabId: number, selectedText: string }): void } export type EventKey = keyof Events @@ -53,6 +54,12 @@ export function ping(_: { _toTab?: number }) { return 'pong' } +export function getSelectedText(_: { _toTab?: number }) { + const selection = window.getSelection() + const selectedText = selection?.toString().trim() || '' + return selectedText +} + export const contentFunctions = { emit: (ev: E, ...args: Parameters) => { eventEmitter.emit(ev, ...args) @@ -63,6 +70,7 @@ export const contentFunctions = { getPagePDFContent, getPageContentType, getDocumentContent, + getSelectedText, ping, } as const diff --git a/utils/rpc/sidepanel-fns.ts b/utils/rpc/sidepanel-fns.ts index 14e64e23..36cf74b3 100644 --- a/utils/rpc/sidepanel-fns.ts +++ b/utils/rpc/sidepanel-fns.ts @@ -14,6 +14,7 @@ export type Events = { gmailAction(options: { action: 'summary' | 'reply' | 'compose', data: unknown, tabInfo: TabInfo }): void updateModelList(): void updateChatList(): void + selectionChanged(options: { tabId: number, selectedText: string }): void } export type EventKey = keyof Events diff --git a/utils/user-config/defaults.ts b/utils/user-config/defaults.ts index e9cf9a90..aa2da0b4 100644 --- a/utils/user-config/defaults.ts +++ b/utils/user-config/defaults.ts @@ -209,18 +209,22 @@ ${tools.map((tool) => renderPrompt`${new PromptBasedToolBuilder(tool)}`).join('\ # WORKFLOW -Simple two-step process for ALL queries: +Simple step-by-step process for ALL queries: -### Step 1: Always Check Selected Tab First -- Start with brief explanation: "Let me first check the selected tab to see if it contains relevant information" +### Step 1: Check for Selected Text First +- If user_message contains tags, prioritize understanding and responding to the selected content +- The selected text indicates the user's specific focus area and should guide all subsequent tool usage + +### Step 2: Always Check Selected Tab First +- Start with brief explanation: "Let me first check the selected tab to see if it contains relevant information about [selected text topic]" - Use view_tab for the SELECTED tab (marked as SELECTED in available tabs) - This is mandatory regardless of query type -### Step 2: Click on Relevant Links Found +### Step 3: Click on Relevant Links Found - If the selected tab shows relevant interactive elements, use click to explore them - Prioritize click over other tools when relevant links are available -### Step 3: Other Tools as Needed +### Step 4: Other Tools as Needed - Use other available resources: view_pdf, view_image, other tabs ${hasOnlineSearch ? '- Use search_online only if existing resources don\'t provide sufficient information' : '- Note: Web search is currently disabled. Focus on available local resources.'} - Use fetch_page for specific URLs mentioned by user From 2c551a36533b7cc4c03eca115bcbea355f19bb38 Mon Sep 17 00:00:00 2001 From: NekoWings Date: Thu, 16 Oct 2025 17:00:22 +0800 Subject: [PATCH 04/25] feat: Add camera functionality and support for captured pages - Introduced CameraButton component for capturing screenshots. - Added SVG icons for camera, captured page, and selected text. - Enhanced AttachmentSelector to handle captured page attachments. - Updated context attachment types to include CapturedPageAttachment. - Implemented image preview tooltip for attachments in AttachmentSelector. - Modified localization files to include new strings for selected text and captured page. - Updated agent storage and chat utilities to support captured page handling. --- assets/icons/camera.svg | 8 ++ assets/icons/captured-page.svg | 16 +++ assets/icons/selected-text.svg | 13 ++ .../components/AttachmentSelector.vue | 97 ++++++++++++++- .../components/Chat/CameraButton.vue | 112 ++++++++++++++++++ .../sidepanel/components/Chat/index.vue | 43 ++++--- entrypoints/sidepanel/utils/agent/strorage.ts | 2 +- .../sidepanel/utils/chat/tool-calls/index.ts | 3 +- locales/de.json | 4 +- locales/en.json | 4 +- locales/es.json | 4 +- locales/fr.json | 4 +- locales/id.json | 4 +- locales/ja.json | 4 +- locales/ko.json | 4 +- locales/pt.json | 4 +- locales/ru.json | 4 +- locales/th.json | 4 +- locales/vi.json | 4 +- locales/zh-CN.json | 4 +- locales/zh-TW.json | 4 +- types/chat.ts | 11 +- utils/prompts/chat.ts | 33 +++++- 23 files changed, 344 insertions(+), 46 deletions(-) create mode 100644 assets/icons/camera.svg create mode 100644 assets/icons/captured-page.svg create mode 100644 assets/icons/selected-text.svg create mode 100644 entrypoints/sidepanel/components/Chat/CameraButton.vue diff --git a/assets/icons/camera.svg b/assets/icons/camera.svg new file mode 100644 index 00000000..bc3976ed --- /dev/null +++ b/assets/icons/camera.svg @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/assets/icons/captured-page.svg b/assets/icons/captured-page.svg new file mode 100644 index 00000000..47bf0f8c --- /dev/null +++ b/assets/icons/captured-page.svg @@ -0,0 +1,16 @@ + + + + + + + \ No newline at end of file diff --git a/assets/icons/selected-text.svg b/assets/icons/selected-text.svg new file mode 100644 index 00000000..96fd057d --- /dev/null +++ b/assets/icons/selected-text.svg @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/entrypoints/sidepanel/components/AttachmentSelector.vue b/entrypoints/sidepanel/components/AttachmentSelector.vue index 474ecab9..846b7459 100644 --- a/entrypoints/sidepanel/components/AttachmentSelector.vue +++ b/entrypoints/sidepanel/components/AttachmentSelector.vue @@ -1,5 +1,35 @@