@mindexec/cli 0.2.477 → 0.2.478
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +323 -323
- package/codex-model-catalog.js +202 -202
- package/codex-runtime.js +1608 -1608
- package/desktop-workspace-state.cjs +120 -120
- package/electron/main.cjs +517 -517
- package/electron/preload.cjs +17 -17
- package/electron/recurring-update-owner-smoke.mjs +55 -55
- package/electron/recurring-update-owner.cjs +97 -97
- package/electron/source-smoke.mjs +108 -108
- package/electron/update-feed-resolver.cjs +79 -79
- package/electron/update-feed.json +10 -10
- package/electron/update-manager-smoke.mjs +146 -146
- package/electron/update-manager.cjs +297 -297
- package/electron/windows-package-smoke.mjs +156 -156
- package/launch-bridge.cjs +236 -236
- package/lzo1x.js +110 -110
- package/mode4-atlas-worker.js +440 -440
- package/mode4-atlas.js +130 -130
- package/package.json +1 -1
- package/port-guard.cjs +285 -285
- package/remote-fast/osx-arm64/MacScreenCaptureKitH264.swift +339 -339
- package/remote-fast/osx-arm64/MacSystemAudioCapture.swift +214 -214
- package/remote-fast/osx-arm64/UnixRawCaptureHelper.mjs +80 -80
- package/remote-fast/osx-arm64/livedesk-raw-capture-resampler.mjs +73 -73
- package/remote-fast/osx-arm64/mindexec-remote-fast.dll +0 -0
- package/remote-fast/osx-x64/MacScreenCaptureKitH264.swift +339 -339
- package/remote-fast/osx-x64/MacSystemAudioCapture.swift +214 -214
- package/remote-fast/osx-x64/UnixRawCaptureHelper.mjs +80 -80
- package/remote-fast/osx-x64/livedesk-raw-capture-resampler.mjs +73 -73
- package/remote-fast/osx-x64/mindexec-remote-fast.dll +0 -0
- package/remote-fast/win-x64/mindexec-remote-fast.dll +0 -0
- package/remote-hub.js +3698 -3698
- package/scripts/auth-session-smoke.mjs +262 -262
- package/scripts/board-render-ownership-smoke.mjs +85 -85
- package/scripts/codex-model-catalog-smoke.mjs +150 -150
- package/scripts/codex-runtime-home-smoke.mjs +58 -58
- package/scripts/codex-sdk-image-live-smoke.mjs +43 -43
- package/scripts/codex-sdk-image-runtime-smoke.mjs +122 -122
- package/scripts/codex-sdk-runtime-smoke.mjs +89 -89
- package/scripts/desktop-update-publisher-smoke.mjs +89 -89
- package/scripts/desktop-update-worker-smoke.mjs +72 -72
- package/scripts/desktop-workspace-state-smoke.mjs +81 -81
- package/scripts/drive-installed-update-smoke.mjs +67 -67
- package/scripts/npm-fresh-install-smoke.mjs +216 -216
- package/scripts/publish-mindexec-desktop-updates.mjs +224 -224
- package/scripts/remote-agent-managed-smoke.mjs +318 -318
- package/scripts/remote-agent-package-smoke.mjs +64 -64
- package/scripts/remote-agent-ws-smoke.mjs +269 -269
- package/scripts/remote-fast-live-rate-smoke.mjs +343 -343
- package/scripts/remote-fast-mdm-browser-smoke.mjs +453 -453
- package/scripts/remote-fleet-render-smoke.mjs +1350 -1350
- package/scripts/remote-frame-ws-smoke.mjs +234 -234
- package/scripts/remote-http-smoke.mjs +592 -592
- package/scripts/remote-hub-identity-smoke.mjs +146 -146
- package/scripts/remote-hub-scale-smoke.mjs +124 -124
- package/scripts/remote-hub-smoke.mjs +699 -699
- package/scripts/remote-input-ws-smoke.mjs +263 -263
- package/scripts/remote-registry-follower-smoke.mjs +815 -815
- package/scripts/setup-tree-sitter-grammars.mjs +59 -59
- package/server.js +7690 -7690
- package/start-bridge.bat +32 -32
- package/start-bridge.sh +81 -81
- package/wwwroot/_content/MindExecution.Plugins.Admin/css/admin-dashboard.css +546 -546
- package/wwwroot/_content/MindExecution.Plugins.YouTube/css/youtube-search.css +251 -251
- package/wwwroot/_content/MindExecution.Shared/css/app.css +1 -1
- package/wwwroot/_content/MindExecution.Shared/css/mind-map-overrides.css +3789 -3789
- package/wwwroot/_content/MindExecution.Shared/js/agent-visualization.js +27 -27
- package/wwwroot/_content/MindExecution.Shared/js/background-themes.js +1449 -1449
- package/wwwroot/_content/MindExecution.Shared/js/code-master.js +8316 -8316
- package/wwwroot/_content/MindExecution.Shared/js/file-system-helper.js +338 -338
- package/wwwroot/_content/MindExecution.Shared/js/marked.min.js +69 -69
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-animated-image-preview.js +270 -270
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-automation-edge-motion.js +170 -170
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-board-render-ownership.js +81 -81
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +10506 -10506
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-hotpath.js +902 -902
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +24637 -24637
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-dev-guards.js +325 -325
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-dnd.js +1491 -1491
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-glow-shader.js +70 -70
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-image-policy.js +218 -218
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-interactions.js +7065 -7065
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-plan-worker.js +160 -160
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-renderer.js +9956 -9956
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-video-placeholder.js +107 -107
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-logic-workers.js +1004 -1004
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-menu-manager.js +1351 -1351
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-multi-select.js +865 -865
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-node-search-worker.js +554 -554
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-nodes.js +4744 -4744
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-normal-boundary-optimizer.js +624 -624
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-object-manager.js +241 -241
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-pipeline.js +2290 -2290
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-remote-codecs.js +202 -202
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-render-plan.js +1971 -1971
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-text-lod-system.js +93 -93
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-texture-factory.js +2391 -2391
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-video-playback-controls.js +95 -95
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-visibility-worker.js +895 -895
- package/wwwroot/_content/MindExecution.Shared/js/mindmap-toolbar.js +636 -636
- package/wwwroot/_content/MindExecution.Shared/js/native-drop-handler.js +45 -45
- package/wwwroot/_content/MindExecution.Shared/js/plan-master.js +117 -117
- package/wwwroot/_content/MindExecution.Shared/js/renderers/CSS3DRenderer.js +114 -114
- package/wwwroot/_content/MindExecution.Shared/js/texture-worker-manager.js +10 -10
- package/wwwroot/_content/MindExecution.Shared/js/three.min.js +6 -6
- package/wwwroot/_content/MindExecution.Shared/js/token-manager.js +7 -7
- package/wwwroot/_content/MindExecution.Shared/js/troika-bundle.js +6 -6
- package/wwwroot/_content/MindExecution.Shared/js/troika-bundle.js.map +2 -2
- package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/css/all.min.css +8 -8
- package/wwwroot/_content/MindExecution.Shared/models/vocab.txt +30522 -30522
- package/wwwroot/_framework/{Google.Protobuf.9h59ukbel7.dll → Google.Protobuf.33g2gb5is0.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Core.ty1xewqp36.dll → MindExecution.Core.z100xo4mg0.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Kernel.h7rqjoo69o.dll → MindExecution.Kernel.i9cldrx8yc.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Admin.wrfrcvhjde.dll → MindExecution.Plugins.Admin.48lzqddb0a.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Business.csbcyjurdh.dll → MindExecution.Plugins.Business.7m8u1q142p.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Concept.buomiysqjn.dll → MindExecution.Plugins.Concept.cl5wmr53od.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Directory.7k00i2v769.dll → MindExecution.Plugins.Directory.slipi39041.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.85i5ocraro.dll → MindExecution.Plugins.PlanMaster.qfge05l671.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.YouTube.nbolrfakte.dll → MindExecution.Plugins.YouTube.tn7v29ut77.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Shared.nqh4porv7x.dll → MindExecution.Shared.rjqlf0umjn.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Web.211q3bf4mr.dll → MindExecution.Web.rwawrxxk38.dll} +0 -0
- package/wwwroot/_framework/blazor.boot.json +23 -23
- package/wwwroot/_headers +40 -40
- package/wwwroot/_redirects +1 -1
- package/wwwroot/index.html +772 -772
- package/wwwroot/js/marketing-tool.js +180 -180
- package/wwwroot/manifest.webmanifest +23 -23
- package/wwwroot/service-worker-assets.js +78 -106
- package/wwwroot/service-worker.js +33 -33
- package/tree-sitter-grammars/README.md +0 -18
- package/tree-sitter-grammars/tree-sitter-c_sharp.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-go.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-java.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-javascript.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-python.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-rust.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-tsx.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-typescript.wasm +0 -0
- package/wwwroot/_content/MindExecution.Plugins.Directory/background.png +0 -0
- package/wwwroot/_content/MindExecution.Shared/fonts/NotoSansKR-Bold.ttf +0 -0
- package/wwwroot/_content/MindExecution.Shared/fonts/NotoSansKR-Regular.ttf +0 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js.backup +0 -1059
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-dnd.js.bak +0 -434
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-object-manager.js.backup +0 -372
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-texture-factory.js.backup +0 -1258
|
@@ -1,554 +1,554 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const MAX_PROMPT_CHARS = 2048;
|
|
4
|
-
const MAX_RESPONSE_CHARS = 16000;
|
|
5
|
-
const MAX_METADATA_TEXT_CHARS = 8192;
|
|
6
|
-
const MAX_SOURCE_PATH_CHARS = 2048;
|
|
7
|
-
const NGRAM_SIZE = 3;
|
|
8
|
-
const SEARCH_VIEWPORT_FOV_DEGREES = 45;
|
|
9
|
-
const SEARCH_VIEWPORT_ASPECT_RATIO_FALLBACK = 1.6;
|
|
10
|
-
const SEARCH_VIEWPORT_IN_VIEW_BONUS = 4;
|
|
11
|
-
const SEARCH_VIEWPORT_CENTER_BOOST_MAX = 8;
|
|
12
|
-
const SEARCH_VIEWPORT_NEAR_MARGIN = 1.35;
|
|
13
|
-
const TOKEN_REGEX = /[\p{L}\p{M}\p{N}]{2,}/gu;
|
|
14
|
-
|
|
15
|
-
function createState() {
|
|
16
|
-
return {
|
|
17
|
-
boardId: '',
|
|
18
|
-
documents: new Map(),
|
|
19
|
-
tokenIndex: new Map(),
|
|
20
|
-
prefixIndex: new Map(),
|
|
21
|
-
ngramIndex: new Map()
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
let runtime = createState();
|
|
26
|
-
|
|
27
|
-
function normalizeBoardId(boardId) {
|
|
28
|
-
return String(boardId ?? '').trim();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function limitText(value, maxLength) {
|
|
32
|
-
const text = String(value ?? '');
|
|
33
|
-
if (!text) {
|
|
34
|
-
return '';
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return text.length <= maxLength
|
|
38
|
-
? text
|
|
39
|
-
: text.slice(0, maxLength);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function shouldIndexResponse(contentType) {
|
|
43
|
-
return contentType === 'text'
|
|
44
|
-
|| contentType === 'note'
|
|
45
|
-
|| contentType === 'memo'
|
|
46
|
-
|| contentType === 'markdown'
|
|
47
|
-
|| contentType === 'code'
|
|
48
|
-
|| contentType === 'csv-table'
|
|
49
|
-
|| contentType === 'directory';
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function buildMetadataText(metadata) {
|
|
53
|
-
if (!metadata) {
|
|
54
|
-
return '';
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (typeof metadata === 'string') {
|
|
58
|
-
return limitText(metadata, MAX_METADATA_TEXT_CHARS);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const values = [];
|
|
62
|
-
const entries = Array.isArray(metadata)
|
|
63
|
-
? metadata
|
|
64
|
-
: Object.entries(metadata);
|
|
65
|
-
for (let i = 0; i < entries.length; i++) {
|
|
66
|
-
const entry = entries[i];
|
|
67
|
-
if (Array.isArray(entry)) {
|
|
68
|
-
const key = limitText(entry[0], 128).trim();
|
|
69
|
-
const value = limitText(entry[1], 1024).trim();
|
|
70
|
-
if (key) values.push(key);
|
|
71
|
-
if (value) values.push(value);
|
|
72
|
-
} else if (entry) {
|
|
73
|
-
const value = limitText(entry, 1024).trim();
|
|
74
|
-
if (value) values.push(value);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (values.join(' ').length >= MAX_METADATA_TEXT_CHARS) {
|
|
78
|
-
break;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return limitText(values.join(' '), MAX_METADATA_TEXT_CHARS);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function normalizeText(value) {
|
|
86
|
-
if (value === null || value === undefined) {
|
|
87
|
-
return '';
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
let normalized = String(value);
|
|
91
|
-
if (!normalized.trim()) {
|
|
92
|
-
return '';
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
try {
|
|
96
|
-
normalized = normalized.normalize('NFKC');
|
|
97
|
-
} catch {
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
normalized = normalized.toLowerCase();
|
|
101
|
-
|
|
102
|
-
let result = '';
|
|
103
|
-
let wroteWhitespace = false;
|
|
104
|
-
for (let i = 0; i < normalized.length; i++) {
|
|
105
|
-
const ch = normalized[i];
|
|
106
|
-
if (/\s/u.test(ch)) {
|
|
107
|
-
if (!wroteWhitespace) {
|
|
108
|
-
result += ' ';
|
|
109
|
-
wroteWhitespace = true;
|
|
110
|
-
}
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const code = ch.codePointAt(0);
|
|
115
|
-
if (typeof code === 'number' && code <= 31) {
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
result += ch;
|
|
120
|
-
wroteWhitespace = false;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return result.trim();
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function removeWhitespace(value) {
|
|
127
|
-
return String(value ?? '').replace(/\s+/gu, '');
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function extractTokens(normalizedText) {
|
|
131
|
-
if (!normalizedText) {
|
|
132
|
-
return [];
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const matches = normalizedText.match(TOKEN_REGEX);
|
|
136
|
-
if (!matches || matches.length === 0) {
|
|
137
|
-
return [];
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return matches.filter((token) => token.length >= 2);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function buildPrefixes(tokens) {
|
|
144
|
-
const prefixes = [];
|
|
145
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
146
|
-
const token = tokens[i];
|
|
147
|
-
if (!token) {
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const maxLength = Math.min(8, token.length);
|
|
152
|
-
for (let length = 2; length <= maxLength; length++) {
|
|
153
|
-
prefixes.push(token.slice(0, length));
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return prefixes;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function buildNGrams(normalizedCompactText) {
|
|
161
|
-
if (!normalizedCompactText) {
|
|
162
|
-
return [];
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (normalizedCompactText.length <= NGRAM_SIZE) {
|
|
166
|
-
return [normalizedCompactText];
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const ngrams = [];
|
|
170
|
-
for (let i = 0; i <= normalizedCompactText.length - NGRAM_SIZE; i++) {
|
|
171
|
-
ngrams.push(normalizedCompactText.slice(i, i + NGRAM_SIZE));
|
|
172
|
-
}
|
|
173
|
-
return ngrams;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function addPosting(index, key, nodeId) {
|
|
177
|
-
if (!key) {
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
let postings = index.get(key);
|
|
182
|
-
if (!postings) {
|
|
183
|
-
postings = new Set();
|
|
184
|
-
index.set(key, postings);
|
|
185
|
-
}
|
|
186
|
-
postings.add(nodeId);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function removePosting(index, key, nodeId) {
|
|
190
|
-
const postings = index.get(key);
|
|
191
|
-
if (!postings) {
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
postings.delete(nodeId);
|
|
196
|
-
if (postings.size === 0) {
|
|
197
|
-
index.delete(key);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function addPostingMatches(index, key, candidateScores, weight) {
|
|
202
|
-
const postings = index.get(key);
|
|
203
|
-
if (!postings) {
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
postings.forEach((nodeId) => {
|
|
208
|
-
candidateScores.set(
|
|
209
|
-
nodeId,
|
|
210
|
-
Number(candidateScores.get(nodeId) || 0) + weight
|
|
211
|
-
);
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function clearState(boardId) {
|
|
216
|
-
runtime = createState();
|
|
217
|
-
runtime.boardId = normalizeBoardId(boardId);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function normalizeNodeSnapshot(node) {
|
|
221
|
-
if (!node || typeof node !== 'object') {
|
|
222
|
-
return null;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const nodeId = String(node.id ?? node.Id ?? '').trim();
|
|
226
|
-
if (!nodeId) {
|
|
227
|
-
return null;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const contentType = String(node.contentType ?? node.ContentType ?? '').trim().toLowerCase();
|
|
231
|
-
|
|
232
|
-
return {
|
|
233
|
-
id: nodeId,
|
|
234
|
-
contentType,
|
|
235
|
-
prompt: limitText(node.prompt ?? node.Prompt, MAX_PROMPT_CHARS),
|
|
236
|
-
response: limitText(node.response ?? node.Response, MAX_RESPONSE_CHARS),
|
|
237
|
-
metadataText: buildMetadataText(node.metadataText ?? node.MetadataText ?? node.metadata ?? node.Metadata),
|
|
238
|
-
sourcePath: limitText(node.sourcePath ?? node.SourcePath ?? node.sourceFilePath ?? node.SourceFilePath, MAX_SOURCE_PATH_CHARS),
|
|
239
|
-
positionX: Number(node.positionX ?? node.PositionX ?? node.x ?? 0),
|
|
240
|
-
positionY: Number(node.positionY ?? node.PositionY ?? node.y ?? 0),
|
|
241
|
-
width: Math.max(1, Number(node.width ?? node.Width ?? 1)),
|
|
242
|
-
height: Math.max(1, Number(node.height ?? node.Height ?? 1))
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
function buildDocument(node) {
|
|
247
|
-
const prompt = normalizeText(node.prompt);
|
|
248
|
-
const response = normalizeText(shouldIndexResponse(node.contentType) ? node.response : '');
|
|
249
|
-
const metadataText = normalizeText(node.metadataText);
|
|
250
|
-
const sourcePath = normalizeText(node.sourcePath);
|
|
251
|
-
const normalizedContentType = normalizeText(node.contentType);
|
|
252
|
-
|
|
253
|
-
const combined = [prompt, metadataText, sourcePath, response, normalizedContentType]
|
|
254
|
-
.filter(Boolean)
|
|
255
|
-
.join(' ');
|
|
256
|
-
const tokens = new Set(extractTokens(combined));
|
|
257
|
-
const prefixes = new Set(buildPrefixes(Array.from(tokens)));
|
|
258
|
-
const ngrams = new Set(buildNGrams(removeWhitespace(combined)));
|
|
259
|
-
|
|
260
|
-
return {
|
|
261
|
-
nodeId: node.id,
|
|
262
|
-
contentType: node.contentType,
|
|
263
|
-
positionX: node.positionX,
|
|
264
|
-
positionY: node.positionY,
|
|
265
|
-
width: node.width,
|
|
266
|
-
height: node.height,
|
|
267
|
-
normalizedPrompt: prompt,
|
|
268
|
-
normalizedResponse: response,
|
|
269
|
-
normalizedMetadata: metadataText,
|
|
270
|
-
normalizedSourcePath: sourcePath,
|
|
271
|
-
normalizedContentType,
|
|
272
|
-
tokens,
|
|
273
|
-
prefixes,
|
|
274
|
-
ngrams
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
function removeDocument(nodeId) {
|
|
279
|
-
const existing = runtime.documents.get(nodeId);
|
|
280
|
-
if (!existing) {
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
existing.tokens.forEach((token) => removePosting(runtime.tokenIndex, token, nodeId));
|
|
285
|
-
existing.prefixes.forEach((prefix) => removePosting(runtime.prefixIndex, prefix, nodeId));
|
|
286
|
-
existing.ngrams.forEach((ngram) => removePosting(runtime.ngramIndex, ngram, nodeId));
|
|
287
|
-
runtime.documents.delete(nodeId);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
function upsertNode(node) {
|
|
291
|
-
const normalizedNode = normalizeNodeSnapshot(node);
|
|
292
|
-
if (!normalizedNode) {
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
removeDocument(normalizedNode.id);
|
|
297
|
-
|
|
298
|
-
const document = buildDocument(normalizedNode);
|
|
299
|
-
runtime.documents.set(document.nodeId, document);
|
|
300
|
-
|
|
301
|
-
document.tokens.forEach((token) => addPosting(runtime.tokenIndex, token, document.nodeId));
|
|
302
|
-
document.prefixes.forEach((prefix) => addPosting(runtime.prefixIndex, prefix, document.nodeId));
|
|
303
|
-
document.ngrams.forEach((ngram) => addPosting(runtime.ngramIndex, ngram, document.nodeId));
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
function rebuildBoard(boardId, nodes) {
|
|
307
|
-
clearState(boardId);
|
|
308
|
-
const sourceNodes = Array.isArray(nodes) ? nodes : [];
|
|
309
|
-
for (let i = 0; i < sourceNodes.length; i++) {
|
|
310
|
-
upsertNode(sourceNodes[i]);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
function scoreDocument(document, normalizedQuery, queryTokens, queryNGrams) {
|
|
315
|
-
let score = 0;
|
|
316
|
-
|
|
317
|
-
if (document.normalizedPrompt === normalizedQuery) {
|
|
318
|
-
score += 120;
|
|
319
|
-
} else if (document.normalizedPrompt.includes(normalizedQuery)) {
|
|
320
|
-
score += 54;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
if (document.normalizedMetadata.includes(normalizedQuery)) {
|
|
324
|
-
score += 26;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
if (document.normalizedSourcePath.includes(normalizedQuery)) {
|
|
328
|
-
score += 18;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
if (document.normalizedResponse.includes(normalizedQuery)) {
|
|
332
|
-
score += 14;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
if (document.normalizedContentType.includes(normalizedQuery)) {
|
|
336
|
-
score += 8;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
for (let i = 0; i < queryTokens.length; i++) {
|
|
340
|
-
const token = queryTokens[i];
|
|
341
|
-
if (document.tokens.has(token)) {
|
|
342
|
-
score += 7;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
if (document.normalizedPrompt.startsWith(token)) {
|
|
346
|
-
score += 10;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
for (let i = 0; i < queryNGrams.length; i++) {
|
|
351
|
-
if (document.ngrams.has(queryNGrams[i])) {
|
|
352
|
-
score += 0.85;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
return score;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
function calculateViewportBoost(document, camera) {
|
|
360
|
-
if (!camera) {
|
|
361
|
-
return 0;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
const cameraZ = Math.max(400, Number(camera.z || 0) || 1200);
|
|
365
|
-
const aspect = Math.max(0.1, Number(camera.aspect || SEARCH_VIEWPORT_ASPECT_RATIO_FALLBACK));
|
|
366
|
-
const halfViewHeight = Math.max(
|
|
367
|
-
480,
|
|
368
|
-
cameraZ * Math.tan((SEARCH_VIEWPORT_FOV_DEGREES * Math.PI / 180) / 2)
|
|
369
|
-
);
|
|
370
|
-
const halfViewWidth = halfViewHeight * aspect;
|
|
371
|
-
|
|
372
|
-
const nodeWidth = Math.max(1, Number(document.width || 1));
|
|
373
|
-
const nodeHeight = Math.max(1, Number(document.height || 1));
|
|
374
|
-
const centerX = Number(document.positionX || 0) + (nodeWidth / 2);
|
|
375
|
-
const centerY = Number(document.positionY || 0) - (nodeHeight / 2);
|
|
376
|
-
const normalizedX = Math.abs(centerX - Number(camera.x || 0)) / Math.max(1, halfViewWidth);
|
|
377
|
-
const normalizedY = Math.abs(centerY - Number(camera.y || 0)) / Math.max(1, halfViewHeight);
|
|
378
|
-
const radialDistance = Math.sqrt((normalizedX * normalizedX) + (normalizedY * normalizedY));
|
|
379
|
-
|
|
380
|
-
if (radialDistance <= 1) {
|
|
381
|
-
return SEARCH_VIEWPORT_IN_VIEW_BONUS + ((1 - radialDistance) * SEARCH_VIEWPORT_CENTER_BOOST_MAX);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
if (radialDistance <= SEARCH_VIEWPORT_NEAR_MARGIN) {
|
|
385
|
-
const falloff = 1 - ((radialDistance - 1) / Math.max(0.001, SEARCH_VIEWPORT_NEAR_MARGIN - 1));
|
|
386
|
-
return Math.max(0, falloff) * (SEARCH_VIEWPORT_IN_VIEW_BONUS * 0.75);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
return 0;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
function handleSearchQuery(message) {
|
|
393
|
-
const boardId = normalizeBoardId(message.boardId);
|
|
394
|
-
if (!boardId || boardId !== runtime.boardId) {
|
|
395
|
-
self.postMessage({
|
|
396
|
-
type: 'SEARCH_RESULT',
|
|
397
|
-
boardId,
|
|
398
|
-
seq: Number(message.seq || 0),
|
|
399
|
-
query: String(message.query || ''),
|
|
400
|
-
hits: []
|
|
401
|
-
});
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
const startedAt = (typeof performance !== 'undefined' && typeof performance.now === 'function')
|
|
406
|
-
? performance.now()
|
|
407
|
-
: Date.now();
|
|
408
|
-
const normalizedLimit = Math.max(1, Number(message.limit || 30));
|
|
409
|
-
const normalizedQuery = normalizeText(message.query);
|
|
410
|
-
if (!normalizedQuery) {
|
|
411
|
-
self.postMessage({
|
|
412
|
-
type: 'SEARCH_RESULT',
|
|
413
|
-
boardId,
|
|
414
|
-
seq: Number(message.seq || 0),
|
|
415
|
-
query: String(message.query || ''),
|
|
416
|
-
hits: []
|
|
417
|
-
});
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
const allowedContentTypes = Array.isArray(message.contentTypes) && message.contentTypes.length > 0
|
|
422
|
-
? new Set(
|
|
423
|
-
message.contentTypes
|
|
424
|
-
.map((value) => String(value || '').trim().toLowerCase())
|
|
425
|
-
.filter(Boolean)
|
|
426
|
-
)
|
|
427
|
-
: null;
|
|
428
|
-
const queryTokens = extractTokens(normalizedQuery);
|
|
429
|
-
const queryPrefixes = buildPrefixes(queryTokens);
|
|
430
|
-
const queryNGrams = buildNGrams(removeWhitespace(normalizedQuery));
|
|
431
|
-
const candidateScores = new Map();
|
|
432
|
-
|
|
433
|
-
for (let i = 0; i < queryTokens.length; i++) {
|
|
434
|
-
addPostingMatches(runtime.tokenIndex, queryTokens[i], candidateScores, 8);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
for (let i = 0; i < queryPrefixes.length; i++) {
|
|
438
|
-
const prefix = queryPrefixes[i];
|
|
439
|
-
addPostingMatches(runtime.prefixIndex, prefix, candidateScores, prefix.length <= 3 ? 4 : 2);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
for (let i = 0; i < queryNGrams.length; i++) {
|
|
443
|
-
addPostingMatches(runtime.ngramIndex, queryNGrams[i], candidateScores, 1.15);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
let candidateIds = [];
|
|
447
|
-
if (candidateScores.size > 0) {
|
|
448
|
-
candidateIds = Array.from(candidateScores.entries())
|
|
449
|
-
.sort((a, b) => {
|
|
450
|
-
if (b[1] !== a[1]) {
|
|
451
|
-
return b[1] - a[1];
|
|
452
|
-
}
|
|
453
|
-
return String(a[0]).localeCompare(String(b[0]));
|
|
454
|
-
})
|
|
455
|
-
.slice(0, Math.max(normalizedLimit * 16, 256))
|
|
456
|
-
.map((entry) => entry[0]);
|
|
457
|
-
} else {
|
|
458
|
-
candidateIds = Array.from(runtime.documents.keys());
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
const ranked = [];
|
|
462
|
-
for (let i = 0; i < candidateIds.length; i++) {
|
|
463
|
-
const document = runtime.documents.get(candidateIds[i]);
|
|
464
|
-
if (!document) {
|
|
465
|
-
continue;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
if (allowedContentTypes && allowedContentTypes.size > 0 && !allowedContentTypes.has(document.contentType)) {
|
|
469
|
-
continue;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
const score = scoreDocument(document, normalizedQuery, queryTokens, queryNGrams);
|
|
473
|
-
if (score <= 0) {
|
|
474
|
-
continue;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
ranked.push({
|
|
478
|
-
nodeId: document.nodeId,
|
|
479
|
-
score: score + calculateViewportBoost(document, message.camera || null)
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
ranked.sort((a, b) => {
|
|
484
|
-
if (b.score !== a.score) {
|
|
485
|
-
return b.score - a.score;
|
|
486
|
-
}
|
|
487
|
-
return String(a.nodeId).localeCompare(String(b.nodeId));
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
const completedAt = (typeof performance !== 'undefined' && typeof performance.now === 'function')
|
|
491
|
-
? performance.now()
|
|
492
|
-
: Date.now();
|
|
493
|
-
|
|
494
|
-
self.postMessage({
|
|
495
|
-
type: 'SEARCH_RESULT',
|
|
496
|
-
boardId,
|
|
497
|
-
seq: Number(message.seq || 0),
|
|
498
|
-
query: String(message.query || ''),
|
|
499
|
-
hits: ranked.slice(0, normalizedLimit),
|
|
500
|
-
profile: {
|
|
501
|
-
durationMs: Number(Math.max(0, completedAt - startedAt).toFixed(2)),
|
|
502
|
-
candidateCount: candidateIds.length,
|
|
503
|
-
rankedCount: ranked.length
|
|
504
|
-
}
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
self.onmessage = function (event) {
|
|
509
|
-
const message = event?.data || null;
|
|
510
|
-
if (!message || typeof message !== 'object') {
|
|
511
|
-
return;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
switch (message.type) {
|
|
515
|
-
case 'RESET_BOARD':
|
|
516
|
-
clearState(message.boardId);
|
|
517
|
-
break;
|
|
518
|
-
case 'REBUILD_BOARD':
|
|
519
|
-
rebuildBoard(message.boardId, message.nodes);
|
|
520
|
-
break;
|
|
521
|
-
case 'UPSERT_NODE':
|
|
522
|
-
if (normalizeBoardId(message.boardId) === runtime.boardId) {
|
|
523
|
-
upsertNode(message.node);
|
|
524
|
-
}
|
|
525
|
-
break;
|
|
526
|
-
case 'REMOVE_NODE':
|
|
527
|
-
if (normalizeBoardId(message.boardId) === runtime.boardId) {
|
|
528
|
-
removeDocument(String(message.nodeId || '').trim());
|
|
529
|
-
}
|
|
530
|
-
break;
|
|
531
|
-
case 'BATCH_SYNC':
|
|
532
|
-
if (normalizeBoardId(message.boardId) === runtime.boardId) {
|
|
533
|
-
const removedIds = Array.isArray(message.removedIds) ? message.removedIds : [];
|
|
534
|
-
for (let i = 0; i < removedIds.length; i++) {
|
|
535
|
-
removeDocument(String(removedIds[i] || '').trim());
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
const nodes = Array.isArray(message.nodes) ? message.nodes : [];
|
|
539
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
540
|
-
upsertNode(nodes[i]);
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
break;
|
|
544
|
-
case 'SEARCH_QUERY':
|
|
545
|
-
handleSearchQuery(message);
|
|
546
|
-
break;
|
|
547
|
-
default:
|
|
548
|
-
break;
|
|
549
|
-
}
|
|
550
|
-
};
|
|
551
|
-
|
|
552
|
-
self.postMessage({
|
|
553
|
-
type: 'READY'
|
|
554
|
-
});
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const MAX_PROMPT_CHARS = 2048;
|
|
4
|
+
const MAX_RESPONSE_CHARS = 16000;
|
|
5
|
+
const MAX_METADATA_TEXT_CHARS = 8192;
|
|
6
|
+
const MAX_SOURCE_PATH_CHARS = 2048;
|
|
7
|
+
const NGRAM_SIZE = 3;
|
|
8
|
+
const SEARCH_VIEWPORT_FOV_DEGREES = 45;
|
|
9
|
+
const SEARCH_VIEWPORT_ASPECT_RATIO_FALLBACK = 1.6;
|
|
10
|
+
const SEARCH_VIEWPORT_IN_VIEW_BONUS = 4;
|
|
11
|
+
const SEARCH_VIEWPORT_CENTER_BOOST_MAX = 8;
|
|
12
|
+
const SEARCH_VIEWPORT_NEAR_MARGIN = 1.35;
|
|
13
|
+
const TOKEN_REGEX = /[\p{L}\p{M}\p{N}]{2,}/gu;
|
|
14
|
+
|
|
15
|
+
function createState() {
|
|
16
|
+
return {
|
|
17
|
+
boardId: '',
|
|
18
|
+
documents: new Map(),
|
|
19
|
+
tokenIndex: new Map(),
|
|
20
|
+
prefixIndex: new Map(),
|
|
21
|
+
ngramIndex: new Map()
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let runtime = createState();
|
|
26
|
+
|
|
27
|
+
function normalizeBoardId(boardId) {
|
|
28
|
+
return String(boardId ?? '').trim();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function limitText(value, maxLength) {
|
|
32
|
+
const text = String(value ?? '');
|
|
33
|
+
if (!text) {
|
|
34
|
+
return '';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return text.length <= maxLength
|
|
38
|
+
? text
|
|
39
|
+
: text.slice(0, maxLength);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function shouldIndexResponse(contentType) {
|
|
43
|
+
return contentType === 'text'
|
|
44
|
+
|| contentType === 'note'
|
|
45
|
+
|| contentType === 'memo'
|
|
46
|
+
|| contentType === 'markdown'
|
|
47
|
+
|| contentType === 'code'
|
|
48
|
+
|| contentType === 'csv-table'
|
|
49
|
+
|| contentType === 'directory';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function buildMetadataText(metadata) {
|
|
53
|
+
if (!metadata) {
|
|
54
|
+
return '';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (typeof metadata === 'string') {
|
|
58
|
+
return limitText(metadata, MAX_METADATA_TEXT_CHARS);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const values = [];
|
|
62
|
+
const entries = Array.isArray(metadata)
|
|
63
|
+
? metadata
|
|
64
|
+
: Object.entries(metadata);
|
|
65
|
+
for (let i = 0; i < entries.length; i++) {
|
|
66
|
+
const entry = entries[i];
|
|
67
|
+
if (Array.isArray(entry)) {
|
|
68
|
+
const key = limitText(entry[0], 128).trim();
|
|
69
|
+
const value = limitText(entry[1], 1024).trim();
|
|
70
|
+
if (key) values.push(key);
|
|
71
|
+
if (value) values.push(value);
|
|
72
|
+
} else if (entry) {
|
|
73
|
+
const value = limitText(entry, 1024).trim();
|
|
74
|
+
if (value) values.push(value);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (values.join(' ').length >= MAX_METADATA_TEXT_CHARS) {
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return limitText(values.join(' '), MAX_METADATA_TEXT_CHARS);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function normalizeText(value) {
|
|
86
|
+
if (value === null || value === undefined) {
|
|
87
|
+
return '';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let normalized = String(value);
|
|
91
|
+
if (!normalized.trim()) {
|
|
92
|
+
return '';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
normalized = normalized.normalize('NFKC');
|
|
97
|
+
} catch {
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
normalized = normalized.toLowerCase();
|
|
101
|
+
|
|
102
|
+
let result = '';
|
|
103
|
+
let wroteWhitespace = false;
|
|
104
|
+
for (let i = 0; i < normalized.length; i++) {
|
|
105
|
+
const ch = normalized[i];
|
|
106
|
+
if (/\s/u.test(ch)) {
|
|
107
|
+
if (!wroteWhitespace) {
|
|
108
|
+
result += ' ';
|
|
109
|
+
wroteWhitespace = true;
|
|
110
|
+
}
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const code = ch.codePointAt(0);
|
|
115
|
+
if (typeof code === 'number' && code <= 31) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
result += ch;
|
|
120
|
+
wroteWhitespace = false;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return result.trim();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function removeWhitespace(value) {
|
|
127
|
+
return String(value ?? '').replace(/\s+/gu, '');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function extractTokens(normalizedText) {
|
|
131
|
+
if (!normalizedText) {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const matches = normalizedText.match(TOKEN_REGEX);
|
|
136
|
+
if (!matches || matches.length === 0) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return matches.filter((token) => token.length >= 2);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function buildPrefixes(tokens) {
|
|
144
|
+
const prefixes = [];
|
|
145
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
146
|
+
const token = tokens[i];
|
|
147
|
+
if (!token) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const maxLength = Math.min(8, token.length);
|
|
152
|
+
for (let length = 2; length <= maxLength; length++) {
|
|
153
|
+
prefixes.push(token.slice(0, length));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return prefixes;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function buildNGrams(normalizedCompactText) {
|
|
161
|
+
if (!normalizedCompactText) {
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (normalizedCompactText.length <= NGRAM_SIZE) {
|
|
166
|
+
return [normalizedCompactText];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const ngrams = [];
|
|
170
|
+
for (let i = 0; i <= normalizedCompactText.length - NGRAM_SIZE; i++) {
|
|
171
|
+
ngrams.push(normalizedCompactText.slice(i, i + NGRAM_SIZE));
|
|
172
|
+
}
|
|
173
|
+
return ngrams;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function addPosting(index, key, nodeId) {
|
|
177
|
+
if (!key) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
let postings = index.get(key);
|
|
182
|
+
if (!postings) {
|
|
183
|
+
postings = new Set();
|
|
184
|
+
index.set(key, postings);
|
|
185
|
+
}
|
|
186
|
+
postings.add(nodeId);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function removePosting(index, key, nodeId) {
|
|
190
|
+
const postings = index.get(key);
|
|
191
|
+
if (!postings) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
postings.delete(nodeId);
|
|
196
|
+
if (postings.size === 0) {
|
|
197
|
+
index.delete(key);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function addPostingMatches(index, key, candidateScores, weight) {
|
|
202
|
+
const postings = index.get(key);
|
|
203
|
+
if (!postings) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
postings.forEach((nodeId) => {
|
|
208
|
+
candidateScores.set(
|
|
209
|
+
nodeId,
|
|
210
|
+
Number(candidateScores.get(nodeId) || 0) + weight
|
|
211
|
+
);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function clearState(boardId) {
|
|
216
|
+
runtime = createState();
|
|
217
|
+
runtime.boardId = normalizeBoardId(boardId);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function normalizeNodeSnapshot(node) {
|
|
221
|
+
if (!node || typeof node !== 'object') {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const nodeId = String(node.id ?? node.Id ?? '').trim();
|
|
226
|
+
if (!nodeId) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const contentType = String(node.contentType ?? node.ContentType ?? '').trim().toLowerCase();
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
id: nodeId,
|
|
234
|
+
contentType,
|
|
235
|
+
prompt: limitText(node.prompt ?? node.Prompt, MAX_PROMPT_CHARS),
|
|
236
|
+
response: limitText(node.response ?? node.Response, MAX_RESPONSE_CHARS),
|
|
237
|
+
metadataText: buildMetadataText(node.metadataText ?? node.MetadataText ?? node.metadata ?? node.Metadata),
|
|
238
|
+
sourcePath: limitText(node.sourcePath ?? node.SourcePath ?? node.sourceFilePath ?? node.SourceFilePath, MAX_SOURCE_PATH_CHARS),
|
|
239
|
+
positionX: Number(node.positionX ?? node.PositionX ?? node.x ?? 0),
|
|
240
|
+
positionY: Number(node.positionY ?? node.PositionY ?? node.y ?? 0),
|
|
241
|
+
width: Math.max(1, Number(node.width ?? node.Width ?? 1)),
|
|
242
|
+
height: Math.max(1, Number(node.height ?? node.Height ?? 1))
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function buildDocument(node) {
|
|
247
|
+
const prompt = normalizeText(node.prompt);
|
|
248
|
+
const response = normalizeText(shouldIndexResponse(node.contentType) ? node.response : '');
|
|
249
|
+
const metadataText = normalizeText(node.metadataText);
|
|
250
|
+
const sourcePath = normalizeText(node.sourcePath);
|
|
251
|
+
const normalizedContentType = normalizeText(node.contentType);
|
|
252
|
+
|
|
253
|
+
const combined = [prompt, metadataText, sourcePath, response, normalizedContentType]
|
|
254
|
+
.filter(Boolean)
|
|
255
|
+
.join(' ');
|
|
256
|
+
const tokens = new Set(extractTokens(combined));
|
|
257
|
+
const prefixes = new Set(buildPrefixes(Array.from(tokens)));
|
|
258
|
+
const ngrams = new Set(buildNGrams(removeWhitespace(combined)));
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
nodeId: node.id,
|
|
262
|
+
contentType: node.contentType,
|
|
263
|
+
positionX: node.positionX,
|
|
264
|
+
positionY: node.positionY,
|
|
265
|
+
width: node.width,
|
|
266
|
+
height: node.height,
|
|
267
|
+
normalizedPrompt: prompt,
|
|
268
|
+
normalizedResponse: response,
|
|
269
|
+
normalizedMetadata: metadataText,
|
|
270
|
+
normalizedSourcePath: sourcePath,
|
|
271
|
+
normalizedContentType,
|
|
272
|
+
tokens,
|
|
273
|
+
prefixes,
|
|
274
|
+
ngrams
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function removeDocument(nodeId) {
|
|
279
|
+
const existing = runtime.documents.get(nodeId);
|
|
280
|
+
if (!existing) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
existing.tokens.forEach((token) => removePosting(runtime.tokenIndex, token, nodeId));
|
|
285
|
+
existing.prefixes.forEach((prefix) => removePosting(runtime.prefixIndex, prefix, nodeId));
|
|
286
|
+
existing.ngrams.forEach((ngram) => removePosting(runtime.ngramIndex, ngram, nodeId));
|
|
287
|
+
runtime.documents.delete(nodeId);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function upsertNode(node) {
|
|
291
|
+
const normalizedNode = normalizeNodeSnapshot(node);
|
|
292
|
+
if (!normalizedNode) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
removeDocument(normalizedNode.id);
|
|
297
|
+
|
|
298
|
+
const document = buildDocument(normalizedNode);
|
|
299
|
+
runtime.documents.set(document.nodeId, document);
|
|
300
|
+
|
|
301
|
+
document.tokens.forEach((token) => addPosting(runtime.tokenIndex, token, document.nodeId));
|
|
302
|
+
document.prefixes.forEach((prefix) => addPosting(runtime.prefixIndex, prefix, document.nodeId));
|
|
303
|
+
document.ngrams.forEach((ngram) => addPosting(runtime.ngramIndex, ngram, document.nodeId));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function rebuildBoard(boardId, nodes) {
|
|
307
|
+
clearState(boardId);
|
|
308
|
+
const sourceNodes = Array.isArray(nodes) ? nodes : [];
|
|
309
|
+
for (let i = 0; i < sourceNodes.length; i++) {
|
|
310
|
+
upsertNode(sourceNodes[i]);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function scoreDocument(document, normalizedQuery, queryTokens, queryNGrams) {
|
|
315
|
+
let score = 0;
|
|
316
|
+
|
|
317
|
+
if (document.normalizedPrompt === normalizedQuery) {
|
|
318
|
+
score += 120;
|
|
319
|
+
} else if (document.normalizedPrompt.includes(normalizedQuery)) {
|
|
320
|
+
score += 54;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (document.normalizedMetadata.includes(normalizedQuery)) {
|
|
324
|
+
score += 26;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (document.normalizedSourcePath.includes(normalizedQuery)) {
|
|
328
|
+
score += 18;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (document.normalizedResponse.includes(normalizedQuery)) {
|
|
332
|
+
score += 14;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (document.normalizedContentType.includes(normalizedQuery)) {
|
|
336
|
+
score += 8;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
for (let i = 0; i < queryTokens.length; i++) {
|
|
340
|
+
const token = queryTokens[i];
|
|
341
|
+
if (document.tokens.has(token)) {
|
|
342
|
+
score += 7;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (document.normalizedPrompt.startsWith(token)) {
|
|
346
|
+
score += 10;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
for (let i = 0; i < queryNGrams.length; i++) {
|
|
351
|
+
if (document.ngrams.has(queryNGrams[i])) {
|
|
352
|
+
score += 0.85;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return score;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function calculateViewportBoost(document, camera) {
|
|
360
|
+
if (!camera) {
|
|
361
|
+
return 0;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const cameraZ = Math.max(400, Number(camera.z || 0) || 1200);
|
|
365
|
+
const aspect = Math.max(0.1, Number(camera.aspect || SEARCH_VIEWPORT_ASPECT_RATIO_FALLBACK));
|
|
366
|
+
const halfViewHeight = Math.max(
|
|
367
|
+
480,
|
|
368
|
+
cameraZ * Math.tan((SEARCH_VIEWPORT_FOV_DEGREES * Math.PI / 180) / 2)
|
|
369
|
+
);
|
|
370
|
+
const halfViewWidth = halfViewHeight * aspect;
|
|
371
|
+
|
|
372
|
+
const nodeWidth = Math.max(1, Number(document.width || 1));
|
|
373
|
+
const nodeHeight = Math.max(1, Number(document.height || 1));
|
|
374
|
+
const centerX = Number(document.positionX || 0) + (nodeWidth / 2);
|
|
375
|
+
const centerY = Number(document.positionY || 0) - (nodeHeight / 2);
|
|
376
|
+
const normalizedX = Math.abs(centerX - Number(camera.x || 0)) / Math.max(1, halfViewWidth);
|
|
377
|
+
const normalizedY = Math.abs(centerY - Number(camera.y || 0)) / Math.max(1, halfViewHeight);
|
|
378
|
+
const radialDistance = Math.sqrt((normalizedX * normalizedX) + (normalizedY * normalizedY));
|
|
379
|
+
|
|
380
|
+
if (radialDistance <= 1) {
|
|
381
|
+
return SEARCH_VIEWPORT_IN_VIEW_BONUS + ((1 - radialDistance) * SEARCH_VIEWPORT_CENTER_BOOST_MAX);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (radialDistance <= SEARCH_VIEWPORT_NEAR_MARGIN) {
|
|
385
|
+
const falloff = 1 - ((radialDistance - 1) / Math.max(0.001, SEARCH_VIEWPORT_NEAR_MARGIN - 1));
|
|
386
|
+
return Math.max(0, falloff) * (SEARCH_VIEWPORT_IN_VIEW_BONUS * 0.75);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return 0;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function handleSearchQuery(message) {
|
|
393
|
+
const boardId = normalizeBoardId(message.boardId);
|
|
394
|
+
if (!boardId || boardId !== runtime.boardId) {
|
|
395
|
+
self.postMessage({
|
|
396
|
+
type: 'SEARCH_RESULT',
|
|
397
|
+
boardId,
|
|
398
|
+
seq: Number(message.seq || 0),
|
|
399
|
+
query: String(message.query || ''),
|
|
400
|
+
hits: []
|
|
401
|
+
});
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const startedAt = (typeof performance !== 'undefined' && typeof performance.now === 'function')
|
|
406
|
+
? performance.now()
|
|
407
|
+
: Date.now();
|
|
408
|
+
const normalizedLimit = Math.max(1, Number(message.limit || 30));
|
|
409
|
+
const normalizedQuery = normalizeText(message.query);
|
|
410
|
+
if (!normalizedQuery) {
|
|
411
|
+
self.postMessage({
|
|
412
|
+
type: 'SEARCH_RESULT',
|
|
413
|
+
boardId,
|
|
414
|
+
seq: Number(message.seq || 0),
|
|
415
|
+
query: String(message.query || ''),
|
|
416
|
+
hits: []
|
|
417
|
+
});
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const allowedContentTypes = Array.isArray(message.contentTypes) && message.contentTypes.length > 0
|
|
422
|
+
? new Set(
|
|
423
|
+
message.contentTypes
|
|
424
|
+
.map((value) => String(value || '').trim().toLowerCase())
|
|
425
|
+
.filter(Boolean)
|
|
426
|
+
)
|
|
427
|
+
: null;
|
|
428
|
+
const queryTokens = extractTokens(normalizedQuery);
|
|
429
|
+
const queryPrefixes = buildPrefixes(queryTokens);
|
|
430
|
+
const queryNGrams = buildNGrams(removeWhitespace(normalizedQuery));
|
|
431
|
+
const candidateScores = new Map();
|
|
432
|
+
|
|
433
|
+
for (let i = 0; i < queryTokens.length; i++) {
|
|
434
|
+
addPostingMatches(runtime.tokenIndex, queryTokens[i], candidateScores, 8);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
for (let i = 0; i < queryPrefixes.length; i++) {
|
|
438
|
+
const prefix = queryPrefixes[i];
|
|
439
|
+
addPostingMatches(runtime.prefixIndex, prefix, candidateScores, prefix.length <= 3 ? 4 : 2);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
for (let i = 0; i < queryNGrams.length; i++) {
|
|
443
|
+
addPostingMatches(runtime.ngramIndex, queryNGrams[i], candidateScores, 1.15);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
let candidateIds = [];
|
|
447
|
+
if (candidateScores.size > 0) {
|
|
448
|
+
candidateIds = Array.from(candidateScores.entries())
|
|
449
|
+
.sort((a, b) => {
|
|
450
|
+
if (b[1] !== a[1]) {
|
|
451
|
+
return b[1] - a[1];
|
|
452
|
+
}
|
|
453
|
+
return String(a[0]).localeCompare(String(b[0]));
|
|
454
|
+
})
|
|
455
|
+
.slice(0, Math.max(normalizedLimit * 16, 256))
|
|
456
|
+
.map((entry) => entry[0]);
|
|
457
|
+
} else {
|
|
458
|
+
candidateIds = Array.from(runtime.documents.keys());
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const ranked = [];
|
|
462
|
+
for (let i = 0; i < candidateIds.length; i++) {
|
|
463
|
+
const document = runtime.documents.get(candidateIds[i]);
|
|
464
|
+
if (!document) {
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (allowedContentTypes && allowedContentTypes.size > 0 && !allowedContentTypes.has(document.contentType)) {
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const score = scoreDocument(document, normalizedQuery, queryTokens, queryNGrams);
|
|
473
|
+
if (score <= 0) {
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
ranked.push({
|
|
478
|
+
nodeId: document.nodeId,
|
|
479
|
+
score: score + calculateViewportBoost(document, message.camera || null)
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
ranked.sort((a, b) => {
|
|
484
|
+
if (b.score !== a.score) {
|
|
485
|
+
return b.score - a.score;
|
|
486
|
+
}
|
|
487
|
+
return String(a.nodeId).localeCompare(String(b.nodeId));
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
const completedAt = (typeof performance !== 'undefined' && typeof performance.now === 'function')
|
|
491
|
+
? performance.now()
|
|
492
|
+
: Date.now();
|
|
493
|
+
|
|
494
|
+
self.postMessage({
|
|
495
|
+
type: 'SEARCH_RESULT',
|
|
496
|
+
boardId,
|
|
497
|
+
seq: Number(message.seq || 0),
|
|
498
|
+
query: String(message.query || ''),
|
|
499
|
+
hits: ranked.slice(0, normalizedLimit),
|
|
500
|
+
profile: {
|
|
501
|
+
durationMs: Number(Math.max(0, completedAt - startedAt).toFixed(2)),
|
|
502
|
+
candidateCount: candidateIds.length,
|
|
503
|
+
rankedCount: ranked.length
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
self.onmessage = function (event) {
|
|
509
|
+
const message = event?.data || null;
|
|
510
|
+
if (!message || typeof message !== 'object') {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
switch (message.type) {
|
|
515
|
+
case 'RESET_BOARD':
|
|
516
|
+
clearState(message.boardId);
|
|
517
|
+
break;
|
|
518
|
+
case 'REBUILD_BOARD':
|
|
519
|
+
rebuildBoard(message.boardId, message.nodes);
|
|
520
|
+
break;
|
|
521
|
+
case 'UPSERT_NODE':
|
|
522
|
+
if (normalizeBoardId(message.boardId) === runtime.boardId) {
|
|
523
|
+
upsertNode(message.node);
|
|
524
|
+
}
|
|
525
|
+
break;
|
|
526
|
+
case 'REMOVE_NODE':
|
|
527
|
+
if (normalizeBoardId(message.boardId) === runtime.boardId) {
|
|
528
|
+
removeDocument(String(message.nodeId || '').trim());
|
|
529
|
+
}
|
|
530
|
+
break;
|
|
531
|
+
case 'BATCH_SYNC':
|
|
532
|
+
if (normalizeBoardId(message.boardId) === runtime.boardId) {
|
|
533
|
+
const removedIds = Array.isArray(message.removedIds) ? message.removedIds : [];
|
|
534
|
+
for (let i = 0; i < removedIds.length; i++) {
|
|
535
|
+
removeDocument(String(removedIds[i] || '').trim());
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const nodes = Array.isArray(message.nodes) ? message.nodes : [];
|
|
539
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
540
|
+
upsertNode(nodes[i]);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
break;
|
|
544
|
+
case 'SEARCH_QUERY':
|
|
545
|
+
handleSearchQuery(message);
|
|
546
|
+
break;
|
|
547
|
+
default:
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
self.postMessage({
|
|
553
|
+
type: 'READY'
|
|
554
|
+
});
|