total-recall-brain 3.41.3 → 3.41.5
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/bin/total-recall.mjs +12 -2
- package/docs/reference/cli-reference.md +18 -0
- package/frontend/dist/assets/{index-DFenQQEQ.js → index-WT3gs-x6.js} +75 -70
- package/frontend/dist/index.html +1 -1
- package/package.json +7 -5
- package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +48 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/login-projections.mjs +42 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/mesh-secrets.mjs +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/routes/secrets.mjs +5 -2
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-carriers.mjs +76 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-prune.mjs +37 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-sync.mjs +53 -17
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +3114 -36
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +3114 -36
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +3131 -28
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +6 -2
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +3132 -29
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package.json +5 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +3164 -73
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +5 -3
- package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/grants.mjs +7 -3
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/mesh.mjs +61 -38
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/leader-election.mjs +57 -2
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh.mjs +220 -36
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/server/routes/mesh.mjs +7 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/README.md +65 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/BRICK.md +59 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/index.mjs +534 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +139 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +5937 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +26 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +376 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md +37 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/adapters/dsh.mjs +446 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/alerts.mjs +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +398 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/documentation.mjs +172 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/executor.mjs +82 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/learner.mjs +90 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +415 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/runtime.mjs +36 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/setup.mjs +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/tracker.mjs +133 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +233 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/DESIGN.md +32 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +845 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/capability/BRICK.md +64 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/capability/index.mjs +250 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +619 -41
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +3134 -31
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +9 -3
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +413 -14
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/config.mjs +129 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/generate.mjs +159 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/graph.mjs +197 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/lint.mjs +153 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/mesh-vaults.mjs +261 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +502 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/search.mjs +114 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/token.mjs +96 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/upstream-watcher.mjs +375 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/DESIGN.md +35 -22
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +1225 -364
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/openwiki.mjs +8987 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +3114 -83
- package/scaffold/.agent/skills/total-recall/plugins/security/package.json +5 -1
- package/src/cli/compact.mjs +108 -0
- package/src/cli/config.mjs +3 -0
- package/src/cli/connect.mjs +12 -116
- package/src/cli/context.mjs +52 -23
- package/src/cli/forget.mjs +2 -0
- package/src/cli/harness.mjs +0 -1
- package/src/cli/plugin/create.mjs +56 -3
- package/src/cli/plugin/index.mjs +15 -3
- package/src/cli/plugin/monetization.mjs +232 -12
- package/src/cli/preflight.mjs +161 -0
- package/src/cli/release.mjs +386 -0
- package/src/core/cli-commands.mjs +4 -0
- package/src/core/context-compactor.mjs +143 -0
- package/src/core/context-compiler.mjs +8 -1
- package/src/core/default-plugins.mjs +1 -1
- package/src/core/meta-harness.mjs +16 -38
- package/src/core/plugin-dependencies.mjs +6 -1
- package/src/core/preflight-gate.mjs +77 -0
- package/src/core/preflight-receipt.mjs +123 -0
- package/src/core/runtime.mjs +0 -3
- package/src/core/surface.mjs +39 -2
- package/src/server/invoke-durability.mjs +39 -0
- package/src/server/invoke-write-child.mjs +25 -0
- package/src/server/routes/invoke.mjs +7 -0
|
@@ -1,540 +1,1401 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* OpenWiki — Knowledge Graph & Multi-Repo Documentation Wiki
|
|
3
|
+
* Bespoke Card Face: Deep Slate & Knowledge Amber Interface.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Exposes live hub discovery, full markdown reading, evidence search across all vaults,
|
|
6
|
+
* interactive knowledge topology graph, targeted on-demand generation, fleet refresh,
|
|
7
|
+
* Total Recall auth secrets vault key management, and documentation link hygiene linting.
|
|
8
|
+
*
|
|
9
|
+
* Operates without mock data, zero templates, zero emojis, zero Winamp skeuomorphism.
|
|
7
10
|
*/
|
|
8
11
|
|
|
9
|
-
const DASH = '\u2014';
|
|
10
|
-
|
|
11
|
-
function escapeHtml(str) {
|
|
12
|
-
if (str === null || str === undefined) return '';
|
|
13
|
-
return String(str).replace(/[&<>"']/g, c => ({
|
|
14
|
-
'&': '&',
|
|
15
|
-
'<': '<',
|
|
16
|
-
'>': '>',
|
|
17
|
-
'"': '"',
|
|
18
|
-
"'": '''
|
|
19
|
-
}[c]));
|
|
20
|
-
}
|
|
21
|
-
|
|
22
12
|
export class OpenWikiCard extends HTMLElement {
|
|
13
|
+
static get screens() {
|
|
14
|
+
return [
|
|
15
|
+
{ id: 'hubs', label: 'Hubs' },
|
|
16
|
+
{ id: 'search', label: 'Search' },
|
|
17
|
+
{ id: 'graph', label: 'Graph' },
|
|
18
|
+
{ id: 'generate', label: 'Generate' },
|
|
19
|
+
{ id: 'refresh', label: 'Fleet Refresh' },
|
|
20
|
+
{ id: 'token', label: 'Auth Vault' },
|
|
21
|
+
{ id: 'lint', label: 'Health' },
|
|
22
|
+
{ id: 'upstream', label: 'Upstream Watcher' },
|
|
23
|
+
{ id: 'config', label: 'Configuration' }
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get screens() {
|
|
28
|
+
return OpenWikiCard.screens;
|
|
29
|
+
}
|
|
30
|
+
|
|
23
31
|
constructor() {
|
|
24
32
|
super();
|
|
33
|
+
this.activeScreen = 'hubs';
|
|
34
|
+
this.scope = 'workspace'; // 'workspace' | 'all-vaults' | '<mesh-node>'
|
|
35
|
+
this.meshNodes = [];
|
|
25
36
|
this.hubs = [];
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
37
|
+
this.selectedHub = null;
|
|
38
|
+
this.selectedHubContent = null;
|
|
39
|
+
this.loading = false;
|
|
40
|
+
this.statusText = 'READY';
|
|
28
41
|
this.targetPath = 'openwiki';
|
|
29
|
-
this.dryRun =
|
|
30
|
-
this.
|
|
31
|
-
this.
|
|
32
|
-
this.
|
|
42
|
+
this.dryRun = false;
|
|
43
|
+
this.executionResult = null;
|
|
44
|
+
this.errorMessage = null;
|
|
45
|
+
this.lastRefreshedAt = null;
|
|
46
|
+
this.upstream = null;
|
|
47
|
+
|
|
48
|
+
// Search state
|
|
49
|
+
this.searchQuery = '';
|
|
50
|
+
this.searchResults = [];
|
|
51
|
+
this.searchStats = null;
|
|
52
|
+
|
|
53
|
+
// Graph state
|
|
54
|
+
this.graphData = null;
|
|
55
|
+
this.graphFormat = 'svg'; // 'svg' | 'mermaid' | 'json'
|
|
56
|
+
|
|
57
|
+
// Token state
|
|
58
|
+
this.tokenStatus = {
|
|
59
|
+
openrouter: { key: 'OPENROUTER_API_KEY', configured: false, status: 'unknown' },
|
|
60
|
+
google: { key: 'GOOGLE_API_KEY', configured: false, status: 'unknown' }
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Lint state
|
|
64
|
+
this.lintData = null;
|
|
65
|
+
|
|
66
|
+
// Generator state
|
|
67
|
+
this.generateRepo = '';
|
|
68
|
+
this.generateLogs = [];
|
|
69
|
+
|
|
70
|
+
// Fleet refresh state
|
|
71
|
+
this.refreshReports = [];
|
|
72
|
+
this.refreshCounts = null;
|
|
73
|
+
|
|
74
|
+
// Configuration state
|
|
75
|
+
this.config = {
|
|
76
|
+
provider: 'openrouter',
|
|
77
|
+
model: 'deepseek/deepseek-chat',
|
|
78
|
+
fallback_model: 'qwen/qwen-2.5-coder-32b-instruct',
|
|
79
|
+
max_tokens: 4096,
|
|
80
|
+
timeout_ms: 300000
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
this._boundOnScreenChange = this.handleExternalScreenChange.bind(this);
|
|
33
84
|
}
|
|
34
85
|
|
|
35
86
|
connectedCallback() {
|
|
36
87
|
if (!this.shadowRoot) {
|
|
37
88
|
this.attachShadow({ mode: 'open' });
|
|
38
89
|
}
|
|
90
|
+
this.addEventListener('tr:screen-change', this._boundOnScreenChange);
|
|
91
|
+
this.addEventListener('plugin-show-screen', this._boundOnScreenChange);
|
|
39
92
|
this.render();
|
|
40
93
|
this.refreshHubs();
|
|
94
|
+
this.loadTokenStatus();
|
|
95
|
+
this.loadConfig();
|
|
41
96
|
}
|
|
42
97
|
|
|
43
|
-
|
|
44
|
-
|
|
98
|
+
disconnectedCallback() {
|
|
99
|
+
this.removeEventListener('tr:screen-change', this._boundOnScreenChange);
|
|
100
|
+
this.removeEventListener('plugin-show-screen', this._boundOnScreenChange);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
handleExternalScreenChange(event) {
|
|
104
|
+
const screenId = event.detail?.screen || event.detail?.screenId || event.detail?.id;
|
|
105
|
+
if (screenId && OpenWikiCard.screens.some((s) => s.id === screenId)) {
|
|
106
|
+
this.setScreen(screenId);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
setScreen(id) {
|
|
111
|
+
if (this.activeScreen === id && !this.selectedHub) return;
|
|
112
|
+
this.activeScreen = id;
|
|
113
|
+
this.selectedHub = null;
|
|
114
|
+
this.selectedHubContent = null;
|
|
115
|
+
this.errorMessage = null;
|
|
116
|
+
this.render();
|
|
117
|
+
|
|
118
|
+
if (typeof this.host?.screenChanged === 'function') {
|
|
119
|
+
try {
|
|
120
|
+
this.host.screenChanged({ screen: id });
|
|
121
|
+
} catch {
|
|
122
|
+
// optional host callback
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (id === 'graph' && !this.graphData) {
|
|
127
|
+
this.loadGraph();
|
|
128
|
+
} else if (id === 'token') {
|
|
129
|
+
this.loadTokenStatus();
|
|
130
|
+
} else if (id === 'lint' && !this.lintData) {
|
|
131
|
+
this.loadLint();
|
|
132
|
+
} else if (id === 'refresh' && (!this.refreshReports || this.refreshReports.length === 0)) {
|
|
133
|
+
this.loadRefreshReport();
|
|
134
|
+
} else if (id === 'config') {
|
|
135
|
+
this.loadConfig();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
setScope(newScope) {
|
|
140
|
+
if (this.scope === newScope) return;
|
|
141
|
+
this.scope = newScope;
|
|
142
|
+
this.refreshHubs();
|
|
143
|
+
if (this.activeScreen === 'graph') this.loadGraph();
|
|
144
|
+
if (this.activeScreen === 'lint') this.loadLint();
|
|
145
|
+
if (this.activeScreen === 'search' && this.searchQuery) this.executeSearch(this.searchQuery);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async loadConfig() {
|
|
45
149
|
try {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
} else if (Array.isArray(res)) {
|
|
53
|
-
parsed = res;
|
|
54
|
-
}
|
|
55
|
-
} else if (typeof res === 'string' && res.trim().startsWith('{')) {
|
|
56
|
-
const json = JSON.parse(res);
|
|
57
|
-
parsed = Array.isArray(json.hubs) ? json.hubs : [];
|
|
58
|
-
this.totalLines = json.total_lines || 0;
|
|
150
|
+
if (this.host && typeof this.host.run === 'function') {
|
|
151
|
+
const res = await this.host.run('config', ['show', '--json']);
|
|
152
|
+
if (res?.config) {
|
|
153
|
+
this.config = res.config;
|
|
154
|
+
this.render();
|
|
155
|
+
}
|
|
59
156
|
}
|
|
157
|
+
} catch {
|
|
158
|
+
// Retain active defaults
|
|
159
|
+
}
|
|
160
|
+
}
|
|
60
161
|
|
|
61
|
-
|
|
162
|
+
async setConfigValue(key, value) {
|
|
163
|
+
this.loading = true;
|
|
164
|
+
this.errorMessage = null;
|
|
165
|
+
this.render();
|
|
166
|
+
try {
|
|
167
|
+
if (this.host && typeof this.host.run === 'function') {
|
|
168
|
+
const res = await this.host.run('config', ['set', key, String(value), '--json']);
|
|
169
|
+
if (res?.config) {
|
|
170
|
+
this.config = res.config;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} catch (err) {
|
|
174
|
+
this.errorMessage = err.message || `Failed to update ${key}`;
|
|
175
|
+
} finally {
|
|
176
|
+
this.loading = false;
|
|
62
177
|
this.render();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async loadTokenStatus() {
|
|
182
|
+
try {
|
|
183
|
+
if (this.host && typeof this.host.run === 'function') {
|
|
184
|
+
const res = await this.host.run('token', ['status', '--json']);
|
|
185
|
+
if (res?.tokens) {
|
|
186
|
+
this.tokenStatus = res.tokens;
|
|
187
|
+
this.render();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
63
190
|
} catch {
|
|
191
|
+
// optional fallback
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async saveToken(provider, tokenValue) {
|
|
196
|
+
if (!tokenValue || !tokenValue.trim()) return;
|
|
197
|
+
this.loading = true;
|
|
198
|
+
this.errorMessage = null;
|
|
199
|
+
this.render();
|
|
200
|
+
try {
|
|
201
|
+
if (this.host && typeof this.host.run === 'function') {
|
|
202
|
+
await this.host.run('token', ['set', provider, tokenValue.trim(), '--json']);
|
|
203
|
+
await this.loadTokenStatus();
|
|
204
|
+
}
|
|
205
|
+
} catch (err) {
|
|
206
|
+
this.errorMessage = err.message || `Failed to save ${provider} token`;
|
|
207
|
+
} finally {
|
|
208
|
+
this.loading = false;
|
|
209
|
+
this.render();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async refreshHubs() {
|
|
214
|
+
this.loading = true;
|
|
215
|
+
this.errorMessage = null;
|
|
216
|
+
this.render();
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
if (this.host && typeof this.host.run === 'function') {
|
|
220
|
+
const args = ['--json'];
|
|
221
|
+
if (this.scope === 'all-vaults') {
|
|
222
|
+
args.push('--all-vaults');
|
|
223
|
+
} else if (this.scope !== 'workspace') {
|
|
224
|
+
args.push(`--node=${this.scope}`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const res = await this.host.run('hubs', args);
|
|
228
|
+
if (res && Array.isArray(res.hubs)) {
|
|
229
|
+
this.hubs = res.hubs;
|
|
230
|
+
this.statusText = 'SYNCHRONIZED';
|
|
231
|
+
} else {
|
|
232
|
+
this.hubs = [];
|
|
233
|
+
this.statusText = 'UNINDEXED';
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
const statusRes = await this.host.run('status', ['--json']);
|
|
238
|
+
if (statusRes?.upstream) {
|
|
239
|
+
this.upstream = statusRes.upstream;
|
|
240
|
+
}
|
|
241
|
+
} catch {
|
|
242
|
+
// Upstream metadata optional
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
this.statusText = 'DISCONNECTED';
|
|
246
|
+
}
|
|
247
|
+
this.lastRefreshedAt = new Date().toLocaleTimeString();
|
|
248
|
+
} catch (err) {
|
|
249
|
+
this.statusText = 'ERROR';
|
|
250
|
+
this.errorMessage = err.message || 'Hub synchronization failure';
|
|
64
251
|
this.hubs = [];
|
|
252
|
+
} finally {
|
|
253
|
+
this.loading = false;
|
|
254
|
+
this.render();
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async readHubDocument(hub) {
|
|
259
|
+
this.selectedHub = hub;
|
|
260
|
+
this.loading = true;
|
|
261
|
+
this.render();
|
|
262
|
+
try {
|
|
263
|
+
if (this.host && typeof this.host.readFile === 'function') {
|
|
264
|
+
this.selectedHubContent = await this.host.readFile(hub.path);
|
|
265
|
+
} else {
|
|
266
|
+
this.selectedHubContent = `# ${hub.title}\n\n*File:* \`${hub.path}\`\n\n*Lines:* ${hub.lines} | *Size:* ${hub.size_bytes} bytes\n\n*(Full live rendering available)*`;
|
|
267
|
+
}
|
|
268
|
+
} catch {
|
|
269
|
+
this.selectedHubContent = `# ${hub.title}\n\n*Unable to load full content from ${hub.path}*`;
|
|
270
|
+
} finally {
|
|
271
|
+
this.loading = false;
|
|
65
272
|
this.render();
|
|
66
273
|
}
|
|
67
274
|
}
|
|
68
275
|
|
|
69
|
-
async
|
|
70
|
-
if (!
|
|
71
|
-
|
|
72
|
-
|
|
276
|
+
async executeSearch(query) {
|
|
277
|
+
if (!query || !query.trim()) {
|
|
278
|
+
this.searchResults = [];
|
|
279
|
+
this.searchStats = null;
|
|
280
|
+
this.render();
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
this.loading = true;
|
|
285
|
+
this.searchQuery = query.trim();
|
|
286
|
+
this.errorMessage = null;
|
|
73
287
|
this.render();
|
|
74
288
|
|
|
75
289
|
try {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
290
|
+
if (this.host && typeof this.host.run === 'function') {
|
|
291
|
+
const args = [this.searchQuery, '--json'];
|
|
292
|
+
if (this.scope === 'all-vaults') args.push('--all-vaults');
|
|
293
|
+
else if (this.scope !== 'workspace') args.push(`--node=${this.scope}`);
|
|
294
|
+
|
|
295
|
+
const res = await this.host.run('search', args);
|
|
296
|
+
if (res && Array.isArray(res.results)) {
|
|
297
|
+
this.searchResults = res.results;
|
|
298
|
+
this.searchStats = {
|
|
299
|
+
totalHubs: res.totalHubsScanned || 0,
|
|
300
|
+
count: res.count || 0,
|
|
301
|
+
scope: res.scope || this.scope
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
} catch (err) {
|
|
306
|
+
this.errorMessage = err.message || 'Search execution failure';
|
|
307
|
+
this.searchResults = [];
|
|
308
|
+
} finally {
|
|
309
|
+
this.loading = false;
|
|
310
|
+
this.render();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async loadGraph() {
|
|
315
|
+
this.loading = true;
|
|
316
|
+
this.errorMessage = null;
|
|
317
|
+
this.render();
|
|
318
|
+
try {
|
|
319
|
+
if (this.host && typeof this.host.run === 'function') {
|
|
320
|
+
const args = ['--format=json', '--json'];
|
|
321
|
+
if (this.scope === 'all-vaults') args.push('--all-vaults');
|
|
322
|
+
else if (this.scope !== 'workspace') args.push(`--node=${this.scope}`);
|
|
323
|
+
|
|
324
|
+
const res = await this.host.run('graph', args);
|
|
325
|
+
if (res?.nodes) {
|
|
326
|
+
this.graphData = res;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
87
329
|
} catch (err) {
|
|
88
|
-
this.
|
|
89
|
-
ok: false,
|
|
90
|
-
message: err instanceof Error ? err.message : 'Ingestion failed'
|
|
91
|
-
};
|
|
330
|
+
this.errorMessage = err.message || 'Graph build failure';
|
|
92
331
|
} finally {
|
|
93
|
-
this.
|
|
332
|
+
this.loading = false;
|
|
94
333
|
this.render();
|
|
95
334
|
}
|
|
96
335
|
}
|
|
97
336
|
|
|
98
|
-
async
|
|
99
|
-
|
|
100
|
-
this.
|
|
101
|
-
this.lastResult = null;
|
|
337
|
+
async loadLint() {
|
|
338
|
+
this.loading = true;
|
|
339
|
+
this.errorMessage = null;
|
|
102
340
|
this.render();
|
|
341
|
+
try {
|
|
342
|
+
if (this.host && typeof this.host.run === 'function') {
|
|
343
|
+
const args = ['--json'];
|
|
344
|
+
if (this.scope === 'all-vaults') args.push('--all-vaults');
|
|
345
|
+
else if (this.scope !== 'workspace') args.push(`--node=${this.scope}`);
|
|
346
|
+
|
|
347
|
+
const res = await this.host.run('lint', args);
|
|
348
|
+
if (res) {
|
|
349
|
+
this.lintData = res;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
} catch (err) {
|
|
353
|
+
this.errorMessage = err.message || 'Lint execution failure';
|
|
354
|
+
} finally {
|
|
355
|
+
this.loading = false;
|
|
356
|
+
this.render();
|
|
357
|
+
}
|
|
358
|
+
}
|
|
103
359
|
|
|
360
|
+
async loadRefreshReport() {
|
|
361
|
+
this.loading = true;
|
|
362
|
+
this.errorMessage = null;
|
|
363
|
+
this.render();
|
|
104
364
|
try {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
365
|
+
if (this.host && typeof this.host.run === 'function') {
|
|
366
|
+
const res = await this.host.run('refresh', ['--json']);
|
|
367
|
+
if (res) {
|
|
368
|
+
this.refreshReports = Array.isArray(res.reports) ? res.reports : [];
|
|
369
|
+
this.refreshCounts = res.counts || null;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
112
372
|
} catch (err) {
|
|
113
|
-
this.
|
|
114
|
-
ok: false,
|
|
115
|
-
message: err instanceof Error ? err.message : 'Bootstrap failed'
|
|
116
|
-
};
|
|
373
|
+
this.errorMessage = err.message || 'Fleet status check failure';
|
|
117
374
|
} finally {
|
|
118
|
-
this.
|
|
375
|
+
this.loading = false;
|
|
119
376
|
this.render();
|
|
120
377
|
}
|
|
121
378
|
}
|
|
122
379
|
|
|
380
|
+
async executeFleetRefresh() {
|
|
381
|
+
if (this.loading) return;
|
|
382
|
+
this.loading = true;
|
|
383
|
+
this.errorMessage = null;
|
|
384
|
+
this.render();
|
|
385
|
+
|
|
386
|
+
try {
|
|
387
|
+
if (this.host && typeof this.host.run === 'function') {
|
|
388
|
+
const res = await this.host.run('refresh', ['--apply', '--json']);
|
|
389
|
+
if (res?.reports) {
|
|
390
|
+
this.refreshReports = res.reports;
|
|
391
|
+
this.refreshCounts = res.counts;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
} catch (err) {
|
|
395
|
+
this.errorMessage = err.message || 'Fleet refresh execution failure';
|
|
396
|
+
} finally {
|
|
397
|
+
this.loading = false;
|
|
398
|
+
this.render();
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
async executeGenerate() {
|
|
403
|
+
if (this.loading) return;
|
|
404
|
+
this.loading = true;
|
|
405
|
+
this.errorMessage = null;
|
|
406
|
+
this.generateLogs = [`Dispatching on-demand generator for '${this.generateRepo || 'current repository'}'...`];
|
|
407
|
+
this.render();
|
|
408
|
+
|
|
409
|
+
try {
|
|
410
|
+
if (this.host && typeof this.host.run === 'function') {
|
|
411
|
+
const args = ['--json'];
|
|
412
|
+
if (this.generateRepo) args.push(`--repo=${this.generateRepo}`);
|
|
413
|
+
if (this.scope !== 'workspace' && this.scope !== 'all-vaults') {
|
|
414
|
+
args.push(`--node=${this.scope}`);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const res = await this.host.run('generate', args);
|
|
418
|
+
this.generateLogs.push(res?.ok ? `Generation completed successfully in ${Math.round((res.durationMs || 0)/1000)}s` : 'Generation finished with warnings');
|
|
419
|
+
if (res?.outputDir) this.generateLogs.push(`Output: ${res.outputDir}`);
|
|
420
|
+
}
|
|
421
|
+
} catch (err) {
|
|
422
|
+
this.errorMessage = err.message || 'Generation execution failure';
|
|
423
|
+
this.generateLogs.push(`Error: ${this.errorMessage}`);
|
|
424
|
+
} finally {
|
|
425
|
+
this.loading = false;
|
|
426
|
+
this.refreshHubs();
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
async executeIngest() {
|
|
431
|
+
if (this.loading) return;
|
|
432
|
+
this.loading = true;
|
|
433
|
+
this.errorMessage = null;
|
|
434
|
+
this.render();
|
|
435
|
+
|
|
436
|
+
const args = [this.targetPath || 'openwiki'];
|
|
437
|
+
if (this.dryRun) args.push('--dry-run');
|
|
438
|
+
|
|
439
|
+
try {
|
|
440
|
+
if (this.host && typeof this.host.run === 'function') {
|
|
441
|
+
await this.host.run('ingest', args);
|
|
442
|
+
}
|
|
443
|
+
} catch (err) {
|
|
444
|
+
this.errorMessage = err.message || 'Ingestion cycle failure';
|
|
445
|
+
} finally {
|
|
446
|
+
this.loading = false;
|
|
447
|
+
this.refreshHubs();
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
async executeWatch() {
|
|
452
|
+
if (this.loading) return;
|
|
453
|
+
this.loading = true;
|
|
454
|
+
this.errorMessage = null;
|
|
455
|
+
this.render();
|
|
456
|
+
|
|
457
|
+
try {
|
|
458
|
+
if (this.host && typeof this.host.run === 'function') {
|
|
459
|
+
const res = await this.host.run('watch', ['--json']);
|
|
460
|
+
if (res?.status) {
|
|
461
|
+
this.upstream = {
|
|
462
|
+
recordedVersion: res.status.currentVersion,
|
|
463
|
+
latestVersion: res.status.latestVersion
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
} catch (err) {
|
|
468
|
+
this.errorMessage = err.message || 'Upstream surveillance failure';
|
|
469
|
+
} finally {
|
|
470
|
+
this.loading = false;
|
|
471
|
+
this.refreshHubs();
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
123
475
|
render() {
|
|
124
|
-
|
|
125
|
-
|
|
476
|
+
if (!this.shadowRoot) return;
|
|
477
|
+
|
|
478
|
+
const countText = this.hubs.length ? `${this.hubs.length} Hubs` : '0 Hubs';
|
|
479
|
+
const totalLines = this.hubs.reduce((acc, h) => acc + (h.lines || 0), 0);
|
|
480
|
+
const linesText = `${totalLines.toLocaleString()} Lines`;
|
|
126
481
|
|
|
127
|
-
|
|
482
|
+
this.shadowRoot.innerHTML = `
|
|
128
483
|
<style>
|
|
129
484
|
:host {
|
|
130
|
-
--face-bg: #0f172a;
|
|
131
|
-
--face-surface: #1e293b;
|
|
132
|
-
--face-line: #334155;
|
|
133
|
-
--face-text: #f8fafc;
|
|
134
|
-
--face-muted: #94a3b8;
|
|
135
|
-
--face-brand: #3b82f6;
|
|
136
|
-
--face-brand-hover: #60a5fa;
|
|
137
|
-
--face-accent: #facc15;
|
|
138
|
-
--face-ok: #10b981;
|
|
139
|
-
--face-warn: #f59e0b;
|
|
140
|
-
--face-danger: #ef4444;
|
|
141
|
-
--face-dark: #0f172a;
|
|
142
|
-
|
|
143
485
|
display: block;
|
|
144
|
-
font-family: var(--font-sans, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
|
|
145
|
-
color: var(--text-primary, var(--face-text));
|
|
146
|
-
background: var(--bg-secondary, var(--face-bg));
|
|
147
|
-
border: 1px solid var(--border, var(--face-line));
|
|
148
|
-
border-radius: 8px;
|
|
149
|
-
padding: 16px;
|
|
150
486
|
box-sizing: border-box;
|
|
151
|
-
|
|
487
|
+
--bg-base: #090d16;
|
|
488
|
+
--bg-surface: #111827;
|
|
489
|
+
--bg-elevated: #1f2937;
|
|
490
|
+
--border-subtle: #374151;
|
|
491
|
+
--border-focus: #f59e0b;
|
|
492
|
+
--text-main: #f9fafb;
|
|
493
|
+
--text-muted: #9ca3af;
|
|
494
|
+
--accent-amber: #f59e0b;
|
|
495
|
+
--accent-green: #10b981;
|
|
496
|
+
--accent-red: #ef4444;
|
|
497
|
+
background: var(--bg-base);
|
|
498
|
+
color: var(--text-main);
|
|
499
|
+
border: 1px solid var(--border-subtle);
|
|
500
|
+
border-radius: 10px;
|
|
501
|
+
padding: 16px;
|
|
502
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
503
|
+
font-size: 13px;
|
|
152
504
|
}
|
|
153
505
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
animation-duration: 0.01ms !important;
|
|
157
|
-
animation-iteration-count: 1 !important;
|
|
158
|
-
transition-duration: 0.01ms !important;
|
|
159
|
-
}
|
|
506
|
+
*, *::before, *::after {
|
|
507
|
+
box-sizing: inherit;
|
|
160
508
|
}
|
|
161
509
|
|
|
162
|
-
|
|
163
|
-
.header {
|
|
510
|
+
.header-bar {
|
|
164
511
|
display: flex;
|
|
165
512
|
align-items: center;
|
|
166
513
|
justify-content: space-between;
|
|
167
|
-
|
|
514
|
+
padding-bottom: 12px;
|
|
515
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
168
516
|
gap: 12px;
|
|
169
|
-
padding-bottom: 14px;
|
|
170
|
-
border-bottom: 1px solid var(--border, var(--face-line));
|
|
171
|
-
margin-bottom: 16px;
|
|
172
517
|
}
|
|
173
518
|
|
|
174
|
-
.brand-
|
|
519
|
+
.brand-cluster {
|
|
175
520
|
display: flex;
|
|
176
521
|
align-items: center;
|
|
177
522
|
gap: 10px;
|
|
178
523
|
}
|
|
179
524
|
|
|
180
|
-
.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
letter-spacing: 0.05em;
|
|
185
|
-
color: var(--card-accent, var(--face-accent));
|
|
186
|
-
background: rgba(250, 204, 21, 0.12);
|
|
187
|
-
padding: 6px 10px;
|
|
188
|
-
border-radius: 6px;
|
|
189
|
-
border: 1px solid rgba(250, 204, 21, 0.3);
|
|
525
|
+
.crest-svg {
|
|
526
|
+
width: 24px;
|
|
527
|
+
height: 24px;
|
|
528
|
+
fill: var(--accent-amber);
|
|
190
529
|
}
|
|
191
530
|
|
|
192
|
-
.title
|
|
193
|
-
display: flex;
|
|
194
|
-
flex-direction: column;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
.plugin-title {
|
|
198
|
-
font-size: 14px;
|
|
531
|
+
.brand-title {
|
|
199
532
|
font-weight: 700;
|
|
200
|
-
|
|
201
|
-
|
|
533
|
+
font-size: 15px;
|
|
534
|
+
letter-spacing: 0.05em;
|
|
535
|
+
color: var(--text-main);
|
|
536
|
+
text-transform: uppercase;
|
|
202
537
|
}
|
|
203
538
|
|
|
204
|
-
.
|
|
205
|
-
font-
|
|
206
|
-
|
|
207
|
-
|
|
539
|
+
.wordmark-badge {
|
|
540
|
+
font-family: ui-monospace, monospace;
|
|
541
|
+
font-size: 10px;
|
|
542
|
+
background: rgba(245, 158, 11, 0.15);
|
|
543
|
+
color: var(--accent-amber);
|
|
544
|
+
border: 1px solid rgba(245, 158, 11, 0.3);
|
|
545
|
+
padding: 2px 6px;
|
|
546
|
+
border-radius: 4px;
|
|
547
|
+
font-weight: 600;
|
|
208
548
|
}
|
|
209
549
|
|
|
210
|
-
.
|
|
550
|
+
.scope-control {
|
|
211
551
|
display: flex;
|
|
212
552
|
align-items: center;
|
|
213
553
|
gap: 6px;
|
|
214
|
-
font-family: var(--font-mono, 'JetBrains Mono', monospace);
|
|
215
|
-
font-size: 11px;
|
|
216
|
-
font-weight: 700;
|
|
217
|
-
color: var(--face-accent);
|
|
218
|
-
background: rgba(250, 204, 21, 0.12);
|
|
219
|
-
border: 1px solid rgba(250, 204, 21, 0.3);
|
|
220
|
-
padding: 5px 12px;
|
|
221
|
-
border-radius: 6px;
|
|
222
|
-
text-transform: uppercase;
|
|
223
554
|
}
|
|
224
555
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
556
|
+
.scope-select {
|
|
557
|
+
background: var(--bg-surface);
|
|
558
|
+
color: var(--text-main);
|
|
559
|
+
border: 1px solid var(--border-subtle);
|
|
560
|
+
border-radius: 6px;
|
|
561
|
+
padding: 4px 8px;
|
|
562
|
+
font-size: 12px;
|
|
563
|
+
cursor: pointer;
|
|
231
564
|
}
|
|
232
565
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
566
|
+
/* Screen Navigation */
|
|
567
|
+
.screen-nav {
|
|
568
|
+
display: flex;
|
|
569
|
+
gap: 4px;
|
|
570
|
+
margin: 12px 0;
|
|
571
|
+
overflow-x: auto;
|
|
572
|
+
padding-bottom: 4px;
|
|
237
573
|
}
|
|
238
574
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
575
|
+
.nav-btn {
|
|
576
|
+
background: transparent;
|
|
577
|
+
color: var(--text-muted);
|
|
578
|
+
border: 1px solid transparent;
|
|
242
579
|
border-radius: 6px;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
580
|
+
padding: 6px 12px;
|
|
581
|
+
font-size: 12px;
|
|
582
|
+
font-weight: 600;
|
|
583
|
+
cursor: pointer;
|
|
584
|
+
white-space: nowrap;
|
|
585
|
+
min-height: 44px;
|
|
586
|
+
transition: all 0.15s ease;
|
|
587
|
+
}
|
|
588
|
+
.nav-btn:hover {
|
|
589
|
+
color: var(--text-main);
|
|
590
|
+
background: var(--bg-surface);
|
|
591
|
+
}
|
|
592
|
+
.nav-btn.active {
|
|
593
|
+
color: var(--accent-amber);
|
|
594
|
+
background: rgba(245, 158, 11, 0.12);
|
|
595
|
+
border-color: rgba(245, 158, 11, 0.4);
|
|
248
596
|
}
|
|
249
597
|
|
|
250
|
-
|
|
598
|
+
/* Metrics Deck */
|
|
599
|
+
.metric-deck {
|
|
600
|
+
display: grid;
|
|
601
|
+
grid-template-columns: repeat(3, 1fr);
|
|
602
|
+
gap: 8px;
|
|
603
|
+
margin-bottom: 14px;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.metric-cell {
|
|
607
|
+
background: var(--bg-surface);
|
|
608
|
+
border: 1px solid var(--border-subtle);
|
|
609
|
+
border-radius: 6px;
|
|
610
|
+
padding: 8px 10px;
|
|
611
|
+
}
|
|
612
|
+
.metric-label {
|
|
251
613
|
font-size: 11px;
|
|
252
|
-
|
|
614
|
+
color: var(--text-muted);
|
|
253
615
|
text-transform: uppercase;
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
616
|
+
}
|
|
617
|
+
.metric-val {
|
|
618
|
+
font-size: 15px;
|
|
619
|
+
font-weight: 700;
|
|
620
|
+
margin-top: 2px;
|
|
621
|
+
color: var(--text-main);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/* Content Deck */
|
|
625
|
+
.content-view {
|
|
626
|
+
background: var(--bg-surface);
|
|
627
|
+
border: 1px solid var(--border-subtle);
|
|
628
|
+
border-radius: 8px;
|
|
629
|
+
padding: 14px;
|
|
630
|
+
min-height: 260px;
|
|
257
631
|
}
|
|
258
632
|
|
|
259
633
|
.hub-list {
|
|
260
634
|
display: flex;
|
|
261
635
|
flex-direction: column;
|
|
262
636
|
gap: 6px;
|
|
263
|
-
max-height: 280px;
|
|
264
|
-
overflow-y: auto;
|
|
265
637
|
}
|
|
266
638
|
|
|
267
639
|
.hub-item {
|
|
268
|
-
padding: 10px 12px;
|
|
269
|
-
background: rgba(15, 23, 42, 0.6);
|
|
270
|
-
border: 1px solid var(--border, var(--face-line));
|
|
271
|
-
border-radius: 5px;
|
|
272
640
|
display: flex;
|
|
273
641
|
align-items: center;
|
|
274
642
|
justify-content: space-between;
|
|
275
|
-
|
|
643
|
+
padding: 8px 10px;
|
|
644
|
+
background: var(--bg-elevated);
|
|
645
|
+
border: 1px solid var(--border-subtle);
|
|
646
|
+
border-radius: 6px;
|
|
647
|
+
cursor: pointer;
|
|
648
|
+
min-height: 44px;
|
|
649
|
+
transition: border-color 0.15s;
|
|
276
650
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
display: flex;
|
|
280
|
-
flex-direction: column;
|
|
281
|
-
gap: 2px;
|
|
651
|
+
.hub-item:hover {
|
|
652
|
+
border-color: var(--accent-amber);
|
|
282
653
|
}
|
|
283
|
-
|
|
284
654
|
.hub-title {
|
|
285
|
-
font-
|
|
286
|
-
|
|
287
|
-
font-weight: 700;
|
|
288
|
-
color: var(--text-primary, var(--face-text));
|
|
655
|
+
font-weight: 600;
|
|
656
|
+
color: var(--text-main);
|
|
289
657
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
658
|
+
.repo-pill {
|
|
659
|
+
background: rgba(255, 255, 255, 0.08);
|
|
660
|
+
padding: 2px 6px;
|
|
661
|
+
border-radius: 4px;
|
|
662
|
+
font-family: ui-monospace, monospace;
|
|
663
|
+
font-size: 11px;
|
|
294
664
|
}
|
|
295
665
|
|
|
296
|
-
|
|
666
|
+
/* Reader */
|
|
667
|
+
.reader-header {
|
|
297
668
|
display: flex;
|
|
298
669
|
align-items: center;
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
670
|
+
justify-content: space-between;
|
|
671
|
+
margin-bottom: 12px;
|
|
672
|
+
padding-bottom: 8px;
|
|
673
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
674
|
+
}
|
|
675
|
+
.back-btn {
|
|
676
|
+
background: var(--bg-elevated);
|
|
677
|
+
color: var(--accent-amber);
|
|
678
|
+
border: 1px solid var(--border-subtle);
|
|
679
|
+
border-radius: 6px;
|
|
680
|
+
padding: 4px 10px;
|
|
681
|
+
font-weight: 600;
|
|
682
|
+
cursor: pointer;
|
|
683
|
+
min-height: 44px;
|
|
684
|
+
}
|
|
685
|
+
.markdown-body {
|
|
686
|
+
font-family: Georgia, Cambria, serif;
|
|
687
|
+
line-height: 1.6;
|
|
688
|
+
color: #e5e7eb;
|
|
689
|
+
max-height: 380px;
|
|
690
|
+
overflow-y: auto;
|
|
691
|
+
white-space: pre-wrap;
|
|
303
692
|
}
|
|
304
693
|
|
|
305
|
-
/*
|
|
306
|
-
.
|
|
694
|
+
/* Search */
|
|
695
|
+
.search-box {
|
|
307
696
|
display: flex;
|
|
308
|
-
|
|
309
|
-
|
|
697
|
+
gap: 8px;
|
|
698
|
+
margin-bottom: 12px;
|
|
310
699
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
700
|
+
.search-input {
|
|
701
|
+
flex: 1;
|
|
702
|
+
background: var(--bg-elevated);
|
|
703
|
+
border: 1px solid var(--border-subtle);
|
|
314
704
|
border-radius: 6px;
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
gap: 10px;
|
|
705
|
+
padding: 8px 12px;
|
|
706
|
+
color: var(--text-main);
|
|
707
|
+
font-size: 13px;
|
|
708
|
+
min-height: 44px;
|
|
320
709
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
710
|
+
.action-btn {
|
|
711
|
+
background: var(--accent-amber);
|
|
712
|
+
color: #000;
|
|
713
|
+
font-weight: 700;
|
|
714
|
+
border: none;
|
|
715
|
+
border-radius: 6px;
|
|
716
|
+
padding: 8px 14px;
|
|
717
|
+
cursor: pointer;
|
|
718
|
+
min-height: 44px;
|
|
326
719
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
font-size: 10px;
|
|
330
|
-
text-transform: uppercase;
|
|
331
|
-
color: var(--text-secondary, var(--face-muted));
|
|
332
|
-
letter-spacing: 0.05em;
|
|
720
|
+
.action-btn:hover {
|
|
721
|
+
background: #d97706;
|
|
333
722
|
}
|
|
334
723
|
|
|
335
|
-
.
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
724
|
+
.result-item {
|
|
725
|
+
background: var(--bg-elevated);
|
|
726
|
+
border: 1px solid var(--border-subtle);
|
|
727
|
+
border-radius: 6px;
|
|
728
|
+
padding: 10px;
|
|
729
|
+
margin-bottom: 8px;
|
|
730
|
+
}
|
|
731
|
+
.result-heading {
|
|
732
|
+
font-weight: 600;
|
|
733
|
+
color: var(--accent-amber);
|
|
734
|
+
margin-bottom: 4px;
|
|
735
|
+
}
|
|
736
|
+
.result-snippet {
|
|
737
|
+
font-style: italic;
|
|
738
|
+
color: #d1d5db;
|
|
339
739
|
font-size: 12px;
|
|
340
|
-
|
|
341
|
-
background: rgba(15, 23, 42, 0.8);
|
|
342
|
-
border: 1px solid var(--border, var(--face-line));
|
|
343
|
-
border-radius: 5px;
|
|
344
|
-
box-sizing: border-box;
|
|
345
|
-
width: 100%;
|
|
740
|
+
line-height: 1.4;
|
|
346
741
|
}
|
|
347
742
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
743
|
+
/* Graph SVG */
|
|
744
|
+
.graph-canvas-wrap {
|
|
745
|
+
width: 100%;
|
|
746
|
+
min-height: 280px;
|
|
747
|
+
background: #0b0f19;
|
|
748
|
+
border-radius: 6px;
|
|
749
|
+
border: 1px solid var(--border-subtle);
|
|
750
|
+
display: flex;
|
|
751
|
+
align-items: center;
|
|
752
|
+
justify-content: center;
|
|
753
|
+
overflow: auto;
|
|
754
|
+
padding: 10px;
|
|
351
755
|
}
|
|
352
756
|
|
|
353
|
-
|
|
757
|
+
/* Token Screen */
|
|
758
|
+
.token-row {
|
|
354
759
|
display: flex;
|
|
355
760
|
align-items: center;
|
|
356
|
-
|
|
761
|
+
justify-content: space-between;
|
|
762
|
+
background: var(--bg-elevated);
|
|
763
|
+
border: 1px solid var(--border-subtle);
|
|
764
|
+
border-radius: 6px;
|
|
765
|
+
padding: 10px 12px;
|
|
766
|
+
margin-bottom: 8px;
|
|
767
|
+
min-height: 44px;
|
|
768
|
+
}
|
|
769
|
+
.token-key {
|
|
770
|
+
font-family: ui-monospace, monospace;
|
|
771
|
+
font-weight: 600;
|
|
772
|
+
}
|
|
773
|
+
.token-badge {
|
|
357
774
|
font-size: 11px;
|
|
358
|
-
|
|
359
|
-
|
|
775
|
+
padding: 2px 8px;
|
|
776
|
+
border-radius: 4px;
|
|
777
|
+
font-weight: 600;
|
|
360
778
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
779
|
+
.token-badge.ok {
|
|
780
|
+
background: rgba(16, 185, 129, 0.2);
|
|
781
|
+
color: var(--accent-green);
|
|
782
|
+
border: 1px solid var(--accent-green);
|
|
783
|
+
}
|
|
784
|
+
.token-badge.missing {
|
|
785
|
+
background: rgba(239, 68, 68, 0.2);
|
|
786
|
+
color: var(--accent-red);
|
|
787
|
+
border: 1px solid var(--accent-red);
|
|
366
788
|
}
|
|
367
789
|
|
|
368
|
-
.
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
790
|
+
.auth-notice-card {
|
|
791
|
+
background: rgba(245, 158, 11, 0.08);
|
|
792
|
+
border: 1px solid rgba(245, 158, 11, 0.3);
|
|
793
|
+
border-radius: 6px;
|
|
794
|
+
padding: 10px 12px;
|
|
795
|
+
margin-bottom: 12px;
|
|
372
796
|
font-size: 12px;
|
|
373
|
-
|
|
374
|
-
color: var(--face-dark);
|
|
375
|
-
background: var(--face-accent);
|
|
376
|
-
border: none;
|
|
377
|
-
border-radius: 5px;
|
|
378
|
-
cursor: pointer;
|
|
379
|
-
transition: background 0.15s ease;
|
|
797
|
+
color: #fde68a;
|
|
380
798
|
}
|
|
381
799
|
|
|
382
|
-
.
|
|
383
|
-
|
|
800
|
+
.config-deck {
|
|
801
|
+
display: flex;
|
|
802
|
+
flex-direction: column;
|
|
803
|
+
gap: 12px;
|
|
384
804
|
}
|
|
385
805
|
|
|
386
|
-
.
|
|
387
|
-
|
|
388
|
-
|
|
806
|
+
.ingest-deck {
|
|
807
|
+
background: var(--bg-elevated);
|
|
808
|
+
border: 1px solid var(--border-subtle);
|
|
809
|
+
border-radius: 6px;
|
|
810
|
+
padding: 12px;
|
|
811
|
+
margin-top: 14px;
|
|
389
812
|
}
|
|
390
813
|
|
|
391
|
-
.
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
background: transparent;
|
|
399
|
-
border: 1px solid var(--border, var(--face-line));
|
|
400
|
-
border-radius: 5px;
|
|
814
|
+
.preset-chip {
|
|
815
|
+
display: inline-block;
|
|
816
|
+
background: var(--bg-elevated);
|
|
817
|
+
border: 1px solid var(--border-subtle);
|
|
818
|
+
padding: 4px 10px;
|
|
819
|
+
border-radius: 4px;
|
|
820
|
+
font-size: 11px;
|
|
401
821
|
cursor: pointer;
|
|
402
|
-
|
|
822
|
+
margin-right: 6px;
|
|
823
|
+
margin-bottom: 6px;
|
|
403
824
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
background: rgba(255, 255, 255, 0.05);
|
|
825
|
+
.preset-chip:hover {
|
|
826
|
+
border-color: var(--accent-amber);
|
|
407
827
|
}
|
|
408
828
|
|
|
409
|
-
.
|
|
829
|
+
.error-banner {
|
|
830
|
+
background: rgba(239, 68, 68, 0.15);
|
|
831
|
+
border: 1px solid var(--accent-red);
|
|
832
|
+
color: #fca5a5;
|
|
410
833
|
padding: 8px 12px;
|
|
411
|
-
border-radius:
|
|
412
|
-
|
|
413
|
-
font-size: 11px;
|
|
834
|
+
border-radius: 6px;
|
|
835
|
+
margin-bottom: 10px;
|
|
414
836
|
}
|
|
415
837
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
838
|
+
@media (prefers-reduced-motion: reduce) {
|
|
839
|
+
* {
|
|
840
|
+
animation: none !important;
|
|
841
|
+
transition: none !important;
|
|
842
|
+
}
|
|
420
843
|
}
|
|
844
|
+
</style>
|
|
421
845
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
846
|
+
<div class="header-bar">
|
|
847
|
+
<div class="brand-cluster">
|
|
848
|
+
<svg class="crest-svg" viewBox="0 0 24 24">
|
|
849
|
+
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
|
|
850
|
+
</svg>
|
|
851
|
+
<span class="brand-title">OpenWiki</span>
|
|
852
|
+
<span class="wordmark-badge">MESH KNOWLEDGE</span>
|
|
853
|
+
</div>
|
|
854
|
+
|
|
855
|
+
<div class="scope-control">
|
|
856
|
+
<span style="font-size: 11px; color: var(--text-muted);">Scope:</span>
|
|
857
|
+
<select class="scope-select" id="scope-select">
|
|
858
|
+
<option value="workspace" ${this.scope === 'workspace' ? 'selected' : ''}>Current Workspace</option>
|
|
859
|
+
<option value="all-vaults" ${this.scope === 'all-vaults' ? 'selected' : ''}>All Vaults (Fleet)</option>
|
|
860
|
+
</select>
|
|
861
|
+
</div>
|
|
862
|
+
</div>
|
|
863
|
+
|
|
864
|
+
<nav class="screen-nav">
|
|
865
|
+
${OpenWikiCard.screens
|
|
866
|
+
.map(
|
|
867
|
+
(s) => `
|
|
868
|
+
<button class="nav-btn ${this.activeScreen === s.id ? 'active' : ''}" data-screen="${s.id}">
|
|
869
|
+
${s.label}
|
|
870
|
+
</button>
|
|
871
|
+
`
|
|
872
|
+
)
|
|
873
|
+
.join('')}
|
|
874
|
+
</nav>
|
|
875
|
+
|
|
876
|
+
${this.errorMessage ? `<div class="error-banner">${this.escape(this.errorMessage)}</div>` : ''}
|
|
877
|
+
|
|
878
|
+
<div class="metric-deck">
|
|
879
|
+
<div class="metric-cell">
|
|
880
|
+
<div class="metric-label">Knowledge Hubs</div>
|
|
881
|
+
<div class="metric-val">${countText}</div>
|
|
882
|
+
</div>
|
|
883
|
+
<div class="metric-cell">
|
|
884
|
+
<div class="metric-label">Indexed Volume</div>
|
|
885
|
+
<div class="metric-val">${linesText}</div>
|
|
886
|
+
</div>
|
|
887
|
+
<div class="metric-cell">
|
|
888
|
+
<div class="metric-label">Mesh Status</div>
|
|
889
|
+
<div class="metric-val" style="color: var(--accent-amber);">${this.statusText}</div>
|
|
890
|
+
</div>
|
|
891
|
+
</div>
|
|
892
|
+
|
|
893
|
+
<div class="content-view">
|
|
894
|
+
${this.renderActiveScreen()}
|
|
895
|
+
</div>
|
|
896
|
+
`;
|
|
897
|
+
|
|
898
|
+
this.attachEventListeners();
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
renderActiveScreen() {
|
|
902
|
+
if (this.selectedHub) {
|
|
903
|
+
return this.renderReaderScreen();
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
switch (this.activeScreen) {
|
|
907
|
+
case 'hubs':
|
|
908
|
+
return this.renderHubsScreen();
|
|
909
|
+
case 'search':
|
|
910
|
+
return this.renderSearchScreen();
|
|
911
|
+
case 'graph':
|
|
912
|
+
return this.renderGraphScreen();
|
|
913
|
+
case 'generate':
|
|
914
|
+
return this.renderGenerateScreen();
|
|
915
|
+
case 'refresh':
|
|
916
|
+
return this.renderRefreshScreen();
|
|
917
|
+
case 'token':
|
|
918
|
+
return this.renderTokenScreen();
|
|
919
|
+
case 'lint':
|
|
920
|
+
return this.renderLintScreen();
|
|
921
|
+
case 'upstream':
|
|
922
|
+
return this.renderUpstreamScreen();
|
|
923
|
+
case 'config':
|
|
924
|
+
return this.renderConfigScreen();
|
|
925
|
+
default:
|
|
926
|
+
return this.renderHubsScreen();
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
renderHubsScreen() {
|
|
931
|
+
if (!this.hubs || this.hubs.length === 0) {
|
|
932
|
+
return `
|
|
933
|
+
<div style="text-align: center; padding: 30px; color: var(--text-muted);">
|
|
934
|
+
<div>No documentation hubs discovered for scope: <strong>${this.escape(this.scope)}</strong>.</div>
|
|
935
|
+
<button class="action-btn" id="btn-refresh-hubs" style="margin-top: 12px;">Sync Hubs</button>
|
|
936
|
+
</div>
|
|
937
|
+
`;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
return `
|
|
941
|
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
|
942
|
+
<span style="font-weight: 600; color: var(--text-muted);">Click a hub to read documentation:</span>
|
|
943
|
+
<button class="back-btn" id="btn-refresh-hubs">Refresh</button>
|
|
944
|
+
</div>
|
|
945
|
+
<div class="hub-list">
|
|
946
|
+
${this.hubs
|
|
947
|
+
.map(
|
|
948
|
+
(h, idx) => `
|
|
949
|
+
<div class="hub-item" data-hub-index="${idx}">
|
|
950
|
+
<div>
|
|
951
|
+
<span class="repo-pill">${this.escape(h.repoName || 'workspace')}</span>
|
|
952
|
+
<span class="hub-title" style="margin-left: 6px;">${this.escape(h.title)}</span>
|
|
953
|
+
</div>
|
|
954
|
+
<div class="hub-meta">
|
|
955
|
+
<span>${h.lines || 0} lines</span>
|
|
956
|
+
<span>${h.size_bytes ? Math.round(h.size_bytes / 1024) + ' KB' : ''}</span>
|
|
957
|
+
</div>
|
|
958
|
+
</div>
|
|
959
|
+
`
|
|
960
|
+
)
|
|
961
|
+
.join('')}
|
|
962
|
+
</div>
|
|
963
|
+
<div class="ingest-deck">
|
|
964
|
+
<div style="font-weight: 600; margin-bottom: 4px;">SSSS Vault Ingestion</div>
|
|
965
|
+
<div style="font-size: 11px; color: var(--text-muted); margin-bottom: 8px;">Embed current hub markdown into semantic memory categories.</div>
|
|
966
|
+
<button class="action-btn" id="btn-ingest-exec">Ingest into Vault</button>
|
|
967
|
+
</div>
|
|
968
|
+
`;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
renderReaderScreen() {
|
|
972
|
+
const hub = this.selectedHub;
|
|
973
|
+
return `
|
|
974
|
+
<div class="reader-header">
|
|
975
|
+
<div>
|
|
976
|
+
<span class="repo-pill">${this.escape(hub.repoName || 'workspace')}</span>
|
|
977
|
+
<strong style="margin-left: 8px; color: var(--accent-amber);">${this.escape(hub.title)}</strong>
|
|
978
|
+
</div>
|
|
979
|
+
<button class="back-btn" id="btn-back-to-hubs">Back to Hubs</button>
|
|
980
|
+
</div>
|
|
981
|
+
<div class="markdown-body">${this.escape(this.selectedHubContent || 'Loading...')}</div>
|
|
982
|
+
`;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
renderSearchScreen() {
|
|
986
|
+
return `
|
|
987
|
+
<div class="search-box">
|
|
988
|
+
<input class="search-input" id="search-input" placeholder="Search architecture, concepts, APIs across vaults..." value="${this.escape(this.searchQuery)}" />
|
|
989
|
+
<button class="action-btn" id="btn-search-exec">Search</button>
|
|
990
|
+
</div>
|
|
991
|
+
${
|
|
992
|
+
this.searchStats
|
|
993
|
+
? `<div style="font-size: 11px; color: var(--text-muted); margin-bottom: 8px;">
|
|
994
|
+
Found ${this.searchStats.count} passage(s) across ${this.searchStats.totalHubs} hubs (${this.searchStats.scope})
|
|
995
|
+
</div>`
|
|
996
|
+
: ''
|
|
997
|
+
}
|
|
998
|
+
<div class="search-results">
|
|
999
|
+
${
|
|
1000
|
+
this.searchResults.length === 0
|
|
1001
|
+
? `<div style="text-align: center; color: var(--text-muted); padding: 20px;">Enter search query to extract evidence passages.</div>`
|
|
1002
|
+
: this.searchResults
|
|
1003
|
+
.map(
|
|
1004
|
+
(r) => `
|
|
1005
|
+
<div class="result-item">
|
|
1006
|
+
<div class="result-heading">
|
|
1007
|
+
<span class="repo-pill">${this.escape(r.repoName)}</span>
|
|
1008
|
+
${this.escape(r.title)} > ${this.escape(r.section)} (Line ${r.line})
|
|
1009
|
+
</div>
|
|
1010
|
+
<div class="result-snippet">"${this.escape(r.snippet)}"</div>
|
|
1011
|
+
</div>
|
|
1012
|
+
`
|
|
1013
|
+
)
|
|
1014
|
+
.join('')
|
|
426
1015
|
}
|
|
1016
|
+
</div>
|
|
1017
|
+
`;
|
|
1018
|
+
}
|
|
427
1019
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
1020
|
+
renderGraphScreen() {
|
|
1021
|
+
if (!this.graphData || !this.graphData.nodes) {
|
|
1022
|
+
return `
|
|
1023
|
+
<div style="text-align: center; padding: 30px; color: var(--text-muted);">
|
|
1024
|
+
<div>Knowledge topology graph not loaded.</div>
|
|
1025
|
+
<button class="action-btn" id="btn-load-graph" style="margin-top: 10px;">Extract Topology</button>
|
|
1026
|
+
</div>
|
|
1027
|
+
`;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
const { nodes, edges, orphans } = this.graphData;
|
|
1031
|
+
|
|
1032
|
+
return `
|
|
1033
|
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
|
|
1034
|
+
<span style="font-weight: 600; color: var(--text-muted);">
|
|
1035
|
+
Topology: ${nodes.length} Nodes, ${edges.length} Edges, ${orphans.length} Orphans
|
|
1036
|
+
</span>
|
|
1037
|
+
<button class="back-btn" id="btn-load-graph">Refresh Graph</button>
|
|
1038
|
+
</div>
|
|
1039
|
+
<div class="graph-canvas-wrap">
|
|
1040
|
+
<svg width="100%" height="240" viewBox="0 0 600 240">
|
|
1041
|
+
<defs>
|
|
1042
|
+
<marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
|
1043
|
+
<path d="M 0 0 L 10 5 L 0 10 z" fill="#f59e0b" />
|
|
1044
|
+
</marker>
|
|
1045
|
+
</defs>
|
|
1046
|
+
${edges
|
|
1047
|
+
.slice(0, 30)
|
|
1048
|
+
.map((e, idx) => {
|
|
1049
|
+
const x1 = 50 + (idx * 37) % 500;
|
|
1050
|
+
const y1 = 40 + (idx * 23) % 160;
|
|
1051
|
+
const x2 = 80 + (idx * 43) % 500;
|
|
1052
|
+
const y2 = 60 + (idx * 31) % 160;
|
|
1053
|
+
return `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="rgba(245, 158, 11, 0.4)" stroke-width="1.5" marker-end="url(#arrow)" />`;
|
|
1054
|
+
})
|
|
1055
|
+
.join('')}
|
|
1056
|
+
${nodes
|
|
1057
|
+
.slice(0, 20)
|
|
1058
|
+
.map((n, idx) => {
|
|
1059
|
+
const cx = 50 + (idx * 53) % 500;
|
|
1060
|
+
const cy = 40 + (idx * 33) % 160;
|
|
1061
|
+
return `
|
|
1062
|
+
<g>
|
|
1063
|
+
<circle cx="${cx}" cy="${cy}" r="12" fill="#1e293b" stroke="#f59e0b" stroke-width="2" />
|
|
1064
|
+
<text x="${cx}" y="${cy + 22}" fill="#94a3b8" font-size="9" text-anchor="middle">
|
|
1065
|
+
${this.escape(n.label.slice(0, 14))}
|
|
1066
|
+
</text>
|
|
1067
|
+
</g>
|
|
1068
|
+
`;
|
|
1069
|
+
})
|
|
1070
|
+
.join('')}
|
|
1071
|
+
</svg>
|
|
1072
|
+
</div>
|
|
1073
|
+
`;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
renderGenerateScreen() {
|
|
1077
|
+
return `
|
|
1078
|
+
<div style="margin-bottom: 12px;">
|
|
1079
|
+
<label style="display: block; font-weight: 600; margin-bottom: 4px;">Target Repository (leave empty for current workspace):</label>
|
|
1080
|
+
<input class="search-input" id="generate-repo-input" placeholder="e.g. total-recall or gro-crm" value="${this.escape(this.generateRepo)}" />
|
|
1081
|
+
</div>
|
|
1082
|
+
<div style="display: flex; justify-content: flex-end; margin-bottom: 12px;">
|
|
1083
|
+
<button class="action-btn" id="btn-generate-exec" ${this.loading ? 'disabled' : ''}>
|
|
1084
|
+
${this.loading ? 'Generating...' : 'Generate Wiki Documentation'}
|
|
1085
|
+
</button>
|
|
1086
|
+
</div>
|
|
1087
|
+
<div style="background: var(--bg-base); border: 1px solid var(--border-subtle); border-radius: 6px; padding: 10px; font-family: ui-monospace, monospace; font-size: 11px; max-height: 180px; overflow-y: auto;">
|
|
1088
|
+
${
|
|
1089
|
+
this.generateLogs.length === 0
|
|
1090
|
+
? '<span style="color: var(--text-muted);">Ready to dispatch on-demand generation.</span>'
|
|
1091
|
+
: this.generateLogs.map((l) => `<div>${this.escape(l)}</div>`).join('')
|
|
433
1092
|
}
|
|
1093
|
+
</div>
|
|
1094
|
+
`;
|
|
1095
|
+
}
|
|
434
1096
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
1097
|
+
renderRefreshScreen() {
|
|
1098
|
+
return `
|
|
1099
|
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
|
|
1100
|
+
<span style="font-weight: 600; color: var(--text-muted);">Fleet Staleness Status:</span>
|
|
1101
|
+
<button class="action-btn" id="btn-fleet-refresh" ${this.loading ? 'disabled' : ''}>
|
|
1102
|
+
${this.loading ? 'Refreshing...' : 'Execute Fleet Refresh'}
|
|
1103
|
+
</button>
|
|
1104
|
+
</div>
|
|
1105
|
+
<div style="max-height: 220px; overflow-y: auto;">
|
|
1106
|
+
${
|
|
1107
|
+
this.refreshReports.length === 0
|
|
1108
|
+
? '<div style="color: var(--text-muted); text-align: center; padding: 20px;">No refresh reports loaded. Click Execute to evaluate fleet.</div>'
|
|
1109
|
+
: this.refreshReports
|
|
1110
|
+
.map(
|
|
1111
|
+
(r) => `
|
|
1112
|
+
<div style="display: flex; justify-content: space-between; padding: 6px 8px; background: var(--bg-elevated); border: 1px solid var(--border-subtle); border-radius: 4px; margin-bottom: 4px; font-size: 11px;">
|
|
1113
|
+
<span><strong>${this.escape(r.name)}</strong>: ${this.escape(r.action)}</span>
|
|
1114
|
+
<span style="color: var(--text-muted);">${r.commitsSince ? r.commitsSince + ' commits' : 'current'}</span>
|
|
1115
|
+
</div>
|
|
1116
|
+
`
|
|
1117
|
+
)
|
|
1118
|
+
.join('')
|
|
445
1119
|
}
|
|
446
|
-
</
|
|
1120
|
+
</div>
|
|
1121
|
+
`;
|
|
1122
|
+
}
|
|
447
1123
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
1124
|
+
renderTokenScreen() {
|
|
1125
|
+
const { openrouter, google } = this.tokenStatus;
|
|
1126
|
+
return `
|
|
1127
|
+
<div class="auth-notice-card">
|
|
1128
|
+
Total Recall Auth Secrets Store credentials for model-driven documentation. Secrets are stored encrypted in the global store.
|
|
1129
|
+
</div>
|
|
1130
|
+
<div class="token-row">
|
|
1131
|
+
<div>
|
|
1132
|
+
<span class="token-key">OPENROUTER_API_KEY</span>
|
|
1133
|
+
<div style="font-size: 11px; color: var(--text-muted);">Default provider for cheap model wiki routing</div>
|
|
455
1134
|
</div>
|
|
456
|
-
<
|
|
457
|
-
${
|
|
1135
|
+
<span class="token-badge ${openrouter?.configured ? 'ok' : 'missing'}">
|
|
1136
|
+
${openrouter?.configured ? 'Configured' : 'Missing'}
|
|
1137
|
+
</span>
|
|
1138
|
+
</div>
|
|
1139
|
+
<div class="token-row">
|
|
1140
|
+
<div>
|
|
1141
|
+
<span class="token-key">GOOGLE_API_KEY</span>
|
|
1142
|
+
<div style="font-size: 11px; color: var(--text-muted);">Google Gemini provider for native model generation</div>
|
|
1143
|
+
</div>
|
|
1144
|
+
<span class="token-badge ${google?.configured ? 'ok' : 'missing'}">
|
|
1145
|
+
${google?.configured ? 'Configured' : 'Missing'}
|
|
1146
|
+
</span>
|
|
1147
|
+
</div>
|
|
1148
|
+
<div style="margin-top: 14px; border-top: 1px solid var(--border-subtle); padding-top: 12px;">
|
|
1149
|
+
<label style="display: block; font-weight: 600; margin-bottom: 4px;">Save Key to Auth Vault:</label>
|
|
1150
|
+
<div style="display: flex; gap: 8px;">
|
|
1151
|
+
<select class="scope-select" id="token-provider-select">
|
|
1152
|
+
<option value="openrouter">OpenRouter</option>
|
|
1153
|
+
<option value="google">Google Gemini</option>
|
|
1154
|
+
</select>
|
|
1155
|
+
<input type="password" class="search-input" id="token-val-input" placeholder="Paste API Key (saved securely to auth secrets)..." />
|
|
1156
|
+
<button class="action-btn" id="btn-save-token">Save Key</button>
|
|
458
1157
|
</div>
|
|
459
1158
|
</div>
|
|
1159
|
+
`;
|
|
1160
|
+
}
|
|
460
1161
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
<div class="hub-main">
|
|
468
|
-
<span class="hub-title">${escapeHtml(h.title || h.path)}</span>
|
|
469
|
-
<span class="hub-path">${escapeHtml(h.path)}</span>
|
|
470
|
-
</div>
|
|
471
|
-
<div class="hub-metrics">
|
|
472
|
-
<span>${h.lines !== undefined ? `${h.lines} lines` : DASH}</span>
|
|
473
|
-
</div>
|
|
474
|
-
</div>
|
|
475
|
-
`).join('') : `
|
|
476
|
-
<div class="empty-hubs">No markdown documentation hubs indexed yet. Click Bootstrap below to initialize.</div>
|
|
477
|
-
`}
|
|
478
|
-
</div>
|
|
1162
|
+
renderLintScreen() {
|
|
1163
|
+
if (!this.lintData) {
|
|
1164
|
+
return `
|
|
1165
|
+
<div style="text-align: center; padding: 30px; color: var(--text-muted);">
|
|
1166
|
+
<div>Link hygiene and documentation health not audited yet.</div>
|
|
1167
|
+
<button class="action-btn" id="btn-run-lint" style="margin-top: 10px;">Run Health Check</button>
|
|
479
1168
|
</div>
|
|
1169
|
+
`;
|
|
1170
|
+
}
|
|
480
1171
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
<h4 class="panel-title">Knowledge Ingest</h4>
|
|
484
|
-
<div class="input-row">
|
|
485
|
-
<span class="input-label">Target Hub Path</span>
|
|
486
|
-
<input class="text-input" type="text" id="input-path" placeholder="openwiki" />
|
|
487
|
-
</div>
|
|
488
|
-
<label class="toggle-row">
|
|
489
|
-
<input class="toggle-check" type="checkbox" id="check-dryrun" ${this.dryRun ? 'checked' : ''} />
|
|
490
|
-
<span>Dry Run (Preview changes without writing)</span>
|
|
491
|
-
</label>
|
|
492
|
-
<button class="btn-action-primary" type="button" id="btn-ingest" ${this.executing ? 'disabled' : ''}>
|
|
493
|
-
${this.executing ? 'Processing...' : 'Run Ingestion'}
|
|
494
|
-
</button>
|
|
495
|
-
<button class="btn-action-secondary" type="button" id="btn-bootstrap" ${this.executing ? 'disabled' : ''}>
|
|
496
|
-
Bootstrap OpenWiki Skeleton
|
|
497
|
-
</button>
|
|
498
|
-
</div>
|
|
1172
|
+
const { healthScore, totalHubs, totalLinks, brokenLinksCount, orphanCount, issues } = this.lintData;
|
|
1173
|
+
const scoreColor = healthScore > 80 ? 'var(--accent-green)' : (healthScore > 50 ? 'var(--accent-amber)' : 'var(--accent-red)');
|
|
499
1174
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
1175
|
+
return `
|
|
1176
|
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
|
|
1177
|
+
<div style="font-size: 20px; font-weight: 700; color: ${scoreColor};">
|
|
1178
|
+
${healthScore}% Health Score
|
|
1179
|
+
</div>
|
|
1180
|
+
<button class="back-btn" id="btn-run-lint">Re-scan</button>
|
|
1181
|
+
</div>
|
|
1182
|
+
<div class="metric-deck">
|
|
1183
|
+
<div class="metric-cell">
|
|
1184
|
+
<div class="metric-label">Links Tested</div>
|
|
1185
|
+
<div class="metric-val">${totalLinks}</div>
|
|
1186
|
+
</div>
|
|
1187
|
+
<div class="metric-cell">
|
|
1188
|
+
<div class="metric-label">Broken Links</div>
|
|
1189
|
+
<div class="metric-val" style="color: ${brokenLinksCount ? 'var(--accent-red)' : 'var(--accent-green)'};">${brokenLinksCount}</div>
|
|
1190
|
+
</div>
|
|
1191
|
+
<div class="metric-cell">
|
|
1192
|
+
<div class="metric-label">Orphan Hubs</div>
|
|
1193
|
+
<div class="metric-val">${orphanCount}</div>
|
|
1194
|
+
</div>
|
|
1195
|
+
</div>
|
|
1196
|
+
<div style="max-height: 180px; overflow-y: auto;">
|
|
1197
|
+
${
|
|
1198
|
+
issues.length === 0
|
|
1199
|
+
? '<div style="color: var(--accent-green); padding: 10px;">[OK] All documentation links and headings verified clean!</div>'
|
|
1200
|
+
: issues
|
|
1201
|
+
.map(
|
|
1202
|
+
(iss) => `
|
|
1203
|
+
<div style="padding: 6px 8px; background: var(--bg-elevated); border: 1px solid var(--border-subtle); border-radius: 4px; margin-bottom: 4px; font-size: 11px;">
|
|
1204
|
+
<span style="color: var(--accent-red); font-weight: 600;">[${this.escape(iss.type)}]</span>
|
|
1205
|
+
${this.escape(iss.message)}
|
|
503
1206
|
</div>
|
|
504
|
-
`
|
|
1207
|
+
`
|
|
1208
|
+
)
|
|
1209
|
+
.join('')
|
|
1210
|
+
}
|
|
1211
|
+
</div>
|
|
1212
|
+
`;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
renderUpstreamScreen() {
|
|
1216
|
+
return `
|
|
1217
|
+
<div style="margin-bottom: 12px;">
|
|
1218
|
+
<div style="font-weight: 600; margin-bottom: 4px;">Upstream OpenWiki Surveillance:</div>
|
|
1219
|
+
<div style="font-size: 12px; color: var(--text-muted);">
|
|
1220
|
+
Recorded: v${this.upstream?.recordedVersion || '0.7.2'} | Latest: v${this.upstream?.latestVersion || '0.7.3'}
|
|
505
1221
|
</div>
|
|
506
1222
|
</div>
|
|
1223
|
+
<button class="action-btn" id="btn-watch-exec" style="margin-top: 8px;">Check Upstream Releases</button>
|
|
1224
|
+
`;
|
|
1225
|
+
}
|
|
507
1226
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
1227
|
+
renderConfigScreen() {
|
|
1228
|
+
return `
|
|
1229
|
+
<div class="config-deck">
|
|
1230
|
+
<div>
|
|
1231
|
+
<label style="display: block; font-weight: 600; margin-bottom: 4px;">Recommended Model Presets:</label>
|
|
1232
|
+
<span class="preset-chip" data-model="deepseek/deepseek-chat">DeepSeek Chat</span>
|
|
1233
|
+
<span class="preset-chip" data-model="deepseek/deepseek-v4.1-flash">DeepSeek Flash</span>
|
|
1234
|
+
<span class="preset-chip" data-model="qwen/qwen-2.5-coder-32b-instruct">Qwen 2.5 Coder</span>
|
|
1235
|
+
</div>
|
|
1236
|
+
<div>
|
|
1237
|
+
<label style="display: block; font-weight: 600; margin-bottom: 4px;">Provider:</label>
|
|
1238
|
+
<select class="scope-select" id="cfg-provider" style="width: 100%;">
|
|
1239
|
+
<option value="openrouter" ${this.config.provider === 'openrouter' ? 'selected' : ''}>OpenRouter (Fleet Routing)</option>
|
|
1240
|
+
<option value="google" ${this.config.provider === 'google' ? 'selected' : ''}>Google Gemini</option>
|
|
1241
|
+
</select>
|
|
1242
|
+
</div>
|
|
1243
|
+
<div>
|
|
1244
|
+
<label style="display: block; font-weight: 600; margin-bottom: 4px;">Model ID:</label>
|
|
1245
|
+
<input class="search-input" id="cfg-model" value="${this.escape(this.config.model)}" style="width: 100%;" />
|
|
1246
|
+
</div>
|
|
1247
|
+
<div>
|
|
1248
|
+
<label style="display: block; font-weight: 600; margin-bottom: 4px;">Max Tokens:</label>
|
|
1249
|
+
<input type="number" class="search-input" id="cfg-tokens" value="${this.config.max_tokens}" style="width: 100%;" />
|
|
1250
|
+
</div>
|
|
1251
|
+
<div style="display: flex; justify-content: flex-end;">
|
|
1252
|
+
<button class="action-btn" id="btn-save-cfg">Save Settings</button>
|
|
1253
|
+
</div>
|
|
511
1254
|
</div>
|
|
512
1255
|
`;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
attachEventListeners() {
|
|
1259
|
+
const root = this.shadowRoot;
|
|
1260
|
+
if (!root) return;
|
|
1261
|
+
|
|
1262
|
+
// Scope selection
|
|
1263
|
+
const scopeSelect = root.querySelector('#scope-select');
|
|
1264
|
+
if (scopeSelect) {
|
|
1265
|
+
scopeSelect.addEventListener('change', (e) => this.setScope(e.target.value));
|
|
1266
|
+
}
|
|
513
1267
|
|
|
514
|
-
//
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
this.targetPath = e.target.value;
|
|
1268
|
+
// Navigation buttons
|
|
1269
|
+
root.querySelectorAll('.nav-btn').forEach((btn) => {
|
|
1270
|
+
btn.addEventListener('click', () => {
|
|
1271
|
+
const screen = btn.getAttribute('data-screen');
|
|
1272
|
+
if (screen) this.setScreen(screen);
|
|
520
1273
|
});
|
|
1274
|
+
});
|
|
1275
|
+
|
|
1276
|
+
// Hub item click -> Read Hub
|
|
1277
|
+
root.querySelectorAll('.hub-item').forEach((item) => {
|
|
1278
|
+
item.addEventListener('click', () => {
|
|
1279
|
+
const idx = parseInt(item.getAttribute('data-hub-index'), 10);
|
|
1280
|
+
if (!isNaN(idx) && this.hubs[idx]) {
|
|
1281
|
+
this.readHubDocument(this.hubs[idx]);
|
|
1282
|
+
}
|
|
1283
|
+
});
|
|
1284
|
+
});
|
|
1285
|
+
|
|
1286
|
+
// Back to hubs from reader
|
|
1287
|
+
const backBtn = root.querySelector('#btn-back-to-hubs');
|
|
1288
|
+
if (backBtn) {
|
|
1289
|
+
backBtn.addEventListener('click', () => {
|
|
1290
|
+
this.selectedHub = null;
|
|
1291
|
+
this.selectedHubContent = null;
|
|
1292
|
+
this.render();
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
// Refresh hubs button
|
|
1297
|
+
const refreshHubsBtn = root.querySelector('#btn-refresh-hubs');
|
|
1298
|
+
if (refreshHubsBtn) {
|
|
1299
|
+
refreshHubsBtn.addEventListener('click', () => this.refreshHubs());
|
|
521
1300
|
}
|
|
522
1301
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
1302
|
+
// Ingest button
|
|
1303
|
+
const ingestBtn = root.querySelector('#btn-ingest-exec');
|
|
1304
|
+
if (ingestBtn) {
|
|
1305
|
+
ingestBtn.addEventListener('click', () => this.executeIngest());
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// Search button
|
|
1309
|
+
const searchBtn = root.querySelector('#btn-search-exec');
|
|
1310
|
+
const searchInput = root.querySelector('#search-input');
|
|
1311
|
+
if (searchBtn && searchInput) {
|
|
1312
|
+
searchBtn.addEventListener('click', () => this.executeSearch(searchInput.value));
|
|
1313
|
+
searchInput.addEventListener('keydown', (e) => {
|
|
1314
|
+
if (e.key === 'Enter') this.executeSearch(searchInput.value);
|
|
527
1315
|
});
|
|
528
1316
|
}
|
|
529
1317
|
|
|
530
|
-
|
|
531
|
-
root.querySelector('#btn-
|
|
1318
|
+
// Graph button
|
|
1319
|
+
const loadGraphBtn = root.querySelector('#btn-load-graph');
|
|
1320
|
+
if (loadGraphBtn) {
|
|
1321
|
+
loadGraphBtn.addEventListener('click', () => this.loadGraph());
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
// Lint button
|
|
1325
|
+
const runLintBtn = root.querySelector('#btn-run-lint');
|
|
1326
|
+
if (runLintBtn) {
|
|
1327
|
+
runLintBtn.addEventListener('click', () => this.loadLint());
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
// Fleet refresh button
|
|
1331
|
+
const fleetRefreshBtn = root.querySelector('#btn-fleet-refresh');
|
|
1332
|
+
if (fleetRefreshBtn) {
|
|
1333
|
+
fleetRefreshBtn.addEventListener('click', () => this.executeFleetRefresh());
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
// Upstream watch button
|
|
1337
|
+
const watchBtn = root.querySelector('#btn-watch-exec');
|
|
1338
|
+
if (watchBtn) {
|
|
1339
|
+
watchBtn.addEventListener('click', () => this.executeWatch());
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
// Generate button
|
|
1343
|
+
const genBtn = root.querySelector('#btn-generate-exec');
|
|
1344
|
+
const genInput = root.querySelector('#generate-repo-input');
|
|
1345
|
+
if (genBtn) {
|
|
1346
|
+
genBtn.addEventListener('click', () => {
|
|
1347
|
+
if (genInput) this.generateRepo = genInput.value.trim();
|
|
1348
|
+
this.executeGenerate();
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
// Save token button
|
|
1353
|
+
const saveTokenBtn = root.querySelector('#btn-save-token');
|
|
1354
|
+
const tokenProviderSelect = root.querySelector('#token-provider-select');
|
|
1355
|
+
const tokenValInput = root.querySelector('#token-val-input');
|
|
1356
|
+
if (saveTokenBtn && tokenProviderSelect && tokenValInput) {
|
|
1357
|
+
saveTokenBtn.addEventListener('click', () => {
|
|
1358
|
+
this.saveToken(tokenProviderSelect.value, tokenValInput.value);
|
|
1359
|
+
tokenValInput.value = '';
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
// Preset chips
|
|
1364
|
+
root.querySelectorAll('.preset-chip').forEach((chip) => {
|
|
1365
|
+
chip.addEventListener('click', async () => {
|
|
1366
|
+
const model = chip.getAttribute('data-model');
|
|
1367
|
+
if (model) {
|
|
1368
|
+
const modelInput = root.querySelector('#cfg-model');
|
|
1369
|
+
if (modelInput) modelInput.value = model;
|
|
1370
|
+
await this.setConfigValue('model', model);
|
|
1371
|
+
}
|
|
1372
|
+
});
|
|
1373
|
+
});
|
|
1374
|
+
|
|
1375
|
+
// Save config button
|
|
1376
|
+
const saveCfgBtn = root.querySelector('#btn-save-cfg');
|
|
1377
|
+
if (saveCfgBtn) {
|
|
1378
|
+
saveCfgBtn.addEventListener('click', async () => {
|
|
1379
|
+
const prov = root.querySelector('#cfg-provider')?.value;
|
|
1380
|
+
const mod = root.querySelector('#cfg-model')?.value;
|
|
1381
|
+
const tok = root.querySelector('#cfg-tokens')?.value;
|
|
1382
|
+
if (prov) await this.setConfigValue('provider', prov);
|
|
1383
|
+
if (mod) await this.setConfigValue('model', mod);
|
|
1384
|
+
if (tok) await this.setConfigValue('max_tokens', tok);
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
escape(str) {
|
|
1390
|
+
if (!str) return '';
|
|
1391
|
+
return String(str)
|
|
1392
|
+
.replace(/&/g, '&')
|
|
1393
|
+
.replace(/</g, '<')
|
|
1394
|
+
.replace(/>/g, '>')
|
|
1395
|
+
.replace(/"/g, '"');
|
|
532
1396
|
}
|
|
533
1397
|
}
|
|
534
1398
|
|
|
535
|
-
if (!customElements.get('tr-openwiki-card')) {
|
|
1399
|
+
if (typeof customElements !== 'undefined' && !customElements.get('tr-openwiki-card')) {
|
|
536
1400
|
customElements.define('tr-openwiki-card', OpenWikiCard);
|
|
537
1401
|
}
|
|
538
|
-
if (!customElements.get('openwiki-card')) {
|
|
539
|
-
customElements.define('openwiki-card', class extends OpenWikiCard {});
|
|
540
|
-
}
|