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
3 changed files with 148 additions and 10 deletions
Showing only changes of commit d3f7b63a79 - Show all commits

View File

@@ -596,6 +596,7 @@ async function refreshToolbar() {
let refreshTimer = null;
let contentChangeTimer = null;
function scheduleToolbarRefresh() {
if (refreshTimer) return;
refreshTimer = window.setTimeout(() => {
@@ -610,9 +611,22 @@ function scheduleToolbarRefresh() {
}, 200);
}
function scheduleContentChangeNotice() {
if (contentChangeTimer) return;
contentChangeTimer = window.setTimeout(() => {
contentChangeTimer = null;
chrome.runtime.sendMessage({ type: "SITE_CONTENT_CHANGED" }, () => {
if (chrome.runtime.lastError) {
return;
}
});
}, 250);
}
const observer = new MutationObserver(() => {
if (suppressObserver) return;
scheduleToolbarRefresh();
scheduleContentChangeNotice();
});
observer.observe(document.documentElement, { childList: true, subtree: true });

View File

@@ -77,7 +77,7 @@
</div>
<div class="meta">
<span id="postingCount">Site Text: 0 chars</span>
<span id="promptCount">Task: 0 chars</span>
<span id="promptCount">Total: 0 chars</span>
<span id="status" class="status">Idle</span>
</div>
</section>

View File

@@ -57,7 +57,9 @@ const state = {
selectedTaskId: "",
selectedEnvId: "",
selectedProfileId: "",
alwaysShowOutput: false
alwaysShowOutput: false,
activeTabId: null,
pendingConfigRefresh: false
};
async function switchState(stateName) {
@@ -118,6 +120,7 @@ function applyPopupDraft(draft) {
} else if (typeof draft.siteTextSelector === "string") {
state.siteTextTarget = { kind: "css", selector: draft.siteTextSelector };
}
updateCounts();
}
function matchUrl(url, pattern) {
@@ -677,6 +680,10 @@ function setAnalyzing(isAnalyzing) {
updateTaskSelectState();
updateEnvSelectState();
updateProfileSelectState();
if (!isAnalyzing && state.pendingConfigRefresh) {
state.pendingConfigRefresh = false;
scheduleConfigRefresh();
}
}
function updateOutputVisibility() {
@@ -686,12 +693,93 @@ function updateOutputVisibility() {
outputSection.classList.toggle("hidden", shouldHide);
}
function getSelectedTask() {
if (state.forcedTask) return state.forcedTask;
const selectedId = taskSelect?.value || state.selectedTaskId;
return state.tasks.find((item) => item.id === selectedId) || state.tasks[0] || null;
}
function getSelectedProfile() {
const selectedId = profileSelect?.value || state.selectedProfileId;
return (
state.profiles.find((item) => item.id === selectedId) ||
state.profiles[0] ||
null
);
}
function getSelectedEnv() {
const selectedId = envSelect?.value || state.selectedEnvId;
return state.envs.find((item) => item.id === selectedId) || state.envs[0] || null;
}
function buildTotalPromptText() {
const task = getSelectedTask();
const profile = getSelectedProfile();
const env = getSelectedEnv();
const systemPrompt = env?.systemPrompt || "";
const userPrompt = buildUserMessage(
profile?.text || "",
task?.text || "",
state.siteText || ""
);
return systemPrompt ? `${systemPrompt}\n\n${userPrompt}` : userPrompt;
}
function updateSiteTextCount() {
postingCountEl.textContent = `Site Text: ${state.siteText.length} chars`;
const length = (state.siteText || "").length;
postingCountEl.textContent = `Site Text: ${length} chars`;
}
function updatePromptCount(count) {
promptCountEl.textContent = `Task: ${count} chars`;
const total =
typeof count === "number" ? count : buildTotalPromptText().length;
promptCountEl.textContent = `Total: ${total} chars`;
}
function updateCounts() {
updateSiteTextCount();
updatePromptCount();
}
let siteContentRefreshTimer = null;
function scheduleSiteContentRefresh() {
if (siteContentRefreshTimer) return;
siteContentRefreshTimer = window.setTimeout(() => {
siteContentRefreshTimer = null;
void refreshSiteContentCounts();
}, 250);
}
let configRefreshTimer = null;
function scheduleConfigRefresh() {
if (state.isAnalyzing) {
state.pendingConfigRefresh = true;
return;
}
if (configRefreshTimer) return;
configRefreshTimer = window.setTimeout(() => {
configRefreshTimer = null;
void loadConfig();
}, 250);
}
async function refreshSiteContentCounts() {
if (state.isAnalyzing) return;
if (state.currentPopupState !== "normal") return;
if (!state.siteTextTarget) return;
try {
const response = await sendToActiveTab({
type: "EXTRACT_BY_SELECTOR",
target: state.siteTextTarget
});
if (!response?.ok) return;
state.siteText = response.extracted || "";
state.siteTextTarget = response.target || state.siteTextTarget;
updateCounts();
} catch {
// Ignore refresh failures; counts will update on next explicit extract.
}
}
function renderTasks(tasks) {
@@ -792,6 +880,7 @@ function setEnvironmentSelection(envId) {
envSelect.value = target;
}
state.selectedEnvId = target;
updatePromptCount();
}
function setProfileSelection(profileId) {
@@ -803,6 +892,7 @@ function setProfileSelection(profileId) {
profileSelect.value = target;
}
state.selectedProfileId = target;
updatePromptCount();
}
function selectTask(taskId, { resetEnv } = { resetEnv: false }) {
@@ -814,6 +904,7 @@ function selectTask(taskId, { resetEnv } = { resetEnv: false }) {
setEnvironmentSelection(getTaskDefaultEnvId(task));
setProfileSelection(getTaskDefaultProfileId(task));
}
updatePromptCount();
}
async function persistSelections() {
@@ -900,6 +991,7 @@ function ensurePort() {
async function loadConfig() {
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
const currentUrl = tabs[0]?.url || "";
state.activeTabId = tabs[0]?.id || null;
const { lastPopupState, [POPUP_DRAFT_KEY]: popupDraft } = await getStorage([
"lastPopupState",
@@ -968,6 +1060,9 @@ async function loadConfig() {
state.currentWorkspace = activeWorkspace;
currentWorkspaceName.textContent = activeWorkspace.name || "Global";
}
if (state.currentSite && !state.siteTextTarget) {
state.siteTextTarget = normalizeStoredExtractTarget(state.currentSite);
}
if (stored.theme) {
state.globalTheme = stored.theme;
}
@@ -1005,6 +1100,7 @@ async function loadConfig() {
state.selectedTaskId = "";
setEnvironmentSelection(effectiveEnvs[0]?.id || "");
setProfileSelection(effectiveProfiles[0]?.id || "");
updateCounts();
return;
}
@@ -1040,6 +1136,10 @@ async function loadConfig() {
await persistSelections();
}
updateCounts();
if (state.currentSite) {
await refreshSiteContentCounts();
}
maybeRunDefaultTask();
}
@@ -1068,8 +1168,7 @@ async function handleExtract() {
state.siteText = response.extracted || "";
state.siteTextTarget = response.target || target;
updateSiteTextCount();
updatePromptCount(0);
updateCounts();
setStatus("Text extracted.");
return true;
} catch (error) {
@@ -1199,7 +1298,7 @@ async function handleAnalyze() {
task.text || "",
state.siteText
);
updatePromptCount(promptText.length);
updatePromptCount();
state.outputRaw = "";
renderOutput();
@@ -1298,6 +1397,7 @@ async function runMinimalExtraction(text, minLength = 5) {
state.siteText = response.extracted;
state.siteTextTarget = response.target || { kind: "textScope", text: trimmed };
extractedPreview.textContent = state.siteText;
updateCounts();
await fillSiteDefaultsFromTab();
switchState("review");
await persistPopupDraft();
@@ -1336,6 +1436,7 @@ extractFullBtn.addEventListener("click", async () => {
state.siteText = response.extracted;
state.siteTextTarget = target;
extractedPreview.textContent = state.siteText;
updateCounts();
await fillSiteDefaultsFromTab();
switchState("review");
await persistPopupDraft();
@@ -1372,6 +1473,7 @@ retryExtractBtn.addEventListener("click", () => {
if (workspaceSelect) workspaceSelect.value = "global";
state.siteText = "";
state.siteTextTarget = null;
updateCounts();
setMinimalStatus("");
void clearPopupDraft();
setStatus("Ready.");
@@ -1427,7 +1529,7 @@ confirmSiteBtn.addEventListener("click", async () => {
currentWorkspaceName.textContent = state.currentWorkspace.name || "Global";
await loadConfig();
await switchState("normal");
updateSiteTextCount();
updateCounts();
setStatus("Site saved.");
});
@@ -1450,8 +1552,7 @@ profileSelect.addEventListener("change", () => {
void persistSelections();
});
updateSiteTextCount();
updatePromptCount(0);
updateCounts();
renderOutput();
setAnalyzing(false);
void loadTheme();
@@ -1597,4 +1698,27 @@ chrome.storage.onChanged.addListener((changes) => {
state.alwaysShowOutput = Boolean(changes.alwaysShowOutput.newValue);
updateOutputVisibility();
}
const configKeys = [
"tasks",
"envConfigs",
"profiles",
"shortcuts",
"workspaces",
"sites",
"theme",
"alwaysShowOutput"
];
if (configKeys.some((key) => changes[key])) {
scheduleConfigRefresh();
}
});
chrome.runtime.onMessage.addListener((message, sender) => {
if (message?.type !== "SITE_CONTENT_CHANGED") return;
const senderTabId = sender?.tab?.id || null;
if (state.activeTabId && senderTabId && senderTabId !== state.activeTabId) {
return;
}
scheduleSiteContentRefresh();
});