From f5c4853569efd81e992659e2481f1f889be942ae Mon Sep 17 00:00:00 2001 From: CoffeePatch Date: Tue, 15 Sep 2026 09:12:34 +0530 Subject: [PATCH 1/4] feat(pageAction): add dedicated prompt field with two-stage non-recursive resolution --- .../public/_locales/de/messages.json | 18 ++ .../public/_locales/en/messages.json | 24 +++ .../public/_locales/es/messages.json | 18 ++ .../public/_locales/fr/messages.json | 18 ++ .../public/_locales/hi/messages.json | 18 ++ .../public/_locales/id/messages.json | 18 ++ .../public/_locales/it/messages.json | 18 ++ .../public/_locales/ja/messages.json | 18 ++ .../public/_locales/ko/messages.json | 18 ++ .../public/_locales/ms/messages.json | 18 ++ .../public/_locales/pt_BR/messages.json | 18 ++ .../public/_locales/pt_PT/messages.json | 18 ++ .../public/_locales/ru/messages.json | 18 ++ .../public/_locales/zh_CN/messages.json | 18 ++ .../extension/src/action/pageAction.test.ts | 173 ++++++++++++++++++ packages/extension/src/action/pageAction.ts | 26 ++- .../option/editor/CommandEditDialog.tsx | 2 + .../option/editor/PageActionSection.tsx | 58 +++++- .../editor/commandChangedDetector.test.ts | 38 ++++ .../option/editor/commandChangedDetector.ts | 10 +- .../src/components/pageAction/InputEditor.tsx | 1 + .../src/components/pageAction/InputPopup.tsx | 9 + .../hooks/pageAction/usePageActionRunner.ts | 2 + packages/extension/src/services/ipc.ts | 3 + .../pageAction/background-sidePanel.ts | 3 + .../src/services/pageAction/background.ts | 18 +- .../pageAction/backgroundDispatcher.test.ts | 2 + .../pageAction/backgroundDispatcher.ts | 31 ++-- .../services/pageAction/dispatcher.test.ts | 37 ++++ .../src/services/pageAction/dispatcher.ts | 27 +-- .../src/services/pageAction/helper.test.ts | 115 +++++++++++- .../src/services/pageAction/helper.ts | 61 ++++++ .../src/services/pageAction/insertSymbols.ts | 2 + .../services/pageAction/pageActionTypes.ts | 1 + packages/extension/src/types/index.ts | 1 + packages/extension/src/types/schema.test.ts | 87 +++++++++ packages/extension/src/types/schema.ts | 1 + packages/hub/src/types/pageAction.ts | 1 + 38 files changed, 916 insertions(+), 51 deletions(-) create mode 100644 packages/extension/src/action/pageAction.test.ts diff --git a/packages/extension/public/_locales/de/messages.json b/packages/extension/public/_locales/de/messages.json index 10e205480..4138bd480 100644 --- a/packages/extension/public/_locales/de/messages.json +++ b/packages/extension/public/_locales/de/messages.json @@ -491,6 +491,18 @@ "Option_pageAction_delay": { "message": "Verzögerungszeit" }, + "Option_pageAction_prompt": { + "message": "Prompt" + }, + "Option_pageAction_prompt_desc": { + "message": "Vorlage für den einzufügenden Prompt. Sie können Variablen aus dem Menü hinzufügen." + }, + "Option_pageAction_prompt_desc_2": { + "message": "Fügen Sie dynamische Variablen wie markierten Text ein und verweisen Sie in Schritten mit {{Prompt}} darauf." + }, + "Option_pageAction_prompt_placeholder": { + "message": "z. B.: Fassen Sie folgenden Text zusammen:\n{{Ausgewählter Text}}" + }, "Option_aiPrompt_service": { "message": "KI-Dienst" }, @@ -855,6 +867,9 @@ "PageAction_InputMenu_mark_selectionHtml": { "message": "Das HTML des ausgewählten Bereichs wird als Datei angehängt" }, + "PageAction_InputMenu_mark_prompt": { + "message": "Prompt wird eingefügt" + }, "PageAction_InputMenu_insertText": { "message": "Text einfügen" }, @@ -864,6 +879,9 @@ "PageAction_InputMenu_selectedText": { "message": "Ausgewählter Text" }, + "PageAction_InputMenu_prompt": { + "message": "Prompt" + }, "PageAction_InputMenu_url": { "message": "Original-URL" }, diff --git a/packages/extension/public/_locales/en/messages.json b/packages/extension/public/_locales/en/messages.json index 77b80e6d8..42108d974 100644 --- a/packages/extension/public/_locales/en/messages.json +++ b/packages/extension/public/_locales/en/messages.json @@ -494,6 +494,22 @@ "Option_pageAction_delay": { "message": "Delay time" }, + "Option_pageAction_prompt": { + "message": "Prompt", + "description": "Label for prompt input field in Page Action" + }, + "Option_pageAction_prompt_desc": { + "message": "Prompt template to be inserted. You can add variables from the menu.", + "description": "Description for prompt input field in Page Action" + }, + "Option_pageAction_prompt_desc_2": { + "message": "Insert dynamic variables like selected text, and reference them in steps with {{Prompt}}.", + "description": "Detailed description for prompt usage" + }, + "Option_pageAction_prompt_placeholder": { + "message": "e.g., Summarize the following text:\n{{Selected text}}", + "description": "Placeholder for prompt input field in Page Action" + }, "Option_aiPrompt_service": { "message": "AI Service" }, @@ -865,6 +881,10 @@ "message": "The HTML of the selected range will be attached as a file", "description": "for INSERT.SELECTION_HTML — placeholder {{SelectionHtml}} in prompt templates" }, + "PageAction_InputMenu_mark_prompt": { + "message": "Prompt will be inserted", + "description": "for INSERT.PROMPT" + }, "PageAction_InputMenu_insertText": { "message": "Insert Text" }, @@ -875,6 +895,10 @@ "PageAction_InputMenu_selectedText": { "message": "Selected text" }, + "PageAction_InputMenu_prompt": { + "message": "Prompt", + "description": "Menu item for inserting prompt variable" + }, "PageAction_InputMenu_url": { "message": "Origin URL" }, diff --git a/packages/extension/public/_locales/es/messages.json b/packages/extension/public/_locales/es/messages.json index 4dc78aefc..a5ff384d9 100644 --- a/packages/extension/public/_locales/es/messages.json +++ b/packages/extension/public/_locales/es/messages.json @@ -491,6 +491,18 @@ "Option_pageAction_delay": { "message": "Tiempo de retraso" }, + "Option_pageAction_prompt": { + "message": "Prompt" + }, + "Option_pageAction_prompt_desc": { + "message": "Plantilla del prompt que se insertará. Puede agregar variables desde el menú." + }, + "Option_pageAction_prompt_desc_2": { + "message": "Inserte variables dinámicas como texto seleccionado y referéncielas en los pasos con {{Prompt}}." + }, + "Option_pageAction_prompt_placeholder": { + "message": "ej.: Resume el siguiente texto:\n{{Texto seleccionado}}" + }, "Option_aiPrompt_service": { "message": "Servicio IA" }, @@ -855,6 +867,9 @@ "PageAction_InputMenu_mark_selectionHtml": { "message": "Se adjuntará el HTML del rango seleccionado como archivo" }, + "PageAction_InputMenu_mark_prompt": { + "message": "Se insertará el prompt" + }, "PageAction_InputMenu_insertText": { "message": "Insertar Texto" }, @@ -864,6 +879,9 @@ "PageAction_InputMenu_selectedText": { "message": "Texto seleccionado" }, + "PageAction_InputMenu_prompt": { + "message": "Prompt" + }, "PageAction_InputMenu_url": { "message": "URL de origen" }, diff --git a/packages/extension/public/_locales/fr/messages.json b/packages/extension/public/_locales/fr/messages.json index cd7168273..88267021c 100644 --- a/packages/extension/public/_locales/fr/messages.json +++ b/packages/extension/public/_locales/fr/messages.json @@ -491,6 +491,18 @@ "Option_pageAction_delay": { "message": "Temps de délai" }, + "Option_pageAction_prompt": { + "message": "Prompt" + }, + "Option_pageAction_prompt_desc": { + "message": "Modèle de prompt à insérer. Vous pouvez ajouter des variables depuis le menu." + }, + "Option_pageAction_prompt_desc_2": { + "message": "Insérez des variables dynamiques comme le texte sélectionné et référencez-les dans les étapes avec {{Prompt}}." + }, + "Option_pageAction_prompt_placeholder": { + "message": "ex. : Résumez le texte suivant :\n{{Texte sélectionné}}" + }, "Option_aiPrompt_service": { "message": "Service IA" }, @@ -855,6 +867,9 @@ "PageAction_InputMenu_mark_selectionHtml": { "message": "Le HTML de la plage sélectionnée sera joint en fichier" }, + "PageAction_InputMenu_mark_prompt": { + "message": "Le prompt sera inséré" + }, "PageAction_InputMenu_insertText": { "message": "Insérer du Texte" }, @@ -864,6 +879,9 @@ "PageAction_InputMenu_selectedText": { "message": "Texte sélectionné" }, + "PageAction_InputMenu_prompt": { + "message": "Prompt" + }, "PageAction_InputMenu_url": { "message": "URL d'origine" }, diff --git a/packages/extension/public/_locales/hi/messages.json b/packages/extension/public/_locales/hi/messages.json index 2ec54606d..57b7135b8 100644 --- a/packages/extension/public/_locales/hi/messages.json +++ b/packages/extension/public/_locales/hi/messages.json @@ -491,6 +491,18 @@ "Option_pageAction_delay": { "message": "प्रतीक्षा समय" }, + "Option_pageAction_prompt": { + "message": "प्रॉम्प्ट" + }, + "Option_pageAction_prompt_desc": { + "message": "सम्मिलित किए जाने वाले प्रॉम्प्ट का टेम्पलेट। आप मेनू से चर जोड़ सकते हैं।" + }, + "Option_pageAction_prompt_desc_2": { + "message": "चयनित टेक्स्ट जैसे गतिशील चर डालें, और चरणों में {{प्रॉम्प्ट}} के साथ उनका संदर्भ लें।" + }, + "Option_pageAction_prompt_placeholder": { + "message": "उदा.: निम्नलिखित पाठ का सारांश प्रस्तुत करें:\n{{चयनित पाठ}}" + }, "Option_aiPrompt_service": { "message": "AI सेवा" }, @@ -855,6 +867,9 @@ "PageAction_InputMenu_mark_selectionHtml": { "message": "चुने गए भाग का HTML फ़ाइल के रूप में संलग्न किया जाएगा" }, + "PageAction_InputMenu_mark_prompt": { + "message": "प्रॉम्प्ट डाला जाएगा" + }, "PageAction_InputMenu_insertText": { "message": "टेक्स्ट डालें" }, @@ -864,6 +879,9 @@ "PageAction_InputMenu_selectedText": { "message": "चयनित टेक्स्ट" }, + "PageAction_InputMenu_prompt": { + "message": "प्रॉम्प्ट" + }, "PageAction_InputMenu_url": { "message": "मूल URL" }, diff --git a/packages/extension/public/_locales/id/messages.json b/packages/extension/public/_locales/id/messages.json index 6cd360745..0c06b148f 100644 --- a/packages/extension/public/_locales/id/messages.json +++ b/packages/extension/public/_locales/id/messages.json @@ -491,6 +491,18 @@ "Option_pageAction_delay": { "message": "Waktu tunda" }, + "Option_pageAction_prompt": { + "message": "Prompt" + }, + "Option_pageAction_prompt_desc": { + "message": "Templat prompt yang akan dimasukkan. Anda dapat menambahkan variabel dari menu." + }, + "Option_pageAction_prompt_desc_2": { + "message": "Sisipkan variabel dinamis seperti teks yang dipilih, dan rujuk dalam langkah dengan {{Prompt}}." + }, + "Option_pageAction_prompt_placeholder": { + "message": "cth., Ringkas teks berikut:\n{{Teks yang dipilih}}" + }, "Option_aiPrompt_service": { "message": "Layanan AI" }, @@ -855,6 +867,9 @@ "PageAction_InputMenu_mark_selectionHtml": { "message": "HTML dari rentang yang dipilih akan dilampirkan sebagai file" }, + "PageAction_InputMenu_mark_prompt": { + "message": "Prompt akan dimasukkan" + }, "PageAction_InputMenu_insertText": { "message": "Masukkan Teks" }, @@ -864,6 +879,9 @@ "PageAction_InputMenu_selectedText": { "message": "Teks yang dipilih" }, + "PageAction_InputMenu_prompt": { + "message": "Prompt" + }, "PageAction_InputMenu_url": { "message": "URL Asal" }, diff --git a/packages/extension/public/_locales/it/messages.json b/packages/extension/public/_locales/it/messages.json index f5baeccad..ed65723d4 100644 --- a/packages/extension/public/_locales/it/messages.json +++ b/packages/extension/public/_locales/it/messages.json @@ -491,6 +491,18 @@ "Option_pageAction_delay": { "message": "Tempo di attesa" }, + "Option_pageAction_prompt": { + "message": "Prompt" + }, + "Option_pageAction_prompt_desc": { + "message": "Modello di prompt da inserire. Puoi aggiungere variabili dal menu." + }, + "Option_pageAction_prompt_desc_2": { + "message": "Inserisci variabili dinamiche come il testo selezionato e fai riferimento ad esse nei passaggi con {{Prompt}}." + }, + "Option_pageAction_prompt_placeholder": { + "message": "es., Riassumi il seguente testo:\n{{Testo selezionato}}" + }, "Option_aiPrompt_service": { "message": "Servizio AI" }, @@ -855,6 +867,9 @@ "PageAction_InputMenu_mark_selectionHtml": { "message": "Verrà allegato come file l'HTML dell'intervallo selezionato" }, + "PageAction_InputMenu_mark_prompt": { + "message": "Verrà inserito il prompt" + }, "PageAction_InputMenu_insertText": { "message": "Inserisci testo" }, @@ -864,6 +879,9 @@ "PageAction_InputMenu_selectedText": { "message": "Testo selezionato" }, + "PageAction_InputMenu_prompt": { + "message": "Prompt" + }, "PageAction_InputMenu_url": { "message": "URL originale" }, diff --git a/packages/extension/public/_locales/ja/messages.json b/packages/extension/public/_locales/ja/messages.json index ad47d4c21..9dd9f05a9 100644 --- a/packages/extension/public/_locales/ja/messages.json +++ b/packages/extension/public/_locales/ja/messages.json @@ -491,6 +491,18 @@ "Option_pageAction_delay": { "message": "遅延時間" }, + "Option_pageAction_prompt": { + "message": "プロンプト" + }, + "Option_pageAction_prompt_desc": { + "message": "入力するプロンプトのテンプレートです。メニューから変数を追加できます。" + }, + "Option_pageAction_prompt_desc_2": { + "message": "選択テキストなどの動的変数を挿入し、ステップ内で {{プロンプトが挿入されます}} で参照できます。" + }, + "Option_pageAction_prompt_placeholder": { + "message": "例:以下のテキストを要約してください。\n{{選択テキストが挿入されます}}" + }, "Option_aiPrompt_service": { "message": "AIサービス" }, @@ -852,6 +864,9 @@ "PageAction_InputMenu_mark_selectionHtml": { "message": "選択範囲のHTMLを添付します" }, + "PageAction_InputMenu_mark_prompt": { + "message": "プロンプトが挿入されます" + }, "PageAction_InputMenu_insertText": { "message": "テキスト挿入" }, @@ -861,6 +876,9 @@ "PageAction_InputMenu_selectedText": { "message": "選択テキスト" }, + "PageAction_InputMenu_prompt": { + "message": "プロンプト" + }, "PageAction_InputMenu_url": { "message": "表示元URL" }, diff --git a/packages/extension/public/_locales/ko/messages.json b/packages/extension/public/_locales/ko/messages.json index bbd9cd83f..9680f5f57 100644 --- a/packages/extension/public/_locales/ko/messages.json +++ b/packages/extension/public/_locales/ko/messages.json @@ -491,6 +491,18 @@ "Option_pageAction_delay": { "message": "지연 시간" }, + "Option_pageAction_prompt": { + "message": "프롬프트" + }, + "Option_pageAction_prompt_desc": { + "message": "입력할 프롬프트 템플릿입니다. 메뉴에서 변수를 추가할 수 있습니다." + }, + "Option_pageAction_prompt_desc_2": { + "message": "선택한 텍스트와 같은 동적 변수를 삽입하고 단계에서 {{프롬프트}}로 참조할 수 있습니다." + }, + "Option_pageAction_prompt_placeholder": { + "message": "예: 다음 텍스트 요약:\n{{선택한 텍스트}}" + }, "Option_aiPrompt_service": { "message": "AI 서비스" }, @@ -855,6 +867,9 @@ "PageAction_InputMenu_mark_selectionHtml": { "message": "선택한 범위의 HTML이 파일로 첨부됩니다" }, + "PageAction_InputMenu_mark_prompt": { + "message": "프롬프트가 삽입됩니다" + }, "PageAction_InputMenu_insertText": { "message": "텍스트 삽입" }, @@ -864,6 +879,9 @@ "PageAction_InputMenu_selectedText": { "message": "선택한 텍스트" }, + "PageAction_InputMenu_prompt": { + "message": "프롬프트" + }, "PageAction_InputMenu_url": { "message": "원본 URL" }, diff --git a/packages/extension/public/_locales/ms/messages.json b/packages/extension/public/_locales/ms/messages.json index fc1bc9224..681c91ae8 100644 --- a/packages/extension/public/_locales/ms/messages.json +++ b/packages/extension/public/_locales/ms/messages.json @@ -491,6 +491,18 @@ "Option_pageAction_delay": { "message": "Masa kelewatan" }, + "Option_pageAction_prompt": { + "message": "Prom" + }, + "Option_pageAction_prompt_desc": { + "message": "Templat prom yang akan dimasukkan. Anda boleh menambah pemboleh ubah daripada menu." + }, + "Option_pageAction_prompt_desc_2": { + "message": "Sisipkan pemboleh ubah dinamik seperti teks yang dipilih, dan rujuk dalam langkah dengan {{Prom}}." + }, + "Option_pageAction_prompt_placeholder": { + "message": "cth., Ringkaskan teks berikut:\n{{Teks yang dipilih}}" + }, "Option_aiPrompt_service": { "message": "Perkhidmatan AI" }, @@ -855,6 +867,9 @@ "PageAction_InputMenu_mark_selectionHtml": { "message": "HTML julat yang dipilih akan dilampirkan sebagai fail" }, + "PageAction_InputMenu_mark_prompt": { + "message": "Prom akan dimasukkan" + }, "PageAction_InputMenu_insertText": { "message": "Masukkan Teks" }, @@ -864,6 +879,9 @@ "PageAction_InputMenu_selectedText": { "message": "Teks yang dipilih" }, + "PageAction_InputMenu_prompt": { + "message": "Prom" + }, "PageAction_InputMenu_url": { "message": "URL Asal" }, diff --git a/packages/extension/public/_locales/pt_BR/messages.json b/packages/extension/public/_locales/pt_BR/messages.json index 8768173a8..cd33b6442 100644 --- a/packages/extension/public/_locales/pt_BR/messages.json +++ b/packages/extension/public/_locales/pt_BR/messages.json @@ -491,6 +491,18 @@ "Option_pageAction_delay": { "message": "Tempo de atraso" }, + "Option_pageAction_prompt": { + "message": "Prompt" + }, + "Option_pageAction_prompt_desc": { + "message": "Modelo de prompt a ser inserido. Você pode adicionar variáveis pelo menu." + }, + "Option_pageAction_prompt_desc_2": { + "message": "Insira variáveis dinâmicas como o texto selecionado e faça referência a elas nas etapas com {{Prompt}}." + }, + "Option_pageAction_prompt_placeholder": { + "message": "ex.: Resuma o seguinte texto:\n{{Texto selecionado}}" + }, "Option_aiPrompt_service": { "message": "Serviço de IA" }, @@ -855,6 +867,9 @@ "PageAction_InputMenu_mark_selectionHtml": { "message": "O HTML do intervalo selecionado será anexado como arquivo" }, + "PageAction_InputMenu_mark_prompt": { + "message": "O prompt será inserido" + }, "PageAction_InputMenu_insertText": { "message": "Inserir Texto" }, @@ -864,6 +879,9 @@ "PageAction_InputMenu_selectedText": { "message": "Texto selecionado" }, + "PageAction_InputMenu_prompt": { + "message": "Prompt" + }, "PageAction_InputMenu_url": { "message": "URL de origem" }, diff --git a/packages/extension/public/_locales/pt_PT/messages.json b/packages/extension/public/_locales/pt_PT/messages.json index 0f45b75f8..b5c6dda60 100644 --- a/packages/extension/public/_locales/pt_PT/messages.json +++ b/packages/extension/public/_locales/pt_PT/messages.json @@ -491,6 +491,18 @@ "Option_pageAction_delay": { "message": "Tempo de atraso" }, + "Option_pageAction_prompt": { + "message": "Prompt" + }, + "Option_pageAction_prompt_desc": { + "message": "Modelo de prompt a ser inserido. Pode adicionar variáveis a partir do menu." + }, + "Option_pageAction_prompt_desc_2": { + "message": "Insira variáveis dinâmicas como o texto selecionado e faça referência a elas nos passos com {{Prompt}}." + }, + "Option_pageAction_prompt_placeholder": { + "message": "ex.: Resuma o seguinte texto:\n{{Texto selecionado}}" + }, "Option_aiPrompt_service": { "message": "Serviço de IA" }, @@ -855,6 +867,9 @@ "PageAction_InputMenu_mark_selectionHtml": { "message": "O HTML do intervalo selecionado será anexado como ficheiro" }, + "PageAction_InputMenu_mark_prompt": { + "message": "O prompt será inserido" + }, "PageAction_InputMenu_insertText": { "message": "Inserir Texto" }, @@ -864,6 +879,9 @@ "PageAction_InputMenu_selectedText": { "message": "Texto selecionado" }, + "PageAction_InputMenu_prompt": { + "message": "Prompt" + }, "PageAction_InputMenu_url": { "message": "URL de origem" }, diff --git a/packages/extension/public/_locales/ru/messages.json b/packages/extension/public/_locales/ru/messages.json index 742199b4c..0fa98855f 100644 --- a/packages/extension/public/_locales/ru/messages.json +++ b/packages/extension/public/_locales/ru/messages.json @@ -491,6 +491,18 @@ "Option_pageAction_delay": { "message": "Время ожидания" }, + "Option_pageAction_prompt": { + "message": "Промпт" + }, + "Option_pageAction_prompt_desc": { + "message": "Шаблон промпта для ввода. Вы можете добавлять переменные из меню." + }, + "Option_pageAction_prompt_desc_2": { + "message": "Вставляйте динамические переменные, такие как выбранный текст, и ссылайтесь на них в шагах с помощью {{Промпт}}." + }, + "Option_pageAction_prompt_placeholder": { + "message": "напр.: Сократите следующий текст:\n{{Выбранный текст}}" + }, "Option_aiPrompt_service": { "message": "Сервис ИИ" }, @@ -855,6 +867,9 @@ "PageAction_InputMenu_mark_selectionHtml": { "message": "HTML выбранного диапазона будет прикреплён в виде файла" }, + "PageAction_InputMenu_mark_prompt": { + "message": "Будет вставлен промпт" + }, "PageAction_InputMenu_insertText": { "message": "Вставить текст" }, @@ -864,6 +879,9 @@ "PageAction_InputMenu_selectedText": { "message": "Выбранный текст" }, + "PageAction_InputMenu_prompt": { + "message": "Промпт" + }, "PageAction_InputMenu_url": { "message": "Оригинальный URL" }, diff --git a/packages/extension/public/_locales/zh_CN/messages.json b/packages/extension/public/_locales/zh_CN/messages.json index b0a416613..477b38972 100644 --- a/packages/extension/public/_locales/zh_CN/messages.json +++ b/packages/extension/public/_locales/zh_CN/messages.json @@ -491,6 +491,18 @@ "Option_pageAction_delay": { "message": "等待时间" }, + "Option_pageAction_prompt": { + "message": "提示词" + }, + "Option_pageAction_prompt_desc": { + "message": "这是要输入的提示词模板。可以从菜单中添加变量。" + }, + "Option_pageAction_prompt_desc_2": { + "message": "可以插入选中文本等动态变量,并在步骤中通过 {{提示词}} 引用。" + }, + "Option_pageAction_prompt_placeholder": { + "message": "例:请概括以下文本:\n{{将插入选中的文本}}" + }, "Option_aiPrompt_service": { "message": "AI服务" }, @@ -855,6 +867,9 @@ "PageAction_InputMenu_mark_selectionHtml": { "message": "将以文件形式附加所选范围的HTML" }, + "PageAction_InputMenu_mark_prompt": { + "message": "将插入提示词" + }, "PageAction_InputMenu_insertText": { "message": "插入文本" }, @@ -864,6 +879,9 @@ "PageAction_InputMenu_selectedText": { "message": "选中的文本" }, + "PageAction_InputMenu_prompt": { + "message": "提示词" + }, "PageAction_InputMenu_url": { "message": "原始URL" }, diff --git a/packages/extension/src/action/pageAction.test.ts b/packages/extension/src/action/pageAction.test.ts new file mode 100644 index 000000000..f401ad049 --- /dev/null +++ b/packages/extension/src/action/pageAction.test.ts @@ -0,0 +1,173 @@ +import { describe, it, expect, vi, beforeEach } from "vitest" +import { OPEN_MODE, PAGE_ACTION_OPEN_MODE, PAGE_ACTION_EVENT } from "@/const" +import { PageAction } from "./pageAction" +import { Ipc, BgCommand } from "@/services/ipc" + +vi.mock("@/services/ipc", () => ({ + Ipc: { + send: vi.fn(), + }, + BgCommand: { + openAndRunPageAction: "openAndRunPageAction", + }, +})) + +vi.mock("@/services/screen", () => ({ + getWindowPosition: vi.fn().mockResolvedValue({ top: 100, left: 100 }), +})) + +describe("PageAction.execute", () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + const baseCommand = { + id: "pa-cmd-1", + title: "Test Page Action", + iconUrl: "https://example.com/icon.png", + openMode: OPEN_MODE.PAGE_ACTION, + pageActionOption: { + startUrl: "https://ai.example.com", + openMode: PAGE_ACTION_OPEN_MODE.POPUP, + steps: [ + { + id: "step-1", + param: { + type: PAGE_ACTION_EVENT.input, + label: "Input prompt", + selector: "#input", + value: "{{Prompt}}", + }, + }, + ], + prompt: "Summarize: {{SelectedText}}", + }, + } + + it("PA-01: sends prompt in openAndRunPageAction payload", async () => { + await PageAction.execute({ + command: baseCommand as any, + selectionText: "Selected content", + position: { x: 50, y: 50 }, + }) + + expect(Ipc.send).toHaveBeenCalledWith( + BgCommand.openAndRunPageAction, + expect.objectContaining({ + commandId: "pa-cmd-1", + selectedText: "Selected content", + prompt: "Summarize: {{SelectedText}}", + url: expect.objectContaining({ + searchUrl: "https://ai.example.com", + selectionText: "Selected content", + useClipboard: false, + }), + }), + ) + }) + + it("PA-02: detects indirect clipboard usage when step has {{Prompt}} and prompt has {{Clipboard}}", async () => { + const commandWithClipboardPrompt = { + ...baseCommand, + pageActionOption: { + ...baseCommand.pageActionOption, + prompt: "Translate clipboard: {{Clipboard}}", + }, + } + + await PageAction.execute({ + command: commandWithClipboardPrompt as any, + selectionText: "", + position: { x: 50, y: 50 }, + }) + + expect(Ipc.send).toHaveBeenCalledWith( + BgCommand.openAndRunPageAction, + expect.objectContaining({ + url: expect.objectContaining({ + useClipboard: true, + }), + }), + ) + }) + + it("PA-03: does not request clipboard if prompt has {{Clipboard}} but no step uses {{Prompt}}", async () => { + const commandWithoutPromptStep = { + ...baseCommand, + pageActionOption: { + ...baseCommand.pageActionOption, + steps: [ + { + id: "step-1", + param: { + type: PAGE_ACTION_EVENT.input, + label: "Input", + selector: "#input", + value: "{{SelectedText}}", + }, + }, + ], + prompt: "Translate clipboard: {{Clipboard}}", + }, + } + + await PageAction.execute({ + command: commandWithoutPromptStep as any, + selectionText: "hello", + position: { x: 50, y: 50 }, + }) + + expect(Ipc.send).toHaveBeenCalledWith( + BgCommand.openAndRunPageAction, + expect.objectContaining({ + url: expect.objectContaining({ + useClipboard: false, + }), + }), + ) + }) + + it("PA-04: works with legacy Page Action commands without prompt field", async () => { + const legacyCommand = { + id: "legacy-cmd", + title: "Legacy Page Action", + iconUrl: "https://example.com/icon.png", + openMode: OPEN_MODE.PAGE_ACTION, + pageActionOption: { + startUrl: "https://example.com", + openMode: PAGE_ACTION_OPEN_MODE.TAB, + steps: [ + { + id: "step-1", + param: { + type: PAGE_ACTION_EVENT.input, + label: "Direct input", + selector: "#input", + value: "{{SelectedText}}", + }, + }, + ], + }, + } + + await PageAction.execute({ + command: legacyCommand as any, + selectionText: "sample", + position: { x: 10, y: 10 }, + }) + + expect(Ipc.send).toHaveBeenCalledWith( + BgCommand.openAndRunPageAction, + expect.objectContaining({ + commandId: "legacy-cmd", + prompt: undefined, + url: expect.objectContaining({ + searchUrl: "https://example.com", + selectionText: "sample", + useClipboard: false, + }), + }), + ) + }) +}) + diff --git a/packages/extension/src/action/pageAction.ts b/packages/extension/src/action/pageAction.ts index 9c92ec152..f6f043ed5 100644 --- a/packages/extension/src/action/pageAction.ts +++ b/packages/extension/src/action/pageAction.ts @@ -35,13 +35,26 @@ export const PageAction = { return } - // Checks if any step requires clipboard data - const needClipboard = command.pageActionOption.steps.some((step) => { - return ( + // Checks if any step directly or indirectly requires clipboard data + const hasPromptStep = command.pageActionOption.steps.some( + (step) => step.param.type === PAGE_ACTION_EVENT.input && - step.param.value.includes(toInsertTemplate(INSERT.CLIPBOARD)) - ) - }) + step.param.value.includes(toInsertTemplate(INSERT.PROMPT)), + ) + const promptNeedsClipboard = + hasPromptStep && + (command.pageActionOption.prompt?.includes( + toInsertTemplate(INSERT.CLIPBOARD), + ) ?? false) + + const needClipboard = + promptNeedsClipboard || + command.pageActionOption.steps.some((step) => { + return ( + step.param.type === PAGE_ACTION_EVENT.input && + step.param.value.includes(toInsertTemplate(INSERT.CLIPBOARD)) + ) + }) const url: UrlParam = { searchUrl: command.pageActionOption.startUrl, @@ -73,6 +86,7 @@ export const PageAction = { srcUrl: pageUrl ?? "", openMode, userVariables: userVariables ?? command.pageActionOption.userVariables, + prompt: command.pageActionOption.prompt, }) }, } diff --git a/packages/extension/src/components/option/editor/CommandEditDialog.tsx b/packages/extension/src/components/option/editor/CommandEditDialog.tsx index 52d0d8b91..4f956220a 100644 --- a/packages/extension/src/components/option/editor/CommandEditDialog.tsx +++ b/packages/extension/src/components/option/editor/CommandEditDialog.tsx @@ -194,6 +194,7 @@ const getDefault = ( pageUrl: "", openMode: PAGE_ACTION_OPEN_MODE.POPUP, steps: [], + prompt: "", }, } } @@ -412,6 +413,7 @@ const CommandEditDialogInner = ({ openMode: getValues("pageActionOption.openMode"), size: getValues("popupOption") ?? POPUP_OPTION, steps: getValues("pageActionOption.steps"), + prompt: getValues("pageActionOption.prompt"), }, ) await Ipc.send(BgCommand.startPageActionRecorder, { diff --git a/packages/extension/src/components/option/editor/PageActionSection.tsx b/packages/extension/src/components/option/editor/PageActionSection.tsx index a8ce3148d..c145c819a 100644 --- a/packages/extension/src/components/option/editor/PageActionSection.tsx +++ b/packages/extension/src/components/option/editor/PageActionSection.tsx @@ -1,8 +1,20 @@ import { useState, useEffect } from "react" import { useFieldArray } from "react-hook-form" import { Disc3 } from "lucide-react" -import { FormLabel, FormDescription } from "@/components/ui/form" +import { + FormControl, + FormField, + FormItem, + FormLabel, + FormDescription, + FormMessage, +} from "@/components/ui/form" +import { Textarea } from "@/components/ui/textarea" import type { PageAction } from "@/services/pageAction" +import { + convSymbolsToReadableKeys, + convReadableKeysToSymbols, +} from "@/services/pageAction" import { t as _t } from "@/services/i18n" const t = (key: string, p?: string[]) => _t(`Option_${key}`, p) import { cn, isEmpty, capitalize } from "@/lib/utils" @@ -15,6 +27,7 @@ import { PAGE_ACTION_OPEN_MODE } from "@/const" import { TEST_IDS } from "@/testIds" import { InputField } from "@/components/option/field/InputField" import { OpenModeToggleField } from "@/components/option/field/OpenModeToggleField" +import { InputMenu } from "@/components/pageAction/InputPopup" import { StepList } from "@/components/pageAction/StepList" import { InputEditor } from "@/components/pageAction/InputEditor" import { RemoveDialog } from "@/components/option/RemoveDialog" @@ -30,6 +43,8 @@ export const PageActionSection = ({ form, openRecorder, }: PageActionSectionProps) => { + const [promptTextarea, setPromptTextarea] = + useState(null) const { register, getValues, watch, setValue } = form const openMode = watch("pageActionOption.openMode") @@ -147,6 +162,47 @@ export const PageActionSection = ({ type="pageAction" /> + ( + +
+ {t("pageAction_prompt")} + {t("pageAction_prompt_desc")} + + {t("pageAction_prompt_desc_2")} + +
+
+ + +