From 372afd5ed2ad0b29c46da4ba8d281bb24b499518 Mon Sep 17 00:00:00 2001 From: Peisong Xiao Date: Mon, 19 Jan 2026 19:40:49 -0500 Subject: [PATCH] added temporary prompt mode --- sitecompanion/manifest.json | 2 +- sitecompanion/popup.css | 35 ++++++++++++++ sitecompanion/popup.html | 19 ++++++-- sitecompanion/popup.js | 92 +++++++++++++++++++++++++++++++++---- 4 files changed, 134 insertions(+), 14 deletions(-) diff --git a/sitecompanion/manifest.json b/sitecompanion/manifest.json index 632cb92..e562857 100644 --- a/sitecompanion/manifest.json +++ b/sitecompanion/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "SiteCompanion", - "version": "0.4.7", + "version": "0.4.8", "description": "AI companion for site-bound text extraction and tasks.", "permissions": ["storage", "activeTab"], "host_permissions": [""], diff --git a/sitecompanion/popup.css b/sitecompanion/popup.css index f35dba3..aaa89b8 100644 --- a/sitecompanion/popup.css +++ b/sitecompanion/popup.css @@ -157,6 +157,41 @@ select { min-width: 0; } +.custom-task-row { + align-items: stretch; +} + +.custom-task-field { + flex: 1; + margin: 0; +} + +.custom-task-field textarea { + width: 100%; + padding: 8px; + border-radius: 10px; + border: 1px solid var(--border); + background: var(--input-bg); + color: var(--input-fg); + font-size: 12px; + resize: vertical; + min-height: 52px; +} + +.custom-task-actions { + display: flex; + flex-direction: column; + gap: 6px; +} + +.custom-task-actions button { + width: 100%; +} + +body.custom-task-mode .output { + max-height: 210px; +} + .hidden { display: none !important; } diff --git a/sitecompanion/popup.html b/sitecompanion/popup.html index 3518f7c..624ab0c 100644 --- a/sitecompanion/popup.html +++ b/sitecompanion/popup.html @@ -65,13 +65,26 @@ -
+
- - + +
+ + +
+
+
diff --git a/sitecompanion/popup.js b/sitecompanion/popup.js index 58ac7f5..a560497 100644 --- a/sitecompanion/popup.js +++ b/sitecompanion/popup.js @@ -3,6 +3,13 @@ const abortBtn = document.getElementById("abortBtn"); const taskSelect = document.getElementById("taskSelect"); const envSelect = document.getElementById("envSelect"); const profileSelect = document.getElementById("profileSelect"); +const customTaskBtn = document.getElementById("customTaskBtn"); +const normalTaskBtn = document.getElementById("normalTaskBtn"); +const customTaskInput = document.getElementById("customTaskInput"); +const normalTaskRow = document.getElementById("normalTaskRow"); +const customTaskRow = document.getElementById("customTaskRow"); +const taskActions = document.getElementById("taskActions"); +const taskActionsSlot = document.getElementById("taskActionsSlot"); const outputEl = document.getElementById("output"); const statusEl = document.getElementById("status"); const postingCountEl = document.getElementById("postingCount"); @@ -20,6 +27,8 @@ const LAST_TASK_KEY = "lastSelectedTaskId"; const LAST_ENV_KEY = "lastSelectedEnvId"; const LAST_PROFILE_KEY = "lastSelectedProfileId"; const POPUP_DRAFT_KEY = "popupDraft"; +const CUSTOM_TASK_MODE_KEY = "customTaskMode"; +const CUSTOM_TASK_TEXT_KEY = "customTaskText"; const unknownSiteState = document.getElementById("unknownSiteState"); const extractionReviewState = document.getElementById("extractionReviewState"); @@ -59,7 +68,9 @@ const state = { selectedProfileId: "", alwaysShowOutput: false, activeTabId: null, - pendingConfigRefresh: false + pendingConfigRefresh: false, + customTaskMode: false, + customTaskText: "" }; async function switchState(stateName) { @@ -693,6 +704,39 @@ function updateOutputVisibility() { outputSection.classList.toggle("hidden", shouldHide); } +async function persistCustomTaskState() { + await chrome.storage.local.set({ + [CUSTOM_TASK_MODE_KEY]: state.customTaskMode, + [CUSTOM_TASK_TEXT_KEY]: state.customTaskText + }); +} + +function setCustomTaskMode(enabled, { persist = true } = {}) { + state.customTaskMode = Boolean(enabled); + document.body.classList.toggle("custom-task-mode", state.customTaskMode); + if (state.customTaskMode) { + normalTaskRow?.classList.add("hidden"); + customTaskRow?.classList.remove("hidden"); + if (taskActionsSlot && taskActions) { + taskActionsSlot.appendChild(taskActions); + } + if (customTaskInput) { + customTaskInput.value = state.customTaskText || ""; + customTaskInput.focus(); + } + } else { + customTaskRow?.classList.add("hidden"); + normalTaskRow?.classList.remove("hidden"); + if (normalTaskRow && taskActions) { + normalTaskRow.appendChild(taskActions); + } + } + updatePromptCount(); + if (persist) { + void persistCustomTaskState(); + } +} + function getSelectedTask() { if (state.forcedTask) return state.forcedTask; const selectedId = taskSelect?.value || state.selectedTaskId; @@ -718,9 +762,12 @@ function buildTotalPromptText() { const profile = getSelectedProfile(); const env = getSelectedEnv(); const systemPrompt = env?.systemPrompt || ""; + const customText = (state.customTaskText || "").trim(); + const taskText = + state.customTaskMode && !state.forcedTask ? customText : task?.text || ""; const userPrompt = buildUserMessage( profile?.text || "", - task?.text || "", + taskText, state.siteText || "" ); return systemPrompt ? `${systemPrompt}\n\n${userPrompt}` : userPrompt; @@ -1020,7 +1067,9 @@ async function loadConfig() { "alwaysShowOutput", LAST_TASK_KEY, LAST_ENV_KEY, - LAST_PROFILE_KEY + LAST_PROFILE_KEY, + CUSTOM_TASK_MODE_KEY, + CUSTOM_TASK_TEXT_KEY ]); const tasks = normalizeConfigList(stored.tasks); const envs = normalizeConfigList(stored.envConfigs); @@ -1069,6 +1118,8 @@ async function loadConfig() { state.alwaysShowOutput = Boolean(stored.alwaysShowOutput); applyTheme(resolveThemeForPopup(state.globalTheme)); updateOutputVisibility(); + state.customTaskMode = Boolean(stored[CUSTOM_TASK_MODE_KEY]); + state.customTaskText = stored[CUSTOM_TASK_TEXT_KEY] || ""; const effectiveEnvs = resolveEffectiveList( envs, @@ -1095,6 +1146,10 @@ async function loadConfig() { renderTasks(effectiveTasks); renderEnvironments(effectiveEnvs); renderProfiles(effectiveProfiles); + if (customTaskInput) { + customTaskInput.value = state.customTaskText; + } + setCustomTaskMode(state.customTaskMode, { persist: false }); if (!effectiveTasks.length) { state.selectedTaskId = ""; @@ -1193,10 +1248,11 @@ async function handleAnalyze() { const taskId = taskSelect.value; const forcedTask = state.forcedTask; const task = forcedTask || state.tasks.find((item) => item.id === taskId); + const useCustomTask = state.customTaskMode && !forcedTask; if (forcedTask) { state.forcedTask = null; } - if (!task) { + if (!useCustomTask && !task) { setStatus("Select a task."); return; } @@ -1238,6 +1294,12 @@ async function handleAnalyze() { } const resolvedSystemPrompt = activeEnv.systemPrompt ?? systemPrompt ?? ""; + const customTaskText = (state.customTaskText || "").trim(); + const resolvedTaskText = useCustomTask ? customTaskText : task?.text || ""; + if (useCustomTask && !resolvedTaskText) { + setStatus("Enter a custom task."); + return; + } const resolvedApiConfigId = activeEnv.apiConfigId || activeApiConfigId || resolvedConfigs[0]?.id || ""; const activeConfig = @@ -1293,11 +1355,6 @@ async function handleAnalyze() { } } - const promptText = buildUserMessage( - profileText, - task.text || "", - state.siteText - ); updatePromptCount(); state.outputRaw = ""; @@ -1319,7 +1376,7 @@ async function handleAnalyze() { model: resolvedModel, systemPrompt: resolvedSystemPrompt, profileText, - taskText: task.text || "", + taskText: resolvedTaskText, siteText: state.siteText, tabId: tab.id } @@ -1533,6 +1590,20 @@ confirmSiteBtn.addEventListener("click", async () => { setStatus("Site saved."); }); +customTaskBtn?.addEventListener("click", () => { + setCustomTaskMode(true); +}); + +normalTaskBtn?.addEventListener("click", () => { + setCustomTaskMode(false); +}); + +customTaskInput?.addEventListener("input", () => { + state.customTaskText = customTaskInput.value || ""; + updatePromptCount(); + void persistCustomTaskState(); +}); + runBtn.addEventListener("click", handleExtractAndAnalyze); abortBtn.addEventListener("click", handleAbort); settingsBtn.addEventListener("click", () => chrome.runtime.openOptionsPage()); @@ -1576,6 +1647,7 @@ async function loadShortcutRunRequest() { state.shortcutRunPending = true; await chrome.storage.local.remove(SHORTCUT_RUN_KEY); + setCustomTaskMode(false); if (!state.tasks.length) { await loadConfig();