@swarmvaultai/viewer 0.7.30 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/assets/index-CRcCxyS8.css +1 -0
- package/dist/assets/index-QQ74kUX8.js +368 -0
- package/dist/index.html +2 -2
- package/dist/lib.d.ts +58 -1
- package/dist/lib.js +196 -0
- package/package.json +19 -11
- package/dist/assets/index-BHjjw4rU.css +0 -1
- package/dist/assets/index-DxKn2KOc.js +0 -331
package/dist/index.html
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>SwarmVault Graph</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-QQ74kUX8.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CRcCxyS8.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
package/dist/lib.d.ts
CHANGED
|
@@ -172,6 +172,29 @@ type ViewerApprovalSummary = {
|
|
|
172
172
|
acceptedCount: number;
|
|
173
173
|
rejectedCount: number;
|
|
174
174
|
};
|
|
175
|
+
type ViewerApprovalDiffLine = {
|
|
176
|
+
type: "add" | "remove" | "context";
|
|
177
|
+
value: string;
|
|
178
|
+
};
|
|
179
|
+
type ViewerApprovalDiffHunk = {
|
|
180
|
+
oldStart: number;
|
|
181
|
+
oldLines: number;
|
|
182
|
+
newStart: number;
|
|
183
|
+
newLines: number;
|
|
184
|
+
lines: ViewerApprovalDiffLine[];
|
|
185
|
+
};
|
|
186
|
+
type ViewerApprovalFrontmatterChange = {
|
|
187
|
+
key: string;
|
|
188
|
+
before?: unknown;
|
|
189
|
+
after?: unknown;
|
|
190
|
+
protected: boolean;
|
|
191
|
+
};
|
|
192
|
+
type ViewerApprovalStructuredDiff = {
|
|
193
|
+
hunks: ViewerApprovalDiffHunk[];
|
|
194
|
+
addedLines: number;
|
|
195
|
+
removedLines: number;
|
|
196
|
+
frontmatterChanges: ViewerApprovalFrontmatterChange[];
|
|
197
|
+
};
|
|
175
198
|
type ViewerApprovalEntry = {
|
|
176
199
|
pageId: string;
|
|
177
200
|
title: string;
|
|
@@ -183,6 +206,9 @@ type ViewerApprovalEntry = {
|
|
|
183
206
|
previousPath?: string;
|
|
184
207
|
currentContent?: string;
|
|
185
208
|
stagedContent?: string;
|
|
209
|
+
diff?: string;
|
|
210
|
+
structuredDiff?: ViewerApprovalStructuredDiff;
|
|
211
|
+
warnings?: string[];
|
|
186
212
|
};
|
|
187
213
|
type ViewerApprovalDetail = ViewerApprovalSummary & {
|
|
188
214
|
entries: ViewerApprovalEntry[];
|
|
@@ -199,6 +225,26 @@ type ViewerCandidateRecord = {
|
|
|
199
225
|
sourceIds: string[];
|
|
200
226
|
createdAt: string;
|
|
201
227
|
updatedAt: string;
|
|
228
|
+
score?: number;
|
|
229
|
+
scoreBreakdown?: Record<string, number>;
|
|
230
|
+
};
|
|
231
|
+
type ViewerLintFinding = {
|
|
232
|
+
id: string;
|
|
233
|
+
severity: "error" | "warning" | "info";
|
|
234
|
+
category: string;
|
|
235
|
+
message: string;
|
|
236
|
+
pageId?: string;
|
|
237
|
+
pagePath?: string;
|
|
238
|
+
nodeId?: string;
|
|
239
|
+
detectedAt?: string;
|
|
240
|
+
};
|
|
241
|
+
type ViewerWorkspaceBundle = {
|
|
242
|
+
graph: ViewerGraphArtifact;
|
|
243
|
+
approvals: ViewerApprovalSummary[];
|
|
244
|
+
candidates: ViewerCandidateRecord[];
|
|
245
|
+
watchStatus: ViewerWatchStatus;
|
|
246
|
+
graphReport: ViewerGraphReport | null;
|
|
247
|
+
lintFindings: ViewerLintFinding[];
|
|
202
248
|
};
|
|
203
249
|
type ViewerWatchStatus = {
|
|
204
250
|
generatedAt: string;
|
|
@@ -327,5 +373,16 @@ declare function applyReviewAction(approvalId: string, action: "accept" | "rejec
|
|
|
327
373
|
declare function fetchCandidates(): Promise<ViewerCandidateRecord[]>;
|
|
328
374
|
declare function applyCandidateAction(target: string, action: "promote" | "archive"): Promise<ViewerCandidateRecord>;
|
|
329
375
|
declare function fetchWatchStatus(): Promise<ViewerWatchStatus>;
|
|
376
|
+
declare function fetchLintFindings(): Promise<ViewerLintFinding[]>;
|
|
377
|
+
declare function fetchWorkspaceBundle(): Promise<ViewerWorkspaceBundle | null>;
|
|
378
|
+
type SubgraphExportPayload = {
|
|
379
|
+
generatedAt: string;
|
|
380
|
+
rootNodeId?: string;
|
|
381
|
+
nodes: ViewerGraphNode[];
|
|
382
|
+
edges: ViewerGraphEdge[];
|
|
383
|
+
};
|
|
384
|
+
declare function buildSubgraphExport(graph: ViewerGraphArtifact, nodeIds: string[]): SubgraphExportPayload;
|
|
385
|
+
declare function downloadDataUrl(filename: string, dataUrl: string): void;
|
|
386
|
+
declare function downloadText(filename: string, text: string, mime?: string): void;
|
|
330
387
|
|
|
331
|
-
export { type ViewerApprovalDetail, type ViewerApprovalEntry, type ViewerApprovalSummary, type ViewerCandidateRecord, type ViewerGraphArtifact, type ViewerGraphEdge, type ViewerGraphExplainResult, type ViewerGraphHyperedge, type ViewerGraphNode, type ViewerGraphPage, type ViewerGraphPathResult, type ViewerGraphQueryResult, type ViewerGraphReport, type ViewerOutputAsset, type ViewerPagePayload, type ViewerReviewActionResult, type ViewerSearchOptions, type ViewerSearchResult, type ViewerWatchStatus, applyCandidateAction, applyReviewAction, fetchApprovalDetail, fetchApprovals, fetchCandidates, fetchGraphArtifact, fetchGraphExplain, fetchGraphPath, fetchGraphQuery, fetchGraphReport, fetchViewerPage, fetchWatchStatus, searchViewerPages };
|
|
388
|
+
export { type SubgraphExportPayload, type ViewerApprovalDetail, type ViewerApprovalDiffHunk, type ViewerApprovalDiffLine, type ViewerApprovalEntry, type ViewerApprovalFrontmatterChange, type ViewerApprovalStructuredDiff, type ViewerApprovalSummary, type ViewerCandidateRecord, type ViewerGraphArtifact, type ViewerGraphEdge, type ViewerGraphExplainResult, type ViewerGraphHyperedge, type ViewerGraphNode, type ViewerGraphPage, type ViewerGraphPathResult, type ViewerGraphQueryResult, type ViewerGraphReport, type ViewerLintFinding, type ViewerOutputAsset, type ViewerPagePayload, type ViewerReviewActionResult, type ViewerSearchOptions, type ViewerSearchResult, type ViewerWatchStatus, type ViewerWorkspaceBundle, applyCandidateAction, applyReviewAction, buildSubgraphExport, downloadDataUrl, downloadText, fetchApprovalDetail, fetchApprovals, fetchCandidates, fetchGraphArtifact, fetchGraphExplain, fetchGraphPath, fetchGraphQuery, fetchGraphReport, fetchLintFindings, fetchViewerPage, fetchWatchStatus, fetchWorkspaceBundle, searchViewerPages };
|
package/dist/lib.js
CHANGED
|
@@ -172,6 +172,142 @@ function explainEmbeddedGraphTarget(graph, target) {
|
|
|
172
172
|
].join("\n")
|
|
173
173
|
};
|
|
174
174
|
}
|
|
175
|
+
function uniqueByKey(items, key) {
|
|
176
|
+
const seen = /* @__PURE__ */ new Set();
|
|
177
|
+
const result = [];
|
|
178
|
+
for (const item of items) {
|
|
179
|
+
const k = key(item);
|
|
180
|
+
if (seen.has(k)) continue;
|
|
181
|
+
seen.add(k);
|
|
182
|
+
result.push(item);
|
|
183
|
+
}
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
function embeddedScoreMatch(query, candidate) {
|
|
187
|
+
const q = query.trim().toLowerCase();
|
|
188
|
+
const c = candidate.trim().toLowerCase();
|
|
189
|
+
if (!q || !c) return 0;
|
|
190
|
+
if (c === q) return 100;
|
|
191
|
+
if (c.startsWith(q)) return 80;
|
|
192
|
+
if (c.includes(q)) return 60;
|
|
193
|
+
const qTokens = q.split(/\s+/).filter(Boolean);
|
|
194
|
+
const cTokens = new Set(c.split(/\s+/).filter(Boolean));
|
|
195
|
+
const overlap = qTokens.filter((token) => cTokens.has(token)).length;
|
|
196
|
+
return overlap ? overlap * 10 : 0;
|
|
197
|
+
}
|
|
198
|
+
function embeddedGraphQuery(graph, question, searchResults, options) {
|
|
199
|
+
const traversal = options?.traversal ?? "bfs";
|
|
200
|
+
const budget = Math.max(3, Math.min(options?.budget ?? 12, 50));
|
|
201
|
+
const pagesById = new Map((graph.pages ?? []).map((page) => [page.id, page]));
|
|
202
|
+
const pageMatchesRaw = searchResults.map((result) => {
|
|
203
|
+
const page = pagesById.get(result.pageId);
|
|
204
|
+
const score = Math.max(embeddedScoreMatch(question, result.title), embeddedScoreMatch(question, result.path));
|
|
205
|
+
if (!page || score <= 0) return null;
|
|
206
|
+
return { type: "page", id: page.id, label: page.title, score };
|
|
207
|
+
});
|
|
208
|
+
const pageMatches = pageMatchesRaw.filter(
|
|
209
|
+
(match) => match !== null
|
|
210
|
+
);
|
|
211
|
+
const nodeMatches = graph.nodes.map((node) => ({
|
|
212
|
+
type: "node",
|
|
213
|
+
id: node.id,
|
|
214
|
+
label: node.label,
|
|
215
|
+
score: Math.max(embeddedScoreMatch(question, node.label), embeddedScoreMatch(question, node.id))
|
|
216
|
+
})).filter((match) => match.score > 0);
|
|
217
|
+
const hyperedgeMatches = (graph.hyperedges ?? []).map((hyperedge) => ({
|
|
218
|
+
type: "hyperedge",
|
|
219
|
+
id: hyperedge.id,
|
|
220
|
+
label: hyperedge.label,
|
|
221
|
+
score: Math.max(
|
|
222
|
+
embeddedScoreMatch(question, hyperedge.label),
|
|
223
|
+
embeddedScoreMatch(question, hyperedge.why),
|
|
224
|
+
embeddedScoreMatch(question, hyperedge.relation)
|
|
225
|
+
)
|
|
226
|
+
})).filter((match) => match.score > 0);
|
|
227
|
+
const matches = uniqueByKey([...pageMatches, ...nodeMatches, ...hyperedgeMatches], (match) => `${match.type}:${match.id}`).sort((left, right) => right.score - left.score || left.label.localeCompare(right.label)).slice(0, 12);
|
|
228
|
+
const seeds = uniqueByKey(
|
|
229
|
+
[
|
|
230
|
+
...searchResults.flatMap((result) => pagesById.get(result.pageId)?.nodeIds ?? []),
|
|
231
|
+
...matches.filter((match) => match.type === "page").flatMap((match) => pagesById.get(match.id)?.nodeIds ?? []),
|
|
232
|
+
...matches.filter((match) => match.type === "node").map((match) => match.id),
|
|
233
|
+
...matches.filter((match) => match.type === "hyperedge").flatMap((match) => (graph.hyperedges ?? []).find((hyperedge) => hyperedge.id === match.id)?.nodeIds ?? [])
|
|
234
|
+
],
|
|
235
|
+
(item) => item
|
|
236
|
+
).filter(Boolean);
|
|
237
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
238
|
+
const pushNeighbor = (nodeId, neighbor) => {
|
|
239
|
+
if (!adjacency.has(nodeId)) adjacency.set(nodeId, []);
|
|
240
|
+
adjacency.get(nodeId)?.push(neighbor);
|
|
241
|
+
};
|
|
242
|
+
for (const edge of graph.edges) {
|
|
243
|
+
pushNeighbor(edge.source, { edge, nodeId: edge.target, direction: "outgoing" });
|
|
244
|
+
pushNeighbor(edge.target, { edge, nodeId: edge.source, direction: "incoming" });
|
|
245
|
+
}
|
|
246
|
+
for (const [nodeId, items] of adjacency.entries()) {
|
|
247
|
+
items.sort(
|
|
248
|
+
(left, right) => (right.edge.confidence ?? 0) - (left.edge.confidence ?? 0) || left.edge.relation.localeCompare(right.edge.relation)
|
|
249
|
+
);
|
|
250
|
+
adjacency.set(nodeId, items);
|
|
251
|
+
}
|
|
252
|
+
const visitedNodeIds = [];
|
|
253
|
+
const visitedEdgeIds = /* @__PURE__ */ new Set();
|
|
254
|
+
const seen = /* @__PURE__ */ new Set();
|
|
255
|
+
const frontier = [...seeds];
|
|
256
|
+
while (frontier.length && visitedNodeIds.length < budget) {
|
|
257
|
+
const current = traversal === "dfs" ? frontier.pop() : frontier.shift();
|
|
258
|
+
if (!current || seen.has(current)) continue;
|
|
259
|
+
seen.add(current);
|
|
260
|
+
visitedNodeIds.push(current);
|
|
261
|
+
for (const neighbor of adjacency.get(current) ?? []) {
|
|
262
|
+
visitedEdgeIds.add(neighbor.edge.id);
|
|
263
|
+
if (!seen.has(neighbor.nodeId)) frontier.push(neighbor.nodeId);
|
|
264
|
+
if (visitedNodeIds.length + frontier.length >= budget * 2) break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
const nodesById = new Map(graph.nodes.map((node) => [node.id, node]));
|
|
268
|
+
const pageIds = uniqueByKey(
|
|
269
|
+
[
|
|
270
|
+
...searchResults.map((result) => result.pageId),
|
|
271
|
+
...matches.filter((match) => match.type === "page").map((match) => match.id),
|
|
272
|
+
...visitedNodeIds.flatMap((nodeId) => {
|
|
273
|
+
const node = nodesById.get(nodeId);
|
|
274
|
+
return node?.pageId ? [node.pageId] : [];
|
|
275
|
+
})
|
|
276
|
+
],
|
|
277
|
+
(item) => item
|
|
278
|
+
);
|
|
279
|
+
const communities = uniqueByKey(
|
|
280
|
+
visitedNodeIds.map((nodeId) => nodesById.get(nodeId)?.communityId).filter((communityId) => Boolean(communityId)),
|
|
281
|
+
(item) => item
|
|
282
|
+
);
|
|
283
|
+
const hyperedgeIds = uniqueByKey(
|
|
284
|
+
(graph.hyperedges ?? []).filter((hyperedge) => hyperedge.nodeIds.some((nodeId) => visitedNodeIds.includes(nodeId))).map((hyperedge) => hyperedge.id),
|
|
285
|
+
(item) => item
|
|
286
|
+
);
|
|
287
|
+
return {
|
|
288
|
+
question,
|
|
289
|
+
traversal,
|
|
290
|
+
seedNodeIds: seeds,
|
|
291
|
+
seedPageIds: uniqueByKey(
|
|
292
|
+
[...searchResults.map((result) => result.pageId), ...matches.filter((match) => match.type === "page").map((match) => match.id)],
|
|
293
|
+
(item) => item
|
|
294
|
+
),
|
|
295
|
+
visitedNodeIds,
|
|
296
|
+
visitedEdgeIds: [...visitedEdgeIds],
|
|
297
|
+
hyperedgeIds,
|
|
298
|
+
pageIds,
|
|
299
|
+
communities,
|
|
300
|
+
matches,
|
|
301
|
+
summary: [
|
|
302
|
+
`Seeds: ${seeds.join(", ") || "none"}`,
|
|
303
|
+
`Visited nodes: ${visitedNodeIds.length}`,
|
|
304
|
+
`Visited edges: ${visitedEdgeIds.size}`,
|
|
305
|
+
`Touched group patterns: ${hyperedgeIds.length}`,
|
|
306
|
+
`Communities: ${communities.join(", ") || "none"}`,
|
|
307
|
+
`Pages: ${pageIds.join(", ") || "none"}`
|
|
308
|
+
].join("\n")
|
|
309
|
+
};
|
|
310
|
+
}
|
|
175
311
|
function normalizeSnippet(content, query) {
|
|
176
312
|
const normalized = content.replace(/\s+/g, " ").trim();
|
|
177
313
|
if (!query) {
|
|
@@ -273,6 +409,11 @@ async function fetchViewerPage(path) {
|
|
|
273
409
|
return response.json();
|
|
274
410
|
}
|
|
275
411
|
async function fetchGraphQuery(question, options = {}) {
|
|
412
|
+
const embedded = embeddedData();
|
|
413
|
+
if (embedded) {
|
|
414
|
+
const searchResults = await searchViewerPages(question, { limit: 10 });
|
|
415
|
+
return embeddedGraphQuery(embedded.graph, question, searchResults, options);
|
|
416
|
+
}
|
|
276
417
|
const params = new URLSearchParams({
|
|
277
418
|
q: question,
|
|
278
419
|
traversal: options.traversal ?? "bfs",
|
|
@@ -396,9 +537,62 @@ async function fetchWatchStatus() {
|
|
|
396
537
|
}
|
|
397
538
|
return response.json();
|
|
398
539
|
}
|
|
540
|
+
async function fetchLintFindings() {
|
|
541
|
+
if (embeddedData()) {
|
|
542
|
+
return [];
|
|
543
|
+
}
|
|
544
|
+
const response = await fetch("/api/lint");
|
|
545
|
+
if (response.status === 404) {
|
|
546
|
+
return [];
|
|
547
|
+
}
|
|
548
|
+
if (!response.ok) {
|
|
549
|
+
throw new Error(`Failed to load lint findings: ${response.status} ${response.statusText}`);
|
|
550
|
+
}
|
|
551
|
+
return response.json();
|
|
552
|
+
}
|
|
553
|
+
async function fetchWorkspaceBundle() {
|
|
554
|
+
if (embeddedData()) {
|
|
555
|
+
return null;
|
|
556
|
+
}
|
|
557
|
+
const response = await fetch("/api/workspace");
|
|
558
|
+
if (response.status === 404) return null;
|
|
559
|
+
if (!response.ok) {
|
|
560
|
+
throw new Error(`Failed to load workspace bundle: ${response.status} ${response.statusText}`);
|
|
561
|
+
}
|
|
562
|
+
return response.json();
|
|
563
|
+
}
|
|
564
|
+
function buildSubgraphExport(graph, nodeIds) {
|
|
565
|
+
const nodeSet = new Set(nodeIds);
|
|
566
|
+
const nodes = graph.nodes.filter((node) => nodeSet.has(node.id));
|
|
567
|
+
const edges = graph.edges.filter((edge) => nodeSet.has(edge.source) && nodeSet.has(edge.target));
|
|
568
|
+
return {
|
|
569
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
570
|
+
rootNodeId: nodeIds[0],
|
|
571
|
+
nodes,
|
|
572
|
+
edges
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
function downloadDataUrl(filename, dataUrl) {
|
|
576
|
+
if (typeof document === "undefined") return;
|
|
577
|
+
const link = document.createElement("a");
|
|
578
|
+
link.href = dataUrl;
|
|
579
|
+
link.download = filename;
|
|
580
|
+
document.body.appendChild(link);
|
|
581
|
+
link.click();
|
|
582
|
+
document.body.removeChild(link);
|
|
583
|
+
}
|
|
584
|
+
function downloadText(filename, text, mime = "text/plain") {
|
|
585
|
+
const blob = new Blob([text], { type: mime });
|
|
586
|
+
const url = URL.createObjectURL(blob);
|
|
587
|
+
downloadDataUrl(filename, url);
|
|
588
|
+
setTimeout(() => URL.revokeObjectURL(url), 1500);
|
|
589
|
+
}
|
|
399
590
|
export {
|
|
400
591
|
applyCandidateAction,
|
|
401
592
|
applyReviewAction,
|
|
593
|
+
buildSubgraphExport,
|
|
594
|
+
downloadDataUrl,
|
|
595
|
+
downloadText,
|
|
402
596
|
fetchApprovalDetail,
|
|
403
597
|
fetchApprovals,
|
|
404
598
|
fetchCandidates,
|
|
@@ -407,7 +601,9 @@ export {
|
|
|
407
601
|
fetchGraphPath,
|
|
408
602
|
fetchGraphQuery,
|
|
409
603
|
fetchGraphReport,
|
|
604
|
+
fetchLintFindings,
|
|
410
605
|
fetchViewerPage,
|
|
411
606
|
fetchWatchStatus,
|
|
607
|
+
fetchWorkspaceBundle,
|
|
412
608
|
searchViewerPages
|
|
413
609
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarmvaultai/viewer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Graph viewer package for SwarmVault graph artifacts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/lib.js",
|
|
@@ -37,20 +37,21 @@
|
|
|
37
37
|
"engines": {
|
|
38
38
|
"node": ">=24.0.0"
|
|
39
39
|
},
|
|
40
|
-
"scripts": {
|
|
41
|
-
"build": "pnpm run build:lib && pnpm run build:app",
|
|
42
|
-
"build:lib": "tsup src/lib.ts --format esm --dts --out-dir dist --clean",
|
|
43
|
-
"build:app": "vite build",
|
|
44
|
-
"test": "vitest run",
|
|
45
|
-
"typecheck": "tsc --noEmit",
|
|
46
|
-
"prepublishOnly": "node ../../scripts/check-release-sync.mjs && node ../../scripts/check-published-manifests.mjs"
|
|
47
|
-
},
|
|
48
40
|
"dependencies": {
|
|
49
41
|
"cytoscape": "^3.33.1",
|
|
42
|
+
"highlight.js": "^11.10.0",
|
|
50
43
|
"react": "^19.1.1",
|
|
51
|
-
"react-dom": "^19.1.1"
|
|
44
|
+
"react-dom": "^19.1.1",
|
|
45
|
+
"react-markdown": "^9.0.1",
|
|
46
|
+
"rehype-highlight": "^7.0.0",
|
|
47
|
+
"rehype-slug": "^6.0.0",
|
|
48
|
+
"remark-gfm": "^4.0.0"
|
|
52
49
|
},
|
|
53
50
|
"devDependencies": {
|
|
51
|
+
"@testing-library/dom": "^10.4.0",
|
|
52
|
+
"@testing-library/jest-dom": "^6.5.0",
|
|
53
|
+
"@testing-library/react": "^16.0.1",
|
|
54
|
+
"@testing-library/user-event": "^14.5.2",
|
|
54
55
|
"@types/node": "^24.6.0",
|
|
55
56
|
"@types/react": "^19.1.13",
|
|
56
57
|
"@types/react-dom": "^19.1.9",
|
|
@@ -60,5 +61,12 @@
|
|
|
60
61
|
"typescript": "^5.9.2",
|
|
61
62
|
"vite": "^7.1.7",
|
|
62
63
|
"vitest": "^3.2.4"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "pnpm run build:lib && pnpm run build:app",
|
|
67
|
+
"build:lib": "tsup src/lib.ts --format esm --dts --out-dir dist --clean",
|
|
68
|
+
"build:app": "vite build",
|
|
69
|
+
"test": "vitest run",
|
|
70
|
+
"typecheck": "tsc --noEmit"
|
|
63
71
|
}
|
|
64
|
-
}
|
|
72
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{--c-bg-base: #020617;--c-bg-surface: #0c1222;--c-bg-elevated: rgba(15, 23, 42, .88);--c-bg-inset: rgba(2, 6, 23, .82);--c-bg-input: #070d1a;--c-border: rgba(148, 163, 184, .1);--c-border-subtle: rgba(148, 163, 184, .06);--c-border-focus: rgba(125, 211, 252, .28);--c-border-danger: rgba(248, 113, 113, .24);--c-border-warning: rgba(251, 191, 36, .2);--c-text-primary: #e2e8f0;--c-text-secondary: #94a3b8;--c-text-muted: #64748b;--c-text-accent: #7dd3fc;--c-text-error: #f87171;--c-text-warning: #fbbf24;--c-accent-bg: rgba(14, 165, 233, .08);--c-danger-bg: rgba(127, 29, 29, .18);--font-sans: "Inter", "Avenir Next", "Segoe UI", system-ui, sans-serif;--font-mono: "IBM Plex Mono", "SF Mono", "Fira Code", monospace;--text-2xs: .6875rem;--text-xs: .75rem;--text-sm: .8125rem;--text-base: .9375rem;--text-lg: 1.0625rem;--text-xl: 1.25rem;--sp-1: 4px;--sp-2: 8px;--sp-3: 12px;--sp-4: 16px;--sp-5: 20px;--sp-6: 24px;--radius-sm: 4px;--radius-md: 6px;--radius-lg: 10px;--sidebar-width: 220px;--rail-width: 400px;--bar-height: 42px;color-scheme:dark;font-family:var(--font-sans);background:var(--c-bg-base);color:var(--c-text-primary)}*,*:before,*:after{box-sizing:border-box}body{margin:0;height:100vh;overflow:hidden;font-size:var(--text-sm);line-height:1.5;-webkit-font-smoothing:antialiased}#root{height:100vh;overflow:hidden}.app-shell{display:grid;grid-template-columns:var(--sidebar-width) minmax(0,1fr) var(--rail-width);grid-template-rows:var(--bar-height) minmax(0,1fr);grid-template-areas:"bar bar bar" "sidebar center rail";height:100vh;overflow:hidden}.app-bar{grid-area:bar;display:flex;align-items:center;gap:var(--sp-3);padding:0 var(--sp-5);background:var(--c-bg-surface);border-bottom:1px solid var(--c-border);z-index:10}.app-bar-title{font-size:var(--text-sm);font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--c-text-accent)}.app-bar-subtitle{font-size:var(--text-xs);color:var(--c-text-muted);letter-spacing:.04em}.sidebar{grid-area:sidebar;overflow-y:auto;padding:var(--sp-3);background:var(--c-bg-surface);border-right:1px solid var(--c-border);display:flex;flex-direction:column;gap:var(--sp-3);scrollbar-width:thin;scrollbar-color:var(--c-text-muted) transparent}.sidebar-section{display:flex;flex-direction:column;gap:var(--sp-2)}.sidebar-heading{font-size:var(--text-2xs);font-weight:600;text-transform:uppercase;letter-spacing:.1em;color:var(--c-text-muted);padding:var(--sp-1) 0;border-bottom:1px solid var(--c-border-subtle);margin-bottom:var(--sp-1)}.sidebar-section-toggle{all:unset;display:flex;align-items:center;gap:var(--sp-2);width:100%;cursor:pointer;font-size:var(--text-2xs);font-weight:600;text-transform:uppercase;letter-spacing:.1em;color:var(--c-text-muted);padding:var(--sp-1) 0;border-bottom:1px solid var(--c-border-subtle);margin-bottom:var(--sp-1);transition:color .15s}.sidebar-section-toggle:hover{color:var(--c-text-secondary)}.sidebar-section-toggle:before{content:"▸";font-size:.6em;transition:transform .2s ease;display:inline-block}.sidebar-section-toggle.is-expanded:before{transform:rotate(90deg)}.sidebar-section-toggle .filter-badge{margin-left:auto;font-family:var(--font-mono);font-size:var(--text-2xs);color:var(--c-text-accent);font-weight:500}.sidebar-section-body{display:grid;gap:var(--sp-2);max-height:0;overflow:hidden;transition:max-height .25s ease,opacity .2s ease;opacity:0}.sidebar-section-body.is-expanded{max-height:600px;opacity:1}.filter-group{display:flex;flex-direction:column;gap:3px}.filter-label{font-size:var(--text-2xs);text-transform:uppercase;letter-spacing:.06em;color:var(--c-text-muted)}.center-area{grid-area:center;display:grid;grid-template-rows:auto minmax(0,1fr) auto;overflow:hidden}.stats-strip{display:flex;gap:var(--sp-2);padding:var(--sp-2) var(--sp-3);border-bottom:1px solid var(--c-border-subtle);background:var(--c-bg-surface);align-items:center}.stats-group{display:flex;gap:var(--sp-3);align-items:baseline}.stats-divider{width:1px;height:14px;background:var(--c-border);align-self:center;flex-shrink:0}.stat{display:flex;align-items:baseline;gap:var(--sp-1)}.stat-label{font-size:var(--text-2xs);color:var(--c-text-muted);text-transform:uppercase;letter-spacing:.06em}.stat-value{font-family:var(--font-mono);font-size:var(--text-sm);color:var(--c-text-primary);font-weight:500}.overview-banner{display:flex;align-items:center;gap:var(--sp-2);padding:var(--sp-2) var(--sp-3);border-bottom:1px solid var(--c-border-subtle);background:#fbbf2414;color:var(--c-text-secondary);font-size:var(--text-xs);letter-spacing:.02em}.overview-banner code{font-family:var(--font-mono);font-size:inherit;color:var(--c-text-warning)}.canvas{min-height:0;background:radial-gradient(ellipse at 50% 40%,rgba(14,165,233,.025),transparent 65%),radial-gradient(circle at 1px 1px,rgba(148,163,184,.045) 1px,transparent 0),var(--c-bg-base);background-size:100% 100%,28px 28px,100% 100%}.canvas-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--sp-3)}.loading-text{font-size:var(--text-base);color:var(--c-text-muted);letter-spacing:.06em;font-weight:400;animation:pulse-fade 2s ease-in-out infinite}@keyframes pulse-fade{0%,to{opacity:.35}50%{opacity:.85}}.canvas-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--sp-2);color:var(--c-text-muted)}.canvas-empty-icon{font-size:2rem;opacity:.4}.report-tabs{border-top:1px solid var(--c-border);background:var(--c-bg-surface);max-height:360px;overflow:hidden}.report-tabs .tabs{border:none;border-radius:0;background:transparent}.report-tabs .tab-panel{max-height:310px;overflow-y:auto}.report-tabs-empty{padding:var(--sp-3);text-align:center;color:var(--c-text-muted);font-size:var(--text-sm)}.report-stats{display:flex;gap:var(--sp-4);font-size:var(--text-sm);color:var(--c-text-secondary)}.report-stats strong{font-family:var(--font-mono);color:var(--c-text-primary);margin-left:var(--sp-1)}.surprise-row{display:flex;align-items:center;gap:var(--sp-2);flex-wrap:wrap}.detail-rail{grid-area:rail;overflow-y:auto;padding:var(--sp-3);background:var(--c-bg-surface);border-left:1px solid var(--c-border);display:flex;flex-direction:column;gap:var(--sp-3);scrollbar-width:thin;scrollbar-color:var(--c-text-muted) transparent}.tabs{border-radius:var(--radius-lg);border:1px solid var(--c-border);background:var(--c-bg-elevated);overflow:hidden}.tab-bar{display:flex;border-bottom:1px solid var(--c-border);padding:0 var(--sp-1);gap:0}.tab-btn{all:unset;padding:var(--sp-2) var(--sp-3);font-size:var(--text-xs);text-transform:uppercase;letter-spacing:.06em;color:var(--c-text-muted);cursor:pointer;border-bottom:2px solid transparent;transition:color .12s,border-color .12s;white-space:nowrap}.tab-btn.is-active{color:var(--c-text-primary);border-bottom-color:var(--c-text-accent)}.tab-btn:hover:not(.is-active){color:var(--c-text-secondary)}.tab-btn:focus-visible{outline:2px solid var(--c-border-focus);outline-offset:-2px;border-radius:var(--radius-sm)}.tab-count{margin-left:var(--sp-1);font-family:var(--font-mono);font-size:var(--text-2xs);color:var(--c-text-accent)}.tab-panel{padding:var(--sp-3);max-height:320px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--c-text-muted) transparent}.panel{background:var(--c-bg-elevated);border:1px solid var(--c-border);border-radius:var(--radius-lg);padding:var(--sp-3);transition:border-color .15s}.panel-heading{margin:0 0 var(--sp-2);font-size:var(--text-base);font-weight:700;text-transform:none;letter-spacing:.01em;color:var(--c-text-primary)}.page-panel{min-height:120px}.card-list{display:grid;gap:var(--sp-2)}.card{background:var(--c-bg-inset);border:1px solid var(--c-border);border-radius:var(--radius-md);padding:var(--sp-2) var(--sp-3);display:flex;flex-direction:column;gap:4px}.card>.input+.input,.card>.input+.btn,.card>.btn+.input{margin-top:2px}.card-warning{border-color:var(--c-border-warning)}.card-title{font-size:var(--text-sm);font-weight:600;color:var(--c-text-primary);margin:0;line-height:1.3}.result-card{all:unset;display:flex;flex-direction:column;gap:3px;background:var(--c-bg-inset);border:1px solid var(--c-border);border-radius:var(--radius-md);padding:var(--sp-2) var(--sp-3);cursor:pointer;transition:border-color .12s,background .12s}.result-card:hover{border-color:var(--c-border-focus);background:#0ea5e908}.result-card.is-active{border-color:var(--c-border-focus);background:#0ea5e90f}.input{background:var(--c-bg-input);color:var(--c-text-primary);border:1px solid var(--c-border);border-radius:var(--radius-sm);padding:6px var(--sp-2);font-family:var(--font-sans);font-size:var(--text-sm);width:100%;outline:none;transition:border-color .12s}.input:focus{border-color:var(--c-border-focus)}.input::placeholder{color:var(--c-text-muted)}select.input{appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%2364748b'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 8px center;padding-right:24px;text-overflow:ellipsis}.btn{all:unset;display:inline-flex;align-items:center;gap:var(--sp-1);height:28px;padding:0 var(--sp-3);font-size:var(--text-xs);font-family:var(--font-sans);border-radius:var(--radius-sm);cursor:pointer;border:1px solid var(--c-border);background:transparent;color:var(--c-text-primary);transition:border-color .12s,background .12s;white-space:nowrap}.btn:hover{border-color:var(--c-border-focus);background:var(--c-accent-bg)}.btn:focus-visible{outline:2px solid var(--c-border-focus);outline-offset:1px}.btn:disabled{opacity:.35;cursor:default;pointer-events:none}.btn-primary{border-color:var(--c-border-focus);background:var(--c-accent-bg)}.btn-danger{border-color:var(--c-border-danger);background:var(--c-danger-bg);color:var(--c-text-error)}.btn-ghost{border:none;background:transparent;color:var(--c-text-accent);padding:0 var(--sp-1);height:auto;font-size:var(--text-xs)}.btn-ghost:hover{text-decoration:underline;border:none;background:transparent}.action-row{display:flex;flex-wrap:wrap;gap:var(--sp-2);margin-top:var(--sp-1)}.chip-row{display:flex;flex-wrap:wrap;gap:var(--sp-1);margin-top:var(--sp-1)}.label{font-size:var(--text-2xs);text-transform:uppercase;letter-spacing:.06em;color:var(--c-text-muted);font-weight:500}.meta-grid{display:grid;grid-template-columns:auto 1fr;gap:3px var(--sp-3);font-size:var(--text-xs);align-items:baseline}.meta-label{color:var(--c-text-muted);text-transform:uppercase;letter-spacing:.04em;font-size:var(--text-2xs);white-space:nowrap}.meta-value{color:var(--c-text-primary);overflow-wrap:break-word;word-break:break-word}.meta-value-truncate{max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block}.meta-value.mono,.text-mono{font-family:var(--font-mono)}.linked-section{margin-top:var(--sp-2);padding-top:var(--sp-2);border-top:1px solid var(--c-border-subtle)}.asset-preview{display:grid;gap:var(--sp-2);margin-top:var(--sp-2)}.asset-card{margin:0;display:grid;gap:var(--sp-1)}.asset-card img{width:100%;max-height:200px;object-fit:contain;border-radius:var(--radius-md);border:1px solid var(--c-border);background:var(--c-bg-inset)}.asset-card figcaption{color:var(--c-text-muted);font-size:var(--text-2xs)}.content-pre{margin:var(--sp-2) 0 0;white-space:pre-wrap;overflow:auto;max-height:340px;padding:var(--sp-3);border-radius:var(--radius-md);background:var(--c-bg-inset);border:1px solid var(--c-border);font-family:var(--font-mono);font-size:var(--text-xs);color:var(--c-text-secondary);line-height:1.55;scrollbar-width:thin;scrollbar-color:var(--c-text-muted) transparent}.content-truncation-note{margin-top:var(--sp-1);font-size:var(--text-2xs);color:var(--c-text-muted);font-style:italic}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--sp-5) var(--sp-3);color:var(--c-text-muted);text-align:center;min-height:80px;gap:var(--sp-2)}.empty-state-icon{font-size:1.5rem;opacity:.4;line-height:1}mark{background:#7dd3fc26;color:var(--c-text-accent);border-radius:2px;padding:0 2px}.text-sm{font-size:var(--text-sm)}.text-muted{color:var(--c-text-muted)}.text-secondary{color:var(--c-text-secondary)}.text-error{color:var(--c-text-error);font-size:var(--text-xs)}code{font-family:var(--font-mono);font-size:var(--text-xs)}p,h3,h4{margin:0}a{color:var(--c-text-accent);text-decoration:none}a:hover{text-decoration:underline}@media(max-width:900px){.app-shell{grid-template-columns:1fr;grid-template-rows:var(--bar-height) auto minmax(300px,1fr) auto;grid-template-areas:"bar" "sidebar" "center" "rail";height:auto;min-height:100vh;overflow:auto}.sidebar{border-right:none;border-bottom:1px solid var(--c-border);max-height:260px}.center-area{grid-template-rows:auto minmax(50vh,1fr) auto}.detail-rail{border-left:none;border-top:1px solid var(--c-border)}.stats-strip{flex-wrap:wrap;gap:var(--sp-2)}}
|