added temporary prompt mode
This commit is contained in:
@@ -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>"],
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,13 +65,26 @@
|
|||||||
<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="runBtn" class="accent">Run</button>
|
<button id="customTaskBtn" class="ghost">Custom</button>
|
||||||
<button id="abortBtn" class="ghost stop-btn hidden" disabled>Stop</button>
|
<div id="taskActions">
|
||||||
|
<button id="runBtn" class="accent">Run</button>
|
||||||
|
<button id="abortBtn" class="ghost stop-btn hidden" disabled>Stop</button>
|
||||||
|
</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>
|
</div>
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user