v0.4.8-dev New Release #3

Merged
peisongxiao merged 8 commits from v0.4.8-dev into master 2026-01-20 05:41:07 +00:00
4 changed files with 134 additions and 14 deletions
Showing only changes of commit 372afd5ed2 - Show all commits

View File

@@ -1,7 +1,7 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "SiteCompanion", "name": "SiteCompanion",
"version": "0.4.7", "version": "0.4.8",
"description": "AI companion for site-bound text extraction and tasks.", "description": "AI companion for site-bound text extraction and tasks.",
"permissions": ["storage", "activeTab"], "permissions": ["storage", "activeTab"],
"host_permissions": ["<all_urls>"], "host_permissions": ["<all_urls>"],

View File

@@ -157,6 +157,41 @@ select {
min-width: 0; 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 { .hidden {
display: none !important; display: none !important;
} }

View File

@@ -65,15 +65,28 @@
<select id="profileSelect"></select> <select id="profileSelect"></select>
</div> </div>
</div> </div>
<div class="task-row"> <div class="task-row" id="normalTaskRow">
<div class="field inline-field task-field"> <div class="field inline-field task-field">
<label for="taskSelect">Task</label> <label for="taskSelect">Task</label>
<select id="taskSelect"></select> <select id="taskSelect"></select>
</div> </div>
<button id="customTaskBtn" class="ghost">Custom</button>
<div id="taskActions">
<button id="runBtn" class="accent">Run</button> <button id="runBtn" class="accent">Run</button>
<button id="abortBtn" class="ghost stop-btn hidden" disabled>Stop</button> <button id="abortBtn" class="ghost stop-btn hidden" disabled>Stop</button>
</div> </div>
</div> </div>
<div class="task-row custom-task-row hidden" id="customTaskRow">
<div class="field custom-task-field">
<label for="customTaskInput">Custom task</label>
<textarea id="customTaskInput" rows="2" placeholder="Enter temporary custom task..."></textarea>
</div>
<div class="custom-task-actions">
<button id="normalTaskBtn" class="ghost">Normal</button>
<div id="taskActionsSlot"></div>
</div>
</div>
</div>
</div> </div>
<div class="meta"> <div class="meta">
<span id="postingCount">Site Text: 0 chars</span> <span id="postingCount">Site Text: 0 chars</span>

View File

@@ -3,6 +3,13 @@ const abortBtn = document.getElementById("abortBtn");
const taskSelect = document.getElementById("taskSelect"); const taskSelect = document.getElementById("taskSelect");
const envSelect = document.getElementById("envSelect"); const envSelect = document.getElementById("envSelect");
const profileSelect = document.getElementById("profileSelect"); 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 outputEl = document.getElementById("output");
const statusEl = document.getElementById("status"); const statusEl = document.getElementById("status");
const postingCountEl = document.getElementById("postingCount"); const postingCountEl = document.getElementById("postingCount");
@@ -20,6 +27,8 @@ const LAST_TASK_KEY = "lastSelectedTaskId";
const LAST_ENV_KEY = "lastSelectedEnvId"; const LAST_ENV_KEY = "lastSelectedEnvId";
const LAST_PROFILE_KEY = "lastSelectedProfileId"; const LAST_PROFILE_KEY = "lastSelectedProfileId";
const POPUP_DRAFT_KEY = "popupDraft"; const POPUP_DRAFT_KEY = "popupDraft";
const CUSTOM_TASK_MODE_KEY = "customTaskMode";
const CUSTOM_TASK_TEXT_KEY = "customTaskText";
const unknownSiteState = document.getElementById("unknownSiteState"); const unknownSiteState = document.getElementById("unknownSiteState");
const extractionReviewState = document.getElementById("extractionReviewState"); const extractionReviewState = document.getElementById("extractionReviewState");
@@ -59,7 +68,9 @@ const state = {
selectedProfileId: "", selectedProfileId: "",
alwaysShowOutput: false, alwaysShowOutput: false,
activeTabId: null, activeTabId: null,
pendingConfigRefresh: false pendingConfigRefresh: false,
customTaskMode: false,
customTaskText: ""
}; };
async function switchState(stateName) { async function switchState(stateName) {
@@ -693,6 +704,39 @@ function updateOutputVisibility() {
outputSection.classList.toggle("hidden", shouldHide); 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() { function getSelectedTask() {
if (state.forcedTask) return state.forcedTask; if (state.forcedTask) return state.forcedTask;
const selectedId = taskSelect?.value || state.selectedTaskId; const selectedId = taskSelect?.value || state.selectedTaskId;
@@ -718,9 +762,12 @@ function buildTotalPromptText() {
const profile = getSelectedProfile(); const profile = getSelectedProfile();
const env = getSelectedEnv(); const env = getSelectedEnv();
const systemPrompt = env?.systemPrompt || ""; const systemPrompt = env?.systemPrompt || "";
const customText = (state.customTaskText || "").trim();
const taskText =
state.customTaskMode && !state.forcedTask ? customText : task?.text || "";
const userPrompt = buildUserMessage( const userPrompt = buildUserMessage(
profile?.text || "", profile?.text || "",
task?.text || "", taskText,
state.siteText || "" state.siteText || ""
); );
return systemPrompt ? `${systemPrompt}\n\n${userPrompt}` : userPrompt; return systemPrompt ? `${systemPrompt}\n\n${userPrompt}` : userPrompt;
@@ -1020,7 +1067,9 @@ async function loadConfig() {
"alwaysShowOutput", "alwaysShowOutput",
LAST_TASK_KEY, LAST_TASK_KEY,
LAST_ENV_KEY, LAST_ENV_KEY,
LAST_PROFILE_KEY LAST_PROFILE_KEY,
CUSTOM_TASK_MODE_KEY,
CUSTOM_TASK_TEXT_KEY
]); ]);
const tasks = normalizeConfigList(stored.tasks); const tasks = normalizeConfigList(stored.tasks);
const envs = normalizeConfigList(stored.envConfigs); const envs = normalizeConfigList(stored.envConfigs);
@@ -1069,6 +1118,8 @@ async function loadConfig() {
state.alwaysShowOutput = Boolean(stored.alwaysShowOutput); state.alwaysShowOutput = Boolean(stored.alwaysShowOutput);
applyTheme(resolveThemeForPopup(state.globalTheme)); applyTheme(resolveThemeForPopup(state.globalTheme));
updateOutputVisibility(); updateOutputVisibility();
state.customTaskMode = Boolean(stored[CUSTOM_TASK_MODE_KEY]);
state.customTaskText = stored[CUSTOM_TASK_TEXT_KEY] || "";
const effectiveEnvs = resolveEffectiveList( const effectiveEnvs = resolveEffectiveList(
envs, envs,
@@ -1095,6 +1146,10 @@ async function loadConfig() {
renderTasks(effectiveTasks); renderTasks(effectiveTasks);
renderEnvironments(effectiveEnvs); renderEnvironments(effectiveEnvs);
renderProfiles(effectiveProfiles); renderProfiles(effectiveProfiles);
if (customTaskInput) {
customTaskInput.value = state.customTaskText;
}
setCustomTaskMode(state.customTaskMode, { persist: false });
if (!effectiveTasks.length) { if (!effectiveTasks.length) {
state.selectedTaskId = ""; state.selectedTaskId = "";
@@ -1193,10 +1248,11 @@ async function handleAnalyze() {
const taskId = taskSelect.value; const taskId = taskSelect.value;
const forcedTask = state.forcedTask; const forcedTask = state.forcedTask;
const task = forcedTask || state.tasks.find((item) => item.id === taskId); const task = forcedTask || state.tasks.find((item) => item.id === taskId);
const useCustomTask = state.customTaskMode && !forcedTask;
if (forcedTask) { if (forcedTask) {
state.forcedTask = null; state.forcedTask = null;
} }
if (!task) { if (!useCustomTask && !task) {
setStatus("Select a task."); setStatus("Select a task.");
return; return;
} }
@@ -1238,6 +1294,12 @@ async function handleAnalyze() {
} }
const resolvedSystemPrompt = const resolvedSystemPrompt =
activeEnv.systemPrompt ?? systemPrompt ?? ""; 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 = const resolvedApiConfigId =
activeEnv.apiConfigId || activeApiConfigId || resolvedConfigs[0]?.id || ""; activeEnv.apiConfigId || activeApiConfigId || resolvedConfigs[0]?.id || "";
const activeConfig = const activeConfig =
@@ -1293,11 +1355,6 @@ async function handleAnalyze() {
} }
} }
const promptText = buildUserMessage(
profileText,
task.text || "",
state.siteText
);
updatePromptCount(); updatePromptCount();
state.outputRaw = ""; state.outputRaw = "";
@@ -1319,7 +1376,7 @@ async function handleAnalyze() {
model: resolvedModel, model: resolvedModel,
systemPrompt: resolvedSystemPrompt, systemPrompt: resolvedSystemPrompt,
profileText, profileText,
taskText: task.text || "", taskText: resolvedTaskText,
siteText: state.siteText, siteText: state.siteText,
tabId: tab.id tabId: tab.id
} }
@@ -1533,6 +1590,20 @@ confirmSiteBtn.addEventListener("click", async () => {
setStatus("Site saved."); 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); runBtn.addEventListener("click", handleExtractAndAnalyze);
abortBtn.addEventListener("click", handleAbort); abortBtn.addEventListener("click", handleAbort);
settingsBtn.addEventListener("click", () => chrome.runtime.openOptionsPage()); settingsBtn.addEventListener("click", () => chrome.runtime.openOptionsPage());
@@ -1576,6 +1647,7 @@ async function loadShortcutRunRequest() {
state.shortcutRunPending = true; state.shortcutRunPending = true;
await chrome.storage.local.remove(SHORTCUT_RUN_KEY); await chrome.storage.local.remove(SHORTCUT_RUN_KEY);
setCustomTaskMode(false);
if (!state.tasks.length) { if (!state.tasks.length) {
await loadConfig(); await loadConfig();