diff --git a/frontend/package.json b/frontend/package.json index 8d250dc..189721c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -40,10 +40,12 @@ "globals": "16.4.0", "prettier": "3.6.2", "tailwindcss": "^4.1.16", + "tsx": "^4.21.0", "typescript": "5.4.5", "typescript-eslint": "8.38.0", "typescript-plugin-css-modules": "latest", "undici": "*", + "uvu": "^0.5.6", "vite": "7.1.11", "vite-tsconfig-paths": "^4.2.1" }, diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx index eab9379..fe594dd 100644 --- a/frontend/src/routes/index.tsx +++ b/frontend/src/routes/index.tsx @@ -19,10 +19,11 @@ import * as math from "mathjs"; import exampleDoc from "~/assets/example-doc-1.txt?raw"; import { DebugPanel } from "~/components/debug-panel/debug-panel"; import { autoSolveParameters } from "~/utils/autosolver"; +import { serializeDoc, mapRangeToPM } from "~/utils/pm-text-mapping"; import "~/global.css"; // API Configuration -const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://127.0.0.1:5000'; +const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://127.0.0.1:15000'; // Types interface SalienceData { @@ -394,8 +395,9 @@ async function fetchSalienceData( pendingRequest.value = controller; syncState.value = "loading"; - // Get current document text - const text = view.state.doc.textContent; + // Serialize document with position mapping + const doc = view.state.doc; + const { text, posMap } = serializeDoc(doc); try { const res = await fetch(`${API_BASE_URL}/salience?model=${encodeURIComponent(model)}`, { @@ -406,16 +408,27 @@ async function fetchSalienceData( }); const data: SalienceData = await res.json(); + // Check if document changed while request was in flight + if (view.state.doc !== doc) { + syncState.value = "dirty"; + pendingRequest.value = null; + return; + } + salienceData.value = data; adjacencyMatrix.value = data.adjacency; - // Set decorations with positions and zero salience - // The reactive task will compute and update the salience values - sentenceDecorations.value = data.intervals.map(([start, end]) => ({ - from: start, - to: end, - salience: 0, - })); + sentenceDecorations.value = data.intervals.map(([start, end]) => { + const pmRange = mapRangeToPM(posMap, start, end); + return { + from: pmRange.from, + to: pmRange.to, + // Resetting salience to zero is part of the plan, there is a reactive + // task which will compute and update the salience values in place when + // the sentenceDecorations Signal is changed. + salience: 0, + }; + }); syncState.value = "clean"; pendingRequest.value = null; diff --git a/frontend/src/utils/pm-text-mapping.test.ts b/frontend/src/utils/pm-text-mapping.test.ts new file mode 100644 index 0000000..532b051 --- /dev/null +++ b/frontend/src/utils/pm-text-mapping.test.ts @@ -0,0 +1,104 @@ +import { test } from "uvu"; +import * as assert from "uvu/assert"; +import { Schema } from "prosemirror-model"; +import { schema as basicSchema } from "prosemirror-schema-basic"; +import { serializeDoc, mapRangeToPM } from "./pm-text-mapping.js"; + +const schema = new Schema({ + nodes: basicSchema.spec.nodes, + marks: basicSchema.spec.marks, +}); + +function createDoc(paragraphs: string[]) { + return schema.node( + "doc", + null, + paragraphs.map((text) => + schema.node( + "paragraph", + null, + text.length > 0 ? [schema.text(text)] : [] + ) + ) + ); +} + +function getTextSlice(doc: any, from: number, to: number): string { + return doc.textBetween(from, to); +} + +// Test: random ranges round-trip correctly +test("random ranges map back to correct text", () => { + const doc = createDoc(["Hello world", "This is a test", "Third paragraph"]); + const { text, posMap } = serializeDoc(doc); + + // text should be "Hello world\nThis is a test\nThird paragraph" + assert.is(text, "Hello world\nThis is a test\nThird paragraph"); + + // Try many random ranges + for (let i = 0; i < 100; i++) { + const from = Math.floor(Math.random() * text.length); + const to = from + 1 + Math.floor(Math.random() * (text.length - from)); + + const serializedSlice = text.slice(from, to); + const pmRange = mapRangeToPM(posMap, from, to); + const pmSlice = getTextSlice(doc, pmRange.from, pmRange.to); + + // The slices should match, except newlines become empty (PM doesn't have newline chars) + const expected = serializedSlice.replace(/\n/g, ""); + assert.is(pmSlice, expected, `Range [${from}, ${to}): "${serializedSlice}"`); + } +}); + +test("single paragraph", () => { + const doc = createDoc(["Just one paragraph"]); + const { text, posMap } = serializeDoc(doc); + + assert.is(text, "Just one paragraph"); + assert.is(posMap.length, text.length); + + const pmRange = mapRangeToPM(posMap, 0, text.length); + assert.is(getTextSlice(doc, pmRange.from, pmRange.to), text); +}); + +test("empty paragraphs", () => { + const doc = createDoc(["First", "", "Third"]); + const { text, posMap } = serializeDoc(doc); + + assert.is(text, "First\n\nThird"); + + // Range spanning the empty paragraph + const pmRange = mapRangeToPM(posMap, 0, text.length); + assert.is(getTextSlice(doc, pmRange.from, pmRange.to), "FirstThird"); +}); + +test("range within single paragraph", () => { + const doc = createDoc(["Hello world", "Second para"]); + const { text, posMap } = serializeDoc(doc); + + // Select "world" from first paragraph + const from = 6; + const to = 11; + assert.is(text.slice(from, to), "world"); + + const pmRange = mapRangeToPM(posMap, from, to); + assert.is(getTextSlice(doc, pmRange.from, pmRange.to), "world"); +}); + +test("range spanning paragraph boundary", () => { + const doc = createDoc(["Hello", "World"]); + const { text, posMap } = serializeDoc(doc); + + assert.is(text, "Hello\nWorld"); + + // Select "lo\nWor" (crossing the boundary) + const from = 3; + const to = 9; + assert.is(text.slice(from, to), "lo\nWor"); + + const pmRange = mapRangeToPM(posMap, from, to); + // PM textBetween won't include newline, so we get "loWor" + assert.is(getTextSlice(doc, pmRange.from, pmRange.to), "loWor"); +}); + +test.run(); diff --git a/frontend/src/utils/pm-text-mapping.ts b/frontend/src/utils/pm-text-mapping.ts new file mode 100644 index 0000000..a989272 --- /dev/null +++ b/frontend/src/utils/pm-text-mapping.ts @@ -0,0 +1,61 @@ +import { Node as PMNode } from "prosemirror-model"; + +/* + * ProseMirror positions are tree positions, not character offsets. + * A document with two paragraphs "ab" and "cd" has positions: + * + * 0 1 2 3 4 5 6 7 8 + *
a b
c d
+ * + * External tools (spell checkers, search, ML models) see plain text: "ab\ncd". + * They report findings as character ranges. To act on these findings + * in the editor, we must map back to ProseMirror positions. + * + * posMap[i] holds the PM position of character i in the serialized text. + * This is the only reliable way to roundtrip between the two worlds. + * There is no formula; the mapping depends on document structure. + */ + +export interface SerializedDoc { + text: string; + // Maps each character index in `text` to its ProseMirror position + posMap: number[]; +} + +// Serialize document to plain text with newlines between paragraphs, +// building a position map as we go +export function serializeDoc(doc: PMNode): SerializedDoc { + const chars: string[] = []; + const posMap: number[] = []; + let isFirstParagraph = true; + + doc.descendants((node, pos) => { + if (node.type.name === "paragraph") { + if (!isFirstParagraph) { + chars.push("\n"); + posMap.push(pos); // newline maps to start of this paragraph + } + isFirstParagraph = false; + } else if (node.isText && node.text) { + for (let i = 0; i < node.text.length; i++) { + chars.push(node.text[i]); + posMap.push(pos + i); + } + } + return true; + }); + + return { text: chars.join(""), posMap }; +} + +// Convert a range from serialized text coordinates to ProseMirror positions +export function mapRangeToPM( + posMap: number[], + from: number, + to: number +): { from: number; to: number } { + return { + from: posMap[from], + to: posMap[to - 1] + 1, // to is exclusive + }; +} diff --git a/frontend/yarn.lock b/frontend/yarn.lock index ac0fad3..f053c43 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -87,6 +87,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/aix-ppc64@npm:0.27.0" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/android-arm64@npm:0.25.12" @@ -94,6 +101,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/android-arm64@npm:0.27.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/android-arm@npm:0.25.12" @@ -101,6 +115,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/android-arm@npm:0.27.0" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/android-x64@npm:0.25.12" @@ -108,6 +129,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/android-x64@npm:0.27.0" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/darwin-arm64@npm:0.25.12" @@ -115,6 +143,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/darwin-arm64@npm:0.27.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/darwin-x64@npm:0.25.12" @@ -122,6 +157,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/darwin-x64@npm:0.27.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/freebsd-arm64@npm:0.25.12" @@ -129,6 +171,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/freebsd-arm64@npm:0.27.0" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/freebsd-x64@npm:0.25.12" @@ -136,6 +185,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/freebsd-x64@npm:0.27.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/linux-arm64@npm:0.25.12" @@ -143,6 +199,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/linux-arm64@npm:0.27.0" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/linux-arm@npm:0.25.12" @@ -150,6 +213,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/linux-arm@npm:0.27.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/linux-ia32@npm:0.25.12" @@ -157,6 +227,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/linux-ia32@npm:0.27.0" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/linux-loong64@npm:0.25.12" @@ -164,6 +241,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/linux-loong64@npm:0.27.0" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/linux-mips64el@npm:0.25.12" @@ -171,6 +255,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/linux-mips64el@npm:0.27.0" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/linux-ppc64@npm:0.25.12" @@ -178,6 +269,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/linux-ppc64@npm:0.27.0" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/linux-riscv64@npm:0.25.12" @@ -185,6 +283,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/linux-riscv64@npm:0.27.0" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/linux-s390x@npm:0.25.12" @@ -192,6 +297,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/linux-s390x@npm:0.27.0" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/linux-x64@npm:0.25.12" @@ -199,6 +311,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/linux-x64@npm:0.27.0" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-arm64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/netbsd-arm64@npm:0.25.12" @@ -206,6 +325,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-arm64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/netbsd-arm64@npm:0.27.0" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/netbsd-x64@npm:0.25.12" @@ -213,6 +339,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/netbsd-x64@npm:0.27.0" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openbsd-arm64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/openbsd-arm64@npm:0.25.12" @@ -220,6 +353,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-arm64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/openbsd-arm64@npm:0.27.0" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/openbsd-x64@npm:0.25.12" @@ -227,6 +367,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/openbsd-x64@npm:0.27.0" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openharmony-arm64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/openharmony-arm64@npm:0.25.12" @@ -234,6 +381,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openharmony-arm64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/openharmony-arm64@npm:0.27.0" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/sunos-x64@npm:0.25.12" @@ -241,6 +395,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/sunos-x64@npm:0.27.0" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/win32-arm64@npm:0.25.12" @@ -248,6 +409,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/win32-arm64@npm:0.27.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/win32-ia32@npm:0.25.12" @@ -255,6 +423,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/win32-ia32@npm:0.27.0" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.25.12": version: 0.25.12 resolution: "@esbuild/win32-x64@npm:0.25.12" @@ -262,6 +437,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.27.0": + version: 0.27.0 + resolution: "@esbuild/win32-x64@npm:0.27.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.7.0": version: 4.9.0 resolution: "@eslint-community/eslint-utils@npm:4.9.0" @@ -2447,6 +2629,13 @@ __metadata: languageName: node linkType: hard +"diff@npm:^5.0.0": + version: 5.2.0 + resolution: "diff@npm:5.2.0" + checksum: 10c0/aed0941f206fe261ecb258dc8d0ceea8abbde3ace5827518ff8d302f0fc9cc81ce116c4d8f379151171336caf0516b79e01abdc1ed1201b6440d895a66689eb4 + languageName: node + linkType: hard + "dom-serializer@npm:^2.0.0": version: 2.0.0 resolution: "dom-serializer@npm:2.0.0" @@ -2805,6 +2994,95 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:~0.27.0": + version: 0.27.0 + resolution: "esbuild@npm:0.27.0" + dependencies: + "@esbuild/aix-ppc64": "npm:0.27.0" + "@esbuild/android-arm": "npm:0.27.0" + "@esbuild/android-arm64": "npm:0.27.0" + "@esbuild/android-x64": "npm:0.27.0" + "@esbuild/darwin-arm64": "npm:0.27.0" + "@esbuild/darwin-x64": "npm:0.27.0" + "@esbuild/freebsd-arm64": "npm:0.27.0" + "@esbuild/freebsd-x64": "npm:0.27.0" + "@esbuild/linux-arm": "npm:0.27.0" + "@esbuild/linux-arm64": "npm:0.27.0" + "@esbuild/linux-ia32": "npm:0.27.0" + "@esbuild/linux-loong64": "npm:0.27.0" + "@esbuild/linux-mips64el": "npm:0.27.0" + "@esbuild/linux-ppc64": "npm:0.27.0" + "@esbuild/linux-riscv64": "npm:0.27.0" + "@esbuild/linux-s390x": "npm:0.27.0" + "@esbuild/linux-x64": "npm:0.27.0" + "@esbuild/netbsd-arm64": "npm:0.27.0" + "@esbuild/netbsd-x64": "npm:0.27.0" + "@esbuild/openbsd-arm64": "npm:0.27.0" + "@esbuild/openbsd-x64": "npm:0.27.0" + "@esbuild/openharmony-arm64": "npm:0.27.0" + "@esbuild/sunos-x64": "npm:0.27.0" + "@esbuild/win32-arm64": "npm:0.27.0" + "@esbuild/win32-ia32": "npm:0.27.0" + "@esbuild/win32-x64": "npm:0.27.0" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/openharmony-arm64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/a3a1deec285337b7dfe25cbb9aa8765d27a0192b610a8477a39bf5bd907a6bdb75e98898b61fb4337114cfadb13163bd95977db14e241373115f548e235b40a2 + languageName: node + linkType: hard + "escape-html@npm:~1.0.3": version: 1.0.3 resolution: "escape-html@npm:1.0.3" @@ -3347,6 +3625,15 @@ __metadata: languageName: node linkType: hard +"get-tsconfig@npm:^4.7.5": + version: 4.13.0 + resolution: "get-tsconfig@npm:4.13.0" + dependencies: + resolve-pkg-maps: "npm:^1.0.0" + checksum: 10c0/2c49ef8d3907047a107f229fd610386fe3b7fe9e42dfd6b42e7406499493cdda8c62e83e57e8d7a98125610774b9f604d3a0ff308d7f9de5c7ac6d1b07cb6036 + languageName: node + linkType: hard + "glob-parent@npm:^5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" @@ -4093,6 +4380,13 @@ __metadata: languageName: node linkType: hard +"kleur@npm:^4.0.3": + version: 4.1.5 + resolution: "kleur@npm:4.1.5" + checksum: 10c0/e9de6cb49657b6fa70ba2d1448fd3d691a5c4370d8f7bbf1c2f64c24d461270f2117e1b0afe8cb3114f13bbd8e51de158c2a224953960331904e636a5e4c0f2a + languageName: node + linkType: hard + "launch-editor@npm:^2.11.1": version: 2.12.0 resolution: "launch-editor@npm:2.12.0" @@ -5055,6 +5349,13 @@ __metadata: languageName: node linkType: hard +"mri@npm:^1.1.0": + version: 1.2.0 + resolution: "mri@npm:1.2.0" + checksum: 10c0/a3d32379c2554cf7351db6237ddc18dc9e54e4214953f3da105b97dc3babe0deb3ffe99cf409b38ea47cc29f9430561ba6b53b24ab8f9ce97a4b50409e4a50e7 + languageName: node + linkType: hard + "ms@npm:2.0.0": version: 2.0.0 resolution: "ms@npm:2.0.0" @@ -5098,10 +5399,12 @@ __metadata: prosemirror-view: "npm:^1.41.3" tailwindcss: "npm:^4.1.16" temml: "npm:^0.11.11" + tsx: "npm:^4.21.0" typescript: "npm:5.4.5" typescript-eslint: "npm:8.38.0" typescript-plugin-css-modules: "npm:latest" undici: "npm:*" + uvu: "npm:^0.5.6" vite: "npm:7.1.11" vite-tsconfig-paths: "npm:^4.2.1" languageName: unknown @@ -5852,6 +6155,13 @@ __metadata: languageName: node linkType: hard +"resolve-pkg-maps@npm:^1.0.0": + version: 1.0.0 + resolution: "resolve-pkg-maps@npm:1.0.0" + checksum: 10c0/fb8f7bbe2ca281a73b7ef423a1cbc786fb244bd7a95cbe5c3fba25b27d327150beca8ba02f622baea65919a57e061eb5005204daa5f93ed590d9b77463a567ab + languageName: node + linkType: hard + "retry@npm:^0.12.0": version: 0.12.0 resolution: "retry@npm:0.12.0" @@ -5956,6 +6266,15 @@ __metadata: languageName: node linkType: hard +"sade@npm:^1.7.3": + version: 1.8.1 + resolution: "sade@npm:1.8.1" + dependencies: + mri: "npm:^1.1.0" + checksum: 10c0/da8a3a5d667ad5ce3bf6d4f054bbb9f711103e5df21003c5a5c1a8a77ce12b640ed4017dd423b13c2307ea7e645adee7c2ae3afe8051b9db16a6f6d3da3f90b1 + languageName: node + linkType: hard + "safe-array-concat@npm:^1.1.3": version: 1.1.3 resolution: "safe-array-concat@npm:1.1.3" @@ -6638,6 +6957,22 @@ __metadata: languageName: node linkType: hard +"tsx@npm:^4.21.0": + version: 4.21.0 + resolution: "tsx@npm:4.21.0" + dependencies: + esbuild: "npm:~0.27.0" + fsevents: "npm:~2.3.3" + get-tsconfig: "npm:^4.7.5" + dependenciesMeta: + fsevents: + optional: true + bin: + tsx: dist/cli.mjs + checksum: 10c0/f5072923cd8459a1f9a26df87823a2ab5754641739d69df2a20b415f61814322b751fa6be85db7c6ec73cf68ba8fac2fd1cfc76bdb0aa86ded984d84d5d2126b + languageName: node + linkType: hard + "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0" @@ -6934,6 +7269,20 @@ __metadata: languageName: node linkType: hard +"uvu@npm:^0.5.6": + version: 0.5.6 + resolution: "uvu@npm:0.5.6" + dependencies: + dequal: "npm:^2.0.0" + diff: "npm:^5.0.0" + kleur: "npm:^4.0.3" + sade: "npm:^1.7.3" + bin: + uvu: bin.js + checksum: 10c0/ad32eb5f7d94bdeb71f80d073003f0138e24f61ed68cecc8e15d2f30838f44c9670577bb1775c8fac894bf93d1bc1583d470a9195e49bfa6efa14cc6f4942bff + languageName: node + linkType: hard + "valibot@npm:>=0.36.0 <2": version: 1.2.0 resolution: "valibot@npm:1.2.0"