@wenathlan/extension 1.1.64 → 1.1.66
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/attentionfeed.d.ts +74 -0
- package/dist/attentionfeed.d.ts.map +1 -0
- package/dist/backgroundruns.d.ts +71 -0
- package/dist/backgroundruns.d.ts.map +1 -0
- package/dist/datagrid.d.ts +46 -0
- package/dist/datagrid.d.ts.map +1 -0
- package/dist/evidenceviews.d.ts +45 -0
- package/dist/evidenceviews.d.ts.map +1 -0
- package/dist/flowlibrary.d.ts +147 -0
- package/dist/flowlibrary.d.ts.map +1 -0
- package/dist/index.d.ts +15 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1437 -3
- package/dist/index.js.map +4 -4
- package/dist/memory.d.ts +104 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/outputcompare.d.ts +68 -0
- package/dist/outputcompare.d.ts.map +1 -0
- package/dist/pickerviews.d.ts +72 -0
- package/dist/pickerviews.d.ts.map +1 -0
- package/dist/policy.d.ts +122 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/portability.d.ts +35 -0
- package/dist/portability.d.ts.map +1 -0
- package/dist/protocol.d.ts +281 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/quickactions.d.ts +46 -0
- package/dist/quickactions.d.ts.map +1 -0
- package/dist/runreplay.d.ts +53 -0
- package/dist/runreplay.d.ts.map +1 -0
- package/dist/siteprefs.d.ts +45 -0
- package/dist/siteprefs.d.ts.map +1 -0
- package/dist/statusviews.d.ts +65 -0
- package/dist/statusviews.d.ts.map +1 -0
- package/dist/surfaces.d.ts +2 -2
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/syncbridge.d.ts +80 -0
- package/dist/syncbridge.d.ts.map +1 -0
- package/dist/tourviews.d.ts +28 -0
- package/dist/tourviews.d.ts.map +1 -0
- package/dist/types.d.ts +444 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/extension/dist/background.js +1841 -5
- package/extension/dist/background.js.map +4 -4
- package/extension/dist/dashboardpage.html +3 -0
- package/extension/dist/dashboardpage.js +108 -0
- package/extension/dist/dashboardpage.js.map +2 -2
- package/extension/dist/manifest.json +1 -1
- package/extension/dist/optionspage.html +5 -0
- package/extension/dist/optionspage.js +222 -0
- package/extension/dist/optionspage.js.map +2 -2
- package/extension/dist/pagebridge.js.map +1 -1
- package/extension/dist/popup.html +2 -2
- package/extension/dist/popup.js +102 -0
- package/extension/dist/popup.js.map +2 -2
- package/extension/dist/sidepanel.html +2 -2
- package/extension/dist/sidepanel.js +219 -3
- package/extension/dist/sidepanel.js.map +2 -2
- package/extension/dist/style.css +3 -1
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
<nav class="actions"><button id="openpanel" class="secondary">Open review panel</button><button id="openoptions" class="secondary">Open options</button><button id="openpopuphint" class="secondary" hidden>Return to popup</button></nav>
|
|
6
6
|
<p id="status" role="status">Loading the dashboard view.</p>
|
|
7
7
|
<section><h2>Sessions and runs</h2><div id="sessiongrid" class="panel"></div></section>
|
|
8
|
+
<section><h2>Flow library</h2><div class="actions"><input id="libraryquery" placeholder="Search templates, publishers and descriptions" aria-label="Flowlibrary search"><button id="libraryrun" class="secondary">Search</button><button id="libraryexport" class="secondary">Export manifest list</button></div><div id="flowlibrary" class="panel"></div><p id="librarystatus" class="muted">The flowlibrary browser shows the publisher, the version and the required grants of every entry before any install.</p></section>
|
|
9
|
+
<section><h2>Background runs</h2><div id="backgroundruns" class="panel"></div></section>
|
|
8
10
|
<section><h2>History search</h2><div class="actions"><input id="historyquery" placeholder="Search sessions, notes and summaries" aria-label="History search"><button id="historyrun" class="secondary">Search</button></div><ol id="historyhits" class="audit"></ol></section>
|
|
9
11
|
<section><h2>Site notes</h2><div id="sitenotes" class="panel"></div></section>
|
|
10
12
|
<section><h2>Transparency views</h2><div id="transparency" class="panel"></div></section>
|
|
11
13
|
<section><h2>Onboarding</h2><div id="onboarding" class="panel"></div></section>
|
|
14
|
+
<section><h2>Drop import</h2><div id="dropzone" class="panel" role="region" aria-label="Drop csv, json or workflow files to import">Drop csv, json or workflow files here.</div><p id="dropstatus" class="muted">The dashboardpage accepts the same dropimport files as the optionspage.</p></section>
|
|
12
15
|
</main><script type="module" src="dashboardpage.js"></script></body>
|
|
13
16
|
</html>
|
|
@@ -6,6 +6,10 @@ var sitenotesroot = document.querySelector("#sitenotes");
|
|
|
6
6
|
var transparencyroot = document.querySelector("#transparency");
|
|
7
7
|
var onboardingroot = document.querySelector("#onboarding");
|
|
8
8
|
var historyinput = document.querySelector("#historyquery");
|
|
9
|
+
var libraryroot = document.querySelector("#flowlibrary");
|
|
10
|
+
var librarystatusnode = document.querySelector("#librarystatus");
|
|
11
|
+
var libraryinput = document.querySelector("#libraryquery");
|
|
12
|
+
var backgroundrunsroot = document.querySelector("#backgroundruns");
|
|
9
13
|
function status(message, error = false) {
|
|
10
14
|
if (statusnode) {
|
|
11
15
|
statusnode.textContent = message;
|
|
@@ -97,6 +101,74 @@ function renderonboarding(onboarding) {
|
|
|
97
101
|
state.textContent = onboarding.done ? "The onboarding walkthrough is done; replay it on demand from the optionspage." : `The onboarding walkthrough stands at ${onboarding.stepscompleted.length} completed step${onboarding.stepscompleted.length === 1 ? "" : "s"}.`;
|
|
98
102
|
onboardingroot.append(state);
|
|
99
103
|
}
|
|
104
|
+
async function renderlibrary(query) {
|
|
105
|
+
if (!libraryroot) return;
|
|
106
|
+
try {
|
|
107
|
+
const view = await request({ kind: "ecosystem", library: { browse: { ...query.trim() !== "" ? { query } : {} } } });
|
|
108
|
+
libraryroot.replaceChildren();
|
|
109
|
+
if (view.library.length === 0) {
|
|
110
|
+
libraryroot.textContent = "No flowlibrary entry matches yet; import a manifest from a file or pull one through a syncbridge hook.";
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const list = document.createElement("ol");
|
|
114
|
+
list.className = "audit";
|
|
115
|
+
for (const row of view.library) {
|
|
116
|
+
const item = document.createElement("li");
|
|
117
|
+
item.textContent = `${row.title} ${row.version} \u2014 ${row.publisher}${row.registry !== void 0 ? ` \u2014 ${row.registry}` : ""} \u2014 ${row.state}${row.sensitive ? " \u2014 sensitive" : ""} \u2014 grants: ${row.grants.join(", ") || "none"}`;
|
|
118
|
+
item.append(" ", button("Steps", async () => {
|
|
119
|
+
const detail = await request({ kind: "ecosystem", library: { entry: { entryid: row.id } } });
|
|
120
|
+
if (librarystatusnode) librarystatusnode.textContent = detail.steps.map((step) => `${step.id}: ${step.kind} \u2014 ${step.label}${step.target !== void 0 ? ` (${step.target})` : ""}${step.fields.length > 0 ? ` [${step.families.join(", ")}: ${step.fields.join(", ")}]` : ""}`).join(" \xB7 ");
|
|
121
|
+
}));
|
|
122
|
+
item.append(" ", button("Grant diff", async () => {
|
|
123
|
+
const diff = await request({ kind: "ecosystem", library: { grants: { entryid: row.id } } });
|
|
124
|
+
if (librarystatusnode) librarystatusnode.textContent = `Grant diff: added ${diff.diff.added.join(", ") || "none"}, held ${diff.diff.kept.join(", ") || "none"}; the added grants map onto originprofiles before the import completes.`;
|
|
125
|
+
}));
|
|
126
|
+
item.append(" ", button("Install", async () => {
|
|
127
|
+
const result = await request({ kind: "ecosystem", library: { install: { entryid: row.id, ...row.sensitive ? { consent: true } : {} } } });
|
|
128
|
+
if (librarystatusnode) librarystatusnode.textContent = `The install landed as the pending workflow ${result.record.id} with ${result.record.steps} steps; the import review approves its step list before anything runs.`;
|
|
129
|
+
await renderlibrary(query);
|
|
130
|
+
}));
|
|
131
|
+
item.append(" ", button("Fork", async () => {
|
|
132
|
+
const result = await request({ kind: "ecosystem", library: { fork: { entryid: row.id } } });
|
|
133
|
+
if (librarystatusnode) librarystatusnode.textContent = `The fork created the independent local workflow ${result.fork.id}; the fork owns its name from its creation on.`;
|
|
134
|
+
}));
|
|
135
|
+
item.append(" ", button("Remove", async () => {
|
|
136
|
+
await request({ kind: "ecosystem", library: { remove: { entryid: row.id } } });
|
|
137
|
+
if (librarystatusnode) librarystatusnode.textContent = `The entry ${row.id} left the flowlibrary while its local forks stayed untouched.`;
|
|
138
|
+
await renderlibrary(query);
|
|
139
|
+
}));
|
|
140
|
+
list.append(item);
|
|
141
|
+
}
|
|
142
|
+
libraryroot.append(list);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
libraryroot.textContent = error instanceof Error ? error.message : String(error);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async function renderbackgroundruns() {
|
|
148
|
+
if (!backgroundrunsroot) return;
|
|
149
|
+
try {
|
|
150
|
+
const view = await request({ kind: "ecosystem", background: { list: true } });
|
|
151
|
+
backgroundrunsroot.replaceChildren();
|
|
152
|
+
if (view.queue.length === 0) {
|
|
153
|
+
backgroundrunsroot.textContent = "No background run stands in the queue; queue a reviewed workflow and it keeps executing with the panel closed.";
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const list = document.createElement("ol");
|
|
157
|
+
list.className = "audit";
|
|
158
|
+
for (const row of view.queue) {
|
|
159
|
+
const item = document.createElement("li");
|
|
160
|
+
item.textContent = `${row.state} \u2014 ${row.workflowid} \u2014 ${row.progress}${row.keepaliveheld ? " \u2014 keepalive held" : ""}`;
|
|
161
|
+
if (row.state === "queued") item.append(" ", button("Cancel", async () => {
|
|
162
|
+
await request({ kind: "ecosystem", background: { cancel: { id: row.id } } });
|
|
163
|
+
await renderbackgroundruns();
|
|
164
|
+
}));
|
|
165
|
+
list.append(item);
|
|
166
|
+
}
|
|
167
|
+
backgroundrunsroot.append(list);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
backgroundrunsroot.textContent = error instanceof Error ? error.message : String(error);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
100
172
|
async function render() {
|
|
101
173
|
const view = await request({ kind: "surface", dashboard: { view: true } });
|
|
102
174
|
rendersessiongrid(view.sessionview);
|
|
@@ -118,6 +190,20 @@ document.querySelector("#historyrun")?.addEventListener("click", () => {
|
|
|
118
190
|
historyinput?.addEventListener("keydown", (event) => {
|
|
119
191
|
if (event.key === "Enter") document.querySelector("#historyrun")?.click();
|
|
120
192
|
});
|
|
193
|
+
document.querySelector("#libraryrun")?.addEventListener("click", () => {
|
|
194
|
+
void renderlibrary(libraryinput?.value ?? "").catch((error) => status(error instanceof Error ? error.message : String(error), true));
|
|
195
|
+
});
|
|
196
|
+
libraryinput?.addEventListener("keydown", (event) => {
|
|
197
|
+
if (event.key === "Enter") document.querySelector("#libraryrun")?.click();
|
|
198
|
+
});
|
|
199
|
+
document.querySelector("#libraryexport")?.addEventListener("click", () => {
|
|
200
|
+
void request({ kind: "ecosystem", library: { export: true } }).then((result) => {
|
|
201
|
+
const manifests = result.manifests;
|
|
202
|
+
if (librarystatusnode) librarystatusnode.textContent = `The manifest list exported ${manifests.length} entr${manifests.length === 1 ? "y" : "ies"} for audit with digest, publisher, version, state and provenance and no step payload.`;
|
|
203
|
+
}).catch((error) => {
|
|
204
|
+
if (librarystatusnode) librarystatusnode.textContent = error instanceof Error ? error.message : String(error);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
121
207
|
var surfacechannel = typeof BroadcastChannel === "function" ? new BroadcastChannel("devthinksurfaces") : void 0;
|
|
122
208
|
surfacechannel?.addEventListener("message", () => {
|
|
123
209
|
void render().catch(() => {
|
|
@@ -126,4 +212,26 @@ surfacechannel?.addEventListener("message", () => {
|
|
|
126
212
|
void render().catch((error) => status(error instanceof Error ? error.message : String(error), true));
|
|
127
213
|
void renderhistorysearch("").catch(() => {
|
|
128
214
|
});
|
|
215
|
+
void renderlibrary("").catch(() => {
|
|
216
|
+
});
|
|
217
|
+
void renderbackgroundruns().catch(() => {
|
|
218
|
+
});
|
|
219
|
+
var dropzonenode = document.querySelector("#dropzone");
|
|
220
|
+
var dropstatusnode = document.querySelector("#dropstatus");
|
|
221
|
+
if (dropzonenode) {
|
|
222
|
+
dropzonenode.addEventListener("dragover", (event) => {
|
|
223
|
+
event.preventDefault();
|
|
224
|
+
});
|
|
225
|
+
dropzonenode.addEventListener("drop", (event) => {
|
|
226
|
+
event.preventDefault();
|
|
227
|
+
const file = event.dataTransfer?.files[0];
|
|
228
|
+
if (file === void 0) return;
|
|
229
|
+
void file.text().then((head) => request({ kind: "views", dropimport: { file: { filename: file.name, bytes: file.size, head: head.slice(0, 2e3) } } })).then((result) => {
|
|
230
|
+
const session = result.session;
|
|
231
|
+
if (dropstatusnode) dropstatusnode.textContent = `The dropimport detected the ${session.kind} kind of ${session.filename} (${session.bytes} byte${session.bytes === 1 ? "" : "s"}); the import path takes the file from here.`;
|
|
232
|
+
}).catch((error) => {
|
|
233
|
+
if (dropstatusnode) dropstatusnode.textContent = error instanceof Error ? error.message : String(error);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
}
|
|
129
237
|
//# sourceMappingURL=dashboardpage.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../dashboardpage.ts"],
|
|
4
|
-
"sourcesContent": ["export {};\n\n/**\n * Dashboardpage runtime of the 1.1.64 family.\n * The dashboardpage opens in a new tab from the popup and the sidepanel and aggregates the sessions, the runs, the notes and the transparency views of the profile workspace; it renders the sessiongrid and the historysearch of the 1.1.63 session interface beside the transparency report of the 1.1.62 family and the onboarding state, and it subscribes to session and run updates through the single broadcast channel so the view stays live without a reload.\n */\n\ntype sessionview = {\n grid: Array<{ sessionid: string; runid: string; origins: string[]; state: string; outcome: string; steps: number; completed: number; lock: string; tabid?: number; sealhash?: string; updatedat: number; actions: string[] }>;\n notes: Array<{ id: string; origin: string; title: string; body: string; author: string; sensitive: boolean; updatedat: number }>;\n summaries: Array<{ runid: string; origins: string[]; kinds: string[]; steps: number; provenance: string; distilledat: number }>;\n};\ntype transparencyview = {\n grants: Array<{ origin: string; scope: string; boundary: string; grantedat: number }>;\n windows: Array<{ id: string; origin: string; state: string; boundary: string; startedat: number; expiresat: number }>;\n permdiffs: Array<{ fromversion: string; toversion: string; added: string[]; removed: string[]; computedat: number }>;\n safedefaults: Array<{ origin: string; firstseenat: number }>;\n};\ntype dashboardview = { sessionview: sessionview; transparency: transparencyview; onboarding: { stepscompleted: string[]; done: boolean }; environments?: { offscreengranted: boolean; parseoffload: boolean; workers?: number }; security?: { posture?: string } };\n\nconst statusnode = document.querySelector<HTMLElement>(\"#status\");\nconst sessiongridroot = document.querySelector<HTMLElement>(\"#sessiongrid\");\nconst historyhitsroot = document.querySelector<HTMLElement>(\"#historyhits\");\nconst sitenotesroot = document.querySelector<HTMLElement>(\"#sitenotes\");\nconst transparencyroot = document.querySelector<HTMLElement>(\"#transparency\");\nconst onboardingroot = document.querySelector<HTMLElement>(\"#onboarding\");\nconst historyinput = document.querySelector<HTMLInputElement>(\"#historyquery\");\n\nfunction status(message: string, error = false): void { if (statusnode) { statusnode.textContent = message; statusnode.dataset.state = error ? \"error\" : \"ready\"; } }\nasync function request(message: unknown): Promise<unknown> { const response = await chrome.runtime.sendMessage(message) as { ok: boolean; value?: unknown; error?: string }; if (!response.ok) throw new Error(response.error); return response.value; }\nfunction button(label: string, action: () => Promise<void>): HTMLButtonElement { const element = document.createElement(\"button\"); element.type = \"button\"; element.className = \"secondary\"; element.textContent = label; element.addEventListener(\"click\", () => action().catch(error => status(error instanceof Error ? error.message : String(error), true))); return element; }\n\n/** Renders the sessiongrid rows of the 1.1.63 session interface with their resume, cancelrun and reopen actions as deep links. */\nfunction rendersessiongrid(view: sessionview): void {\n if (!sessiongridroot) return;\n sessiongridroot.replaceChildren();\n if (view.grid.length === 0) { sessiongridroot.textContent = \"No session exists yet; start the first run from the popup taskinput.\"; return; }\n const list = document.createElement(\"ol\");\n list.className = \"audit\";\n for (const row of view.grid) {\n const item = document.createElement(\"li\");\n item.textContent = `${row.state} ${row.runid} \u2014 ${row.origins.join(\", \")} \u2014 ${row.completed}/${row.steps} steps \u2014 ${row.outcome} \u2014 ${row.lock}${row.sealhash !== undefined ? ` \u2014 sealed ${row.sealhash.slice(0, 12)}` : \"\"}`;\n for (const action of row.actions) item.append(\" \", button(action === \"cancelrun\" ? \"Cancel run\" : action === \"resume\" ? \"Resume\" : \"Reopen\", async () => {\n await request({ kind: \"sessions\", grid: action === \"cancelrun\" ? { open: { runid: row.runid } } : action === \"resume\" ? { resume: { runid: row.runid } } : { reopen: { runid: row.runid } } });\n if (action === \"cancelrun\") await request({ kind: \"surface\", bus: { action: { surface: \"dashboardpage\", command: \"cancelrun\" } } });\n status(`The ${action} action of the run ${row.runid} routed through the command bus.`);\n await render();\n }));\n list.append(item);\n }\n sessiongridroot.append(list);\n}\n\n/** Renders the historysearch corpus of the 1.1.63 session interface with the matched terms highlighted. */\nasync function renderhistorysearch(text: string): Promise<void> {\n if (!historyhitsroot) return;\n try {\n const result = await request({ kind: \"sessions\", search: { query: { text } } }) as { hits: Array<{ source: string; title: string; excerpt: string; highlights: string[]; origin?: string; at: number }> };\n historyhitsroot.replaceChildren();\n if (result.hits.length === 0) { historyhitsroot.append(Object.assign(document.createElement(\"li\"), { textContent: \"No history matches yet.\" })); return; }\n for (const hit of result.hits) {\n const item = document.createElement(\"li\");\n item.textContent = `${hit.source} \u2014 ${hit.title}${hit.origin !== undefined ? ` (${hit.origin})` : \"\"}: ${hit.excerpt}${hit.highlights.length > 0 ? ` [${hit.highlights.join(\", \")}]` : \"\"}`;\n historyhitsroot.append(item);\n }\n } catch (error) { historyhitsroot.textContent = error instanceof Error ? error.message : String(error); }\n}\n\n/** Renders the site notes of the session interface with their author provenance. */\nfunction rendersitenotes(view: sessionview): void {\n if (!sitenotesroot) return;\n if (view.notes.length === 0) { sitenotesroot.textContent = \"No site note exists yet; write the first note from the sidepanel.\"; return; }\n sitenotesroot.replaceChildren();\n const list = document.createElement(\"ul\");\n list.className = \"audit\";\n for (const note of view.notes) list.append(Object.assign(document.createElement(\"li\"), { textContent: `${note.title} (${note.origin}, by ${note.author}${note.sensitive ? \", sealed at rest\" : \"\"}): ${note.body}` }));\n sitenotesroot.append(list);\n}\n\n/** Renders the transparency views of the 1.1.62 family: the grants with their boundaries, the window history, the permdiffs and the safedefaults applications. */\nfunction rendertransparency(view: transparencyview): void {\n if (!transparencyroot) return;\n transparencyroot.replaceChildren();\n const grants = document.createElement(\"p\");\n grants.textContent = `Grants: ${view.grants.length} grant row${view.grants.length === 1 ? \"\" : \"s\"}${view.grants.length > 0 ? ` (${view.grants.slice(0, 5).map(grant => `${grant.origin} \u2014 ${grant.scope} \u2014 ${grant.boundary}`).join(\" \u00B7 \")})` : \"\"}.`;\n const windows = document.createElement(\"p\");\n windows.textContent = `Consent windows: ${view.windows.length} recorded window${view.windows.length === 1 ? \"\" : \"s\"}.`;\n const permdiffs = document.createElement(\"p\");\n permdiffs.textContent = `Permdiffs: ${view.permdiffs.length} record${view.permdiffs.length === 1 ? \"\" : \"s\"}${view.permdiffs.length > 0 ? ` (latest ${view.permdiffs[0]?.fromversion} \u2192 ${view.permdiffs[0]?.toversion})` : \"\"}.`;\n const safedefaults = document.createElement(\"p\");\n safedefaults.textContent = `Safedefaults applications: ${view.safedefaults.length} origin${view.safedefaults.length === 1 ? \"\" : \"s\"} under the reads only posture.`;\n transparencyroot.append(grants, windows, permdiffs, safedefaults);\n}\n\n/** Renders the onboarding state with its replay offer on demand. */\nfunction renderonboarding(onboarding: { stepscompleted: string[]; done: boolean }): void {\n if (!onboardingroot) return;\n onboardingroot.replaceChildren();\n const state = document.createElement(\"p\");\n state.textContent = onboarding.done ? \"The onboarding walkthrough is done; replay it on demand from the optionspage.\" : `The onboarding walkthrough stands at ${onboarding.stepscompleted.length} completed step${onboarding.stepscompleted.length === 1 ? \"\" : \"s\"}.`;\n onboardingroot.append(state);\n}\n\n/** Loads the aggregated dashboard view: the sessionview of 1.1.63, the transparency report of 1.1.62 and the onboarding state. */\nasync function render(): Promise<void> {\n const view = await request({ kind: \"surface\", dashboard: { view: true } }) as dashboardview;\n rendersessiongrid(view.sessionview);\n rendersitenotes(view.sessionview);\n rendertransparency(view.transparency);\n renderonboarding(view.onboarding);\n status(`Dashboard view live: ${view.sessionview.grid.length} run${view.sessionview.grid.length === 1 ? \"\" : \"s\"}, ${view.sessionview.notes.length} note${view.sessionview.notes.length === 1 ? \"\" : \"s\"} and ${view.transparency.grants.length} grant row${view.transparency.grants.length === 1 ? \"\" : \"s\"}.`);\n}\n\ndocument.querySelector<HTMLButtonElement>(\"#openpanel\")?.addEventListener(\"click\", () => { void chrome.sidePanel.open({ windowId: chrome.windows.WINDOW_ID_CURRENT }).catch(() => { /* the panel opens beside the dashboard tab */ }); });\ndocument.querySelector<HTMLButtonElement>(\"#openoptions\")?.addEventListener(\"click\", () => { void chrome.tabs.create({ url: chrome.runtime.getURL(\"optionspage.html\") }); });\ndocument.querySelector<HTMLButtonElement>(\"#historyrun\")?.addEventListener(\"click\", () => { void renderhistorysearch(historyinput?.value ?? \"\").catch(error => status(error instanceof Error ? error.message : String(error), true)); });\nhistoryinput?.addEventListener(\"keydown\", event => { if (event.key === \"Enter\") document.querySelector<HTMLButtonElement>(\"#historyrun\")?.click(); });\n\n/** The dashboardpage subscribes to session and run updates through the single broadcast channel. */\nconst surfacechannel: BroadcastChannel | undefined = typeof BroadcastChannel === \"function\" ? new BroadcastChannel(\"devthinksurfaces\") : undefined;\nsurfacechannel?.addEventListener(\"message\", () => { void render().catch(() => { /* a failing refresh keeps the last rendered dashboard */ }); });\n\nvoid render().catch(error => status(error instanceof Error ? error.message : String(error), true));\nvoid renderhistorysearch(\"\").catch(() => { /* an empty first search shows the empty state */ });\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["export {};\n\n/**\n * Dashboardpage runtime of the 1.1.64 family.\n * The dashboardpage opens in a new tab from the popup and the sidepanel and aggregates the sessions, the runs, the notes and the transparency views of the profile workspace; it renders the sessiongrid and the historysearch of the 1.1.63 session interface beside the transparency report of the 1.1.62 family and the onboarding state, and it subscribes to session and run updates through the single broadcast channel so the view stays live without a reload.\n */\n\ntype sessionview = {\n grid: Array<{ sessionid: string; runid: string; origins: string[]; state: string; outcome: string; steps: number; completed: number; lock: string; tabid?: number; sealhash?: string; updatedat: number; actions: string[] }>;\n notes: Array<{ id: string; origin: string; title: string; body: string; author: string; sensitive: boolean; updatedat: number }>;\n summaries: Array<{ runid: string; origins: string[]; kinds: string[]; steps: number; provenance: string; distilledat: number }>;\n};\ntype transparencyview = {\n grants: Array<{ origin: string; scope: string; boundary: string; grantedat: number }>;\n windows: Array<{ id: string; origin: string; state: string; boundary: string; startedat: number; expiresat: number }>;\n permdiffs: Array<{ fromversion: string; toversion: string; added: string[]; removed: string[]; computedat: number }>;\n safedefaults: Array<{ origin: string; firstseenat: number }>;\n};\ntype dashboardview = { sessionview: sessionview; transparency: transparencyview; onboarding: { stepscompleted: string[]; done: boolean }; environments?: { offscreengranted: boolean; parseoffload: boolean; workers?: number }; security?: { posture?: string } };\ntype libraryrow = { id: string; title: string; publisher: string; version: string; grants: string[]; sensitive: boolean; state: string; registry?: string };\ntype backgroundrow = { id: string; workflowid: string; state: string; keepaliveheld: boolean; progress: string };\n\nconst statusnode = document.querySelector<HTMLElement>(\"#status\");\nconst sessiongridroot = document.querySelector<HTMLElement>(\"#sessiongrid\");\nconst historyhitsroot = document.querySelector<HTMLElement>(\"#historyhits\");\nconst sitenotesroot = document.querySelector<HTMLElement>(\"#sitenotes\");\nconst transparencyroot = document.querySelector<HTMLElement>(\"#transparency\");\nconst onboardingroot = document.querySelector<HTMLElement>(\"#onboarding\");\nconst historyinput = document.querySelector<HTMLInputElement>(\"#historyquery\");\nconst libraryroot = document.querySelector<HTMLElement>(\"#flowlibrary\");\nconst librarystatusnode = document.querySelector<HTMLElement>(\"#librarystatus\");\nconst libraryinput = document.querySelector<HTMLInputElement>(\"#libraryquery\");\nconst backgroundrunsroot = document.querySelector<HTMLElement>(\"#backgroundruns\");\n\nfunction status(message: string, error = false): void { if (statusnode) { statusnode.textContent = message; statusnode.dataset.state = error ? \"error\" : \"ready\"; } }\nasync function request(message: unknown): Promise<unknown> { const response = await chrome.runtime.sendMessage(message) as { ok: boolean; value?: unknown; error?: string }; if (!response.ok) throw new Error(response.error); return response.value; }\nfunction button(label: string, action: () => Promise<void>): HTMLButtonElement { const element = document.createElement(\"button\"); element.type = \"button\"; element.className = \"secondary\"; element.textContent = label; element.addEventListener(\"click\", () => action().catch(error => status(error instanceof Error ? error.message : String(error), true))); return element; }\n\n/** Renders the sessiongrid rows of the 1.1.63 session interface with their resume, cancelrun and reopen actions as deep links. */\nfunction rendersessiongrid(view: sessionview): void {\n if (!sessiongridroot) return;\n sessiongridroot.replaceChildren();\n if (view.grid.length === 0) { sessiongridroot.textContent = \"No session exists yet; start the first run from the popup taskinput.\"; return; }\n const list = document.createElement(\"ol\");\n list.className = \"audit\";\n for (const row of view.grid) {\n const item = document.createElement(\"li\");\n item.textContent = `${row.state} ${row.runid} \u2014 ${row.origins.join(\", \")} \u2014 ${row.completed}/${row.steps} steps \u2014 ${row.outcome} \u2014 ${row.lock}${row.sealhash !== undefined ? ` \u2014 sealed ${row.sealhash.slice(0, 12)}` : \"\"}`;\n for (const action of row.actions) item.append(\" \", button(action === \"cancelrun\" ? \"Cancel run\" : action === \"resume\" ? \"Resume\" : \"Reopen\", async () => {\n await request({ kind: \"sessions\", grid: action === \"cancelrun\" ? { open: { runid: row.runid } } : action === \"resume\" ? { resume: { runid: row.runid } } : { reopen: { runid: row.runid } } });\n if (action === \"cancelrun\") await request({ kind: \"surface\", bus: { action: { surface: \"dashboardpage\", command: \"cancelrun\" } } });\n status(`The ${action} action of the run ${row.runid} routed through the command bus.`);\n await render();\n }));\n list.append(item);\n }\n sessiongridroot.append(list);\n}\n\n/** Renders the historysearch corpus of the 1.1.63 session interface with the matched terms highlighted. */\nasync function renderhistorysearch(text: string): Promise<void> {\n if (!historyhitsroot) return;\n try {\n const result = await request({ kind: \"sessions\", search: { query: { text } } }) as { hits: Array<{ source: string; title: string; excerpt: string; highlights: string[]; origin?: string; at: number }> };\n historyhitsroot.replaceChildren();\n if (result.hits.length === 0) { historyhitsroot.append(Object.assign(document.createElement(\"li\"), { textContent: \"No history matches yet.\" })); return; }\n for (const hit of result.hits) {\n const item = document.createElement(\"li\");\n item.textContent = `${hit.source} \u2014 ${hit.title}${hit.origin !== undefined ? ` (${hit.origin})` : \"\"}: ${hit.excerpt}${hit.highlights.length > 0 ? ` [${hit.highlights.join(\", \")}]` : \"\"}`;\n historyhitsroot.append(item);\n }\n } catch (error) { historyhitsroot.textContent = error instanceof Error ? error.message : String(error); }\n}\n\n/** Renders the site notes of the session interface with their author provenance. */\nfunction rendersitenotes(view: sessionview): void {\n if (!sitenotesroot) return;\n if (view.notes.length === 0) { sitenotesroot.textContent = \"No site note exists yet; write the first note from the sidepanel.\"; return; }\n sitenotesroot.replaceChildren();\n const list = document.createElement(\"ul\");\n list.className = \"audit\";\n for (const note of view.notes) list.append(Object.assign(document.createElement(\"li\"), { textContent: `${note.title} (${note.origin}, by ${note.author}${note.sensitive ? \", sealed at rest\" : \"\"}): ${note.body}` }));\n sitenotesroot.append(list);\n}\n\n/** Renders the transparency views of the 1.1.62 family: the grants with their boundaries, the window history, the permdiffs and the safedefaults applications. */\nfunction rendertransparency(view: transparencyview): void {\n if (!transparencyroot) return;\n transparencyroot.replaceChildren();\n const grants = document.createElement(\"p\");\n grants.textContent = `Grants: ${view.grants.length} grant row${view.grants.length === 1 ? \"\" : \"s\"}${view.grants.length > 0 ? ` (${view.grants.slice(0, 5).map(grant => `${grant.origin} \u2014 ${grant.scope} \u2014 ${grant.boundary}`).join(\" \u00B7 \")})` : \"\"}.`;\n const windows = document.createElement(\"p\");\n windows.textContent = `Consent windows: ${view.windows.length} recorded window${view.windows.length === 1 ? \"\" : \"s\"}.`;\n const permdiffs = document.createElement(\"p\");\n permdiffs.textContent = `Permdiffs: ${view.permdiffs.length} record${view.permdiffs.length === 1 ? \"\" : \"s\"}${view.permdiffs.length > 0 ? ` (latest ${view.permdiffs[0]?.fromversion} \u2192 ${view.permdiffs[0]?.toversion})` : \"\"}.`;\n const safedefaults = document.createElement(\"p\");\n safedefaults.textContent = `Safedefaults applications: ${view.safedefaults.length} origin${view.safedefaults.length === 1 ? \"\" : \"s\"} under the reads only posture.`;\n transparencyroot.append(grants, windows, permdiffs, safedefaults);\n}\n\n/** Renders the onboarding state with its replay offer on demand. */\nfunction renderonboarding(onboarding: { stepscompleted: string[]; done: boolean }): void {\n if (!onboardingroot) return;\n onboardingroot.replaceChildren();\n const state = document.createElement(\"p\");\n state.textContent = onboarding.done ? \"The onboarding walkthrough is done; replay it on demand from the optionspage.\" : `The onboarding walkthrough stands at ${onboarding.stepscompleted.length} completed step${onboarding.stepscompleted.length === 1 ? \"\" : \"s\"}.`;\n onboardingroot.append(state);\n}\n\n/** Renders the flowlibrary browser of the 1.1.66 ecosystem family: every row names its publisher, version and required grants, the entry view shows the full step list before install, the grant diff opens the diff dialog and the install lands as a pending import behind the same review. */\nasync function renderlibrary(query: string): Promise<void> {\n if (!libraryroot) return;\n try {\n const view = await request({ kind: \"ecosystem\", library: { browse: { ...(query.trim() !== \"\" ? { query } : {}) } } }) as { library: libraryrow[] };\n libraryroot.replaceChildren();\n if (view.library.length === 0) { libraryroot.textContent = \"No flowlibrary entry matches yet; import a manifest from a file or pull one through a syncbridge hook.\"; return; }\n const list = document.createElement(\"ol\");\n list.className = \"audit\";\n for (const row of view.library) {\n const item = document.createElement(\"li\");\n item.textContent = `${row.title} ${row.version} \u2014 ${row.publisher}${row.registry !== undefined ? ` \u2014 ${row.registry}` : \"\"} \u2014 ${row.state}${row.sensitive ? \" \u2014 sensitive\" : \"\"} \u2014 grants: ${row.grants.join(\", \") || \"none\"}`;\n item.append(\" \", button(\"Steps\", async () => {\n const detail = await request({ kind: \"ecosystem\", library: { entry: { entryid: row.id } } }) as { steps: Array<{ id: string; kind: string; label: string; target?: string; families: string[]; fields: string[] }> };\n if (librarystatusnode) librarystatusnode.textContent = detail.steps.map(step => `${step.id}: ${step.kind} \u2014 ${step.label}${step.target !== undefined ? ` (${step.target})` : \"\"}${step.fields.length > 0 ? ` [${step.families.join(\", \")}: ${step.fields.join(\", \")}]` : \"\"}`).join(\" \u00B7 \");\n }));\n item.append(\" \", button(\"Grant diff\", async () => {\n const diff = await request({ kind: \"ecosystem\", library: { grants: { entryid: row.id } } }) as { diff: { added: string[]; kept: string[] } };\n if (librarystatusnode) librarystatusnode.textContent = `Grant diff: added ${diff.diff.added.join(\", \") || \"none\"}, held ${diff.diff.kept.join(\", \") || \"none\"}; the added grants map onto originprofiles before the import completes.`;\n }));\n item.append(\" \", button(\"Install\", async () => {\n const result = await request({ kind: \"ecosystem\", library: { install: { entryid: row.id, ...(row.sensitive ? { consent: true } : {}) } } }) as { record: { id: string; steps: number } };\n if (librarystatusnode) librarystatusnode.textContent = `The install landed as the pending workflow ${result.record.id} with ${result.record.steps} steps; the import review approves its step list before anything runs.`;\n await renderlibrary(query);\n }));\n item.append(\" \", button(\"Fork\", async () => {\n const result = await request({ kind: \"ecosystem\", library: { fork: { entryid: row.id } } }) as { fork: { id: string } };\n if (librarystatusnode) librarystatusnode.textContent = `The fork created the independent local workflow ${result.fork.id}; the fork owns its name from its creation on.`;\n }));\n item.append(\" \", button(\"Remove\", async () => {\n await request({ kind: \"ecosystem\", library: { remove: { entryid: row.id } } });\n if (librarystatusnode) librarystatusnode.textContent = `The entry ${row.id} left the flowlibrary while its local forks stayed untouched.`;\n await renderlibrary(query);\n }));\n list.append(item);\n }\n libraryroot.append(list);\n } catch (error) { libraryroot.textContent = error instanceof Error ? error.message : String(error); }\n}\n\n/** Renders the background run queue of the 1.1.66 ecosystem family with its progress and its keepalive hold. */\nasync function renderbackgroundruns(): Promise<void> {\n if (!backgroundrunsroot) return;\n try {\n const view = await request({ kind: \"ecosystem\", background: { list: true } }) as { queue: backgroundrow[] };\n backgroundrunsroot.replaceChildren();\n if (view.queue.length === 0) { backgroundrunsroot.textContent = \"No background run stands in the queue; queue a reviewed workflow and it keeps executing with the panel closed.\"; return; }\n const list = document.createElement(\"ol\");\n list.className = \"audit\";\n for (const row of view.queue) {\n const item = document.createElement(\"li\");\n item.textContent = `${row.state} \u2014 ${row.workflowid} \u2014 ${row.progress}${row.keepaliveheld ? \" \u2014 keepalive held\" : \"\"}`;\n if (row.state === \"queued\") item.append(\" \", button(\"Cancel\", async () => { await request({ kind: \"ecosystem\", background: { cancel: { id: row.id } } }); await renderbackgroundruns(); }));\n list.append(item);\n }\n backgroundrunsroot.append(list);\n } catch (error) { backgroundrunsroot.textContent = error instanceof Error ? error.message : String(error); }\n}\n\n/** Loads the aggregated dashboard view: the sessionview of 1.1.63, the transparency report of 1.1.62 and the onboarding state. */\nasync function render(): Promise<void> {\n const view = await request({ kind: \"surface\", dashboard: { view: true } }) as dashboardview;\n rendersessiongrid(view.sessionview);\n rendersitenotes(view.sessionview);\n rendertransparency(view.transparency);\n renderonboarding(view.onboarding);\n status(`Dashboard view live: ${view.sessionview.grid.length} run${view.sessionview.grid.length === 1 ? \"\" : \"s\"}, ${view.sessionview.notes.length} note${view.sessionview.notes.length === 1 ? \"\" : \"s\"} and ${view.transparency.grants.length} grant row${view.transparency.grants.length === 1 ? \"\" : \"s\"}.`);\n}\n\ndocument.querySelector<HTMLButtonElement>(\"#openpanel\")?.addEventListener(\"click\", () => { void chrome.sidePanel.open({ windowId: chrome.windows.WINDOW_ID_CURRENT }).catch(() => { /* the panel opens beside the dashboard tab */ }); });\ndocument.querySelector<HTMLButtonElement>(\"#openoptions\")?.addEventListener(\"click\", () => { void chrome.tabs.create({ url: chrome.runtime.getURL(\"optionspage.html\") }); });\ndocument.querySelector<HTMLButtonElement>(\"#historyrun\")?.addEventListener(\"click\", () => { void renderhistorysearch(historyinput?.value ?? \"\").catch(error => status(error instanceof Error ? error.message : String(error), true)); });\nhistoryinput?.addEventListener(\"keydown\", event => { if (event.key === \"Enter\") document.querySelector<HTMLButtonElement>(\"#historyrun\")?.click(); });\ndocument.querySelector<HTMLButtonElement>(\"#libraryrun\")?.addEventListener(\"click\", () => { void renderlibrary(libraryinput?.value ?? \"\").catch(error => status(error instanceof Error ? error.message : String(error), true)); });\nlibraryinput?.addEventListener(\"keydown\", event => { if (event.key === \"Enter\") document.querySelector<HTMLButtonElement>(\"#libraryrun\")?.click(); });\ndocument.querySelector<HTMLButtonElement>(\"#libraryexport\")?.addEventListener(\"click\", () => { void request({ kind: \"ecosystem\", library: { export: true } }).then(result => { const manifests = (result as { manifests: Array<{ title: string; publisher: string; version: string; digest: string; state: string }> }).manifests; if (librarystatusnode) librarystatusnode.textContent = `The manifest list exported ${manifests.length} entr${manifests.length === 1 ? \"y\" : \"ies\"} for audit with digest, publisher, version, state and provenance and no step payload.`; }).catch(error => { if (librarystatusnode) librarystatusnode.textContent = error instanceof Error ? error.message : String(error); }); });\n\n/** The dashboardpage subscribes to session and run updates through the single broadcast channel. */\nconst surfacechannel: BroadcastChannel | undefined = typeof BroadcastChannel === \"function\" ? new BroadcastChannel(\"devthinksurfaces\") : undefined;\nsurfacechannel?.addEventListener(\"message\", () => { void render().catch(() => { /* a failing refresh keeps the last rendered dashboard */ }); });\n\nvoid render().catch(error => status(error instanceof Error ? error.message : String(error), true));\nvoid renderhistorysearch(\"\").catch(() => { /* an empty first search shows the empty state */ });\nvoid renderlibrary(\"\").catch(() => { /* an empty first browse shows the empty state */ });\nvoid renderbackgroundruns().catch(() => { /* a failing background runs read keeps the last rendered queue */ });\n\n/**\n * Dropimport zone of the 1.1.65 family: the dashboardpage accepts the csv, json and workflow files the user drops with the same file kind detection and the same import path as the optionspage.\n */\nconst dropzonenode = document.querySelector<HTMLElement>(\"#dropzone\");\nconst dropstatusnode = document.querySelector<HTMLElement>(\"#dropstatus\");\nif (dropzonenode) {\n dropzonenode.addEventListener(\"dragover\", event => { event.preventDefault(); });\n dropzonenode.addEventListener(\"drop\", event => {\n event.preventDefault();\n const file = event.dataTransfer?.files[0];\n if (file === undefined) return;\n void file.text().then(head => request({ kind: \"views\", dropimport: { file: { filename: file.name, bytes: file.size, head: head.slice(0, 2000) } } })).then(result => {\n const session = (result as { session: { filename: string; kind: string; bytes: number } }).session;\n if (dropstatusnode) dropstatusnode.textContent = `The dropimport detected the ${session.kind} kind of ${session.filename} (${session.bytes} byte${session.bytes === 1 ? \"\" : \"s\"}); the import path takes the file from here.`;\n }).catch(error => { if (dropstatusnode) dropstatusnode.textContent = error instanceof Error ? error.message : String(error); });\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnOEjO8Z0PDgQyfvawGcaO2j+o0GLCFTLNj7TkYC/Avo9l2NenMRq7gp90Nfd7E9MViv/OMcCKSYZ5unv12QPRtv31C+a5UQWDFAOP/cH5mwMd6hsayElrSoW8ta+FwFqmr9dIFkn7cQEU3YhZr4Gcbs+ycUHOxVgDA4NBKB0rQ6e9VW5LvTw0isRYUrqM+M72vKxHk9zUIYYn/LGPvottKBYi2GLr0PHSeC2UE+Shmq7vcFIXj6hDjvD4kLJ5sKoUllEcZ1TPuBcnHUQ9ndKA5iktXDQOIJCUJmi7a0YJ2PGg7fvpYfT9k0ai/qZ+pIoRfoOEwE01bPoDn7NjeYnNQIDAQAB",
|
|
4
4
|
"name": "Devthink",
|
|
5
|
-
"version": "1.1.
|
|
5
|
+
"version": "1.1.66",
|
|
6
6
|
"description": "A consent-first bridge for reviewed browser-agent tasks.",
|
|
7
7
|
"permissions": [
|
|
8
8
|
"activeTab",
|
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
<section><h2>Task input</h2><div class="panel"><label for="taskinputretention">History retention (ms)</label><input id="taskinputretention" type="number" min="1" placeholder="keep every entry"><button id="taskinputsettings">Save taskinput options</button></div></section>
|
|
10
10
|
<section><h2>Diff preview</h2><div class="panel"><label for="diffpreviewbytes">Offscreen offload byte ceiling</label><input id="diffpreviewbytes" type="number" min="1" placeholder="keep every diff inline"><button id="diffsettings">Save diffpreview options</button></div></section>
|
|
11
11
|
<section><h2>Session interface options</h2><div class="panel"><label for="recallwindow">Recall window (ms)</label><input id="recallwindow" type="number" min="1" placeholder="keep every entry"><label for="noteretention">Note retention (ms)</label><input id="noteretention" type="number" min="1" placeholder="keep every note"><label for="summarywindow">Summary window (steps)</label><input id="summarywindow" type="number" min="0" placeholder="keep every step"><button id="sessionsettings">Save session options</button></div></section>
|
|
12
|
+
<section><h2>Interface finishing options</h2><div class="panel"><label for="themepreference">Theme preference</label><select id="themepreference" aria-label="Theme preference"><option value="">Follow the os preference</option><option value="light">Light</option><option value="dark">Dark</option></select><label for="uilanguage">Interface language</label><select id="uilanguage" aria-label="Interface language"><option value="">English (fallback)</option><option value="pt">Português</option></select><label for="recenttraydepth">Recent tray depth</label><input id="recenttraydepth" type="number" min="1" placeholder="keep every run"><label for="toastlivecount">Step toast live count</label><input id="toastlivecount" type="number" min="1" placeholder="keep every toast live"><label for="notifyconsent">Notification page content consent</label><input id="notifyconsent" type="checkbox"><label for="notifyenabled">Done and attention notifications</label><input id="notifyenabled" type="checkbox" checked><button id="finishingsettings">Save interface finishing options</button></div></section>
|
|
13
|
+
<section><h2>Shortcutkeys</h2><div class="panel"><div id="shortcutlist"></div><label for="shortcutedit">Edit a shortcut (command: combination)</label><input id="shortcutedit" type="text" placeholder="pauserun: alt+q"><button id="shortcutsettings">Save shortcut</button></div></section>
|
|
14
|
+
<section><h2>Import and export</h2><div class="panel"><div class="actions"><button id="exportbundle" class="secondary">Export settings bundle</button><button id="importbundle" class="secondary">Validate and apply an imported bundle</button></div><p id="bundlestatus" class="muted">The bundle never carries secretvault values or unmasked logs.</p><div id="dropzone" class="panel" role="region" aria-label="Drop csv, json or workflow files to import">Drop csv, json or workflow files here.</div></div></section>
|
|
15
|
+
<section><h2>Feature tour and guided tips</h2><div class="panel"><div class="actions"><button id="replaytour" class="secondary">Replay the feature tour</button><button id="recalltips" class="secondary">Recall guided tips</button></div><div id="tourstops" class="panel"></div></div></section>
|
|
12
16
|
<section><h2>Transparency and consent options</h2><iframe src="transparencypage.html" title="Transparency, consent and security options" style="width:100%;min-height:24rem;border:0"></iframe></section>
|
|
17
|
+
<section><h2>Syncbridge</h2><div class="panel"><div id="syncproviderlist"></div><label for="syncprovider">Provider</label><select id="syncprovider" aria-label="Syncbridge provider"><option value="file">File provider</option><option value="web">Web provider stub</option></select><label for="syncendpoint">User configured endpoint</label><input id="syncendpoint" type="text" placeholder="registry url or file label"><button id="synccreate">Add hook (starts opted out)</button><div id="syncconflicts"></div></div></section>
|
|
13
18
|
</main><script type="module" src="optionspage.js"></script></body>
|
|
14
19
|
</html>
|
|
@@ -115,4 +115,226 @@ surfacechannel?.addEventListener("message", (event) => {
|
|
|
115
115
|
});
|
|
116
116
|
void load();
|
|
117
117
|
void renderonboarding();
|
|
118
|
+
var themepreferenceselect = document.querySelector("#themepreference");
|
|
119
|
+
var uilanguageselect = document.querySelector("#uilanguage");
|
|
120
|
+
var recenttraydepthinput = document.querySelector("#recenttraydepth");
|
|
121
|
+
var toastlivecountinput = document.querySelector("#toastlivecount");
|
|
122
|
+
var notifyconsentinput = document.querySelector("#notifyconsent");
|
|
123
|
+
var notifyenabledinput = document.querySelector("#notifyenabled");
|
|
124
|
+
var shortcutlistnode = document.querySelector("#shortcutlist");
|
|
125
|
+
var shortcuteditinput = document.querySelector("#shortcutedit");
|
|
126
|
+
var bundlenode = document.querySelector("#bundlestatus");
|
|
127
|
+
var dropzonenode = document.querySelector("#dropzone");
|
|
128
|
+
var tourstopsnode = document.querySelector("#tourstops");
|
|
129
|
+
async function loadfinishing() {
|
|
130
|
+
try {
|
|
131
|
+
const context = await request({ kind: "context" });
|
|
132
|
+
const preferences = context.surfacepreferences ?? {};
|
|
133
|
+
if (preferences.themepreference !== void 0 && themepreferenceselect) themepreferenceselect.value = preferences.themepreference;
|
|
134
|
+
if (preferences.uilanguage !== void 0 && uilanguageselect) uilanguageselect.value = preferences.uilanguage;
|
|
135
|
+
if (preferences.recenttraydepth !== void 0 && recenttraydepthinput) recenttraydepthinput.value = String(preferences.recenttraydepth);
|
|
136
|
+
if (preferences.toastlivecount !== void 0 && toastlivecountinput) toastlivecountinput.value = String(preferences.toastlivecount);
|
|
137
|
+
if (notifyconsentinput) notifyconsentinput.checked = preferences.notifyconsent === true;
|
|
138
|
+
if (notifyenabledinput) notifyenabledinput.checked = preferences.notifyenabled !== false;
|
|
139
|
+
} catch (error) {
|
|
140
|
+
status(error instanceof Error ? error.message : String(error), true);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async function savefinishing() {
|
|
144
|
+
const themepreference = themepreferenceselect?.value ?? "";
|
|
145
|
+
const uilanguage = uilanguageselect?.value ?? "";
|
|
146
|
+
const recenttraydepth = recenttraydepthinput?.value.trim() ?? "";
|
|
147
|
+
const toastlivecount = toastlivecountinput?.value.trim() ?? "";
|
|
148
|
+
await request({ kind: "views", settings: { ...themepreference !== "" ? { themepreference } : {}, ...uilanguage !== "" ? { uilanguage } : {}, ...recenttraydepth !== "" ? { recenttraydepth: Number(recenttraydepth) } : {}, ...toastlivecount !== "" ? { toastlivecount: Number(toastlivecount) } : {}, notifyconsent: notifyconsentinput?.checked === true, notifyenabled: notifyenabledinput?.checked === true } });
|
|
149
|
+
await applythemelive();
|
|
150
|
+
status("The interface finishing options saved; the theme, the language, the tray depth and the toast live count take effect at once.");
|
|
151
|
+
}
|
|
152
|
+
async function applythemelive() {
|
|
153
|
+
try {
|
|
154
|
+
const result = await request({ kind: "views", theme: { resolve: true, preference: themepreferenceselect?.value ?? "" } });
|
|
155
|
+
for (const [name, value] of Object.entries(result.appearance.tokens)) document.documentElement.style.setProperty(`--theme-${name}`, value);
|
|
156
|
+
document.documentElement.style.setProperty("color-scheme", result.appearance.mode);
|
|
157
|
+
} catch {
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async function rendershortcuts() {
|
|
161
|
+
if (!shortcutlistnode) return;
|
|
162
|
+
try {
|
|
163
|
+
const result = await request({ kind: "views", shortcut: { list: true } });
|
|
164
|
+
shortcutlistnode.replaceChildren();
|
|
165
|
+
const list = document.createElement("ul");
|
|
166
|
+
list.className = "audit";
|
|
167
|
+
for (const binding of result.bindings ?? []) {
|
|
168
|
+
const combination = [...binding.modifiers, binding.key].join("+");
|
|
169
|
+
list.append(Object.assign(document.createElement("li"), { textContent: `${binding.command}: ${binding.display ?? combination}` }));
|
|
170
|
+
}
|
|
171
|
+
shortcutlistnode.append(list);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
shortcutlistnode.textContent = error instanceof Error ? error.message : String(error);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async function saveshortcut() {
|
|
177
|
+
const text = shortcuteditinput?.value.trim() ?? "";
|
|
178
|
+
const split = text.split(":");
|
|
179
|
+
const command = split[0]?.trim() ?? "";
|
|
180
|
+
const combination = split.slice(1).join(":").trim();
|
|
181
|
+
if (command === "" || combination === "") {
|
|
182
|
+
status("The shortcut edit needs its command and its combination, such as pauserun: alt+q.", true);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
await request({ kind: "views", shortcut: { edit: { command, text: combination } } });
|
|
186
|
+
status(`The ${command} shortcut now binds ${combination}; the command keeps its palette gates.`);
|
|
187
|
+
await rendershortcuts();
|
|
188
|
+
}
|
|
189
|
+
async function rendertourstops() {
|
|
190
|
+
if (!tourstopsnode) return;
|
|
191
|
+
try {
|
|
192
|
+
const result = await request({ kind: "views", tour: { stops: true } });
|
|
193
|
+
tourstopsnode.replaceChildren();
|
|
194
|
+
const list = document.createElement("ol");
|
|
195
|
+
list.className = "audit";
|
|
196
|
+
for (const stop of result.stops ?? []) list.append(Object.assign(document.createElement("li"), { textContent: `${stop.title} (${stop.surface}): ${stop.body}` }));
|
|
197
|
+
tourstopsnode.append(list);
|
|
198
|
+
} catch (error) {
|
|
199
|
+
tourstopsnode.textContent = error instanceof Error ? error.message : String(error);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async function exportbundle() {
|
|
203
|
+
const result = await request({ kind: "views", importexport: { export: true } });
|
|
204
|
+
if (bundlenode) bundlenode.textContent = `The bundle of the profile ${result.payload.profile} carries ${result.payload.contents.siteprofiles.length} site profile${result.payload.contents.siteprofiles.length === 1 ? "" : "s"} and ${result.payload.contents.notes.length} note${result.payload.contents.notes.length === 1 ? "" : "s"}; ${result.payload.exclusions.join(" and ")} never enter any bundle.`;
|
|
205
|
+
status("The settings bundle exported with its exclusion list.");
|
|
206
|
+
}
|
|
207
|
+
async function importbundle() {
|
|
208
|
+
const text = window.prompt("Paste the importexport bundle json") ?? "";
|
|
209
|
+
if (text.trim() === "") return;
|
|
210
|
+
const parsed = JSON.parse(text);
|
|
211
|
+
const validation = await request({ kind: "views", importexport: { validate: parsed } });
|
|
212
|
+
if (!validation.ok) {
|
|
213
|
+
status(validation.reason, true);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const applied = await request({ kind: "views", importexport: { apply: { preferences: parsed.contents?.preferences ?? {} } } });
|
|
217
|
+
status(`The bundle applied ${applied.applied.length} preference key${applied.applied.length === 1 ? "" : "s"} after its validation passed.`);
|
|
218
|
+
await loadfinishing();
|
|
219
|
+
}
|
|
220
|
+
function wir(dropimportzone) {
|
|
221
|
+
dropimportzone.addEventListener("dragover", (event) => {
|
|
222
|
+
event.preventDefault();
|
|
223
|
+
});
|
|
224
|
+
dropimportzone.addEventListener("drop", (event) => {
|
|
225
|
+
event.preventDefault();
|
|
226
|
+
const file = event.dataTransfer?.files[0];
|
|
227
|
+
if (file === void 0) return;
|
|
228
|
+
void file.text().then((head) => request({ kind: "views", dropimport: { file: { filename: file.name, bytes: file.size, head: head.slice(0, 2e3) } } })).then((result) => {
|
|
229
|
+
const session = result.session;
|
|
230
|
+
status(`The dropimport detected the ${session.kind} kind of ${session.filename}; the import path takes the file from here.`);
|
|
231
|
+
}).catch((error) => status(error instanceof Error ? error.message : String(error), true));
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
if (dropzonenode) wir(dropzonenode);
|
|
235
|
+
document.querySelector("#finishingsettings")?.addEventListener("click", () => {
|
|
236
|
+
void savefinishing().catch((error) => status(error instanceof Error ? error.message : String(error), true));
|
|
237
|
+
});
|
|
238
|
+
document.querySelector("#shortcutsettings")?.addEventListener("click", () => {
|
|
239
|
+
void saveshortcut().catch((error) => status(error instanceof Error ? error.message : String(error), true));
|
|
240
|
+
});
|
|
241
|
+
document.querySelector("#exportbundle")?.addEventListener("click", () => {
|
|
242
|
+
void exportbundle().catch((error) => status(error instanceof Error ? error.message : String(error), true));
|
|
243
|
+
});
|
|
244
|
+
document.querySelector("#importbundle")?.addEventListener("click", () => {
|
|
245
|
+
void importbundle().catch((error) => status(error instanceof Error ? error.message : String(error), true));
|
|
246
|
+
});
|
|
247
|
+
document.querySelector("#replaytour")?.addEventListener("click", () => {
|
|
248
|
+
void (async () => {
|
|
249
|
+
await request({ kind: "views", tour: { replay: true } });
|
|
250
|
+
status("The featuretour replays with its datagrid, compareviewer and pickeroverlay stops.");
|
|
251
|
+
})().catch((error) => status(error instanceof Error ? error.message : String(error), true));
|
|
252
|
+
});
|
|
253
|
+
document.querySelector("#recalltips")?.addEventListener("click", () => {
|
|
254
|
+
void (async () => {
|
|
255
|
+
await request({ kind: "views", tips: { recall: true } });
|
|
256
|
+
status("The guidedtips recalled; they show again during the picker sessions.");
|
|
257
|
+
})().catch((error) => status(error instanceof Error ? error.message : String(error), true));
|
|
258
|
+
});
|
|
259
|
+
themepreferenceselect?.addEventListener("change", () => {
|
|
260
|
+
void applythemelive().catch(() => {
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
void loadfinishing();
|
|
264
|
+
void applythemelive();
|
|
265
|
+
void rendershortcuts();
|
|
266
|
+
void rendertourstops();
|
|
267
|
+
var syncproviderlist = document.querySelector("#syncproviderlist");
|
|
268
|
+
var syncconflictsroot = document.querySelector("#syncconflicts");
|
|
269
|
+
var syncendpointinput = document.querySelector("#syncendpoint");
|
|
270
|
+
var syncproviderselect = document.querySelector("#syncprovider");
|
|
271
|
+
async function rendersyncbridge() {
|
|
272
|
+
if (syncproviderlist) {
|
|
273
|
+
try {
|
|
274
|
+
const providers = await request({ kind: "ecosystem", sync: { providers: true } });
|
|
275
|
+
const hooks = await request({ kind: "ecosystem", sync: { hooks: true } });
|
|
276
|
+
syncproviderlist.replaceChildren();
|
|
277
|
+
for (const provider of providers.providers) {
|
|
278
|
+
const row = document.createElement("p");
|
|
279
|
+
row.textContent = `${provider.label}: ${provider.note}`;
|
|
280
|
+
syncproviderlist.append(row);
|
|
281
|
+
}
|
|
282
|
+
for (const hook of hooks.hooks) {
|
|
283
|
+
const row = document.createElement("p");
|
|
284
|
+
row.textContent = `${hook.provider} hook \u2014 ${hook.endpoint} \u2014 ${hook.direction} \u2014 ${hook.optin ? "opted in" : "opted out"} \u2014 ${hook.state}`;
|
|
285
|
+
row.append(" ", (() => {
|
|
286
|
+
const element = document.createElement("button");
|
|
287
|
+
element.type = "button";
|
|
288
|
+
element.className = "secondary";
|
|
289
|
+
element.textContent = hook.optin ? "Opt out" : "Opt in";
|
|
290
|
+
element.addEventListener("click", () => {
|
|
291
|
+
void request({ kind: "ecosystem", sync: { hook: { optin: { hookid: hook.id, optin: !hook.optin } } } }).then(() => rendersyncbridge()).catch((error) => status(error instanceof Error ? error.message : String(error), true));
|
|
292
|
+
});
|
|
293
|
+
return element;
|
|
294
|
+
})());
|
|
295
|
+
syncproviderlist.append(row);
|
|
296
|
+
}
|
|
297
|
+
} catch (error) {
|
|
298
|
+
syncproviderlist.textContent = error instanceof Error ? error.message : String(error);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (syncconflictsroot) {
|
|
302
|
+
try {
|
|
303
|
+
const conflicts = await request({ kind: "ecosystem", sync: { conflicts: true } });
|
|
304
|
+
syncconflictsroot.replaceChildren();
|
|
305
|
+
if (conflicts.conflicts.length === 0) {
|
|
306
|
+
syncconflictsroot.textContent = "No syncbridge conflict stands open; both versions surface here whenever digests differ.";
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
for (const conflict of conflicts.conflicts) {
|
|
310
|
+
const row = document.createElement("p");
|
|
311
|
+
row.textContent = `${conflict.manifestid}: local ${conflict.local.version} vs remote ${conflict.remote.version}${conflict.resolution !== void 0 ? ` \u2014 resolved ${conflict.resolution}` : ""}`;
|
|
312
|
+
if (conflict.resolution === void 0) for (const resolution of ["local", "remote", "merge"]) {
|
|
313
|
+
const element = document.createElement("button");
|
|
314
|
+
element.type = "button";
|
|
315
|
+
element.className = "secondary";
|
|
316
|
+
element.textContent = resolution;
|
|
317
|
+
element.addEventListener("click", () => {
|
|
318
|
+
void request({ kind: "ecosystem", sync: { resolve: { conflictid: conflict.id, resolution } } }).then(() => rendersyncbridge()).catch((error) => status(error instanceof Error ? error.message : String(error), true));
|
|
319
|
+
});
|
|
320
|
+
row.append(" ", element);
|
|
321
|
+
}
|
|
322
|
+
syncconflictsroot.append(row);
|
|
323
|
+
}
|
|
324
|
+
} catch (error) {
|
|
325
|
+
syncconflictsroot.textContent = error instanceof Error ? error.message : String(error);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
document.querySelector("#synccreate")?.addEventListener("click", () => {
|
|
330
|
+
void (async () => {
|
|
331
|
+
const endpoint = syncendpointinput?.value.trim() ?? "";
|
|
332
|
+
if (endpoint === "") throw new Error("The syncbridge hook needs its user configured endpoint; no provider address is ever hardcoded.");
|
|
333
|
+
await request({ kind: "ecosystem", sync: { hook: { add: { provider: syncproviderselect?.value === "web" ? "web" : "file", direction: "both", endpoint } } } });
|
|
334
|
+
if (syncendpointinput) syncendpointinput.value = "";
|
|
335
|
+
status(`The syncbridge hook of ${endpoint} was added opted out; no hook ever defaults on.`);
|
|
336
|
+
await rendersyncbridge();
|
|
337
|
+
})().catch((error) => status(error instanceof Error ? error.message : String(error), true));
|
|
338
|
+
});
|
|
339
|
+
void rendersyncbridge();
|
|
118
340
|
//# sourceMappingURL=optionspage.js.map
|