loadtoagent 1.0.0 → 1.3.1

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.
Files changed (86) hide show
  1. package/README.ko.md +7 -0
  2. package/README.md +7 -0
  3. package/README.zh-CN.md +7 -0
  4. package/docs/PROVIDER-CONTRACTS.md +23 -1
  5. package/docs/UI-AUDIT-100.md +118 -0
  6. package/docs/UI-AUDIT-101-200.md +120 -0
  7. package/docs/UI-AUDIT-201-300.md +120 -0
  8. package/main.js +183 -37
  9. package/package.json +15 -4
  10. package/preload.js +11 -0
  11. package/renderer/app-agent-actions.js +125 -53
  12. package/renderer/app-bootstrap.js +54 -9
  13. package/renderer/app-dashboard.js +197 -58
  14. package/renderer/app-drawer-content.js +103 -86
  15. package/renderer/app-drawer-data.js +26 -13
  16. package/renderer/app-drawer.js +79 -55
  17. package/renderer/app-events-dialogs.js +146 -37
  18. package/renderer/app-events-filters.js +172 -9
  19. package/renderer/app-events-navigation.js +78 -31
  20. package/renderer/app-events-sessions.js +171 -4
  21. package/renderer/app-events.js +2 -1
  22. package/renderer/app-graph-model.js +32 -4
  23. package/renderer/app-graph-orchestration.js +16 -13
  24. package/renderer/app-graph-view.js +228 -112
  25. package/renderer/app-management.js +211 -0
  26. package/renderer/app-provider-visibility.js +124 -0
  27. package/renderer/app-quality.js +568 -0
  28. package/renderer/app-run-modal.js +103 -33
  29. package/renderer/app-runtime-overview.js +312 -0
  30. package/renderer/app-session-render.js +94 -37
  31. package/renderer/app-tmux-render.js +119 -38
  32. package/renderer/app.js +155 -76
  33. package/renderer/i18n-messages.js +833 -15
  34. package/renderer/i18n.js +104 -0
  35. package/renderer/index.html +162 -75
  36. package/renderer/shared.js +17 -0
  37. package/renderer/styles-agent-map.css +50 -1
  38. package/renderer/styles-cards.css +26 -0
  39. package/renderer/styles-collaboration.css +18 -88
  40. package/renderer/styles-components.css +126 -14
  41. package/renderer/styles-management.css +355 -0
  42. package/renderer/styles-overlays.css +13 -2
  43. package/renderer/styles-product.css +24 -16
  44. package/renderer/styles-quality.css +312 -0
  45. package/renderer/styles-readability.css +862 -0
  46. package/renderer/styles-responsive-product.css +84 -12
  47. package/renderer/styles-responsive-runtime.css +31 -14
  48. package/renderer/styles-responsive-shell.css +44 -48
  49. package/renderer/styles-responsive-workflows.css +37 -17
  50. package/renderer/styles-run-composer.css +5 -0
  51. package/renderer/styles-runtime-overview.css +285 -0
  52. package/renderer/styles-settings.css +160 -0
  53. package/renderer/styles-terminal.css +339 -2
  54. package/renderer/styles-tmux.css +154 -0
  55. package/renderer/styles-workflow-map.css +362 -15
  56. package/renderer/styles-workflows.css +69 -2
  57. package/renderer/styles.css +27 -12
  58. package/renderer/terminal-agent.js +17 -13
  59. package/renderer/terminal-events.js +233 -40
  60. package/renderer/terminal-workbench.js +176 -78
  61. package/renderer/terminal.js +350 -37
  62. package/src/agentMonitor/claudeParser.js +96 -7
  63. package/src/agentMonitor/codexParser.js +59 -4
  64. package/src/agentMonitor/executionActivity.js +282 -0
  65. package/src/agentMonitor/genericParser.js +56 -13
  66. package/src/agentMonitor/hierarchy.js +17 -0
  67. package/src/agentMonitor/responseIntent.js +51 -0
  68. package/src/agentMonitor.js +21 -3
  69. package/src/agentRunner.js +66 -1
  70. package/src/attentionNotifier.js +71 -0
  71. package/src/automationMonitor.js +258 -0
  72. package/src/contracts.js +10 -1
  73. package/src/ipc/registerAgentIpc.js +9 -3
  74. package/src/ipc/registerAppIpc.js +4 -1
  75. package/src/ipc/registerTerminalIpc.js +12 -6
  76. package/src/macUpdateHelper.js +210 -0
  77. package/src/monitorWorker.js +65 -6
  78. package/src/platformPath.js +80 -0
  79. package/src/processMonitor.js +81 -23
  80. package/src/providerVisibilityStore.js +45 -0
  81. package/src/sessionIntelligence.js +247 -0
  82. package/src/terminalHost.js +381 -0
  83. package/src/terminalHostDaemon.js +60 -0
  84. package/src/terminalManager.js +158 -3
  85. package/src/tmuxMonitor.js +2 -2
  86. package/src/updateInstaller.js +175 -0
@@ -0,0 +1,568 @@
1
+ "use strict";
2
+
3
+ window.LoadToAgentAppFactories = window.LoadToAgentAppFactories || {};
4
+
5
+ window.LoadToAgentAppFactories.createQualityEnhancements = function createQualityEnhancements(context = {}) {
6
+ const t = (key, params) => window.LoadToAgentI18n.t(key, params);
7
+ const {
8
+ $, state, rememberDialogTrigger, restoreDialogTrigger, setDialogOpenState, currentDialog,
9
+ announce, selectView, openRunModal, performUiAction,
10
+ } = context;
11
+ const DASHBOARD_STORAGE_KEY = "loadtoagent:dashboard-preferences:v2";
12
+ const RUN_DRAFT_STORAGE_KEY = "loadtoagent:run-draft:v2";
13
+ const QUALITY_PREF_STORAGE_KEY = "loadtoagent:quality-preferences:v3";
14
+ const DASHBOARD_VERSION = 2;
15
+ const RUN_DRAFT_VERSION = 2;
16
+ const QUALITY_PREF_VERSION = 3;
17
+ const MAX_QUALITY_TEXT = 180;
18
+ let activeCommandIndex = 0;
19
+ let visibleCommands = [];
20
+ let quickDialogGeneration = 0;
21
+ let shortcutDialogGeneration = 0;
22
+ let qualityMutationFrame = 0;
23
+ let qualityGuardsInstalled = false;
24
+
25
+ function safeParse(storage, key) {
26
+ try {
27
+ const value = JSON.parse(storage.getItem(key) || "null");
28
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
29
+ } catch (error) {
30
+ window.LoadToAgentRendererUtils.reportRecoverableError(`quality-storage-${key}`, error);
31
+ return null;
32
+ }
33
+ }
34
+
35
+ function normalizedSearch(value) {
36
+ return String(value || "").slice(0, 240).replace(/\s+/g, " ").trim();
37
+ }
38
+
39
+ function qualityText(value, limit = MAX_QUALITY_TEXT) {
40
+ return String(value || "").replace(/\s+/g, " ").trim().slice(0, limit);
41
+ }
42
+
43
+ function defaultQualityPreferences() {
44
+ return {
45
+ version: QUALITY_PREF_VERSION,
46
+ inputModality: "pointer",
47
+ reduceMotion: false,
48
+ compactDensity: false,
49
+ advancedRunOpen: false,
50
+ };
51
+ }
52
+
53
+ function loadQualityPreferences() {
54
+ const saved = safeParse(localStorage, QUALITY_PREF_STORAGE_KEY);
55
+ const defaults = defaultQualityPreferences();
56
+ state.qualityPreferences = saved?.version === QUALITY_PREF_VERSION ? {
57
+ ...defaults,
58
+ inputModality: saved.inputModality === "keyboard" ? "keyboard" : "pointer",
59
+ reduceMotion: saved.reduceMotion === true,
60
+ compactDensity: saved.compactDensity === true,
61
+ advancedRunOpen: saved.advancedRunOpen === true,
62
+ } : defaults;
63
+ applyQualityPreferences();
64
+ }
65
+
66
+ function saveQualityPreferences() {
67
+ const value = { ...defaultQualityPreferences(), ...(state.qualityPreferences || {}) };
68
+ value.version = QUALITY_PREF_VERSION;
69
+ try {
70
+ localStorage.setItem(QUALITY_PREF_STORAGE_KEY, JSON.stringify(value));
71
+ } catch (error) {
72
+ window.LoadToAgentRendererUtils.reportRecoverableError("quality-preferences-save", error);
73
+ }
74
+ }
75
+
76
+ function applyQualityPreferences() {
77
+ const preferences = state.qualityPreferences || defaultQualityPreferences();
78
+ document.body.dataset.inputModality = preferences.inputModality === "keyboard" ? "keyboard" : "pointer";
79
+ document.body.dataset.qualityMotion = preferences.reduceMotion ? "reduced" : "standard";
80
+ document.body.dataset.qualityDensity = preferences.compactDensity ? "compact" : "comfortable";
81
+ document.body.classList.toggle("quality-keyboard-mode", preferences.inputModality === "keyboard");
82
+ }
83
+
84
+ function loadQualityState() {
85
+ const dashboard = safeParse(localStorage, DASHBOARD_STORAGE_KEY);
86
+ if (dashboard?.version === DASHBOARD_VERSION) {
87
+ state.search = normalizedSearch(dashboard.search);
88
+ state.providerFilters = new Set(
89
+ Array.isArray(dashboard.providers)
90
+ ? dashboard.providers.filter((id) => typeof id === "string" && /^[a-z0-9_-]{1,40}$/i.test(id)).slice(0, 20)
91
+ : [],
92
+ );
93
+ state.workspace = typeof dashboard.workspace === "string" && dashboard.workspace.length <= 2_000
94
+ ? dashboard.workspace
95
+ : "all";
96
+ state.sort = ["recent", "tokens", "context"].includes(dashboard.sort) ? dashboard.sort : "recent";
97
+ } else {
98
+ state.search = "";
99
+ state.providerFilters.clear();
100
+ state.workspace = "all";
101
+ state.sort = "recent";
102
+ }
103
+ const search = $("#searchInput");
104
+ if (search) search.value = state.search;
105
+ const sort = $("#sortSelect");
106
+ if (sort) sort.value = state.sort;
107
+ loadQualityPreferences();
108
+
109
+ const draft = safeParse(sessionStorage, RUN_DRAFT_STORAGE_KEY);
110
+ if (draft?.version === RUN_DRAFT_VERSION) {
111
+ state.runDraft = {
112
+ prompt: typeof draft.prompt === "string" ? draft.prompt.slice(0, 8_000) : "",
113
+ cwd: typeof draft.cwd === "string" ? draft.cwd.slice(0, 2_000) : "",
114
+ model: typeof draft.model === "string" ? draft.model.slice(0, 160) : "",
115
+ allowWrites: draft.allowWrites === true,
116
+ provider: typeof draft.provider === "string" && /^[a-z0-9_-]{1,40}$/i.test(draft.provider) ? draft.provider : "",
117
+ };
118
+ if (state.runDraft.provider) state.runProvider = state.runDraft.provider;
119
+ } else state.runDraft = { prompt: "", cwd: "", model: "", allowWrites: false, provider: "" };
120
+ }
121
+
122
+ function saveDashboardPreferences() {
123
+ const value = {
124
+ version: DASHBOARD_VERSION,
125
+ search: normalizedSearch(state.search),
126
+ providers: [...state.providerFilters],
127
+ workspace: String(state.workspace || "all").slice(0, 2_000),
128
+ sort: ["recent", "tokens", "context"].includes(state.sort) ? state.sort : "recent",
129
+ };
130
+ try {
131
+ localStorage.setItem(DASHBOARD_STORAGE_KEY, JSON.stringify(value));
132
+ } catch (error) {
133
+ window.LoadToAgentRendererUtils.reportRecoverableError("dashboard-preferences-save", error);
134
+ }
135
+ }
136
+
137
+ function currentRunDraft() {
138
+ return {
139
+ version: RUN_DRAFT_VERSION,
140
+ prompt: $("#runPrompt")?.value.slice(0, 8_000) || "",
141
+ cwd: $("#runCwd")?.value.slice(0, 2_000) || "",
142
+ model: $("#runModel")?.value.slice(0, 160) || "",
143
+ allowWrites: Boolean($("#allowWrites")?.checked),
144
+ provider: String(state.runProvider || "").slice(0, 40),
145
+ };
146
+ }
147
+
148
+ function saveRunDraft() {
149
+ const draft = currentRunDraft();
150
+ state.runDraft = { ...draft };
151
+ try {
152
+ sessionStorage.setItem(RUN_DRAFT_STORAGE_KEY, JSON.stringify(draft));
153
+ } catch (error) {
154
+ window.LoadToAgentRendererUtils.reportRecoverableError("run-draft-save", error);
155
+ }
156
+ }
157
+
158
+ function restoreRunDraft() {
159
+ const draft = state.runDraft || {};
160
+ if ($("#runPrompt") && !$("#runPrompt").value) $("#runPrompt").value = draft.prompt || "";
161
+ if ($("#runCwd") && !$("#runCwd").value) $("#runCwd").value = draft.cwd || "";
162
+ if ($("#runModel") && !$("#runModel").value) $("#runModel").value = draft.model || "";
163
+ if ($("#allowWrites")) $("#allowWrites").checked = Boolean(draft.allowWrites);
164
+ if (draft.provider) state.runProvider = draft.provider;
165
+ }
166
+
167
+ function clearRunDraft(options = {}) {
168
+ state.runDraft = { prompt: "", cwd: "", model: "", allowWrites: false, provider: "" };
169
+ try {
170
+ sessionStorage.removeItem(RUN_DRAFT_STORAGE_KEY);
171
+ } catch (error) {
172
+ window.LoadToAgentRendererUtils.reportRecoverableError("run-draft-clear", error);
173
+ }
174
+ if ($("#runPrompt")) $("#runPrompt").value = "";
175
+ if ($("#runCwd")) $("#runCwd").value = "";
176
+ if ($("#runModel")) $("#runModel").value = "";
177
+ if ($("#allowWrites")) $("#allowWrites").checked = false;
178
+ $("#runForm")?.querySelectorAll('[aria-invalid="true"]').forEach((element) => element.removeAttribute("aria-invalid"));
179
+ $("#runError")?.classList.add("hidden");
180
+ context.syncRunComposer?.();
181
+ if (options.focus !== false) $("#runPrompt")?.focus();
182
+ if (options.silent !== true) announce(t("quality.draft_cleared"));
183
+ }
184
+
185
+ function commandDefinitions() {
186
+ return [
187
+ ["all", "⌂", t("app.nav.home"), t("quality.command.view"), () => selectView("all", { focusMain: true })],
188
+ ["active", "●", t("app.nav.active"), t("quality.command.view"), () => selectView("active", { focusMain: true })],
189
+ ["waiting", "!", t("app.nav.needs_review"), t("quality.command.view"), () => selectView("waiting", { focusMain: true })],
190
+ ["runtime", "↻", t("app.nav.runtime"), t("quality.command.view"), () => selectView("runtime", { focusMain: true })],
191
+ ["terminal", ">_", t("app.nav.session_terminal"), t("quality.command.view"), () => selectView("terminal", { focusMain: true })],
192
+ ["tmux", "▦", t("app.nav.tmux"), t("quality.command.view"), () => selectView("tmux", { focusMain: true })],
193
+ ["settings", "⚙", t("app.nav.settings"), t("quality.command.view"), () => selectView("settings", { focusMain: true })],
194
+ ["new-task", "+", t("ui.new_ai_task"), t("quality.command.action"), () => openRunModal()],
195
+ ["probe", "↻", t("ui.check_ai_connections_again"), t("quality.command.action"), () => $("#probeBtn")?.click()],
196
+ ["workspace", "⌘", t("ui.add_workspace"), t("quality.command.action"), () => $("#addWorkspaceBtn")?.click()],
197
+ ["shortcuts", "?", t("quality.shortcuts.title"), t("quality.command.help"), () => openShortcutHelp()],
198
+ ].map(([id, icon, label, group, run]) => ({ id, icon, label, group, run }));
199
+ }
200
+
201
+ function renderQuickCommands() {
202
+ const input = $("#quickPaletteInput");
203
+ const query = String(input?.value || "").replace(/\s+/g, " ").trim().toLocaleLowerCase();
204
+ visibleCommands = commandDefinitions().filter((command) => `${command.label} ${command.group}`.toLocaleLowerCase().includes(query));
205
+ activeCommandIndex = Math.max(0, Math.min(activeCommandIndex, visibleCommands.length - 1));
206
+ const list = $("#quickPaletteList");
207
+ list.innerHTML = visibleCommands.map((command, index) => `<button id="quick-command-${command.id}" type="button" role="option" aria-selected="${index === activeCommandIndex ? "true" : "false"}" tabindex="-1" data-quick-command="${command.id}" class="${index === activeCommandIndex ? "active" : ""}"><span aria-hidden="true">${command.icon}</span><b>${context.esc(command.label)}</b><small>${context.esc(command.group)}</small><i aria-hidden="true">↵</i></button>`).join("");
208
+ const active = visibleCommands[activeCommandIndex];
209
+ if (active) input?.setAttribute("aria-activedescendant", `quick-command-${active.id}`);
210
+ else input?.removeAttribute("aria-activedescendant");
211
+ const status = visibleCommands.length
212
+ ? t("quality.quick_results", { count: visibleCommands.length })
213
+ : t("quality.quick_empty");
214
+ $("#quickPaletteStatus").textContent = status;
215
+ }
216
+
217
+ function openQuickPalette() {
218
+ if (currentDialog?.()) return;
219
+ rememberDialogTrigger();
220
+ quickDialogGeneration = context.motionState?.dialogGeneration || 0;
221
+ const modal = $("#quickPaletteModal");
222
+ setDialogOpenState(modal, true);
223
+ modal.classList.remove("hidden");
224
+ $("#quickPaletteInput").value = "";
225
+ activeCommandIndex = 0;
226
+ renderQuickCommands();
227
+ requestAnimationFrame(() => $("#quickPaletteInput")?.focus());
228
+ }
229
+
230
+ function closeQuickPalette() {
231
+ const modal = $("#quickPaletteModal");
232
+ if (modal.classList.contains("hidden")) return;
233
+ modal.classList.add("hidden");
234
+ setDialogOpenState(modal, false);
235
+ restoreDialogTrigger(quickDialogGeneration);
236
+ }
237
+
238
+ function executeQuickCommand(id) {
239
+ const command = visibleCommands.find((item) => item.id === id) || commandDefinitions().find((item) => item.id === id);
240
+ if (!command) return;
241
+ closeQuickPalette();
242
+ command.run();
243
+ }
244
+
245
+ function openShortcutHelp() {
246
+ if (currentDialog?.()) return;
247
+ rememberDialogTrigger();
248
+ shortcutDialogGeneration = context.motionState?.dialogGeneration || 0;
249
+ const modal = $("#shortcutHelpModal");
250
+ setDialogOpenState(modal, true);
251
+ modal.classList.remove("hidden");
252
+ requestAnimationFrame(() => $("#closeShortcutHelpBtn")?.focus());
253
+ }
254
+
255
+ function closeShortcutHelp() {
256
+ const modal = $("#shortcutHelpModal");
257
+ if (modal.classList.contains("hidden")) return;
258
+ modal.classList.add("hidden");
259
+ setDialogOpenState(modal, false);
260
+ restoreDialogTrigger(shortcutDialogGeneration);
261
+ }
262
+
263
+ async function copyText(value) {
264
+ const text = String(value || "");
265
+ if (!text) return false;
266
+ try {
267
+ if (window.loadtoagent?.writeClipboard) await window.loadtoagent.writeClipboard(text);
268
+ else if (navigator.clipboard?.writeText) await navigator.clipboard.writeText(text);
269
+ else {
270
+ const area = document.createElement("textarea");
271
+ area.value = text;
272
+ area.setAttribute("readonly", "");
273
+ area.className = "clipboard-fallback";
274
+ document.body.append(area);
275
+ area.select();
276
+ const copied = document.execCommand("copy");
277
+ area.remove();
278
+ if (!copied) throw new Error("copy unavailable");
279
+ }
280
+ announce(t("quality.copy_success"));
281
+ context.toast?.(t("quality.copy_success"));
282
+ return true;
283
+ } catch (error) {
284
+ window.LoadToAgentRendererUtils.reportRecoverableError("clipboard-copy", error);
285
+ announce(t("quality.copy_failed"));
286
+ context.toast?.(t("quality.copy_failed"));
287
+ return false;
288
+ }
289
+ }
290
+
291
+ function safeBackdrop(backdrop, close, separateSurface = null) {
292
+ const pointerRoot = separateSurface ? document : backdrop;
293
+ let press = null;
294
+ let releaseTimer = 0;
295
+ const updateMovement = (event) => {
296
+ if (!press || press.pointerId !== event.pointerId) return;
297
+ if (Math.hypot(event.clientX - press.x, event.clientY - press.y) > 6) press.moved = true;
298
+ };
299
+ pointerRoot.addEventListener("pointerdown", (event) => {
300
+ if (event.button !== 0 || (separateSurface && backdrop.classList.contains("hidden"))) {
301
+ press = null;
302
+ return;
303
+ }
304
+ clearTimeout(releaseTimer);
305
+ press = {
306
+ pointerId: event.pointerId,
307
+ startedOnBackdrop: event.target === backdrop,
308
+ x: event.clientX,
309
+ y: event.clientY,
310
+ moved: false,
311
+ };
312
+ }, Boolean(separateSurface));
313
+ pointerRoot.addEventListener("pointermove", updateMovement, Boolean(separateSurface));
314
+ pointerRoot.addEventListener("pointerup", (event) => {
315
+ updateMovement(event);
316
+ clearTimeout(releaseTimer);
317
+ // Keep the press through the following click event, then discard it if
318
+ // the pointerup did not produce a click on the backdrop.
319
+ releaseTimer = setTimeout(() => { press = null; }, 0);
320
+ }, Boolean(separateSurface));
321
+ pointerRoot.addEventListener("pointercancel", () => {
322
+ clearTimeout(releaseTimer);
323
+ press = null;
324
+ }, Boolean(separateSurface));
325
+ backdrop.addEventListener("click", (event) => {
326
+ if (event.target !== backdrop) return;
327
+ const directActivation = !press && event.detail === 0;
328
+ const safePointerActivation = Boolean(press && press.startedOnBackdrop && !press.moved);
329
+ clearTimeout(releaseTimer);
330
+ press = null;
331
+ if (directActivation || safePointerActivation) close();
332
+ });
333
+ }
334
+
335
+ function markInputModality(mode) {
336
+ const preferences = { ...defaultQualityPreferences(), ...(state.qualityPreferences || {}) };
337
+ preferences.inputModality = mode === "keyboard" ? "keyboard" : "pointer";
338
+ state.qualityPreferences = preferences;
339
+ applyQualityPreferences();
340
+ saveQualityPreferences();
341
+ }
342
+
343
+ function describeControl(control) {
344
+ return qualityText(
345
+ control.getAttribute("aria-label")
346
+ || control.getAttribute("title")
347
+ || control.textContent
348
+ || control.getAttribute("data-i18n")
349
+ || control.id
350
+ || control.name,
351
+ );
352
+ }
353
+
354
+ function enhanceControl(control) {
355
+ if (!(control instanceof HTMLElement)) return;
356
+ if (control.dataset.qualityEnhanced === "true") return;
357
+ control.dataset.qualityEnhanced = "true";
358
+ const label = describeControl(control);
359
+ if (label && !control.getAttribute("aria-label") && control.matches(".icon-button, .top-icon-action, .close-button")) {
360
+ control.setAttribute("aria-label", label);
361
+ }
362
+ if (label && !control.getAttribute("title") && control.scrollWidth > control.clientWidth) control.setAttribute("title", label);
363
+ if (control.matches("button") && !control.getAttribute("type")) control.setAttribute("type", "button");
364
+ if (control.matches("button, [role='button'], input, select, textarea")) {
365
+ control.setAttribute("data-quality-control", "");
366
+ if (control.matches("input[required], textarea[required], select[required]")) control.setAttribute("aria-required", "true");
367
+ }
368
+ if (control.matches("button, [role='button']")) {
369
+ const rect = control.getBoundingClientRect?.();
370
+ if (rect && (rect.width < 40 || rect.height < 40)) control.setAttribute("data-quality-touch-target", "padded");
371
+ }
372
+ if (control.matches(":disabled, [aria-disabled='true']")) {
373
+ control.setAttribute("data-quality-disabled", "true");
374
+ if (!control.getAttribute("aria-describedby")) control.setAttribute("data-quality-disabled-reason", t("quality.disabled_reason"));
375
+ }
376
+ }
377
+
378
+ function enhanceQualityControls(root = document) {
379
+ root.querySelectorAll?.("button, [role='button'], input, select, textarea, summary, [tabindex]").forEach(enhanceControl);
380
+ document.querySelectorAll?.("[data-quality-disabled='true']:not(:disabled):not([aria-disabled='true'])").forEach((control) => {
381
+ control.removeAttribute("data-quality-disabled");
382
+ control.removeAttribute("data-quality-disabled-reason");
383
+ });
384
+ }
385
+
386
+ function installQualityMutationObserver() {
387
+ const observer = new MutationObserver(() => {
388
+ cancelAnimationFrame(qualityMutationFrame);
389
+ qualityMutationFrame = requestAnimationFrame(() => enhanceQualityControls());
390
+ });
391
+ observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ["disabled", "aria-disabled", "class", "title", "aria-label"] });
392
+ return observer;
393
+ }
394
+
395
+ function installPressedStateMirrors() {
396
+ document.addEventListener("pointerdown", (event) => {
397
+ const control = event.target.closest?.("button, [role='button']");
398
+ if (control) control.setAttribute("data-quality-pressed", "true");
399
+ }, true);
400
+ ["pointerup", "pointercancel", "pointerleave"].forEach((type) => {
401
+ document.addEventListener(type, (event) => {
402
+ const control = event.target.closest?.("button, [role='button']");
403
+ if (control) control.removeAttribute("data-quality-pressed");
404
+ }, true);
405
+ });
406
+ document.addEventListener("keydown", (event) => {
407
+ if (![" ", "Enter"].includes(event.key)) return;
408
+ const control = event.target.closest?.("button, [role='button']");
409
+ if (control) control.setAttribute("data-quality-pressed", "true");
410
+ }, true);
411
+ document.addEventListener("keyup", (event) => {
412
+ if (![" ", "Enter"].includes(event.key)) return;
413
+ const control = event.target.closest?.("button, [role='button']");
414
+ if (control) control.removeAttribute("data-quality-pressed");
415
+ }, true);
416
+ }
417
+
418
+ function installFormRecovery() {
419
+ document.addEventListener("input", (event) => {
420
+ const field = event.target;
421
+ if (!(field instanceof HTMLElement)) return;
422
+ if (field.matches("[aria-invalid='true']") && qualityText(field.value || field.textContent)) field.removeAttribute("aria-invalid");
423
+ const form = field.closest("form");
424
+ const error = form?.querySelector(".form-error:not(.hidden)");
425
+ if (error && qualityText(field.value || field.textContent)) error.classList.add("hidden");
426
+ }, true);
427
+ document.addEventListener("blur", (event) => {
428
+ const field = event.target;
429
+ if (!(field instanceof HTMLInputElement)) return;
430
+ if (field.matches("#runModel, #tmuxCreateName, #tmuxCreateCwd")) field.value = field.value.trim();
431
+ }, true);
432
+ }
433
+
434
+ function installDetailsStateMemory() {
435
+ const advanced = document.querySelector(".run-advanced");
436
+ if (!advanced) return;
437
+ advanced.open = Boolean(state.qualityPreferences?.advancedRunOpen);
438
+ advanced.addEventListener("toggle", () => {
439
+ state.qualityPreferences = { ...defaultQualityPreferences(), ...(state.qualityPreferences || {}), advancedRunOpen: advanced.open };
440
+ saveQualityPreferences();
441
+ });
442
+ }
443
+
444
+ function installOverflowTitles() {
445
+ const refresh = () => {
446
+ document.querySelectorAll("button, .nav-item, .meta-chip, .session-card h3, .terminal-session-card").forEach((element) => {
447
+ const label = describeControl(element);
448
+ if (label && element.scrollWidth > element.clientWidth && !element.getAttribute("title")) element.setAttribute("title", label);
449
+ });
450
+ };
451
+ refresh();
452
+ window.addEventListener("resize", refresh);
453
+ }
454
+
455
+ function installViewportSafetyClass() {
456
+ const setViewport = () => {
457
+ document.documentElement.dataset.qualityViewport = window.innerWidth < 760 ? "mobile" : window.innerWidth < 1120 ? "tablet" : "desktop";
458
+ };
459
+ setViewport();
460
+ window.addEventListener("resize", setViewport);
461
+ }
462
+
463
+ function installGlobalQualityGuards() {
464
+ if (qualityGuardsInstalled) return;
465
+ qualityGuardsInstalled = true;
466
+ enhanceQualityControls();
467
+ installQualityMutationObserver();
468
+ installPressedStateMirrors();
469
+ installFormRecovery();
470
+ installDetailsStateMemory();
471
+ installOverflowTitles();
472
+ installViewportSafetyClass();
473
+ document.addEventListener("keydown", () => markInputModality("keyboard"), true);
474
+ document.addEventListener("pointerdown", () => markInputModality("pointer"), true);
475
+ }
476
+
477
+ function bindQualityEvents() {
478
+ installGlobalQualityGuards();
479
+ $("#shortcutHelpBtn")?.addEventListener("click", openShortcutHelp);
480
+ $("#closeShortcutHelpBtn")?.addEventListener("click", closeShortcutHelp);
481
+ $("#closeQuickPaletteBtn")?.addEventListener("click", closeQuickPalette);
482
+ safeBackdrop($("#quickPaletteModal"), closeQuickPalette);
483
+ safeBackdrop($("#shortcutHelpModal"), closeShortcutHelp);
484
+ $("#quickPaletteInput")?.addEventListener("input", () => {
485
+ activeCommandIndex = 0;
486
+ renderQuickCommands();
487
+ });
488
+ $("#quickPaletteInput")?.addEventListener("keydown", (event) => {
489
+ if (["ArrowUp", "ArrowDown", "Home", "End"].includes(event.key)) {
490
+ event.preventDefault();
491
+ const count = visibleCommands.length;
492
+ if (!count) return;
493
+ activeCommandIndex = event.key === "Home"
494
+ ? 0
495
+ : event.key === "End"
496
+ ? count - 1
497
+ : (activeCommandIndex + (event.key === "ArrowDown" ? 1 : -1) + count) % count;
498
+ renderQuickCommands();
499
+ $("#quickPaletteList")?.querySelector(".active")?.scrollIntoView({ block: "nearest" });
500
+ } else if (event.key === "Enter" && visibleCommands[activeCommandIndex]) {
501
+ event.preventDefault();
502
+ executeQuickCommand(visibleCommands[activeCommandIndex].id);
503
+ }
504
+ });
505
+ $("#quickPaletteList")?.addEventListener("click", (event) => {
506
+ const button = event.target.closest("[data-quick-command]");
507
+ if (button) executeQuickCommand(button.dataset.quickCommand);
508
+ });
509
+ $("#clearRunDraftBtn")?.addEventListener("click", clearRunDraft);
510
+ $("#runForm")?.addEventListener("input", saveRunDraft);
511
+ $("#runForm")?.addEventListener("change", saveRunDraft);
512
+ $("#emptyClearFiltersBtn")?.addEventListener("click", () => $("#resetFiltersBtn")?.click());
513
+ document.addEventListener("keydown", (event) => {
514
+ const editable = event.target instanceof HTMLElement && Boolean(event.target.closest("input, textarea, select, [contenteditable='true']"));
515
+ if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
516
+ event.preventDefault();
517
+ event.stopImmediatePropagation();
518
+ if ($("#quickPaletteModal").classList.contains("hidden")) openQuickPalette();
519
+ else closeQuickPalette();
520
+ return;
521
+ }
522
+ if (!editable && !event.metaKey && !event.ctrlKey && !event.altKey && event.key === "?" && !currentDialog?.()) {
523
+ event.preventDefault();
524
+ event.stopImmediatePropagation();
525
+ openShortcutHelp();
526
+ return;
527
+ }
528
+ if (event.key !== "Escape") return;
529
+ if (!$("#quickPaletteModal").classList.contains("hidden")) {
530
+ event.preventDefault();
531
+ event.stopImmediatePropagation();
532
+ closeQuickPalette();
533
+ } else if (!$("#shortcutHelpModal").classList.contains("hidden")) {
534
+ event.preventDefault();
535
+ event.stopImmediatePropagation();
536
+ closeShortcutHelp();
537
+ }
538
+ }, true);
539
+ }
540
+
541
+ return {
542
+ DASHBOARD_STORAGE_KEY,
543
+ RUN_DRAFT_STORAGE_KEY,
544
+ normalizedSearch,
545
+ loadQualityState,
546
+ saveDashboardPreferences,
547
+ saveRunDraft,
548
+ restoreRunDraft,
549
+ clearRunDraft,
550
+ renderQuickCommands,
551
+ openQuickPalette,
552
+ closeQuickPalette,
553
+ openShortcutHelp,
554
+ closeShortcutHelp,
555
+ copyText,
556
+ safeBackdrop,
557
+ bindQualityEvents,
558
+ QUALITY_PREF_STORAGE_KEY,
559
+ qualityText,
560
+ defaultQualityPreferences,
561
+ loadQualityPreferences,
562
+ saveQualityPreferences,
563
+ applyQualityPreferences,
564
+ enhanceControl,
565
+ enhanceQualityControls,
566
+ installGlobalQualityGuards,
567
+ };
568
+ };