@uniqbit/mate-core 0.18.5 → 1.0.0-canary.0
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/claude-plugin/hooks/companion-instructions.mjs +9 -0
- package/claude-plugin/hooks/hooks.json +12 -2
- package/package.json +10 -2
- package/src/brand/art.generated.ts +69 -0
- package/src/brand/index.ts +12 -0
- package/src/brand/palette.ts +19 -0
- package/src/brand/svg.generated.ts +8 -0
- package/src/brand/terminal.ts +83 -0
- package/src/brand/types.ts +33 -0
- package/src/cli/commands/artifact/artifact.ts +8 -0
- package/src/cli/commands/artifact/delivery/archive.ts +117 -0
- package/src/cli/commands/artifact/delivery/branch-flags.ts +55 -0
- package/src/cli/commands/artifact/delivery/environment.ts +104 -0
- package/src/cli/commands/artifact/delivery/link.ts +154 -0
- package/src/cli/commands/artifact/delivery/output.ts +58 -0
- package/src/cli/commands/artifact/finish/command.ts +33 -3
- package/src/cli/commands/artifact/finish/engine.ts +47 -3
- package/src/cli/commands/artifact/finish/finisher.ts +10 -0
- package/src/cli/commands/artifact/finish/openspec.ts +177 -138
- package/src/cli/commands/artifact/finish/secret-scan.ts +43 -0
- package/src/cli/commands/artifact/pending/command.ts +31 -3
- package/src/cli/commands/artifact/pending/discovery.ts +21 -57
- package/src/cli/commands/cap/graphify.ts +52 -3
- package/src/cli/commands/cap/index-cmd.ts +8 -1
- package/src/cli/commands/cap/openspec.ts +13 -25
- package/src/cli/commands/companion/open.ts +56 -39
- package/src/cli/commands/companion/update.ts +75 -42
- package/src/cli/commands/doctor.ts +176 -3
- package/src/cli/commands/plugin/install.ts +9 -4
- package/src/cli/commands/plugin/plugin.ts +3 -2
- package/src/cli/commands/report/adapter.ts +11 -2
- package/src/cli/commands/report/contract.ts +21 -0
- package/src/cli/commands/report/highlight.ts +34 -4
- package/src/cli/commands/report/markdown.ts +110 -0
- package/src/cli/commands/report/palette.ts +118 -0
- package/src/cli/commands/report/renderer.ts +456 -153
- package/src/cli/commands/report/types.ts +16 -0
- package/src/cli/commands/setup.ts +7 -15
- package/src/cli/commands/studio/client-asset.ts +16 -0
- package/src/cli/commands/studio/openspec-cli.ts +10 -58
- package/src/cli/commands/studio/routes.ts +2 -0
- package/src/cli/commands/studio/selection.ts +19 -1
- package/src/cli/commands/studio/server.ts +42 -1
- package/src/cli/commands/studio/views/client.ts +6 -616
- package/src/cli/commands/studio/views/dashboard/changes.tsx +5 -10
- package/src/cli/commands/studio/views/document.tsx +137 -69
- package/src/cli/commands/studio/views/markdown.tsx +250 -0
- package/src/cli/commands/studio/views/palette.ts +134 -0
- package/src/cli/commands/studio/views/reports/index.tsx +2 -7
- package/src/cli/commands/studio/views/skills/index.tsx +43 -62
- package/src/cli/commands/studio/views/specs/index.tsx +7 -15
- package/src/cli/commands/studio/views/studio-client/actions.ts +51 -0
- package/src/cli/commands/studio/views/studio-client/companion.ts +13 -0
- package/src/cli/commands/studio/views/studio-client/index.ts +32 -0
- package/src/cli/commands/studio/views/studio-client/keys.ts +3 -0
- package/src/cli/commands/studio/views/studio-client/main.ts +3 -0
- package/src/cli/commands/studio/views/studio-client/page.ts +146 -0
- package/src/cli/commands/studio/views/studio-client/panels.ts +57 -0
- package/src/cli/commands/studio/views/studio-client/reports.ts +122 -0
- package/src/cli/commands/studio/views/studio-client/theme.ts +63 -0
- package/src/cli/commands/studio/views/studio-client/vault-editor.ts +166 -0
- package/src/cli/commands/studio/views/studio-client/vault-pane.ts +202 -0
- package/src/cli/commands/studio/views/studio-client/vault-tree.ts +258 -0
- package/src/cli/commands/studio/views/studio-client.generated.ts +2 -0
- package/src/cli/commands/studio/views/styles.ts +217 -206
- package/src/cli/commands/studio/views/vault/state.ts +3 -5
- package/src/cli/commands/studio/views/workflow/delivery-path.tsx +370 -0
- package/src/cli/commands/studio/views/workflow/graph.ts +220 -0
- package/src/cli/commands/studio/views/workflow/index.tsx +2 -3
- package/src/cli/commands/studio/views/workflow/steps.ts +228 -287
- package/src/cli/commands/wrap.ts +5 -13
- package/src/cli/main.ts +4 -1
- package/src/cli/parse-flags.ts +25 -7
- package/src/cli/setup-selector.tsx +38 -162
- package/src/cli/usage.ts +4 -0
- package/src/hooks/companion-instructions.ts +190 -0
- package/src/hooks/hook-io.ts +63 -0
- package/src/hooks/session-banner.ts +21 -29
- package/src/hooks/session-guidance.ts +9 -17
- package/src/hooks/session-prepare.ts +115 -28
- package/src/hooks/validate-artifact-path.ts +82 -24
- package/src/index.ts +4 -0
- package/src/lib/change-layout.ts +154 -0
- package/src/lib/components/wizard-header.tsx +3 -1
- package/src/lib/context-mode-package.ts +24 -11
- package/src/lib/delivery/archive-change.ts +154 -0
- package/src/lib/delivery/branches.ts +108 -0
- package/src/lib/delivery/environment.ts +29 -0
- package/src/lib/delivery/forge.ts +279 -0
- package/src/lib/delivery/overlap.ts +202 -0
- package/src/lib/delivery/record-delivery.ts +196 -0
- package/src/lib/delivery/record.ts +343 -0
- package/src/lib/delivery/refresh.ts +174 -0
- package/src/lib/delivery/repository-access.ts +97 -0
- package/src/lib/delivery/stages.ts +111 -0
- package/src/lib/delivery/token.ts +58 -0
- package/src/lib/frontmatter.ts +18 -0
- package/src/lib/opencode-plugin-package.ts +5 -5
- package/src/lib/openspec-cli.ts +116 -0
- package/src/lib/openspec-scopes.ts +86 -0
- package/src/lib/orchestrator/adapters/claude-args.ts +50 -0
- package/src/lib/orchestrator/adapters/claude.ts +48 -40
- package/src/lib/orchestrator/adapters/opencode.ts +2 -0
- package/src/lib/orchestrator/editor.ts +9 -9
- package/src/lib/orchestrator/openspec-store.ts +14 -54
- package/src/lib/orchestrator/projection-claude-commands.ts +36 -0
- package/src/lib/orchestrator/projection-claude-links.ts +159 -0
- package/src/lib/orchestrator/projection-claude-skills.ts +19 -137
- package/src/lib/orchestrator/projection-entries.ts +45 -14
- package/src/lib/orchestrator/projection-runtime-documents.ts +15 -6
- package/src/lib/orchestrator/projection-types.ts +3 -0
- package/src/lib/orchestrator/refresh-working-claude-links.ts +87 -0
- package/src/lib/orchestrator/session-prepare.ts +45 -5
- package/src/lib/orchestrator/setup-compatibilities.ts +18 -75
- package/src/lib/orchestrator/types.ts +2 -2
- package/src/lib/orchestrator/wrap-pass.ts +22 -1
- package/src/lib/package-paths.ts +8 -0
- package/src/opencode/companion-hooks.ts +64 -5
- package/src/opencode/companion-policy.ts +37 -18
- package/src/opencode/projected-guidance.ts +4 -1
- package/src/opencode/session-prepare.ts +2 -1
- package/src/opencode/tui.tsx +4 -2
- package/src/runtime/artifact-refusal.ts +63 -0
- package/src/runtime/claude-delivery-plan.ts +89 -7
- package/src/runtime/commit-attribution-guard.ts +181 -0
- package/src/runtime/companion-guidance.ts +15 -3
- package/src/runtime/companion-instructions.ts +340 -0
- package/src/runtime/error-message.ts +4 -0
- package/src/runtime/index.ts +5 -0
- package/src/runtime/managed-block.ts +117 -0
- package/src/runtime/markdown-scan.ts +266 -0
- package/src/runtime/policy.ts +15 -1
- package/src/runtime/projected-flag.ts +7 -0
- package/src/runtime/projected-guidance.ts +1 -0
- package/src/runtime/runtime-document-manifest.ts +62 -11
- package/src/runtime/secret-guard.ts +148 -0
- package/src/runtime/secret-patterns.ts +164 -0
- package/src/runtime/secret-scan.ts +90 -0
- package/src/runtime/session-kind.ts +13 -0
- package/src/runtime/shell-heredoc.ts +81 -0
- package/src/runtime/wrap-state.ts +36 -2
- package/src/runtime/write-atomic.ts +64 -0
- package/src/templates/capabilities/openspec-cap/mate-minimal/schema.yaml +8 -6
- package/src/templates/capabilities/openspec-cap/mate-minimal/templates/spec.md +3 -5
- package/src/templates/capabilities/openspec-cap/mate-v1/schema.yaml +7 -9
- package/src/templates/capabilities/openspec-cap/mate-v1/templates/spec.md +3 -5
- package/src/templates/capabilities/openspec-cap/openspec-conventions.yaml +10 -12
- package/src/templates/mate-skills/THIRD-PARTY-NOTICES.md +40 -0
- package/src/templates/mate-skills/agents/mate-apply-change/SKILL.md +76 -0
- package/src/templates/mate-skills/agents/mate-apply-change/references/openspec-apply-change.md +198 -0
- package/src/templates/mate-skills/agents/mate-archive-change/SKILL.md +59 -0
- package/src/templates/mate-skills/{claude/mate-openspec-backfill → agents/mate-backfill-spec}/SKILL.md +4 -4
- package/src/templates/mate-skills/agents/mate-clarify-intent/SKILL.md +95 -0
- package/src/templates/mate-skills/agents/mate-explore-change/SKILL.md +50 -0
- package/src/templates/mate-skills/agents/mate-explore-change/references/openspec-explore.md +348 -0
- package/src/templates/mate-skills/agents/mate-grill-me/SKILL.md +3 -3
- package/src/templates/mate-skills/agents/mate-interview-me/SKILL.md +5 -5
- package/src/templates/mate-skills/agents/mate-propose-change/SKILL.md +103 -0
- package/src/templates/mate-skills/agents/mate-propose-change/references/openspec-propose.md +167 -0
- package/src/templates/mate-skills/agents/mate-propose-change/references/openspec-update-change.md +101 -0
- package/src/templates/mate-skills/{claude/mate-artifact-publish → agents/mate-publish-artifact}/SKILL.md +91 -23
- package/src/templates/mate-skills/agents/{mate-artifact-publish → mate-publish-artifact}/references/openspec.md +4 -3
- package/src/templates/mate-skills/agents/mate-show-me/SKILL.md +74 -16
- package/src/templates/mate-skills/agents/mate-simplify-code/SKILL.md +74 -68
- package/src/templates/mate-skills/claude/mate-apply-change/SKILL.md +76 -0
- package/src/templates/mate-skills/claude/mate-apply-change/references/openspec-apply-change.md +198 -0
- package/src/templates/mate-skills/claude/mate-archive-change/SKILL.md +59 -0
- package/src/templates/mate-skills/{agents/mate-openspec-backfill → claude/mate-backfill-spec}/SKILL.md +4 -4
- package/src/templates/mate-skills/claude/mate-clarify-intent/SKILL.md +95 -0
- package/src/templates/mate-skills/claude/mate-explore-change/SKILL.md +50 -0
- package/src/templates/mate-skills/claude/mate-explore-change/references/openspec-explore.md +348 -0
- package/src/templates/mate-skills/claude/mate-grill-me/SKILL.md +3 -3
- package/src/templates/mate-skills/claude/mate-interview-me/SKILL.md +5 -5
- package/src/templates/mate-skills/claude/mate-propose-change/SKILL.md +103 -0
- package/src/templates/mate-skills/claude/mate-propose-change/references/openspec-propose.md +167 -0
- package/src/templates/mate-skills/claude/mate-propose-change/references/openspec-update-change.md +101 -0
- package/src/templates/mate-skills/{agents/mate-artifact-publish → claude/mate-publish-artifact}/SKILL.md +91 -23
- package/src/templates/mate-skills/claude/{mate-artifact-publish → mate-publish-artifact}/references/openspec.md +4 -3
- package/src/templates/mate-skills/claude/mate-show-me/SKILL.md +74 -16
- package/src/templates/mate-skills/claude/mate-simplify-code/SKILL.md +74 -68
- package/src/templates/mate-skills/openspec-references.ts +140 -0
- package/src/templates/root/TEMPLATE_AGENTS.md +2 -2
- package/src/tools/setup/__snapshots__/runtime-surface-golden.test.ts.snap +3727 -320
- package/src/tools/setup/capabilities/context-mode.ts +25 -5
- package/src/tools/setup/capabilities/graphify-gate.ts +31 -0
- package/src/tools/setup/capabilities/graphify-secrets.ts +255 -0
- package/src/tools/setup/capabilities/graphify.ts +12 -0
- package/src/tools/setup/capabilities/openspec.ts +151 -270
- package/src/tools/setup/capabilities/tokensave.ts +36 -3
- package/src/tools/setup/context-services.ts +9 -17
- package/src/tools/setup/dynamic-plugins/install.ts +24 -28
- package/src/tools/setup/engine.ts +97 -21
- package/src/tools/setup/managed-output.ts +51 -0
- package/src/tools/setup/mate.ts +86 -11
- package/src/tools/setup/plugin.ts +35 -0
- package/src/tools/setup/plugins/guidance.ts +7 -4
- package/src/tools/setup/providers/agent-file-sections.ts +4 -2
- package/src/tools/setup/providers/claude-attribution.ts +65 -0
- package/src/tools/setup/providers/claude-plugin-folders.ts +625 -0
- package/src/tools/setup/providers/claude-plugin-hooks.ts +39 -19
- package/src/tools/setup/providers/claude.ts +284 -8
- package/src/tools/setup/providers/opencode.ts +180 -1
- package/src/tools/setup/utils.ts +14 -4
- package/src/tools/setup.ts +126 -10
- package/wrappers/bin/graphify +52 -4
- package/wrappers/bin/openspec +33 -9
- package/wrappers/lib/graphify-gate.mjs +8 -0
- package/src/cli/commands/studio/views/workflow/transcript.tsx +0 -190
- package/src/tools/setup/runtime-documents.ts +0 -291
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { REPORT_VIEW_PREFIX } from "../routes";
|
|
2
1
|
import { COMPANION_DIGEST_PATTERN, COMPANION_PARAM } from "../selection";
|
|
2
|
+
import {
|
|
3
|
+
COMPANION_STORAGE_KEY,
|
|
4
|
+
THEME_STORAGE_KEY,
|
|
5
|
+
VAULT_TREE_STORAGE_KEY,
|
|
6
|
+
} from "./studio-client/keys";
|
|
3
7
|
|
|
4
|
-
export
|
|
5
|
-
export const COMPANION_STORAGE_KEY = "mate-studio-companion";
|
|
6
|
-
export const VAULT_TREE_STORAGE_KEY = "mate-studio-vault-tree";
|
|
8
|
+
export { COMPANION_STORAGE_KEY, THEME_STORAGE_KEY, VAULT_TREE_STORAGE_KEY };
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* Runs in `<head>` before first paint. A server-rendered document arrives with
|
|
@@ -40,615 +42,3 @@ export const STUDIO_PREPAINT_SCRIPT = `(function () {
|
|
|
40
42
|
/* a blocked web store only costs the restore, never the page */
|
|
41
43
|
}
|
|
42
44
|
})();`;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* The only browser code Studio ships: cycling the appearance, remembering the
|
|
46
|
-
* companion on screen, switching workflow schema tabs, copying a prompt, the
|
|
47
|
-
* vault's editor, filter and tree toggle, and swapping in a deferred vault tree.
|
|
48
|
-
* Each needs an API the server does not have; all markup comes from the server.
|
|
49
|
-
*/
|
|
50
|
-
export const STUDIO_CLIENT_SCRIPT = `(function () {
|
|
51
|
-
var THEME_KEY = ${JSON.stringify(THEME_STORAGE_KEY)};
|
|
52
|
-
var COMPANION_KEY = ${JSON.stringify(COMPANION_STORAGE_KEY)};
|
|
53
|
-
var THEME_CYCLE = ["system", "dark", "light"];
|
|
54
|
-
var toastTimer;
|
|
55
|
-
|
|
56
|
-
function readTheme() {
|
|
57
|
-
try {
|
|
58
|
-
var stored = localStorage.getItem(THEME_KEY);
|
|
59
|
-
return THEME_CYCLE.indexOf(stored) === -1 ? "system" : stored;
|
|
60
|
-
} catch (error) {
|
|
61
|
-
return "system";
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function storeTheme(theme) {
|
|
66
|
-
try {
|
|
67
|
-
localStorage.setItem(THEME_KEY, theme);
|
|
68
|
-
} catch (error) {
|
|
69
|
-
/* a blocked web store only costs the preference, never the page */
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
var theme = readTheme();
|
|
74
|
-
|
|
75
|
-
function applyTheme(next) {
|
|
76
|
-
theme = next;
|
|
77
|
-
var root = document.documentElement;
|
|
78
|
-
if (next === "system") root.removeAttribute("data-theme");
|
|
79
|
-
else root.setAttribute("data-theme", next);
|
|
80
|
-
var control = document.getElementById("studio-theme");
|
|
81
|
-
if (control) {
|
|
82
|
-
control.textContent = "Theme: " + next;
|
|
83
|
-
control.setAttribute("data-theme-state", next);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function announce(message) {
|
|
88
|
-
var toast = document.getElementById("studio-toast");
|
|
89
|
-
if (!toast) return;
|
|
90
|
-
toast.textContent = message;
|
|
91
|
-
toast.setAttribute("data-shown", "true");
|
|
92
|
-
clearTimeout(toastTimer);
|
|
93
|
-
toastTimer = setTimeout(function () {
|
|
94
|
-
toast.setAttribute("data-shown", "false");
|
|
95
|
-
}, 1400);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function copy(text, label) {
|
|
99
|
-
if (typeof navigator === "undefined" || !navigator.clipboard || typeof navigator.clipboard.writeText !== "function") {
|
|
100
|
-
announce("copying is blocked in this browser");
|
|
101
|
-
return Promise.resolve();
|
|
102
|
-
}
|
|
103
|
-
return Promise.resolve().then(function () { return navigator.clipboard.writeText(text); }).then(
|
|
104
|
-
function () {
|
|
105
|
-
announce("copied " + label);
|
|
106
|
-
},
|
|
107
|
-
function () {
|
|
108
|
-
/* a blocked clipboard costs the copy, never the page */
|
|
109
|
-
announce("copying is blocked in this browser");
|
|
110
|
-
},
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function rememberCompanion() {
|
|
115
|
-
var scope = document.querySelector("[data-companion]");
|
|
116
|
-
var current = scope && scope.getAttribute("data-companion");
|
|
117
|
-
if (!current) return;
|
|
118
|
-
try {
|
|
119
|
-
localStorage.setItem(COMPANION_KEY, current);
|
|
120
|
-
} catch (error) {
|
|
121
|
-
/* a blocked web store only costs the next visit, never the page */
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function wireWorkflowSchemaTabs() {
|
|
126
|
-
document.querySelectorAll("[data-workflow-schema-root]").forEach(function (root) {
|
|
127
|
-
var tabs = root.querySelectorAll("[data-workflow-schema-profile]");
|
|
128
|
-
var panels = root.querySelectorAll("[data-workflow-schema-panel]");
|
|
129
|
-
|
|
130
|
-
function select(profile) {
|
|
131
|
-
tabs.forEach(function (tab) {
|
|
132
|
-
tab.setAttribute(
|
|
133
|
-
"aria-selected",
|
|
134
|
-
tab.getAttribute("data-workflow-schema-profile") === profile ? "true" : "false",
|
|
135
|
-
);
|
|
136
|
-
});
|
|
137
|
-
panels.forEach(function (panel) {
|
|
138
|
-
panel.setAttribute(
|
|
139
|
-
"data-active",
|
|
140
|
-
panel.getAttribute("data-workflow-schema-panel") === profile ? "true" : "false",
|
|
141
|
-
);
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
tabs.forEach(function (tab) {
|
|
146
|
-
tab.addEventListener("click", function () {
|
|
147
|
-
select(tab.getAttribute("data-workflow-schema-profile"));
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
var selected = root.querySelector('[aria-selected="true"]');
|
|
152
|
-
if (selected) select(selected.getAttribute("data-workflow-schema-profile"));
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function wireSkillTabs() {
|
|
157
|
-
document.querySelectorAll("[data-skills-root]").forEach(function (root) {
|
|
158
|
-
var tabs = root.querySelectorAll("[data-skill-profile]");
|
|
159
|
-
var panels = root.querySelectorAll("[data-skill-panel]");
|
|
160
|
-
|
|
161
|
-
function select(profile) {
|
|
162
|
-
tabs.forEach(function (tab) {
|
|
163
|
-
tab.setAttribute(
|
|
164
|
-
"aria-selected",
|
|
165
|
-
tab.getAttribute("data-skill-profile") === profile ? "true" : "false",
|
|
166
|
-
);
|
|
167
|
-
});
|
|
168
|
-
panels.forEach(function (panel) {
|
|
169
|
-
var active = panel.getAttribute("data-skill-panel") === profile;
|
|
170
|
-
panel.setAttribute("data-active", active ? "true" : "false");
|
|
171
|
-
panel.setAttribute("aria-hidden", active ? "false" : "true");
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
tabs.forEach(function (tab) {
|
|
176
|
-
tab.addEventListener("click", function () {
|
|
177
|
-
select(tab.getAttribute("data-skill-profile"));
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
var selected = root.querySelector('[aria-selected="true"]');
|
|
182
|
-
if (selected) select(selected.getAttribute("data-skill-profile"));
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
applyTheme(theme);
|
|
187
|
-
rememberCompanion();
|
|
188
|
-
wireWorkflowSchemaTabs();
|
|
189
|
-
wireSkillTabs();
|
|
190
|
-
|
|
191
|
-
var toggle = document.getElementById("studio-theme");
|
|
192
|
-
if (toggle) {
|
|
193
|
-
toggle.addEventListener("click", function () {
|
|
194
|
-
var next = THEME_CYCLE[(THEME_CYCLE.indexOf(theme) + 1) % THEME_CYCLE.length];
|
|
195
|
-
applyTheme(next);
|
|
196
|
-
storeTheme(next);
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
document.querySelectorAll("[data-copy]").forEach(function (node) {
|
|
201
|
-
node.addEventListener("click", function () {
|
|
202
|
-
var text = node.getAttribute("data-copy");
|
|
203
|
-
if (text) copy(text, node.getAttribute("data-copy-label") || "prompt");
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
/* Delegated: a spec row's controls need no per-node wiring. */
|
|
208
|
-
var AGENT_ORDER = ["claude", "opencode"];
|
|
209
|
-
function copyPromptFor(node) {
|
|
210
|
-
var viewed = document.documentElement.getAttribute("data-agent-viewed");
|
|
211
|
-
var order = viewed ? [viewed].concat(AGENT_ORDER) : AGENT_ORDER;
|
|
212
|
-
for (var index = 0; index < order.length; index += 1) {
|
|
213
|
-
var text = node.getAttribute("data-prompt-" + order[index]);
|
|
214
|
-
if (text) return text;
|
|
215
|
-
}
|
|
216
|
-
return null;
|
|
217
|
-
}
|
|
218
|
-
if (typeof document.addEventListener === "function") document.addEventListener("click", function (event) {
|
|
219
|
-
var target = event.target;
|
|
220
|
-
if (!target || typeof target.closest !== "function") return;
|
|
221
|
-
var run = target.closest("[data-studio-action]");
|
|
222
|
-
if (run) {
|
|
223
|
-
var agents = (run.getAttribute("data-studio-agents") || "").split(" ").filter(Boolean);
|
|
224
|
-
document.dispatchEvent(new CustomEvent("studio:terminal-action", {
|
|
225
|
-
detail: { action: run.getAttribute("data-studio-action"), subject: run.getAttribute("data-studio-subject"), agents: agents },
|
|
226
|
-
}));
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
var prompt = target.closest("[data-studio-copy-prompt]");
|
|
230
|
-
if (prompt) {
|
|
231
|
-
var text = copyPromptFor(prompt);
|
|
232
|
-
if (text) copy(text, "prompt");
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
var savePromise = null;
|
|
237
|
-
function editor() { return document.getElementById("vault-editor"); }
|
|
238
|
-
function status(message) {
|
|
239
|
-
var node = document.getElementById("vault-status");
|
|
240
|
-
if (node) node.textContent = message;
|
|
241
|
-
}
|
|
242
|
-
function dirty() {
|
|
243
|
-
var node = editor();
|
|
244
|
-
return !!node && node.value !== (node.getAttribute("data-vault-original") || "");
|
|
245
|
-
}
|
|
246
|
-
function setIncoming(event) {
|
|
247
|
-
var box = document.getElementById("vault-incoming");
|
|
248
|
-
var content = document.getElementById("vault-incoming-content");
|
|
249
|
-
var recover = document.getElementById("vault-recover");
|
|
250
|
-
if (!box || !content || !recover) return;
|
|
251
|
-
box.hidden = false;
|
|
252
|
-
content.textContent = event.content || "";
|
|
253
|
-
recover.setAttribute("data-vault-content", event.content || "");
|
|
254
|
-
recover.setAttribute("data-vault-token", event.token || "");
|
|
255
|
-
}
|
|
256
|
-
function acceptIncoming(event) {
|
|
257
|
-
var node = editor();
|
|
258
|
-
if (!node) return;
|
|
259
|
-
node.value = event.content || "";
|
|
260
|
-
node.setAttribute("data-vault-original", node.value);
|
|
261
|
-
if (event.token) node.setAttribute("data-vault-token", event.token);
|
|
262
|
-
var box = document.getElementById("vault-incoming");
|
|
263
|
-
if (box) box.hidden = true;
|
|
264
|
-
status("incoming version loaded into the editor");
|
|
265
|
-
}
|
|
266
|
-
function askBeforeNavigation() {
|
|
267
|
-
return !dirty() || window.confirm("Discard unsaved changes?");
|
|
268
|
-
}
|
|
269
|
-
function waitForSave() {
|
|
270
|
-
return savePromise ? savePromise.then(function (result) { return result; }) : Promise.resolve(true);
|
|
271
|
-
}
|
|
272
|
-
function navigate(url) {
|
|
273
|
-
waitForSave().then(function (allowed) {
|
|
274
|
-
if (allowed && askBeforeNavigation()) location.assign(url);
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
function navigationUrl(form, submitter) {
|
|
278
|
-
var target = new URL(form.action || location.href, location.href);
|
|
279
|
-
new FormData(form).forEach(function (value, key) {
|
|
280
|
-
if (typeof value === "string") target.searchParams.append(key, value);
|
|
281
|
-
});
|
|
282
|
-
if (submitter && submitter.name && submitter.value) target.searchParams.set(submitter.name, submitter.value);
|
|
283
|
-
return target.toString();
|
|
284
|
-
}
|
|
285
|
-
function wireNavigation(scope) {
|
|
286
|
-
(scope || document).querySelectorAll('form[method="get"]:not([data-vault-refresh])').forEach(function (form) {
|
|
287
|
-
form.addEventListener("submit", function (event) {
|
|
288
|
-
event.preventDefault();
|
|
289
|
-
navigate(navigationUrl(form, event.submitter));
|
|
290
|
-
});
|
|
291
|
-
form.querySelectorAll("select").forEach(function (select) {
|
|
292
|
-
select.addEventListener("change", function () { navigate(navigationUrl(form)); });
|
|
293
|
-
});
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
function wireVault() {
|
|
297
|
-
var node = editor();
|
|
298
|
-
var root = document.querySelector("[data-vault-root]");
|
|
299
|
-
if (!node || !root) return;
|
|
300
|
-
node.setAttribute("data-vault-original", node.value);
|
|
301
|
-
var save = document.getElementById("vault-save");
|
|
302
|
-
if (save) save.addEventListener("click", function () {
|
|
303
|
-
if (savePromise) return;
|
|
304
|
-
var companion = document.querySelector("[data-companion]");
|
|
305
|
-
var body = {
|
|
306
|
-
companion: companion ? companion.getAttribute("data-companion") : "",
|
|
307
|
-
path: node.getAttribute("data-vault-path"),
|
|
308
|
-
content: node.value,
|
|
309
|
-
token: node.getAttribute("data-vault-token"),
|
|
310
|
-
};
|
|
311
|
-
savePromise = fetch("/api/vault/save", {
|
|
312
|
-
method: "POST",
|
|
313
|
-
headers: { "content-type": "application/json" },
|
|
314
|
-
body: JSON.stringify(body),
|
|
315
|
-
}).then(function (response) {
|
|
316
|
-
return response.json().then(function (result) {
|
|
317
|
-
if (!response.ok) {
|
|
318
|
-
if (response.status === 409) setIncoming(result);
|
|
319
|
-
status(result.reason || "save refused");
|
|
320
|
-
return false;
|
|
321
|
-
}
|
|
322
|
-
node.setAttribute("data-vault-token", result.token || "");
|
|
323
|
-
node.setAttribute("data-vault-original", node.value);
|
|
324
|
-
status("saved");
|
|
325
|
-
return true;
|
|
326
|
-
});
|
|
327
|
-
}).catch(function () {
|
|
328
|
-
status("save failed");
|
|
329
|
-
return false;
|
|
330
|
-
}).finally(function () { savePromise = null; });
|
|
331
|
-
});
|
|
332
|
-
var recover = document.getElementById("vault-recover");
|
|
333
|
-
if (recover) recover.addEventListener("click", function () {
|
|
334
|
-
acceptIncoming({ content: recover.getAttribute("data-vault-content") || "", token: recover.getAttribute("data-vault-token") || "" });
|
|
335
|
-
});
|
|
336
|
-
var eventsUrl = node.getAttribute("data-vault-events");
|
|
337
|
-
if (eventsUrl && typeof EventSource !== "undefined") {
|
|
338
|
-
var events = new EventSource(eventsUrl);
|
|
339
|
-
events.onmessage = function (message) {
|
|
340
|
-
var event;
|
|
341
|
-
try { event = JSON.parse(message.data); } catch (error) { return; }
|
|
342
|
-
if (event.kind === "overwritten") {
|
|
343
|
-
setIncoming(event);
|
|
344
|
-
status("a version was overwritten; its content is available below");
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
if (event.kind === "removed") {
|
|
348
|
-
status("this file no longer exists on disk");
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
if (dirty()) {
|
|
352
|
-
setIncoming(event);
|
|
353
|
-
status("the file changed on disk; unsaved content is still in the editor");
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
node.value = event.content || "";
|
|
357
|
-
node.setAttribute("data-vault-original", node.value);
|
|
358
|
-
if (event.token) node.setAttribute("data-vault-token", event.token);
|
|
359
|
-
status("updated from disk");
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
if (typeof window !== "undefined") window.addEventListener("beforeunload", function (event) {
|
|
364
|
-
if (!dirty()) return;
|
|
365
|
-
event.preventDefault();
|
|
366
|
-
event.returnValue = "";
|
|
367
|
-
});
|
|
368
|
-
function showTree(shown) {
|
|
369
|
-
var root = document.documentElement;
|
|
370
|
-
if (shown) root.removeAttribute("data-vault-tree");
|
|
371
|
-
else root.setAttribute("data-vault-tree", "hidden");
|
|
372
|
-
var toggle = document.getElementById("vault-tree-toggle");
|
|
373
|
-
if (toggle) {
|
|
374
|
-
toggle.textContent = shown ? "Hide files" : "Show files";
|
|
375
|
-
toggle.setAttribute("aria-expanded", shown ? "true" : "false");
|
|
376
|
-
}
|
|
377
|
-
try {
|
|
378
|
-
localStorage.setItem(${JSON.stringify(VAULT_TREE_STORAGE_KEY)}, shown ? "shown" : "hidden");
|
|
379
|
-
} catch (error) {
|
|
380
|
-
/* a blocked web store only costs the preference, never the page */
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
var displayedGeneration = null;
|
|
384
|
-
var latestGeneration = null;
|
|
385
|
-
var refreshSequence = 0;
|
|
386
|
-
var filterSequence = 0;
|
|
387
|
-
var filterTimer = null;
|
|
388
|
-
var filterRequest = null;
|
|
389
|
-
var FILTER_DELAY = 150;
|
|
390
|
-
|
|
391
|
-
function failure(error) {
|
|
392
|
-
return error && error.message ? error.message : String(error);
|
|
393
|
-
}
|
|
394
|
-
function fetchMarkup(url, signal) {
|
|
395
|
-
return fetch(url, { headers: { accept: "text/html" }, signal: signal }).then(function (response) {
|
|
396
|
-
if (!response.ok) throw new Error("status " + response.status);
|
|
397
|
-
return response.text();
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
/** Moves server-rendered nodes into a container; builds no markup of its own. */
|
|
401
|
-
function moveInto(container, markup) {
|
|
402
|
-
var parsed = new DOMParser().parseFromString(markup, "text/html");
|
|
403
|
-
container.replaceChildren.apply(
|
|
404
|
-
container,
|
|
405
|
-
Array.prototype.map.call(parsed.body.childNodes, function (child) { return document.importNode(child, true); }),
|
|
406
|
-
);
|
|
407
|
-
}
|
|
408
|
-
function readGeneration(slot) {
|
|
409
|
-
var value = slot ? slot.getAttribute("data-vault-generation") : null;
|
|
410
|
-
return value === null || value === "" ? null : Number(value);
|
|
411
|
-
}
|
|
412
|
-
/** The indicator clears only once the displayed listing is as new as the newest change seen. */
|
|
413
|
-
function showStale() {
|
|
414
|
-
var note = document.getElementById("vault-stale");
|
|
415
|
-
if (!note) return;
|
|
416
|
-
note.hidden = !(latestGeneration !== null && displayedGeneration !== null && latestGeneration > displayedGeneration);
|
|
417
|
-
}
|
|
418
|
-
function openedByViewer() {
|
|
419
|
-
var keys = [];
|
|
420
|
-
document.querySelectorAll("details[data-vault-children-url][open]").forEach(function (folder) {
|
|
421
|
-
keys.push(folder.getAttribute("data-vault-dir"));
|
|
422
|
-
});
|
|
423
|
-
return keys;
|
|
424
|
-
}
|
|
425
|
-
/** Swaps server-rendered parts into place; a refreshed tree drops loaded folders and reopens the viewer's. */
|
|
426
|
-
function swapVaultSlots(markup) {
|
|
427
|
-
var reopen = openedByViewer();
|
|
428
|
-
var parsed = new DOMParser().parseFromString(markup, "text/html");
|
|
429
|
-
parsed.querySelectorAll("[data-vault-slot]").forEach(function (slot) {
|
|
430
|
-
var name = slot.getAttribute("data-vault-slot");
|
|
431
|
-
var target = document.querySelector('[data-vault-slot="' + name + '"]');
|
|
432
|
-
if (!target) return;
|
|
433
|
-
var node = document.importNode(slot, true);
|
|
434
|
-
target.replaceWith(node);
|
|
435
|
-
wireNavigation(node);
|
|
436
|
-
if (name !== "tree") return;
|
|
437
|
-
displayedGeneration = readGeneration(node);
|
|
438
|
-
node.querySelectorAll("details[data-vault-children-url]").forEach(function (folder) {
|
|
439
|
-
if (reopen.indexOf(folder.getAttribute("data-vault-dir")) !== -1) folder.open = true;
|
|
440
|
-
});
|
|
441
|
-
});
|
|
442
|
-
showStale();
|
|
443
|
-
runFilter();
|
|
444
|
-
}
|
|
445
|
-
function failVaultSlots(message) {
|
|
446
|
-
document.querySelectorAll("[data-vault-slot][aria-busy]").forEach(function (slot) {
|
|
447
|
-
slot.removeAttribute("aria-busy");
|
|
448
|
-
slot.textContent = message;
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
function loadChildren(folder) {
|
|
452
|
-
var url = folder.getAttribute("data-vault-children-url");
|
|
453
|
-
var container = folder.querySelector(".vault-children");
|
|
454
|
-
if (!url || !container || folder.hasAttribute("data-vault-loaded") || folder.hasAttribute("data-vault-loading")) return;
|
|
455
|
-
folder.setAttribute("data-vault-loading", "");
|
|
456
|
-
fetchMarkup(url).then(function (markup) {
|
|
457
|
-
moveInto(container, markup);
|
|
458
|
-
folder.setAttribute("data-vault-loaded", "");
|
|
459
|
-
}).catch(function (error) {
|
|
460
|
-
container.textContent = "This folder could not be loaded: " + failure(error);
|
|
461
|
-
}).finally(function () { folder.removeAttribute("data-vault-loading"); });
|
|
462
|
-
}
|
|
463
|
-
function runFilter() {
|
|
464
|
-
var filter = document.getElementById("vault-filter");
|
|
465
|
-
var results = document.getElementById("vault-filter-results");
|
|
466
|
-
var panel = document.getElementById("vault-tree-panel");
|
|
467
|
-
var layout = document.querySelector("[data-vault-layout]");
|
|
468
|
-
if (!filter || !results || !panel || !layout) return;
|
|
469
|
-
clearTimeout(filterTimer);
|
|
470
|
-
if (filterRequest) filterRequest.abort();
|
|
471
|
-
var needle = filter.value.trim();
|
|
472
|
-
filterSequence += 1;
|
|
473
|
-
if (!needle) {
|
|
474
|
-
results.hidden = true;
|
|
475
|
-
results.replaceChildren();
|
|
476
|
-
panel.removeAttribute("data-vault-filtering");
|
|
477
|
-
return;
|
|
478
|
-
}
|
|
479
|
-
var url = new URL(layout.getAttribute("data-vault-filter-url") || "", location.href);
|
|
480
|
-
url.searchParams.set("q", needle);
|
|
481
|
-
var sequence = filterSequence;
|
|
482
|
-
filterRequest = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
483
|
-
fetchMarkup(url.toString(), filterRequest ? filterRequest.signal : undefined).then(function (markup) {
|
|
484
|
-
if (sequence !== filterSequence) return;
|
|
485
|
-
moveInto(results, markup);
|
|
486
|
-
results.hidden = false;
|
|
487
|
-
panel.setAttribute("data-vault-filtering", "");
|
|
488
|
-
}).catch(function (error) {
|
|
489
|
-
if (sequence !== filterSequence || (error && error.name === "AbortError")) return;
|
|
490
|
-
results.textContent = "The filter could not run: " + failure(error);
|
|
491
|
-
results.hidden = false;
|
|
492
|
-
panel.setAttribute("data-vault-filtering", "");
|
|
493
|
-
});
|
|
494
|
-
}
|
|
495
|
-
/** Replaces the tree, listing and filter results in place; the editor and terminal are left alone. */
|
|
496
|
-
function refreshTree() {
|
|
497
|
-
var layout = document.querySelector("[data-vault-layout]");
|
|
498
|
-
var status = document.getElementById("vault-refresh-status");
|
|
499
|
-
var url = layout ? layout.getAttribute("data-vault-refresh-url") : null;
|
|
500
|
-
if (!url) return;
|
|
501
|
-
refreshSequence += 1;
|
|
502
|
-
var sequence = refreshSequence;
|
|
503
|
-
if (status) status.hidden = true;
|
|
504
|
-
fetchMarkup(url).then(function (markup) {
|
|
505
|
-
if (sequence === refreshSequence) swapVaultSlots(markup);
|
|
506
|
-
}).catch(function (error) {
|
|
507
|
-
if (sequence !== refreshSequence || !status) return;
|
|
508
|
-
status.textContent = "The tree could not be refreshed: " + failure(error) + ". Select Refresh tree to retry.";
|
|
509
|
-
status.hidden = false;
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
function watchTreeChanges(layout) {
|
|
513
|
-
var url = layout.getAttribute("data-vault-changes-url");
|
|
514
|
-
if (!url || typeof EventSource === "undefined") return;
|
|
515
|
-
var changes = new EventSource(url);
|
|
516
|
-
changes.onmessage = function (message) {
|
|
517
|
-
var event;
|
|
518
|
-
try { event = JSON.parse(message.data); } catch (error) { return; }
|
|
519
|
-
if (!event || event.type !== "vault-tree-changed" || typeof event.generation !== "number") return;
|
|
520
|
-
if (latestGeneration === null || event.generation > latestGeneration) latestGeneration = event.generation;
|
|
521
|
-
showStale();
|
|
522
|
-
};
|
|
523
|
-
}
|
|
524
|
-
function wireVaultBrowser() {
|
|
525
|
-
var layout = document.querySelector("[data-vault-layout]");
|
|
526
|
-
if (!layout) return;
|
|
527
|
-
var panel = document.getElementById("vault-tree-panel");
|
|
528
|
-
displayedGeneration = readGeneration(document.querySelector('[data-vault-slot="tree"]'));
|
|
529
|
-
if (panel) {
|
|
530
|
-
panel.addEventListener("click", function (event) {
|
|
531
|
-
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
532
|
-
var link = event.target && event.target.closest ? event.target.closest("a[data-vault-entry]") : null;
|
|
533
|
-
if (!link) return;
|
|
534
|
-
event.preventDefault();
|
|
535
|
-
navigate(link.href);
|
|
536
|
-
});
|
|
537
|
-
panel.addEventListener("toggle", function (event) {
|
|
538
|
-
var folder = event.target;
|
|
539
|
-
if (folder && folder.open && folder.matches && folder.matches("details[data-vault-children-url]")) loadChildren(folder);
|
|
540
|
-
}, true);
|
|
541
|
-
}
|
|
542
|
-
var refresh = document.querySelector("form[data-vault-refresh]");
|
|
543
|
-
if (refresh) refresh.addEventListener("submit", function (event) {
|
|
544
|
-
event.preventDefault();
|
|
545
|
-
refreshTree();
|
|
546
|
-
});
|
|
547
|
-
var filter = document.getElementById("vault-filter");
|
|
548
|
-
if (filter) {
|
|
549
|
-
filter.hidden = false;
|
|
550
|
-
filter.addEventListener("input", function () {
|
|
551
|
-
clearTimeout(filterTimer);
|
|
552
|
-
filterTimer = setTimeout(runFilter, FILTER_DELAY);
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
var toggle = document.getElementById("vault-tree-toggle");
|
|
556
|
-
if (toggle) {
|
|
557
|
-
toggle.hidden = false;
|
|
558
|
-
var hidden = document.documentElement.getAttribute("data-vault-tree") === "hidden";
|
|
559
|
-
toggle.textContent = hidden ? "Show files" : "Hide files";
|
|
560
|
-
toggle.setAttribute("aria-expanded", hidden ? "false" : "true");
|
|
561
|
-
toggle.addEventListener("click", function () {
|
|
562
|
-
showTree(document.documentElement.getAttribute("data-vault-tree") === "hidden");
|
|
563
|
-
});
|
|
564
|
-
}
|
|
565
|
-
watchTreeChanges(layout);
|
|
566
|
-
var deferred = layout.getAttribute("data-vault-view");
|
|
567
|
-
if (!deferred) return;
|
|
568
|
-
fetchMarkup(deferred).then(swapVaultSlots).catch(function (error) {
|
|
569
|
-
failVaultSlots("The files could not be listed: " + failure(error));
|
|
570
|
-
});
|
|
571
|
-
}
|
|
572
|
-
/** Hosted reports: the list, the sandboxed frame, and the toast and badge for a new arrival. */
|
|
573
|
-
function wireReports() {
|
|
574
|
-
var shell = document.querySelector("[data-reports-events-url]");
|
|
575
|
-
if (!shell || typeof EventSource === "undefined") return;
|
|
576
|
-
var current = shell.getAttribute("data-companion");
|
|
577
|
-
var listUrl = shell.getAttribute("data-reports-url");
|
|
578
|
-
var viewUrl = shell.getAttribute("data-reports-view-url");
|
|
579
|
-
var list = document.getElementById("reports-list");
|
|
580
|
-
var frame = document.getElementById("reports-frame");
|
|
581
|
-
var open = document.getElementById("reports-open");
|
|
582
|
-
var empty = document.getElementById("reports-empty");
|
|
583
|
-
var template = document.getElementById("reports-item-template");
|
|
584
|
-
var badge = document.getElementById("reports-badge");
|
|
585
|
-
var toast = document.getElementById("reports-toast");
|
|
586
|
-
var toastTitle = document.getElementById("reports-toast-title");
|
|
587
|
-
var toastOpen = document.getElementById("reports-toast-open");
|
|
588
|
-
var shown = null;
|
|
589
|
-
var toastTimeout;
|
|
590
|
-
function reportHash() {
|
|
591
|
-
var match = /(?:^|[#&])report=([0-9a-f]{32})/.exec(location.hash || "");
|
|
592
|
-
return match ? match[1] : null;
|
|
593
|
-
}
|
|
594
|
-
function show(id) {
|
|
595
|
-
if (!frame) return;
|
|
596
|
-
shown = id;
|
|
597
|
-
var src = ${JSON.stringify(REPORT_VIEW_PREFIX)} + id;
|
|
598
|
-
frame.src = src;
|
|
599
|
-
frame.hidden = false;
|
|
600
|
-
if (open) { open.href = src; open.hidden = false; }
|
|
601
|
-
if (list) list.querySelectorAll("button[data-report-id]").forEach(function (button) {
|
|
602
|
-
button.setAttribute("aria-pressed", button.getAttribute("data-report-id") === id ? "true" : "false");
|
|
603
|
-
});
|
|
604
|
-
try { history.replaceState(null, "", "#report=" + id); } catch (error) { /* the address is a convenience */ }
|
|
605
|
-
}
|
|
606
|
-
function render(reports) {
|
|
607
|
-
if (!list || !template || !template.content.firstElementChild) return;
|
|
608
|
-
list.replaceChildren();
|
|
609
|
-
if (empty) empty.hidden = reports.length > 0;
|
|
610
|
-
reports.forEach(function (report) {
|
|
611
|
-
var item = template.content.firstElementChild.cloneNode(true);
|
|
612
|
-
var button = item.querySelector("button");
|
|
613
|
-
button.setAttribute("data-report-id", report.id);
|
|
614
|
-
button.setAttribute("aria-pressed", report.id === shown ? "true" : "false");
|
|
615
|
-
item.querySelector("[data-report-title]").textContent = report.title;
|
|
616
|
-
item.querySelector("[data-report-time]").textContent = new Date(report.createdAt).toLocaleString();
|
|
617
|
-
button.addEventListener("click", function () { show(report.id); });
|
|
618
|
-
list.append(item);
|
|
619
|
-
});
|
|
620
|
-
if (reports.length === 0 || reports.some(function (report) { return report.id === shown; })) return;
|
|
621
|
-
var named = reportHash();
|
|
622
|
-
show(reports.some(function (report) { return report.id === named; }) ? named : reports[0].id);
|
|
623
|
-
}
|
|
624
|
-
function refreshList() {
|
|
625
|
-
if (!list || !listUrl) return;
|
|
626
|
-
fetch(listUrl, { headers: { accept: "application/json" } }).then(function (response) {
|
|
627
|
-
if (!response.ok) throw new Error("status " + response.status);
|
|
628
|
-
return response.json();
|
|
629
|
-
}).then(function (body) { render(body.reports || []); }).catch(function () { /* the next event or reconnect retries */ });
|
|
630
|
-
}
|
|
631
|
-
function announceReport(event) {
|
|
632
|
-
if (badge) badge.hidden = false;
|
|
633
|
-
if (!toast || !toastTitle) return;
|
|
634
|
-
toastTitle.textContent = "New report: " + event.title;
|
|
635
|
-
if (toastOpen && viewUrl) toastOpen.href = viewUrl + "#report=" + event.id;
|
|
636
|
-
toast.setAttribute("data-shown", "true");
|
|
637
|
-
clearTimeout(toastTimeout);
|
|
638
|
-
toastTimeout = setTimeout(function () { toast.setAttribute("data-shown", "false"); }, 10000);
|
|
639
|
-
}
|
|
640
|
-
var events = new EventSource(shell.getAttribute("data-reports-events-url"));
|
|
641
|
-
events.onopen = refreshList;
|
|
642
|
-
events.onmessage = function (message) {
|
|
643
|
-
var event;
|
|
644
|
-
try { event = JSON.parse(message.data); } catch (error) { return; }
|
|
645
|
-
if (!event || event.type !== "report-published" || event.companion !== current) return;
|
|
646
|
-
if (list) refreshList();
|
|
647
|
-
else announceReport(event);
|
|
648
|
-
};
|
|
649
|
-
}
|
|
650
|
-
wireNavigation();
|
|
651
|
-
wireVault();
|
|
652
|
-
wireVaultBrowser();
|
|
653
|
-
wireReports();
|
|
654
|
-
})();`;
|
|
@@ -19,16 +19,11 @@ function progressPercent(change: StudioChange): number {
|
|
|
19
19
|
|
|
20
20
|
export function Changes({ changes }: ChangesProps) {
|
|
21
21
|
return (
|
|
22
|
-
<section className="
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
</div>
|
|
28
|
-
<span className="section-count">
|
|
29
|
-
{changes.length} {changes.length === 1 ? "change" : "changes"}
|
|
30
|
-
</span>
|
|
31
|
-
</div>
|
|
22
|
+
<section className="view-section lookup-view" aria-label="Changes">
|
|
23
|
+
<p className="view-summary">
|
|
24
|
+
{changes.length} {changes.length === 1 ? "change" : "changes"} · active work in this
|
|
25
|
+
Companion Repository
|
|
26
|
+
</p>
|
|
32
27
|
{changes.length === 0 ? (
|
|
33
28
|
<p className="empty">No changes in this companion.</p>
|
|
34
29
|
) : (
|