@senguoyun/dsh-arkme 0.1.57 → 0.1.59
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/docs/harness-activity.md +18 -0
- package/docs/harness-quick-create.md +13 -0
- package/docs/harness-session-dropdown.md +65 -0
- package/docs/harness-sidebar-settings.md +9 -0
- package/docs/harness-trajectory-navigation.md +28 -0
- package/docs/plugin-release.md +1 -1
- package/lib/client.js +643 -31
- package/lib/client.js.map +1 -1
- package/lib/harness-sidebar-client.js +840 -0
- package/lib/harness-trajectory-client.js +254 -0
- package/lib/index.js +117 -8
- package/lib/persistent-extension.js +1 -1
- package/lib/{persistent-runtime-BnDj8qcY.js → persistent-runtime-ByM3JB3z.js} +15 -7
- package/lib/types/arkme-service.d.ts.map +1 -1
- package/lib/types/client/ArkmeCallSurface.d.ts.map +1 -1
- package/lib/types/client/ArkmePersistentShell.d.ts.map +1 -1
- package/lib/types/client/ArkmeQuickAdd.d.ts +5 -2
- package/lib/types/client/ArkmeQuickAdd.d.ts.map +1 -1
- package/lib/types/client/ArkmeVirtualWorkspace.d.ts +3 -1
- package/lib/types/client/ArkmeVirtualWorkspace.d.ts.map +1 -1
- package/lib/types/client/DeepSeekHarnessSurface.d.ts +2 -1
- package/lib/types/client/DeepSeekHarnessSurface.d.ts.map +1 -1
- package/lib/types/client/frame-menu-dismissal.d.ts +3 -0
- package/lib/types/client/frame-menu-dismissal.d.ts.map +1 -0
- package/lib/types/client/harness-activity-reporter.d.ts +4 -0
- package/lib/types/client/harness-activity-reporter.d.ts.map +1 -0
- package/lib/types/client/harness-activity.d.ts +49 -0
- package/lib/types/client/harness-activity.d.ts.map +1 -0
- package/lib/types/client/harness-new-session.d.ts +3 -0
- package/lib/types/client/harness-new-session.d.ts.map +1 -0
- package/lib/types/client/harness-session-dropdown.d.ts +9 -0
- package/lib/types/client/harness-session-dropdown.d.ts.map +1 -0
- package/lib/types/client/harness-session-hover.d.ts +3 -0
- package/lib/types/client/harness-session-hover.d.ts.map +1 -0
- package/lib/types/client/harness-session-list-default.d.ts +12 -0
- package/lib/types/client/harness-session-list-default.d.ts.map +1 -0
- package/lib/types/client/harness-session-menu-bridge.d.ts +16 -0
- package/lib/types/client/harness-session-menu-bridge.d.ts.map +1 -0
- package/lib/types/client/harness-session-summary.d.ts +28 -0
- package/lib/types/client/harness-session-summary.d.ts.map +1 -0
- package/lib/types/client/harness-sidebar-client.d.ts +5 -0
- package/lib/types/client/harness-sidebar-client.d.ts.map +1 -0
- package/lib/types/client/harness-trajectory-client.d.ts +5 -0
- package/lib/types/client/harness-trajectory-client.d.ts.map +1 -0
- package/lib/types/client/harness-trajectory-menu.d.ts +7 -0
- package/lib/types/client/harness-trajectory-menu.d.ts.map +1 -0
- package/lib/types/client/live-photo-playback.d.ts.map +1 -1
- package/lib/types/client/use-harness-activity.d.ts +3 -0
- package/lib/types/client/use-harness-activity.d.ts.map +1 -0
- package/lib/types/dsh-remote/session-persistence.d.ts +4 -0
- package/lib/types/dsh-remote/session-persistence.d.ts.map +1 -0
- package/lib/types/dsh-remote/transport-fragment.d.ts.map +1 -1
- package/lib/types/harness-embed-contract.d.ts +2 -0
- package/lib/types/harness-embed-contract.d.ts.map +1 -1
- package/lib/types/harness-embed-route.d.ts +3 -1
- package/lib/types/harness-embed-route.d.ts.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/services/call-history-service.d.ts +4 -2
- package/lib/types/services/call-history-service.d.ts.map +1 -1
- package/lib/types/services/chat-service.d.ts.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,840 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@senguoyun/dsh-arkme/harness-sidebar",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react = require("react");
|
|
8
|
+
//#region src/client/harness-session-list-default.ts
|
|
9
|
+
const SESSION_LIST_DEFAULT_MARKER = "arkme.harness.session-list-default.v1";
|
|
10
|
+
function adoptFlatSessionDefault(groupBy, setGroupBy, storage) {
|
|
11
|
+
if (groupBy !== "workspace" && groupBy !== "flat") return false;
|
|
12
|
+
try {
|
|
13
|
+
if (storage.getItem("arkme.harness.session-list-default.v1") === "1") return true;
|
|
14
|
+
} catch {}
|
|
15
|
+
if (groupBy !== "flat") setGroupBy("flat");
|
|
16
|
+
try {
|
|
17
|
+
storage.setItem(SESSION_LIST_DEFAULT_MARKER, "1");
|
|
18
|
+
} catch {}
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Share the native root-scoped store through the public slot/store contract.
|
|
23
|
+
* A renderless footer contribution receives its real actions and useStore.
|
|
24
|
+
* Do not replace the browser, copy its state, call private store resolvers,
|
|
25
|
+
* or rewrite the native persistence key/schema. Only grouping gets a one-time
|
|
26
|
+
* initial choice; ordering (including an existing manual choice) stays native.
|
|
27
|
+
*/
|
|
28
|
+
function installHarnessSessionListDefault(ctx) {
|
|
29
|
+
const slots = ctx.slots;
|
|
30
|
+
if (typeof slots.entriesOfSlot !== "function" || typeof slots.subscribe !== "function" || typeof slots.spec !== "function") return () => {};
|
|
31
|
+
let initialized = false;
|
|
32
|
+
function Defaults({ actions, useStore }) {
|
|
33
|
+
const groupBy = useStore((state) => state.groupBy);
|
|
34
|
+
(0, react.useLayoutEffect)(() => {
|
|
35
|
+
if (initialized) return;
|
|
36
|
+
initialized = adoptFlatSessionDefault(groupBy, actions.setGroupBy, {
|
|
37
|
+
getItem: (key) => window.localStorage.getItem(key),
|
|
38
|
+
setItem: (key, value) => window.localStorage.setItem(key, value)
|
|
39
|
+
});
|
|
40
|
+
}, [actions, groupBy]);
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return slots.inject("sidebar.footer.action", () => {
|
|
44
|
+
let mounted;
|
|
45
|
+
let dispose;
|
|
46
|
+
const sync = () => {
|
|
47
|
+
const store = slots.entriesOfSlot("sidebar.workspaces")[0]?.store;
|
|
48
|
+
const next = slots.spec("sidebar.workspaces")?.scope === "root" && slots.spec("sidebar.footer.action")?.scope === "root" && store !== void 0 && typeof store !== "function" && typeof store.create === "function" && typeof store.spec?.actions?.setGroupBy === "function" ? store : void 0;
|
|
49
|
+
if (next === mounted) return;
|
|
50
|
+
dispose?.();
|
|
51
|
+
dispose = void 0;
|
|
52
|
+
mounted = next;
|
|
53
|
+
if (next) dispose = slots.register({
|
|
54
|
+
name: "sidebar.footer.action",
|
|
55
|
+
id: "arkme-session-list-default",
|
|
56
|
+
store: next
|
|
57
|
+
}, Defaults);
|
|
58
|
+
};
|
|
59
|
+
const unsubscribe = slots.subscribe("sidebar.workspaces", sync);
|
|
60
|
+
sync();
|
|
61
|
+
return () => {
|
|
62
|
+
unsubscribe();
|
|
63
|
+
dispose?.();
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region src/client/harness-session-menu-bridge.ts
|
|
69
|
+
/** Private presentation bridge between Arkme and its existing same-origin Harness frame. */
|
|
70
|
+
const HARNESS_MENU_OPEN = "arkme:harness-menu-open";
|
|
71
|
+
const HARNESS_MENU_CLOSE = "arkme:harness-menu-close";
|
|
72
|
+
const HARNESS_MENU_POSITION = "arkme:harness-menu-position";
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/client/harness-session-dropdown.ts
|
|
75
|
+
const PREFIX = "data-arkme-session-";
|
|
76
|
+
const HEADER = "[data-slot=\"conversation.session.header\"] > header";
|
|
77
|
+
const SIDEBAR = "[data-slot=\"sidebar\"]";
|
|
78
|
+
const COPY = [{
|
|
79
|
+
expand: "打开侧边栏",
|
|
80
|
+
collapse: "收起侧边栏",
|
|
81
|
+
create: "新建会话",
|
|
82
|
+
blank: "新会话",
|
|
83
|
+
sessions: "会话",
|
|
84
|
+
choose: "切换会话",
|
|
85
|
+
search: "搜索会话",
|
|
86
|
+
view: "视图选项",
|
|
87
|
+
add: "添加工作区"
|
|
88
|
+
}, {
|
|
89
|
+
expand: "Open sidebar",
|
|
90
|
+
collapse: "Collapse sidebar",
|
|
91
|
+
create: "New session",
|
|
92
|
+
blank: "New Session",
|
|
93
|
+
sessions: "Sessions",
|
|
94
|
+
choose: "Switch session",
|
|
95
|
+
search: "Search sessions",
|
|
96
|
+
view: "View options",
|
|
97
|
+
add: "Add workspace"
|
|
98
|
+
}];
|
|
99
|
+
function mark(node, key, value = "") {
|
|
100
|
+
if (node.getAttribute(PREFIX + key) !== value) node.setAttribute(PREFIX + key, value);
|
|
101
|
+
}
|
|
102
|
+
function variable(node, key, value) {
|
|
103
|
+
if (node.style.getPropertyValue(key) !== value) node.style.setProperty(key, value);
|
|
104
|
+
}
|
|
105
|
+
/** Recognize only the native shell contract. Unknown upstream layouts keep their sidebar. */
|
|
106
|
+
function shell(doc) {
|
|
107
|
+
const slot = doc.querySelector(SIDEBAR);
|
|
108
|
+
const column = slot?.parentElement;
|
|
109
|
+
const frame = column?.parentElement;
|
|
110
|
+
const root = slot?.firstElementChild;
|
|
111
|
+
const center = frame?.children[1];
|
|
112
|
+
const right = frame?.children[2];
|
|
113
|
+
const tracks = frame?.style.gridTemplateColumns.match(/^\S+px minmax\(0(?:px)?, 1fr\) (\S+px)$/);
|
|
114
|
+
if (!root || !column || !frame || !center || !tracks || frame.firstElementChild !== column || !right?.hasAttribute("data-rightbar-col") || !root.querySelector("[data-slot=\"sidebar.workspaces\"]")) return;
|
|
115
|
+
const brand = root.firstElementChild;
|
|
116
|
+
const toggle = brand?.querySelector("button[aria-label]:last-child");
|
|
117
|
+
const copy = COPY.find((c) => c.expand === toggle?.getAttribute("aria-label") || c.collapse === toggle?.getAttribute("aria-label"));
|
|
118
|
+
const create = [...root.querySelectorAll(":scope > button[aria-label]")].find((button) => button.getAttribute("aria-label") === copy?.create);
|
|
119
|
+
if (!brand || !toggle || !copy || !create) return;
|
|
120
|
+
return {
|
|
121
|
+
slot,
|
|
122
|
+
column,
|
|
123
|
+
frame,
|
|
124
|
+
root,
|
|
125
|
+
center,
|
|
126
|
+
brand,
|
|
127
|
+
toggle,
|
|
128
|
+
create,
|
|
129
|
+
copy,
|
|
130
|
+
rightTrack: tracks[1]
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/** Recognize the official workspace toolbar without moving any React-owned node. */
|
|
134
|
+
function compactTools(value) {
|
|
135
|
+
const header = (value.root.querySelector("[data-slot=\"sidebar.workspaces\"]")?.firstElementChild)?.firstElementChild;
|
|
136
|
+
if (!header) return [];
|
|
137
|
+
const buttons = [...header.querySelectorAll("button[aria-label]")];
|
|
138
|
+
const search = buttons.find((button) => button.getAttribute("aria-label") === value.copy.search);
|
|
139
|
+
const view = buttons.find((button) => button.getAttribute("aria-label") === value.copy.view);
|
|
140
|
+
const children = [...header.children];
|
|
141
|
+
const searchArea = search && children.find((child) => child.contains(search));
|
|
142
|
+
const actions = view && children.find((child) => child.contains(view));
|
|
143
|
+
const heading = header.firstElementChild;
|
|
144
|
+
if (!searchArea || !actions || searchArea === actions || !heading || heading.tagName !== "SPAN" || !header.querySelector("[data-slot=\"sidebar.workspaces.directoryFlow\"]") || [...actions.querySelectorAll("button")].some((button) => ![value.copy.view, value.copy.add].some((label) => label === button.getAttribute("aria-label")))) return [];
|
|
145
|
+
return [
|
|
146
|
+
[header, "tools-header"],
|
|
147
|
+
[heading, "tools-label"],
|
|
148
|
+
[searchArea, "tools-search"],
|
|
149
|
+
[actions, "tools-actions"]
|
|
150
|
+
];
|
|
151
|
+
}
|
|
152
|
+
/** Keep the native title and action slot in their React ancestry; only stack their layout. */
|
|
153
|
+
function summaryLayout(header) {
|
|
154
|
+
if (!header || header.getAttribute("aria-hidden") === "true") return [];
|
|
155
|
+
const actions = header.querySelector("[data-slot=\"conversation.session.header.actions\"]")?.parentElement;
|
|
156
|
+
const cluster = actions?.parentElement;
|
|
157
|
+
const row = cluster?.parentElement;
|
|
158
|
+
const nav = cluster?.querySelector(":scope > nav");
|
|
159
|
+
if (!actions || !cluster || !row || !nav || row.parentElement !== header || cluster.children.length !== 2 || cluster.firstElementChild !== nav || cluster.lastElementChild !== actions) return [];
|
|
160
|
+
return [
|
|
161
|
+
[row, "title-row"],
|
|
162
|
+
[cluster, "title-cluster"],
|
|
163
|
+
[actions, "summary"],
|
|
164
|
+
[nav, "title-nav"]
|
|
165
|
+
];
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Presentation-only adapter: the native SidebarRoot, WorkspaceBrowser, their
|
|
169
|
+
* live slots, handlers, menus, stores and React ancestry all stay mounted in
|
|
170
|
+
* place. No copied session list or private API. Only the sidebar's position,
|
|
171
|
+
* the first grid track, and the title trigger are adapted; disposal restores
|
|
172
|
+
* them. There is no public title/zero-width-sidebar slot in DSH 0.1.5-rc.2.
|
|
173
|
+
*/
|
|
174
|
+
function installHarnessSessionDropdown(doc) {
|
|
175
|
+
const win = doc.defaultView;
|
|
176
|
+
if (!win || !doc.body) return () => {};
|
|
177
|
+
let current;
|
|
178
|
+
let title;
|
|
179
|
+
let open = false;
|
|
180
|
+
let external;
|
|
181
|
+
let disposed = false;
|
|
182
|
+
let scheduled = false;
|
|
183
|
+
const rowFocusCleanups = /* @__PURE__ */ new Map();
|
|
184
|
+
let toolbarMarks = [];
|
|
185
|
+
let summaryMarks = [];
|
|
186
|
+
let initiallyCollapsed = false;
|
|
187
|
+
let expandedByUs = false;
|
|
188
|
+
let columnBefore = {
|
|
189
|
+
role: null,
|
|
190
|
+
label: null,
|
|
191
|
+
inert: false
|
|
192
|
+
};
|
|
193
|
+
const host = doc.createElement("span");
|
|
194
|
+
mark(host, "anchor");
|
|
195
|
+
const trigger = doc.createElement("button");
|
|
196
|
+
trigger.type = "button";
|
|
197
|
+
trigger.setAttribute("aria-haspopup", "dialog");
|
|
198
|
+
trigger.setAttribute("aria-expanded", "false");
|
|
199
|
+
mark(trigger, "trigger");
|
|
200
|
+
const label = doc.createElement("span");
|
|
201
|
+
const arrow = doc.createElement("span");
|
|
202
|
+
const chevron = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
203
|
+
chevron.setAttribute("viewBox", "0 0 16 16");
|
|
204
|
+
chevron.setAttribute("width", "14");
|
|
205
|
+
chevron.setAttribute("height", "14");
|
|
206
|
+
chevron.setAttribute("fill", "none");
|
|
207
|
+
const chevronPath = doc.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
208
|
+
chevronPath.setAttribute("d", "m4 6 4 4 4-4");
|
|
209
|
+
chevronPath.setAttribute("stroke", "currentColor");
|
|
210
|
+
chevronPath.setAttribute("stroke-width", "1.5");
|
|
211
|
+
chevronPath.setAttribute("stroke-linecap", "round");
|
|
212
|
+
chevronPath.setAttribute("stroke-linejoin", "round");
|
|
213
|
+
chevron.append(chevronPath);
|
|
214
|
+
arrow.append(chevron);
|
|
215
|
+
arrow.setAttribute("aria-hidden", "true");
|
|
216
|
+
trigger.append(label, arrow);
|
|
217
|
+
host.append(trigger);
|
|
218
|
+
const style = doc.createElement("style");
|
|
219
|
+
mark(style, "style");
|
|
220
|
+
style.textContent = `
|
|
221
|
+
html[${PREFIX}preview], html[${PREFIX}preview] body { background: transparent !important; }
|
|
222
|
+
html[${PREFIX}preview] body { visibility: hidden !important; }
|
|
223
|
+
html[${PREFIX}preview] [${PREFIX}column], html[${PREFIX}preview] [role="menu"],
|
|
224
|
+
html[${PREFIX}preview] [role="dialog"], html[${PREFIX}preview] [role="alertdialog"] { visibility: visible !important; }
|
|
225
|
+
[${PREFIX}frame] { grid-template-columns: 0px minmax(0, 1fr) var(--arkme-session-rightbar) !important; }
|
|
226
|
+
[${PREFIX}frame] > :nth-child(2) { grid-column: 2; }
|
|
227
|
+
[${PREFIX}frame] > [data-rightbar-col] { grid-column: 3; }
|
|
228
|
+
[${PREFIX}frame] > [data-side="sidebar"] { display: none !important; }
|
|
229
|
+
[${PREFIX}column] {
|
|
230
|
+
position: fixed !important; left: var(--arkme-session-left); top: var(--arkme-session-top);
|
|
231
|
+
width: var(--arkme-session-width); height: auto; max-height: var(--arkme-session-height); z-index: 80;
|
|
232
|
+
border: 1px solid var(--dsw-alias-border-l3, #e5e7eb); border-radius: 10px;
|
|
233
|
+
box-shadow: 0 10px 32px #0000001f; box-sizing: border-box; overflow: visible !important;
|
|
234
|
+
display: none !important; pointer-events: none;
|
|
235
|
+
}
|
|
236
|
+
[${PREFIX}column][${PREFIX}open] { display: block !important; pointer-events: auto; }
|
|
237
|
+
[${PREFIX}root] { width: 100% !important; height: auto; max-height: var(--arkme-session-height); border-radius: 9px; padding: 8px 10px !important; position: relative; }
|
|
238
|
+
[${PREFIX}brand], [${PREFIX}native-title] { display: none !important; }
|
|
239
|
+
[${PREFIX}create] { order: 10; flex-shrink: 0; margin: 8px 0 0 !important; }
|
|
240
|
+
[${PREFIX}root][${PREFIX}compact-tools] [${PREFIX}create] { width: calc(100% - var(--arkme-session-tools-width) - 8px); }
|
|
241
|
+
[${PREFIX}tools-header] { display: contents !important; }
|
|
242
|
+
[${PREFIX}tools-label], [${PREFIX}tools-search] { display: none !important; }
|
|
243
|
+
[${PREFIX}tools-actions] {
|
|
244
|
+
position: absolute !important; bottom: 8px; right: 10px; height: 38px;
|
|
245
|
+
display: flex !important; align-items: center; gap: 4px; max-width: none !important;
|
|
246
|
+
opacity: 1 !important; visibility: visible !important; transform: none !important;
|
|
247
|
+
}
|
|
248
|
+
[${PREFIX}anchor] { display: inline-flex; width: max-content; min-width: 0; max-width: 100%; }
|
|
249
|
+
[${PREFIX}title-row] { align-items: flex-start; }
|
|
250
|
+
[${PREFIX}title-cluster] { flex-direction: column; align-items: stretch; gap: 4px; }
|
|
251
|
+
[${PREFIX}title-nav] { width: 100%; }
|
|
252
|
+
[${PREFIX}summary] { min-width: 0; min-height: 18px; gap: 0; flex-wrap: wrap; color: var(--dsw-alias-label-secondary, #626872); font-size: 12px; line-height: 18px; }
|
|
253
|
+
[${PREFIX}summary] [data-slot="conversation.session.header.actions"] > * { font-size: inherit; line-height: inherit; }
|
|
254
|
+
[${PREFIX}turn-count] { display: inline-flex; align-items: center; white-space: nowrap; }
|
|
255
|
+
[${PREFIX}summary] [data-slot="conversation.session.header.actions"] > * + [${PREFIX}turn-count]::before { content: '·'; margin: 0 7px; }
|
|
256
|
+
[${PREFIX}anchor][${PREFIX}fallback] { width: max-content; position: absolute; top: 10px; left: 20px; z-index: 10; max-width: calc(100% - 40px); }
|
|
257
|
+
[${PREFIX}trigger] {
|
|
258
|
+
display: inline-flex; align-items: stretch; gap: 0; min-width: 0; max-width: 100%; height: 30px;
|
|
259
|
+
border: 1px solid var(--dsw-alias-border-l3, #d4d6da); border-radius: 10px; padding: 0; margin-left: 0; box-sizing: border-box;
|
|
260
|
+
color: var(--dsw-alias-label-primary, inherit); background: var(--dsw-alias-button-elevated-fill, #fff); font: inherit;
|
|
261
|
+
font-size: 16px; font-weight: 600; line-height: 28px; overflow: hidden; cursor: pointer;
|
|
262
|
+
}
|
|
263
|
+
[${PREFIX}trigger] > span:first-child { padding: 0 10px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
264
|
+
[${PREFIX}trigger] > span:last-child {
|
|
265
|
+
color: var(--dsw-alias-label-secondary, #626872); flex: none; width: 28px;
|
|
266
|
+
border-left: 1px solid var(--dsw-alias-border-l3, #d4d6da); display: flex; align-items: center; justify-content: center;
|
|
267
|
+
}
|
|
268
|
+
[${PREFIX}trigger][aria-expanded="true"] svg { transform: rotate(180deg); }
|
|
269
|
+
[${PREFIX}trigger]:hover, [${PREFIX}trigger][aria-expanded="true"] { background: var(--dsw-alias-interactive-bg-hover, #f3f4f6); }
|
|
270
|
+
[${PREFIX}trigger]:focus-visible, [${PREFIX}column] [role="treeitem"]:focus-visible { outline: 2px solid #4c70ef; outline-offset: -2px; }
|
|
271
|
+
`;
|
|
272
|
+
doc.head.append(style);
|
|
273
|
+
function close(focus = false) {
|
|
274
|
+
if (!open) return;
|
|
275
|
+
const request = external;
|
|
276
|
+
external = void 0;
|
|
277
|
+
open = false;
|
|
278
|
+
current?.column.removeAttribute("data-arkme-session-open");
|
|
279
|
+
if (current) current.column.inert = true;
|
|
280
|
+
trigger.setAttribute("aria-expanded", "false");
|
|
281
|
+
if (request) request.onClose(focus);
|
|
282
|
+
else if (focus && trigger.isConnected) trigger.focus();
|
|
283
|
+
}
|
|
284
|
+
function restore() {
|
|
285
|
+
close();
|
|
286
|
+
for (const cleanup of rowFocusCleanups.values()) cleanup();
|
|
287
|
+
for (const [node, key] of toolbarMarks) node.removeAttribute(PREFIX + key);
|
|
288
|
+
toolbarMarks = [];
|
|
289
|
+
for (const [node, key] of summaryMarks) node.removeAttribute(PREFIX + key);
|
|
290
|
+
summaryMarks = [];
|
|
291
|
+
title?.removeAttribute("data-arkme-session-native-title");
|
|
292
|
+
title = void 0;
|
|
293
|
+
host.remove();
|
|
294
|
+
if (current) {
|
|
295
|
+
const { frame, column, root, brand, create, toggle } = current;
|
|
296
|
+
for (const [node, key] of [
|
|
297
|
+
[frame, "frame"],
|
|
298
|
+
[column, "column"],
|
|
299
|
+
[root, "root"],
|
|
300
|
+
[brand, "brand"],
|
|
301
|
+
[create, "create"]
|
|
302
|
+
]) node.removeAttribute(PREFIX + key);
|
|
303
|
+
frame.style.removeProperty("--arkme-session-rightbar");
|
|
304
|
+
root.removeAttribute("data-arkme-session-compact-tools");
|
|
305
|
+
root.style.removeProperty("--arkme-session-tools-width");
|
|
306
|
+
for (const key of [
|
|
307
|
+
"left",
|
|
308
|
+
"top",
|
|
309
|
+
"width",
|
|
310
|
+
"height"
|
|
311
|
+
]) column.style.removeProperty("--arkme-session-" + key);
|
|
312
|
+
column.inert = columnBefore.inert;
|
|
313
|
+
for (const [key, value] of [["role", columnBefore.role], ["aria-label", columnBefore.label]]) if (value === null) column.removeAttribute(key);
|
|
314
|
+
else column.setAttribute(key, value);
|
|
315
|
+
if (expandedByUs && initiallyCollapsed && toggle.isConnected && toggle.getAttribute("aria-label") === current.copy.collapse) toggle.click();
|
|
316
|
+
}
|
|
317
|
+
current = void 0;
|
|
318
|
+
expandedByUs = false;
|
|
319
|
+
}
|
|
320
|
+
function position() {
|
|
321
|
+
if (!current) return;
|
|
322
|
+
const rect = trigger.getBoundingClientRect();
|
|
323
|
+
const width = Math.min(320, win.innerWidth - 24);
|
|
324
|
+
const anchor = external?.anchor();
|
|
325
|
+
const top = anchor ? Math.max(12, Math.min(anchor.top, win.innerHeight - Math.min(560, win.innerHeight - 24) - 12)) : Math.max(8, Math.min(rect.bottom + 7, win.innerHeight - 100));
|
|
326
|
+
variable(current.column, "--arkme-session-left", `${Math.max(12, Math.min(anchor?.left ?? rect.left, win.innerWidth - width - 12))}px`);
|
|
327
|
+
variable(current.column, "--arkme-session-top", `${top}px`);
|
|
328
|
+
variable(current.column, "--arkme-session-width", `${width}px`);
|
|
329
|
+
variable(current.column, "--arkme-session-height", `${Math.min(560, win.innerHeight - top - 12)}px`);
|
|
330
|
+
external?.onLayout();
|
|
331
|
+
}
|
|
332
|
+
function sync() {
|
|
333
|
+
scheduled = false;
|
|
334
|
+
if (disposed) return;
|
|
335
|
+
const next = shell(doc);
|
|
336
|
+
if (!next) {
|
|
337
|
+
restore();
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
if (current?.root !== next.root || current.frame !== next.frame) {
|
|
341
|
+
restore();
|
|
342
|
+
current = next;
|
|
343
|
+
columnBefore = {
|
|
344
|
+
role: next.column.getAttribute("role"),
|
|
345
|
+
label: next.column.getAttribute("aria-label"),
|
|
346
|
+
inert: next.column.inert === true
|
|
347
|
+
};
|
|
348
|
+
initiallyCollapsed = next.toggle.getAttribute("aria-label") === next.copy.expand;
|
|
349
|
+
current.column.inert = true;
|
|
350
|
+
current.column.setAttribute("role", "dialog");
|
|
351
|
+
current.column.setAttribute("aria-label", next.copy.choose);
|
|
352
|
+
} else current = next;
|
|
353
|
+
mark(next.frame, "frame");
|
|
354
|
+
mark(next.column, "column");
|
|
355
|
+
mark(next.root, "root");
|
|
356
|
+
mark(next.brand, "brand");
|
|
357
|
+
mark(next.create, "create");
|
|
358
|
+
const nextTools = compactTools(next);
|
|
359
|
+
for (const [node, key] of toolbarMarks) if (!nextTools.some(([nextNode, nextKey]) => node === nextNode && key === nextKey)) node.removeAttribute(PREFIX + key);
|
|
360
|
+
toolbarMarks = nextTools;
|
|
361
|
+
for (const [node, key] of toolbarMarks) mark(node, key);
|
|
362
|
+
const tools = toolbarMarks.find(([, key]) => key === "tools-actions")?.[0];
|
|
363
|
+
if (tools) {
|
|
364
|
+
mark(next.root, "compact-tools");
|
|
365
|
+
const count = tools.querySelectorAll("button").length;
|
|
366
|
+
variable(next.root, "--arkme-session-tools-width", `${count * 28 + Math.max(0, count - 1) * 4}px`);
|
|
367
|
+
} else {
|
|
368
|
+
next.root.removeAttribute("data-arkme-session-compact-tools");
|
|
369
|
+
next.root.style.removeProperty("--arkme-session-tools-width");
|
|
370
|
+
}
|
|
371
|
+
variable(next.frame, "--arkme-session-rightbar", next.rightTrack);
|
|
372
|
+
const header = next.center.querySelector(HEADER);
|
|
373
|
+
const nextSummary = summaryLayout(header ?? void 0);
|
|
374
|
+
for (const [node, key] of summaryMarks) if (!nextSummary.some(([nextNode, nextKey]) => node === nextNode && key === nextKey)) node.removeAttribute(PREFIX + key);
|
|
375
|
+
summaryMarks = nextSummary;
|
|
376
|
+
for (const [node, key] of summaryMarks) mark(node, key);
|
|
377
|
+
const visibleHeader = header && header.getAttribute("aria-hidden") !== "true";
|
|
378
|
+
const nativeTitle = visibleHeader ? header?.querySelector("nav > :last-child > button:disabled") : void 0;
|
|
379
|
+
if (title !== nativeTitle) {
|
|
380
|
+
title?.removeAttribute("data-arkme-session-native-title");
|
|
381
|
+
title = nativeTitle ?? void 0;
|
|
382
|
+
}
|
|
383
|
+
if (title?.parentElement) {
|
|
384
|
+
mark(title, "native-title");
|
|
385
|
+
host.removeAttribute("data-arkme-session-fallback");
|
|
386
|
+
if (host.parentElement !== title.parentElement) title.before(host);
|
|
387
|
+
} else if (visibleHeader && header.querySelector("nav")) {
|
|
388
|
+
const nav = header.querySelector("nav");
|
|
389
|
+
host.removeAttribute("data-arkme-session-fallback");
|
|
390
|
+
if (host.parentElement !== nav) nav.prepend(host);
|
|
391
|
+
} else {
|
|
392
|
+
mark(host, "fallback");
|
|
393
|
+
if (host.parentElement !== next.center) next.center.append(host);
|
|
394
|
+
}
|
|
395
|
+
const text = title?.textContent?.trim() || (visibleHeader ? next.copy.sessions : next.copy.blank);
|
|
396
|
+
if (label.textContent !== text) label.textContent = text;
|
|
397
|
+
trigger.title = text;
|
|
398
|
+
trigger.setAttribute("aria-label", `${next.copy.choose}:${text}`);
|
|
399
|
+
position();
|
|
400
|
+
}
|
|
401
|
+
function schedule() {
|
|
402
|
+
if (disposed || scheduled) return;
|
|
403
|
+
scheduled = true;
|
|
404
|
+
win.queueMicrotask(sync);
|
|
405
|
+
}
|
|
406
|
+
function show(request) {
|
|
407
|
+
if (!current) return;
|
|
408
|
+
if (current.toggle.getAttribute("aria-label") === current.copy.expand) {
|
|
409
|
+
current.toggle.click();
|
|
410
|
+
expandedByUs = true;
|
|
411
|
+
}
|
|
412
|
+
open = true;
|
|
413
|
+
external = request;
|
|
414
|
+
current.column.inert = false;
|
|
415
|
+
mark(current.column, "open");
|
|
416
|
+
trigger.setAttribute("aria-expanded", request ? "false" : "true");
|
|
417
|
+
if (request) request.accepted = true;
|
|
418
|
+
position();
|
|
419
|
+
win.requestAnimationFrame(() => current?.column.querySelector("[role=\"treeitem\"][aria-selected=\"true\"]")?.scrollIntoView({ block: "nearest" }));
|
|
420
|
+
}
|
|
421
|
+
trigger.addEventListener("click", () => {
|
|
422
|
+
if (open && !external) {
|
|
423
|
+
close();
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
close();
|
|
427
|
+
show();
|
|
428
|
+
});
|
|
429
|
+
function externalOpen(event) {
|
|
430
|
+
const request = event.detail;
|
|
431
|
+
if (!request || !current) return;
|
|
432
|
+
close();
|
|
433
|
+
show(request);
|
|
434
|
+
}
|
|
435
|
+
function externalClose() {
|
|
436
|
+
if (!external) return;
|
|
437
|
+
for (const overlay of doc.querySelectorAll("[role=\"menu\"], [role=\"alertdialog\"], [role=\"dialog\"]:not([data-arkme-session-column])")) overlay.dispatchEvent(new win.KeyboardEvent("keydown", {
|
|
438
|
+
key: "Escape",
|
|
439
|
+
bubbles: true
|
|
440
|
+
}));
|
|
441
|
+
close();
|
|
442
|
+
}
|
|
443
|
+
function pointer(event) {
|
|
444
|
+
if (!open || !current) return;
|
|
445
|
+
const target = event.target;
|
|
446
|
+
if (external && target?.ownerDocument !== doc) return;
|
|
447
|
+
if (target && (host.contains(target) || current.column.contains(target))) return;
|
|
448
|
+
if (target?.closest?.("[role=\"menu\"], [role=\"dialog\"], [role=\"alertdialog\"]")) return;
|
|
449
|
+
close();
|
|
450
|
+
}
|
|
451
|
+
function click(event) {
|
|
452
|
+
if (!open || !current) return;
|
|
453
|
+
const target = event.target;
|
|
454
|
+
if (!target || !current.column.contains(target)) return;
|
|
455
|
+
const row = target.closest("[role=\"treeitem\"][aria-selected]");
|
|
456
|
+
const buttonLabel = target.closest("button")?.getAttribute("aria-label") ?? "";
|
|
457
|
+
const workspaceCreate = /^在“.+”中新建会话$/.test(buttonLabel) || /^New session in /.test(buttonLabel);
|
|
458
|
+
if (row && !target.closest("button, input, [role=\"menu\"]") || current.create.contains(target) || workspaceCreate || target.closest("nav button")) {
|
|
459
|
+
const request = external;
|
|
460
|
+
close(!request);
|
|
461
|
+
request?.onSelect();
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
function key(event) {
|
|
465
|
+
if (!open || !current) return;
|
|
466
|
+
const target = event.target;
|
|
467
|
+
if (doc.querySelector("[role=\"menu\"], [role=\"alertdialog\"], [role=\"dialog\"]:not([data-arkme-session-column])")) return;
|
|
468
|
+
if (event.key === "Escape") {
|
|
469
|
+
event.preventDefault();
|
|
470
|
+
close(true);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
if (!target || target.matches("input, textarea, [contenteditable=\"true\"]")) return;
|
|
474
|
+
if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
|
|
475
|
+
if (!host.contains(target) && !current.column.contains(target)) return;
|
|
476
|
+
const rows = [...current.column.querySelectorAll("[role=\"treeitem\"][aria-selected]")].filter((row) => row.getClientRects().length > 0);
|
|
477
|
+
if (!rows.length) return;
|
|
478
|
+
const index = rows.findIndex((row) => row === target);
|
|
479
|
+
const next = rows[index < 0 ? event.key === "ArrowDown" ? 0 : rows.length - 1 : (index + (event.key === "ArrowDown" ? 1 : -1) + rows.length) % rows.length];
|
|
480
|
+
if (!next.hasAttribute("tabindex")) {
|
|
481
|
+
next.tabIndex = -1;
|
|
482
|
+
const remove = () => {
|
|
483
|
+
next.removeAttribute("tabindex");
|
|
484
|
+
next.removeEventListener("blur", remove);
|
|
485
|
+
rowFocusCleanups.delete(next);
|
|
486
|
+
};
|
|
487
|
+
rowFocusCleanups.set(next, remove);
|
|
488
|
+
next.addEventListener("blur", remove);
|
|
489
|
+
}
|
|
490
|
+
next.focus();
|
|
491
|
+
next.scrollIntoView({ block: "nearest" });
|
|
492
|
+
event.preventDefault();
|
|
493
|
+
}
|
|
494
|
+
function rowKey(event) {
|
|
495
|
+
const target = event.target;
|
|
496
|
+
if (open && (event.key === "Enter" || event.key === " ") && target?.matches("[role=\"treeitem\"][aria-selected]") && current?.column.contains(target)) {
|
|
497
|
+
event.preventDefault();
|
|
498
|
+
target.click();
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
const observer = new win.MutationObserver(schedule);
|
|
502
|
+
observer.observe(doc.body, {
|
|
503
|
+
childList: true,
|
|
504
|
+
subtree: true,
|
|
505
|
+
characterData: true,
|
|
506
|
+
attributes: true,
|
|
507
|
+
attributeFilter: [
|
|
508
|
+
"class",
|
|
509
|
+
"style",
|
|
510
|
+
"aria-hidden",
|
|
511
|
+
"aria-selected",
|
|
512
|
+
"disabled",
|
|
513
|
+
"data-slot"
|
|
514
|
+
]
|
|
515
|
+
});
|
|
516
|
+
doc.addEventListener("pointerdown", pointer, true);
|
|
517
|
+
doc.addEventListener("click", click);
|
|
518
|
+
doc.addEventListener("keydown", key);
|
|
519
|
+
doc.addEventListener("keydown", rowKey);
|
|
520
|
+
doc.addEventListener(HARNESS_MENU_OPEN, externalOpen);
|
|
521
|
+
doc.addEventListener(HARNESS_MENU_CLOSE, externalClose);
|
|
522
|
+
doc.addEventListener(HARNESS_MENU_POSITION, position);
|
|
523
|
+
let parentDoc;
|
|
524
|
+
try {
|
|
525
|
+
if (win.parent !== win) parentDoc = win.parent.document;
|
|
526
|
+
} catch {}
|
|
527
|
+
parentDoc?.addEventListener("pointerdown", pointer, true);
|
|
528
|
+
win.addEventListener("resize", schedule);
|
|
529
|
+
sync();
|
|
530
|
+
return () => {
|
|
531
|
+
disposed = true;
|
|
532
|
+
observer.disconnect();
|
|
533
|
+
doc.removeEventListener("pointerdown", pointer, true);
|
|
534
|
+
doc.removeEventListener("click", click);
|
|
535
|
+
doc.removeEventListener("keydown", key);
|
|
536
|
+
doc.removeEventListener("keydown", rowKey);
|
|
537
|
+
doc.removeEventListener(HARNESS_MENU_OPEN, externalOpen);
|
|
538
|
+
doc.removeEventListener(HARNESS_MENU_CLOSE, externalClose);
|
|
539
|
+
doc.removeEventListener(HARNESS_MENU_POSITION, position);
|
|
540
|
+
parentDoc?.removeEventListener("pointerdown", pointer, true);
|
|
541
|
+
win.removeEventListener("resize", schedule);
|
|
542
|
+
restore();
|
|
543
|
+
style.remove();
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
//#endregion
|
|
547
|
+
//#region src/client/harness-session-summary.ts
|
|
548
|
+
const SLOT = "conversation.session.header.actions";
|
|
549
|
+
const LOCALE = "arkme.harness.sessionSummary";
|
|
550
|
+
/** Missing/changed projection schemas must not be presented as zero conversations. */
|
|
551
|
+
function sessionTurnCount(value) {
|
|
552
|
+
if (typeof value !== "object" || value === null || !("turns" in value)) return;
|
|
553
|
+
const turns = value.turns;
|
|
554
|
+
return typeof turns === "number" && Number.isSafeInteger(turns) && turns >= 0 ? turns : void 0;
|
|
555
|
+
}
|
|
556
|
+
function SessionTurnCount({ useProjection, t }) {
|
|
557
|
+
const turns = sessionTurnCount(useProjection("sessionStats"));
|
|
558
|
+
if (turns === void 0) return null;
|
|
559
|
+
return (0, react.createElement)("span", {
|
|
560
|
+
"data-arkme-session-turn-count": "",
|
|
561
|
+
"data-turns": turns
|
|
562
|
+
}, t("count", { count: turns }));
|
|
563
|
+
}
|
|
564
|
+
/** Add a session-scoped reader; paging, compaction and push updates stay host-owned. */
|
|
565
|
+
function installHarnessSessionSummary(ctx) {
|
|
566
|
+
if (typeof ctx.locale?.register !== "function" || typeof ctx.slots.spec !== "function") return () => {};
|
|
567
|
+
const removeLocale = ctx.locale.register(LOCALE, {
|
|
568
|
+
zh: { count: "{count} 次对话" },
|
|
569
|
+
en: { count: "{count} turns" }
|
|
570
|
+
});
|
|
571
|
+
const removeSlot = ctx.slots.inject(SLOT, () => {
|
|
572
|
+
const spec = ctx.slots.spec(SLOT);
|
|
573
|
+
if (spec?.kind !== "list" || spec.scope !== "session") return () => {};
|
|
574
|
+
return ctx.slots.register({
|
|
575
|
+
name: SLOT,
|
|
576
|
+
id: "arkme-session-turn-count",
|
|
577
|
+
order: 0,
|
|
578
|
+
locale: LOCALE
|
|
579
|
+
}, SessionTurnCount);
|
|
580
|
+
});
|
|
581
|
+
return () => {
|
|
582
|
+
removeSlot();
|
|
583
|
+
removeLocale();
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
//#endregion
|
|
587
|
+
//#region src/client/harness-activity.ts
|
|
588
|
+
/** Display-only account-scoped summary. Official sessions remain the source of running/pending state. */
|
|
589
|
+
const HARNESS_ACTIVITY_ATTRIBUTE = "data-arkme-harness-activity";
|
|
590
|
+
const HARNESS_ACTIVITY_EVENT = "arkme:harness-activity";
|
|
591
|
+
const pendingKinds = /* @__PURE__ */ new Set([
|
|
592
|
+
"approval",
|
|
593
|
+
"plan-review",
|
|
594
|
+
"question"
|
|
595
|
+
]);
|
|
596
|
+
/** Only unread reminder ids are local; no copied sessions, histories or execution states. */
|
|
597
|
+
var HarnessActivityTracker = class {
|
|
598
|
+
scope;
|
|
599
|
+
storage;
|
|
600
|
+
unread = /* @__PURE__ */ new Set();
|
|
601
|
+
previous = /* @__PURE__ */ new Map();
|
|
602
|
+
key;
|
|
603
|
+
persisted = "";
|
|
604
|
+
constructor(scope, storage) {
|
|
605
|
+
this.scope = scope;
|
|
606
|
+
this.storage = storage;
|
|
607
|
+
this.key = `arkme.harness.unviewed.v1:${scope}`;
|
|
608
|
+
try {
|
|
609
|
+
const saved = JSON.parse(storage?.getItem(this.key) ?? "[]");
|
|
610
|
+
if (Array.isArray(saved)) this.unread = new Set(saved.filter((id) => typeof id === "string" && id.length > 0 && id.length <= 512));
|
|
611
|
+
} catch {}
|
|
612
|
+
this.persisted = JSON.stringify([...this.unread].sort());
|
|
613
|
+
}
|
|
614
|
+
update(list, archived, pending, visibleSession) {
|
|
615
|
+
const result = {
|
|
616
|
+
scope: this.scope,
|
|
617
|
+
running: [],
|
|
618
|
+
unread: [],
|
|
619
|
+
pending: []
|
|
620
|
+
};
|
|
621
|
+
if (list.phase !== "ready") return result;
|
|
622
|
+
const current = list.current === void 0 ? void 0 : list.byId[list.current];
|
|
623
|
+
if (current) result.current = {
|
|
624
|
+
id: current.id,
|
|
625
|
+
title: current.blank ? "新会话" : current.displayTitle || "未命名对话"
|
|
626
|
+
};
|
|
627
|
+
else if (list.current === void 0) result.current = null;
|
|
628
|
+
const hidden = new Set(archived);
|
|
629
|
+
const groups = /* @__PURE__ */ new Map();
|
|
630
|
+
for (const id of list.ids) {
|
|
631
|
+
const session = list.byId[id];
|
|
632
|
+
if (!session || session.origin === "subagent" || hidden.has(id) || session.blank) continue;
|
|
633
|
+
groups.set(id, {
|
|
634
|
+
item: {
|
|
635
|
+
id,
|
|
636
|
+
title: session.displayTitle || "未命名对话"
|
|
637
|
+
},
|
|
638
|
+
running: false,
|
|
639
|
+
pending: false,
|
|
640
|
+
completed: session.completed === true
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
for (const session of Object.values(list.byId)) {
|
|
644
|
+
let root = session;
|
|
645
|
+
const seen = /* @__PURE__ */ new Set();
|
|
646
|
+
while (root?.origin === "subagent" && root.parentId && !seen.has(root.id)) {
|
|
647
|
+
seen.add(root.id);
|
|
648
|
+
root = list.byId[root.parentId];
|
|
649
|
+
}
|
|
650
|
+
const group = root && groups.get(root.id);
|
|
651
|
+
if (!group) continue;
|
|
652
|
+
group.running ||= session.running === true;
|
|
653
|
+
const kind = pending === void 0 ? session.pendingInteraction : pending.get(session.id)?.kind;
|
|
654
|
+
group.pending ||= kind !== void 0 && pendingKinds.has(kind);
|
|
655
|
+
}
|
|
656
|
+
for (const id of this.unread) if (!groups.has(id)) this.unread.delete(id);
|
|
657
|
+
for (const [id, group] of groups) {
|
|
658
|
+
const active = group.running || group.pending;
|
|
659
|
+
const previous = this.previous.get(id);
|
|
660
|
+
if (active) this.unread.delete(id);
|
|
661
|
+
else if (previous?.active || group.completed && !previous?.completed) this.unread.add(id);
|
|
662
|
+
if (visibleSession === id) this.unread.delete(id);
|
|
663
|
+
this.previous.set(id, {
|
|
664
|
+
active,
|
|
665
|
+
completed: group.completed
|
|
666
|
+
});
|
|
667
|
+
if (group.pending) result.pending.push(group.item);
|
|
668
|
+
else if (group.running) result.running.push(group.item);
|
|
669
|
+
else if (this.unread.has(id)) result.unread.push(group.item);
|
|
670
|
+
}
|
|
671
|
+
for (const id of this.previous.keys()) if (!groups.has(id)) this.previous.delete(id);
|
|
672
|
+
const saved = JSON.stringify([...this.unread].sort());
|
|
673
|
+
if (saved !== this.persisted) {
|
|
674
|
+
this.persisted = saved;
|
|
675
|
+
try {
|
|
676
|
+
this.storage?.setItem(this.key, saved);
|
|
677
|
+
} catch {}
|
|
678
|
+
}
|
|
679
|
+
return result;
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
//#endregion
|
|
683
|
+
//#region src/client/harness-activity-reporter.ts
|
|
684
|
+
/** A renderless public-slot reader bridges the native iframe's actual state to the Arkme row. */
|
|
685
|
+
function installHarnessActivityReporter(ctx, surface, doc = document) {
|
|
686
|
+
if (typeof ctx.slots.spec !== "function") return () => {};
|
|
687
|
+
const parentDoc = surface.ownerDocument;
|
|
688
|
+
let tracker;
|
|
689
|
+
let latest;
|
|
690
|
+
const notify = () => parentDoc.dispatchEvent(new parentDoc.defaultView.Event(HARNESS_ACTIVITY_EVENT));
|
|
691
|
+
const clear = () => {
|
|
692
|
+
if (!surface.hasAttribute("data-arkme-harness-activity")) return;
|
|
693
|
+
surface.removeAttribute(HARNESS_ACTIVITY_ATTRIBUTE);
|
|
694
|
+
notify();
|
|
695
|
+
};
|
|
696
|
+
const sync = () => {
|
|
697
|
+
const scope = surface.getAttribute("data-arkme-account-scope");
|
|
698
|
+
if (!scope || !surface.getAttribute("data-arkme-account-id")) {
|
|
699
|
+
tracker = void 0;
|
|
700
|
+
clear();
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
if (!latest) return;
|
|
704
|
+
if (tracker?.scope !== scope) {
|
|
705
|
+
let storage;
|
|
706
|
+
try {
|
|
707
|
+
storage = doc.defaultView?.localStorage;
|
|
708
|
+
} catch {}
|
|
709
|
+
tracker = new HarnessActivityTracker(scope, storage);
|
|
710
|
+
}
|
|
711
|
+
const visible = surface.getAttribute("data-arkme-visible") === "true" && surface.getAttribute("data-arkme-follow-session") === "true" && parentDoc.visibilityState === "visible" && parentDoc.hasFocus();
|
|
712
|
+
const value = tracker.update(latest.list, latest.archived, latest.pending, visible ? latest.list.current : void 0);
|
|
713
|
+
const raw = JSON.stringify(value);
|
|
714
|
+
if (surface.getAttribute("data-arkme-harness-activity") !== raw) {
|
|
715
|
+
surface.setAttribute(HARNESS_ACTIVITY_ATTRIBUTE, raw);
|
|
716
|
+
notify();
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
function Publish(props) {
|
|
720
|
+
(0, react.useLayoutEffect)(() => {
|
|
721
|
+
latest = props;
|
|
722
|
+
sync();
|
|
723
|
+
}, [
|
|
724
|
+
props.list,
|
|
725
|
+
props.archived,
|
|
726
|
+
props.pending
|
|
727
|
+
]);
|
|
728
|
+
return null;
|
|
729
|
+
}
|
|
730
|
+
function WithPending({ usePending, ...props }) {
|
|
731
|
+
const pending = usePending((value) => value);
|
|
732
|
+
return (0, react.createElement)(Publish, {
|
|
733
|
+
...props,
|
|
734
|
+
pending
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
function Reporter({ useSessions, useWorkspaces, useSessionPendingInteraction }) {
|
|
738
|
+
const list = useSessions((value) => value);
|
|
739
|
+
const archived = useWorkspaces((value) => value.archivedSessionIds);
|
|
740
|
+
return useSessionPendingInteraction ? (0, react.createElement)(WithPending, {
|
|
741
|
+
list,
|
|
742
|
+
archived,
|
|
743
|
+
usePending: useSessionPendingInteraction
|
|
744
|
+
}) : (0, react.createElement)(Publish, {
|
|
745
|
+
list,
|
|
746
|
+
archived,
|
|
747
|
+
pending: void 0
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
const observer = new MutationObserver(sync);
|
|
751
|
+
observer.observe(surface, {
|
|
752
|
+
attributes: true,
|
|
753
|
+
attributeFilter: [
|
|
754
|
+
"data-arkme-visible",
|
|
755
|
+
"data-arkme-follow-session",
|
|
756
|
+
"data-arkme-account-id",
|
|
757
|
+
"data-arkme-account-scope"
|
|
758
|
+
]
|
|
759
|
+
});
|
|
760
|
+
parentDoc.addEventListener("visibilitychange", sync);
|
|
761
|
+
parentDoc.defaultView?.addEventListener("focus", sync);
|
|
762
|
+
parentDoc.defaultView?.addEventListener("blur", sync);
|
|
763
|
+
const remove = ctx.slots.inject("sidebar.footer.action", () => {
|
|
764
|
+
const spec = ctx.slots.spec("sidebar.footer.action");
|
|
765
|
+
if (spec?.kind !== "list" || spec.scope !== "root") return () => {};
|
|
766
|
+
return ctx.slots.register({
|
|
767
|
+
name: "sidebar.footer.action",
|
|
768
|
+
id: "arkme-harness-activity",
|
|
769
|
+
order: 100
|
|
770
|
+
}, Reporter);
|
|
771
|
+
});
|
|
772
|
+
return () => {
|
|
773
|
+
remove();
|
|
774
|
+
observer.disconnect();
|
|
775
|
+
parentDoc.removeEventListener("visibilitychange", sync);
|
|
776
|
+
parentDoc.defaultView?.removeEventListener("focus", sync);
|
|
777
|
+
parentDoc.defaultView?.removeEventListener("blur", sync);
|
|
778
|
+
clear();
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
//#endregion
|
|
782
|
+
//#region src/client/harness-sidebar-client.ts
|
|
783
|
+
const inject = ["slots", "locale"];
|
|
784
|
+
/** The outer Arkme shell owns authentication and the avatar settings entry. */
|
|
785
|
+
function apply(ctx) {
|
|
786
|
+
ctx.effect(() => {
|
|
787
|
+
const surface = window.frameElement?.parentElement;
|
|
788
|
+
if (surface?.getAttribute("data-arkme-owned") !== "deepseek-harness-surface") return () => void 0;
|
|
789
|
+
let restoreSettings;
|
|
790
|
+
let restoreDropdown;
|
|
791
|
+
let restoreDefault;
|
|
792
|
+
let restoreSummary;
|
|
793
|
+
let restoreActivity;
|
|
794
|
+
const sync = () => {
|
|
795
|
+
const accountId = Number(surface.getAttribute("data-arkme-account-id"));
|
|
796
|
+
const authenticated = Number.isSafeInteger(accountId) && accountId > 0;
|
|
797
|
+
if (authenticated && restoreSettings === void 0) {
|
|
798
|
+
restoreSettings = ctx.slots.inject("sidebar.settings", () => ctx.slots.register({
|
|
799
|
+
name: "sidebar.settings",
|
|
800
|
+
priority: -100
|
|
801
|
+
}, () => null));
|
|
802
|
+
restoreDefault = installHarnessSessionListDefault(ctx);
|
|
803
|
+
restoreSummary = installHarnessSessionSummary(ctx);
|
|
804
|
+
restoreActivity = installHarnessActivityReporter(ctx, surface);
|
|
805
|
+
restoreDropdown = installHarnessSessionDropdown(document);
|
|
806
|
+
} else if (!authenticated) {
|
|
807
|
+
restoreActivity?.();
|
|
808
|
+
restoreActivity = void 0;
|
|
809
|
+
restoreSummary?.();
|
|
810
|
+
restoreSummary = void 0;
|
|
811
|
+
restoreDefault?.();
|
|
812
|
+
restoreDefault = void 0;
|
|
813
|
+
restoreDropdown?.();
|
|
814
|
+
restoreDropdown = void 0;
|
|
815
|
+
restoreSettings?.();
|
|
816
|
+
restoreSettings = void 0;
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
const observer = new MutationObserver(sync);
|
|
820
|
+
observer.observe(surface, {
|
|
821
|
+
attributes: true,
|
|
822
|
+
attributeFilter: ["data-arkme-account-id"]
|
|
823
|
+
});
|
|
824
|
+
sync();
|
|
825
|
+
return () => {
|
|
826
|
+
observer.disconnect();
|
|
827
|
+
restoreActivity?.();
|
|
828
|
+
restoreSummary?.();
|
|
829
|
+
restoreDefault?.();
|
|
830
|
+
restoreDropdown?.();
|
|
831
|
+
restoreSettings?.();
|
|
832
|
+
};
|
|
833
|
+
}, "arkme: native sidebar settings follow account state");
|
|
834
|
+
}
|
|
835
|
+
//#endregion
|
|
836
|
+
exports.apply = apply;
|
|
837
|
+
exports.inject = inject;
|
|
838
|
+
return module.exports;
|
|
839
|
+
}
|
|
840
|
+
});
|