@graphrefly/graphrefly 0.24.0 → 0.26.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/README.md +8 -0
- package/dist/{chunk-QOWVNWOC.js → chunk-3ZWCKRHX.js} +27 -25
- package/dist/{chunk-QOWVNWOC.js.map → chunk-3ZWCKRHX.js.map} +1 -1
- package/dist/chunk-6LDQFTYS.js +102 -0
- package/dist/chunk-6LDQFTYS.js.map +1 -0
- package/dist/{chunk-5WGT55R4.js → chunk-AMCG74RZ.js} +195 -24
- package/dist/chunk-AMCG74RZ.js.map +1 -0
- package/dist/{chunk-AOCBDH4T.js → chunk-BVZYTZ5H.js} +76 -103
- package/dist/chunk-BVZYTZ5H.js.map +1 -0
- package/dist/chunk-FQMKGR6L.js +330 -0
- package/dist/chunk-FQMKGR6L.js.map +1 -0
- package/dist/chunk-HXZEYDUR.js +94 -0
- package/dist/chunk-HXZEYDUR.js.map +1 -0
- package/dist/{chunk-IPLKX3L2.js → chunk-IZYUSJC7.js} +16 -14
- package/dist/{chunk-IPLKX3L2.js.map → chunk-IZYUSJC7.js.map} +1 -1
- package/dist/chunk-J22W6HV3.js +107 -0
- package/dist/chunk-J22W6HV3.js.map +1 -0
- package/dist/{chunk-HWPIFSW2.js → chunk-JSCT3CR4.js} +6 -4
- package/dist/{chunk-HWPIFSW2.js.map → chunk-JSCT3CR4.js.map} +1 -1
- package/dist/chunk-JYXEWPH4.js +62 -0
- package/dist/chunk-JYXEWPH4.js.map +1 -0
- package/dist/chunk-LCE3GF5P.js +866 -0
- package/dist/chunk-LCE3GF5P.js.map +1 -0
- package/dist/chunk-MJ2NKQQL.js +119 -0
- package/dist/chunk-MJ2NKQQL.js.map +1 -0
- package/dist/chunk-N6UR7YVY.js +198 -0
- package/dist/chunk-N6UR7YVY.js.map +1 -0
- package/dist/chunk-OHISZPOJ.js +97 -0
- package/dist/chunk-OHISZPOJ.js.map +1 -0
- package/dist/{chunk-5DJTTKX3.js → chunk-PHOUUNK7.js} +74 -111
- package/dist/chunk-PHOUUNK7.js.map +1 -0
- package/dist/{chunk-PY4XCDLR.js → chunk-RB6QPHJ7.js} +8 -6
- package/dist/{chunk-PY4XCDLR.js.map → chunk-RB6QPHJ7.js.map} +1 -1
- package/dist/chunk-SN4YWWYO.js +171 -0
- package/dist/chunk-SN4YWWYO.js.map +1 -0
- package/dist/chunk-SX52TAR4.js +110 -0
- package/dist/chunk-SX52TAR4.js.map +1 -0
- package/dist/{chunk-XOFWRC73.js → chunk-THTWHNU4.js} +319 -24
- package/dist/chunk-THTWHNU4.js.map +1 -0
- package/dist/{chunk-H4RVA4VE.js → chunk-VYPWMZ6H.js} +2 -2
- package/dist/chunk-XGPU467M.js +136 -0
- package/dist/chunk-XGPU467M.js.map +1 -0
- package/dist/{chunk-TDEXAMGO.js → chunk-ZQMEI34O.js} +206 -574
- package/dist/chunk-ZQMEI34O.js.map +1 -0
- package/dist/compat/index.cjs +7656 -0
- package/dist/compat/index.cjs.map +1 -0
- package/dist/compat/index.d.cts +18 -0
- package/dist/compat/index.d.ts +18 -0
- package/dist/compat/index.js +49 -0
- package/dist/compat/index.js.map +1 -0
- package/dist/compat/jotai/index.cjs +2048 -0
- package/dist/compat/jotai/index.cjs.map +1 -0
- package/dist/compat/jotai/index.d.cts +2 -0
- package/dist/compat/jotai/index.d.ts +2 -0
- package/dist/compat/jotai/index.js +9 -0
- package/dist/compat/jotai/index.js.map +1 -0
- package/dist/compat/nanostores/index.cjs +2175 -0
- package/dist/compat/nanostores/index.cjs.map +1 -0
- package/dist/compat/nanostores/index.d.cts +2 -0
- package/dist/compat/nanostores/index.d.ts +2 -0
- package/dist/compat/nanostores/index.js +23 -0
- package/dist/compat/nanostores/index.js.map +1 -0
- package/dist/compat/nestjs/index.cjs +350 -16
- package/dist/compat/nestjs/index.cjs.map +1 -1
- package/dist/compat/nestjs/index.d.cts +6 -6
- package/dist/compat/nestjs/index.d.ts +6 -6
- package/dist/compat/nestjs/index.js +10 -9
- package/dist/compat/react/index.cjs +141 -0
- package/dist/compat/react/index.cjs.map +1 -0
- package/dist/compat/react/index.d.cts +2 -0
- package/dist/compat/react/index.d.ts +2 -0
- package/dist/compat/react/index.js +12 -0
- package/dist/compat/react/index.js.map +1 -0
- package/dist/compat/solid/index.cjs +128 -0
- package/dist/compat/solid/index.cjs.map +1 -0
- package/dist/compat/solid/index.d.cts +2 -0
- package/dist/compat/solid/index.d.ts +2 -0
- package/dist/compat/solid/index.js +12 -0
- package/dist/compat/solid/index.js.map +1 -0
- package/dist/compat/svelte/index.cjs +131 -0
- package/dist/compat/svelte/index.cjs.map +1 -0
- package/dist/compat/svelte/index.d.cts +2 -0
- package/dist/compat/svelte/index.d.ts +2 -0
- package/dist/compat/svelte/index.js +12 -0
- package/dist/compat/svelte/index.js.map +1 -0
- package/dist/compat/vue/index.cjs +146 -0
- package/dist/compat/vue/index.cjs.map +1 -0
- package/dist/compat/vue/index.d.cts +3 -0
- package/dist/compat/vue/index.d.ts +3 -0
- package/dist/compat/vue/index.js +12 -0
- package/dist/compat/vue/index.js.map +1 -0
- package/dist/compat/zustand/index.cjs +4931 -0
- package/dist/compat/zustand/index.cjs.map +1 -0
- package/dist/compat/zustand/index.d.cts +5 -0
- package/dist/compat/zustand/index.d.ts +5 -0
- package/dist/compat/zustand/index.js +12 -0
- package/dist/compat/zustand/index.js.map +1 -0
- package/dist/core/index.cjs +53 -4
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +3 -3
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.js +26 -24
- package/dist/demo-shell-26p5fVxn.d.cts +102 -0
- package/dist/demo-shell-DEp-nMTl.d.ts +102 -0
- package/dist/extra/index.cjs +290 -110
- package/dist/extra/index.cjs.map +1 -1
- package/dist/extra/index.d.cts +5 -4
- package/dist/extra/index.d.ts +5 -4
- package/dist/extra/index.js +8 -5
- package/dist/extra/sources.cjs +2486 -0
- package/dist/extra/sources.cjs.map +1 -0
- package/dist/extra/sources.d.cts +465 -0
- package/dist/extra/sources.d.ts +465 -0
- package/dist/extra/sources.js +57 -0
- package/dist/extra/sources.js.map +1 -0
- package/dist/graph/index.cjs +408 -14
- package/dist/graph/index.cjs.map +1 -1
- package/dist/graph/index.d.cts +5 -5
- package/dist/graph/index.d.ts +5 -5
- package/dist/graph/index.js +13 -5
- package/dist/{graph-D-3JIQme.d.cts → graph-6tZ5jEzr.d.cts} +195 -4
- package/dist/{graph-B6NFqv3z.d.ts → graph-DQ69XU0g.d.ts} +195 -4
- package/dist/index-B4MP_8V_.d.cts +37 -0
- package/dist/index-BEfE8H_G.d.cts +121 -0
- package/dist/{index-D7XgsUt7.d.ts → index-BW1z3BN9.d.ts} +169 -127
- package/dist/index-BYOHF0zP.d.ts +34 -0
- package/dist/index-B_IP40nB.d.cts +36 -0
- package/dist/index-Bd_fwmLf.d.cts +45 -0
- package/dist/{index-BysCTzJz.d.ts → index-BeIdBfcb.d.cts} +121 -547
- package/dist/index-BjI6ty9z.d.ts +121 -0
- package/dist/index-Bxb5ZYc9.d.cts +34 -0
- package/dist/{index-BJB7t9gg.d.cts → index-C0ZXMaXO.d.cts} +2 -2
- package/dist/{index-b5BYtczN.d.cts → index-C8mdwMXc.d.cts} +169 -127
- package/dist/index-CDAjUFIv.d.ts +36 -0
- package/dist/index-CPgZ5wRl.d.ts +44 -0
- package/dist/{index-AMWewNDe.d.cts → index-CUwyr1Kk.d.cts} +33 -4
- package/dist/index-CUyrtuOf.d.cts +127 -0
- package/dist/{index-C-TXEa7C.d.ts → index-CY2TljO4.d.ts} +2 -2
- package/dist/index-CmnuOibw.d.ts +37 -0
- package/dist/{index-DiobMNwE.d.ts → index-CuYwdKO-.d.ts} +3 -3
- package/dist/index-DFhjO4Gg.d.cts +44 -0
- package/dist/{index-1z8vRTCt.d.cts → index-DdD5MVDL.d.ts} +121 -547
- package/dist/index-DrISNAOm.d.ts +45 -0
- package/dist/index-QBpffFW-.d.cts +86 -0
- package/dist/{index-J7Kc0oIQ.d.cts → index-_oMEWlDq.d.cts} +3 -3
- package/dist/{index-CYkjxu3s.d.ts → index-eJ6T_qGM.d.ts} +33 -4
- package/dist/index-qldRdbQw.d.ts +86 -0
- package/dist/index-xdGjv0nO.d.ts +127 -0
- package/dist/index.cjs +2334 -195
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1007 -648
- package/dist/index.d.ts +1007 -648
- package/dist/index.js +1204 -1172
- package/dist/index.js.map +1 -1
- package/dist/{meta-CnkLA_43.d.ts → meta-BGqSZ7mt.d.ts} +1 -1
- package/dist/{meta-DWbkoq1s.d.cts → meta-C0-8XW6Q.d.cts} +1 -1
- package/dist/{node-B-f-Lu-k.d.cts → node-C_IBuvX2.d.cts} +26 -1
- package/dist/{node-B-f-Lu-k.d.ts → node-C_IBuvX2.d.ts} +26 -1
- package/dist/{observable-DBnrwcar.d.cts → observable-Crr1jgzx.d.cts} +1 -1
- package/dist/{observable-uP-wy_uK.d.ts → observable-DCk45RH5.d.ts} +1 -1
- package/dist/patterns/demo-shell.cjs +5604 -0
- package/dist/patterns/demo-shell.cjs.map +1 -0
- package/dist/patterns/demo-shell.d.cts +6 -0
- package/dist/patterns/demo-shell.d.ts +6 -0
- package/dist/patterns/demo-shell.js +15 -0
- package/dist/patterns/demo-shell.js.map +1 -0
- package/dist/patterns/reactive-layout/index.cjs +843 -29
- package/dist/patterns/reactive-layout/index.cjs.map +1 -1
- package/dist/patterns/reactive-layout/index.d.cts +6 -5
- package/dist/patterns/reactive-layout/index.d.ts +6 -5
- package/dist/patterns/reactive-layout/index.js +25 -10
- package/dist/reactive-layout-BaOQefHu.d.cts +183 -0
- package/dist/reactive-layout-D9gejYXE.d.ts +183 -0
- package/dist/{storage-BuTdpCI1.d.cts → storage-BMycWEh2.d.ts} +9 -1
- package/dist/{storage-F2X1U1x0.d.ts → storage-DiqWHzVI.d.cts} +9 -1
- package/package.json +32 -2
- package/dist/chunk-5DJTTKX3.js.map +0 -1
- package/dist/chunk-5WGT55R4.js.map +0 -1
- package/dist/chunk-AOCBDH4T.js.map +0 -1
- package/dist/chunk-MW4VAKAO.js +0 -47
- package/dist/chunk-MW4VAKAO.js.map +0 -1
- package/dist/chunk-TDEXAMGO.js.map +0 -1
- package/dist/chunk-XOFWRC73.js.map +0 -1
- /package/dist/{chunk-H4RVA4VE.js.map → chunk-VYPWMZ6H.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/patterns/reactive-layout/reactive-layout.ts"],"sourcesContent":["/**\n * Reactive text layout engine (roadmap §7.1 — Pretext parity).\n *\n * Pure-arithmetic text measurement and line breaking without DOM thrashing.\n * Inspired by [Pretext](https://github.com/chenglou/pretext), rebuilt as a\n * GraphReFly graph — inspectable via `describe()`, snapshotable, debuggable.\n *\n * Two-tier DX:\n * - `reactiveLayout({ adapter, text?, font?, lineHeight?, maxWidth?, name? })` — convenience factory\n * - `MeasurementAdapter` — pluggable backends (`measureSegment`; optional `clearCache`)\n */\nimport { monotonicNs } from \"../../core/clock.js\";\nimport type { Node } from \"../../core/node.js\";\nimport { node } from \"../../core/node.js\";\nimport { derived, state } from \"../../core/sugar.js\";\nimport { Graph } from \"../../graph/graph.js\";\nimport { emitToMeta } from \"../_internal.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Pluggable measurement backend. */\nexport interface MeasurementAdapter {\n\tmeasureSegment(text: string, font: string): { width: number };\n\t/** Optional; adapters may omit for read-only / stateless measurement. */\n\tclearCache?(): void;\n}\n\n/** Mutable counters for `analyzeAndMeasure` cache hit ratio (hits / (hits + misses)). */\nexport type SegmentMeasureStats = { hits: number; misses: number };\n\n/** Break kind for each segment (ported from Pretext analysis.ts). */\nexport type SegmentBreakKind = \"text\" | \"space\" | \"zero-width-break\" | \"soft-hyphen\" | \"hard-break\";\n\n/** A measured text segment ready for line breaking. */\nexport type PreparedSegment = {\n\ttext: string;\n\twidth: number;\n\tkind: SegmentBreakKind;\n\t/** Grapheme widths for overflow-wrap: break-word (null if single grapheme). */\n\tgraphemeWidths: number[] | null;\n};\n\n/** A laid-out line with start/end cursors. */\nexport type LayoutLine = {\n\ttext: string;\n\twidth: number;\n\tstartSegment: number;\n\tstartGrapheme: number;\n\tendSegment: number;\n\tendGrapheme: number;\n};\n\n/** Per-character position for hit testing. */\nexport type CharPosition = {\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n\tline: number;\n};\n\n/** Full layout result from the line-breaks derived node. */\nexport type LineBreaksResult = {\n\tlines: LayoutLine[];\n\tlineCount: number;\n};\n\n/**\n * A position within `PreparedSegment[]` — segment + grapheme offset.\n * `graphemeIndex: 0` at segment boundaries.\n *\n * Used by {@link layoutNextLine} for cursor-based line walking; needed when\n * lines have varying widths (multi-column flow, text wrapping around obstacles).\n */\nexport type LayoutCursor = {\n\tsegmentIndex: number;\n\tgraphemeIndex: number;\n};\n\n/** A horizontal span `[left, right]` in pixels — used by flow-layout slot carving. */\nexport type Interval = { left: number; right: number };\n\n/** Result of a single `layoutNextLine` call. */\nexport type LayoutNextLineResult = {\n\ttext: string;\n\twidth: number;\n\tstart: LayoutCursor;\n\tend: LayoutCursor;\n};\n\n/** Optional context for `layoutNextLine` — enables soft-hyphen visible-hyphen rendering. */\nexport type LayoutNextLineContext = {\n\tadapter?: MeasurementAdapter;\n\tfont?: string;\n\tcache?: Map<string, Map<string, number>>;\n};\n\n/** Result of the reactive layout graph's describe-accessible state. */\nexport type ReactiveLayoutBundle = {\n\tgraph: Graph;\n\t/** Set input text. */\n\tsetText: (text: string) => void;\n\t/** Set CSS font string. */\n\tsetFont: (font: string) => void;\n\t/** Set line height (px). */\n\tsetLineHeight: (lineHeight: number) => void;\n\t/** Set max width constraint (px). */\n\tsetMaxWidth: (maxWidth: number) => void;\n\t/** Segments node. */\n\tsegments: Node<PreparedSegment[]>;\n\t/** Line breaks node. */\n\tlineBreaks: Node<LineBreaksResult>;\n\t/** Total height node. */\n\theight: Node<number>;\n\t/** Per-character positions node. */\n\tcharPositions: Node<CharPosition[]>;\n};\n\n// ---------------------------------------------------------------------------\n// Text analysis (ported from Pretext analysis.ts — core subset)\n// ---------------------------------------------------------------------------\n\n// CJK detection (Unicode CJK Unified Ideographs + common ranges)\nfunction isCJK(s: string): boolean {\n\tfor (const ch of s) {\n\t\tconst c = ch.codePointAt(0)!;\n\t\tif (\n\t\t\t(c >= 0x4e00 && c <= 0x9fff) || // CJK Unified Ideographs\n\t\t\t(c >= 0x3400 && c <= 0x4dbf) || // CJK Extension A\n\t\t\t(c >= 0x3000 && c <= 0x303f) || // CJK Symbols and Punctuation\n\t\t\t(c >= 0x3040 && c <= 0x309f) || // Hiragana\n\t\t\t(c >= 0x30a0 && c <= 0x30ff) || // Katakana\n\t\t\t(c >= 0xac00 && c <= 0xd7af) || // Hangul\n\t\t\t(c >= 0xff00 && c <= 0xffef) // Fullwidth Forms\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n// Kinsoku: characters that cannot start a line (CJK punctuation)\nconst kinsokuStart = new Set([\n\t\"\\uff0c\",\n\t\"\\uff0e\",\n\t\"\\uff01\",\n\t\"\\uff1a\",\n\t\"\\uff1b\",\n\t\"\\uff1f\",\n\t\"\\u3001\",\n\t\"\\u3002\",\n\t\"\\u30fb\",\n\t\"\\uff09\",\n\t\"\\u3015\",\n\t\"\\u3009\",\n\t\"\\u300b\",\n\t\"\\u300d\",\n\t\"\\u300f\",\n\t\"\\u3011\",\n]);\n\n// Left-sticky punctuation (merges into preceding segment)\nconst leftStickyPunctuation = new Set([\n\t\".\",\n\t\",\",\n\t\"!\",\n\t\"?\",\n\t\":\",\n\t\";\",\n\t\")\",\n\t\"]\",\n\t\"}\",\n\t\"%\",\n\t'\"',\n\t\"\\u201d\",\n\t\"\\u2019\",\n\t\"\\u00bb\",\n\t\"\\u203a\",\n\t\"\\u2026\",\n]);\n\n/** Normalize collapsible whitespace (CSS white-space: normal). */\nfunction normalizeWhitespace(text: string): string {\n\treturn text.replace(/[\\t\\n\\r\\f ]+/g, \" \").replace(/^ | $/g, \"\");\n}\n\n/**\n * Segment text using Intl.Segmenter (word granularity) and classify break kinds.\n * Returns raw segmentation pieces before merging.\n */\nfunction segmentText(normalized: string): {\n\ttexts: string[];\n\tisWordLike: boolean[];\n\tkinds: SegmentBreakKind[];\n}[] {\n\tconst wordSegmenter = new Intl.Segmenter(undefined, { granularity: \"word\" });\n\tconst pieces: {\n\t\ttexts: string[];\n\t\tisWordLike: boolean[];\n\t\tkinds: SegmentBreakKind[];\n\t}[] = [];\n\n\tfor (const s of wordSegmenter.segment(normalized)) {\n\t\tconst text = s.segment;\n\t\tconst isWordLike = s.isWordLike ?? false;\n\n\t\t// Split segment by break-relevant characters\n\t\tconst texts: string[] = [];\n\t\tconst wordLikes: boolean[] = [];\n\t\tconst kinds: SegmentBreakKind[] = [];\n\n\t\tlet currentText = \"\";\n\t\tlet currentKind: SegmentBreakKind | null = null;\n\n\t\tfor (const ch of text) {\n\t\t\tlet kind: SegmentBreakKind;\n\t\t\tif (ch === \" \") kind = \"space\";\n\t\t\telse if (ch === \"\\u200b\") kind = \"zero-width-break\";\n\t\t\telse if (ch === \"\\u00ad\") kind = \"soft-hyphen\";\n\t\t\telse if (ch === \"\\n\") kind = \"hard-break\";\n\t\t\telse kind = \"text\";\n\n\t\t\tif (currentKind !== null && kind === currentKind) {\n\t\t\t\tcurrentText += ch;\n\t\t\t} else {\n\t\t\t\tif (currentKind !== null) {\n\t\t\t\t\ttexts.push(currentText);\n\t\t\t\t\twordLikes.push(currentKind === \"text\" && isWordLike);\n\t\t\t\t\tkinds.push(currentKind);\n\t\t\t\t}\n\t\t\t\tcurrentText = ch;\n\t\t\t\tcurrentKind = kind;\n\t\t\t}\n\t\t}\n\n\t\tif (currentKind !== null) {\n\t\t\ttexts.push(currentText);\n\t\t\twordLikes.push(currentKind === \"text\" && isWordLike);\n\t\t\tkinds.push(currentKind);\n\t\t}\n\n\t\tpieces.push({ texts, isWordLike: wordLikes, kinds });\n\t}\n\treturn pieces;\n}\n\n/**\n * Merge segmentation pieces: sticky punctuation, CJK per-grapheme splitting,\n * and produce the final measured segment list.\n */\nexport function analyzeAndMeasure(\n\ttext: string,\n\tfont: string,\n\tadapter: MeasurementAdapter,\n\tcache: Map<string, Map<string, number>>,\n\tstats?: SegmentMeasureStats,\n): PreparedSegment[] {\n\tconst normalized = normalizeWhitespace(text);\n\tif (normalized.length === 0) return [];\n\n\tconst pieces = segmentText(normalized);\n\tconst graphemeSegmenter = new Intl.Segmenter(undefined, {\n\t\tgranularity: \"grapheme\",\n\t});\n\n\t// Flatten pieces into a single segment list with merging\n\tconst rawTexts: string[] = [];\n\tconst rawKinds: SegmentBreakKind[] = [];\n\tconst rawWordLike: boolean[] = [];\n\n\tfor (const piece of pieces) {\n\t\tfor (let i = 0; i < piece.texts.length; i++) {\n\t\t\trawTexts.push(piece.texts[i]!);\n\t\t\trawKinds.push(piece.kinds[i]!);\n\t\t\trawWordLike.push(piece.isWordLike[i]!);\n\t\t}\n\t}\n\n\t// Merge: left-sticky punctuation and kinsoku-start into preceding text segment\n\tconst mergedTexts: string[] = [];\n\tconst mergedKinds: SegmentBreakKind[] = [];\n\tconst mergedWordLike: boolean[] = [];\n\n\tfor (let i = 0; i < rawTexts.length; i++) {\n\t\tconst t = rawTexts[i]!;\n\t\tconst k = rawKinds[i]!;\n\t\tconst wl = rawWordLike[i]!;\n\n\t\t// Merge left-sticky punctuation into preceding text\n\t\tif (\n\t\t\tk === \"text\" &&\n\t\t\t!wl &&\n\t\t\tmergedTexts.length > 0 &&\n\t\t\tmergedKinds[mergedKinds.length - 1] === \"text\"\n\t\t) {\n\t\t\tconst isSticky = t.length === 1 && (leftStickyPunctuation.has(t) || kinsokuStart.has(t));\n\t\t\tif (isSticky) {\n\t\t\t\tmergedTexts[mergedTexts.length - 1] += t;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Merge hyphen after word into preceding text (\"well-known\" stays together)\n\t\tif (\n\t\t\tt === \"-\" &&\n\t\t\tmergedTexts.length > 0 &&\n\t\t\tmergedKinds[mergedKinds.length - 1] === \"text\" &&\n\t\t\tmergedWordLike[mergedWordLike.length - 1]\n\t\t) {\n\t\t\tmergedTexts[mergedTexts.length - 1] += t;\n\t\t\tcontinue;\n\t\t}\n\n\t\tmergedTexts.push(t);\n\t\tmergedKinds.push(k);\n\t\tmergedWordLike.push(wl);\n\t}\n\n\t// Get or create font-specific cache\n\tlet fontCache = cache.get(font);\n\tif (!fontCache) {\n\t\tfontCache = new Map<string, number>();\n\t\tcache.set(font, fontCache);\n\t}\n\n\tfunction measureCached(seg: string): number {\n\t\tlet w = fontCache!.get(seg);\n\t\tif (w === undefined) {\n\t\t\tif (stats) stats.misses += 1;\n\t\t\tconst raw = adapter.measureSegment(seg, font).width;\n\t\t\t// Coerce adapter misbehavior (NaN / Infinity / negative) to 0 — downstream\n\t\t\t// arithmetic would propagate NaN widths, breaking line-break decisions and\n\t\t\t// rendering. Cached so the coercion happens once per (font, segment).\n\t\t\tw = Number.isFinite(raw) && raw >= 0 ? raw : 0;\n\t\t\tfontCache!.set(seg, w);\n\t\t} else if (stats) {\n\t\t\tstats.hits += 1;\n\t\t}\n\t\treturn w;\n\t}\n\n\t// Build final prepared segments, splitting CJK into per-grapheme\n\tconst segments: PreparedSegment[] = [];\n\n\tfor (let i = 0; i < mergedTexts.length; i++) {\n\t\tconst t = mergedTexts[i]!;\n\t\tconst k = mergedKinds[i]!;\n\n\t\tif (k !== \"text\") {\n\t\t\t// Non-text segments: space, hard-break, soft-hyphen, zero-width-break\n\t\t\tsegments.push({\n\t\t\t\ttext: t,\n\t\t\t\twidth: k === \"space\" ? measureCached(\" \") * t.length : 0,\n\t\t\t\tkind: k,\n\t\t\t\tgraphemeWidths: null,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// CJK text: split into per-grapheme segments for line breaking\n\t\tif (isCJK(t)) {\n\t\t\tlet unitText = \"\";\n\t\t\tfor (const gs of graphemeSegmenter.segment(t)) {\n\t\t\t\tconst grapheme = gs.segment;\n\n\t\t\t\t// Kinsoku: line-start-prohibited chars stick to preceding unit\n\t\t\t\tif (unitText.length > 0 && kinsokuStart.has(grapheme)) {\n\t\t\t\t\tunitText += grapheme;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (unitText.length > 0) {\n\t\t\t\t\tconst w = measureCached(unitText);\n\t\t\t\t\tsegments.push({\n\t\t\t\t\t\ttext: unitText,\n\t\t\t\t\t\twidth: w,\n\t\t\t\t\t\tkind: \"text\",\n\t\t\t\t\t\tgraphemeWidths: null,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tunitText = grapheme;\n\t\t\t}\n\t\t\tif (unitText.length > 0) {\n\t\t\t\tconst w = measureCached(unitText);\n\t\t\t\tsegments.push({\n\t\t\t\t\ttext: unitText,\n\t\t\t\t\twidth: w,\n\t\t\t\t\tkind: \"text\",\n\t\t\t\t\tgraphemeWidths: null,\n\t\t\t\t});\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Non-CJK text: measure whole segment, pre-compute grapheme widths for break-word\n\t\tconst w = measureCached(t);\n\t\tlet graphemeWidths: number[] | null = null;\n\n\t\tif (mergedWordLike[i] && t.length > 1) {\n\t\t\tconst gWidths: number[] = [];\n\t\t\tfor (const gs of graphemeSegmenter.segment(t)) {\n\t\t\t\tgWidths.push(measureCached(gs.segment));\n\t\t\t}\n\t\t\tif (gWidths.length > 1) {\n\t\t\t\tgraphemeWidths = gWidths;\n\t\t\t}\n\t\t}\n\n\t\tsegments.push({ text: t, width: w, kind: \"text\", graphemeWidths });\n\t}\n\n\treturn segments;\n}\n\n// ---------------------------------------------------------------------------\n// Line breaking (greedy, ported from Pretext line-break.ts — core subset)\n// ---------------------------------------------------------------------------\n\n/**\n * Greedy line-breaking algorithm.\n *\n * Walks segments left to right, accumulating width. Breaks when a segment would\n * overflow maxWidth. Supports:\n * - Trailing space hang (spaces don't trigger breaks)\n * - overflow-wrap: break-word via grapheme widths\n * - Soft hyphens (break opportunity, adds visible hyphen width)\n * - Hard breaks (forced newline)\n */\nexport function computeLineBreaks(\n\tsegments: PreparedSegment[],\n\tmaxWidth: number,\n\tadapter: MeasurementAdapter,\n\tfont: string,\n\tcache: Map<string, Map<string, number>>,\n): LineBreaksResult {\n\tif (segments.length === 0) {\n\t\treturn { lines: [], lineCount: 0 };\n\t}\n\n\tconst lines: LayoutLine[] = [];\n\tlet lineW = 0;\n\tlet hasContent = false;\n\tlet lineStartSeg = 0;\n\tlet lineStartGrapheme = 0;\n\tlet lineEndSeg = 0;\n\tlet lineEndGrapheme = 0;\n\tlet pendingBreakSeg = -1;\n\tlet pendingBreakWidth = 0;\n\n\t// Measure hyphen for soft-hyphen support\n\tlet fontCache = cache.get(font);\n\tif (!fontCache) {\n\t\tfontCache = new Map<string, number>();\n\t\tcache.set(font, fontCache);\n\t}\n\tlet hyphenWidth = fontCache.get(\"-\");\n\tif (hyphenWidth === undefined) {\n\t\thyphenWidth = adapter.measureSegment(\"-\", font).width;\n\t\tfontCache.set(\"-\", hyphenWidth);\n\t}\n\n\tfunction flushLine(endSeg = lineEndSeg, endGrapheme = lineEndGrapheme, width = lineW) {\n\t\t// Build line text\n\t\tlet text = \"\";\n\t\tfor (let i = lineStartSeg; i < endSeg; i++) {\n\t\t\tconst seg = segments[i]!;\n\t\t\tif (seg.kind === \"soft-hyphen\" || seg.kind === \"hard-break\") continue;\n\t\t\tif (i === lineStartSeg && lineStartGrapheme > 0 && seg.graphemeWidths) {\n\t\t\t\t// Partial segment from grapheme break\n\t\t\t\tconst graphemeSegmenter = new Intl.Segmenter(undefined, {\n\t\t\t\t\tgranularity: \"grapheme\",\n\t\t\t\t});\n\t\t\t\tconst graphemes = [...graphemeSegmenter.segment(seg.text)].map((g) => g.segment);\n\t\t\t\ttext += graphemes.slice(lineStartGrapheme).join(\"\");\n\t\t\t} else {\n\t\t\t\ttext += seg.text;\n\t\t\t}\n\t\t}\n\t\t// Handle partial end segment\n\t\tif (endGrapheme > 0 && endSeg < segments.length) {\n\t\t\tconst seg = segments[endSeg]!;\n\t\t\tconst graphemeSegmenter = new Intl.Segmenter(undefined, {\n\t\t\t\tgranularity: \"grapheme\",\n\t\t\t});\n\t\t\tconst graphemes = [...graphemeSegmenter.segment(seg.text)].map((g) => g.segment);\n\t\t\tconst startG = lineStartSeg === endSeg ? lineStartGrapheme : 0;\n\t\t\ttext += graphemes.slice(startG, endGrapheme).join(\"\");\n\t\t}\n\t\t// Add visible hyphen if line ends at soft-hyphen\n\t\tif (\n\t\t\tendSeg > 0 &&\n\t\t\tsegments[endSeg - 1]?.kind === \"soft-hyphen\" &&\n\t\t\t!(lineStartSeg === endSeg && lineStartGrapheme > 0)\n\t\t) {\n\t\t\ttext += \"-\";\n\t\t}\n\n\t\tlines.push({\n\t\t\ttext,\n\t\t\twidth,\n\t\t\tstartSegment: lineStartSeg,\n\t\t\tstartGrapheme: lineStartGrapheme,\n\t\t\tendSegment: endSeg,\n\t\t\tendGrapheme,\n\t\t});\n\t\tlineW = 0;\n\t\thasContent = false;\n\t\tpendingBreakSeg = -1;\n\t\tpendingBreakWidth = 0;\n\t}\n\n\tfunction canBreakAfter(kind: SegmentBreakKind): boolean {\n\t\treturn kind === \"space\" || kind === \"zero-width-break\" || kind === \"soft-hyphen\";\n\t}\n\n\tfunction startLine(segIdx: number, graphemeIdx: number, width: number) {\n\t\thasContent = true;\n\t\tlineStartSeg = segIdx;\n\t\tlineStartGrapheme = graphemeIdx;\n\t\tlineEndSeg = segIdx + 1;\n\t\tlineEndGrapheme = 0;\n\t\tlineW = width;\n\t}\n\n\tfunction startLineAtGrapheme(segIdx: number, graphemeIdx: number, width: number) {\n\t\thasContent = true;\n\t\tlineStartSeg = segIdx;\n\t\tlineStartGrapheme = graphemeIdx;\n\t\tlineEndSeg = segIdx;\n\t\tlineEndGrapheme = graphemeIdx + 1;\n\t\tlineW = width;\n\t}\n\n\tfor (let i = 0; i < segments.length; i++) {\n\t\tconst seg = segments[i]!;\n\n\t\t// Hard break: emit current line, start fresh\n\t\tif (seg.kind === \"hard-break\") {\n\t\t\tif (hasContent) {\n\t\t\t\tflushLine();\n\t\t\t} else {\n\t\t\t\t// Empty line\n\t\t\t\tlines.push({\n\t\t\t\t\ttext: \"\",\n\t\t\t\t\twidth: 0,\n\t\t\t\t\tstartSegment: i,\n\t\t\t\t\tstartGrapheme: 0,\n\t\t\t\t\tendSegment: i,\n\t\t\t\t\tendGrapheme: 0,\n\t\t\t\t});\n\t\t\t}\n\t\t\tlineStartSeg = i + 1;\n\t\t\tlineStartGrapheme = 0;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst w = seg.width;\n\n\t\tif (!hasContent) {\n\t\t\t// First content on a new line\n\t\t\tif (w > maxWidth && seg.graphemeWidths) {\n\t\t\t\t// Word wider than maxWidth: break at grapheme level\n\t\t\t\tappendBreakableSegment(i, 0, seg.graphemeWidths);\n\t\t\t} else {\n\t\t\t\tstartLine(i, 0, w);\n\t\t\t}\n\t\t\tif (canBreakAfter(seg.kind)) {\n\t\t\t\tpendingBreakSeg = i + 1;\n\t\t\t\tpendingBreakWidth = seg.kind === \"space\" ? lineW - w : lineW;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst newW = lineW + w;\n\n\t\tif (newW > maxWidth + 0.005) {\n\t\t\t// Overflow\n\t\t\tif (canBreakAfter(seg.kind)) {\n\t\t\t\t// Trailing space: hang past edge, then break\n\t\t\t\tlineW += w;\n\t\t\t\tlineEndSeg = i + 1;\n\t\t\t\tlineEndGrapheme = 0;\n\t\t\t\tflushLine(i + 1, 0, seg.kind === \"space\" ? lineW - w : lineW);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (pendingBreakSeg >= 0) {\n\t\t\t\t// Break at last break opportunity\n\t\t\t\tflushLine(pendingBreakSeg, 0, pendingBreakWidth);\n\t\t\t\t// Don't advance i — re-process current segment on new line\n\t\t\t\ti--;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (w > maxWidth && seg.graphemeWidths) {\n\t\t\t\t// Break-word: split at grapheme level\n\t\t\t\tflushLine();\n\t\t\t\tappendBreakableSegment(i, 0, seg.graphemeWidths);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// No break opportunity: force break before this segment\n\t\t\tflushLine();\n\t\t\ti--;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Fits on current line\n\t\tlineW = newW;\n\t\tlineEndSeg = i + 1;\n\t\tlineEndGrapheme = 0;\n\n\t\tif (canBreakAfter(seg.kind)) {\n\t\t\tpendingBreakSeg = i + 1;\n\t\t\tpendingBreakWidth = seg.kind === \"space\" ? lineW - w : lineW;\n\t\t}\n\t}\n\n\tif (hasContent) {\n\t\tflushLine();\n\t}\n\n\treturn { lines, lineCount: lines.length };\n\n\tfunction appendBreakableSegment(segIdx: number, startG: number, gWidths: number[]) {\n\t\tfor (let g = startG; g < gWidths.length; g++) {\n\t\t\tconst gw = gWidths[g]!;\n\t\t\tif (!hasContent) {\n\t\t\t\tstartLineAtGrapheme(segIdx, g, gw);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (lineW + gw > maxWidth + 0.005) {\n\t\t\t\tflushLine();\n\t\t\t\tstartLineAtGrapheme(segIdx, g, gw);\n\t\t\t} else {\n\t\t\t\tlineW += gw;\n\t\t\t\tlineEndSeg = segIdx;\n\t\t\t\tlineEndGrapheme = g + 1;\n\t\t\t}\n\t\t}\n\t\t// If we consumed the whole segment, advance end past it\n\t\tif (hasContent && lineEndSeg === segIdx && lineEndGrapheme === gWidths.length) {\n\t\t\tlineEndSeg = segIdx + 1;\n\t\t\tlineEndGrapheme = 0;\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Cursor-based single-line layout (for multi-column flow / shape wrapping)\n// ---------------------------------------------------------------------------\n\nfunction canBreakAfter(kind: SegmentBreakKind): boolean {\n\treturn kind === \"space\" || kind === \"zero-width-break\" || kind === \"soft-hyphen\";\n}\n\n// Module-scoped segmenter — `Intl.Segmenter` construction is non-trivial and\n// its `segment()` method is stateless per-call, so one shared instance is safe\n// and avoids ~N-per-frame allocations when many lines cross segment boundaries.\nlet _graphemeSegmenter: Intl.Segmenter | null = null;\nfunction graphemeSegmenter(): Intl.Segmenter {\n\tif (_graphemeSegmenter === null) {\n\t\t_graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: \"grapheme\" });\n\t}\n\treturn _graphemeSegmenter;\n}\n\nfunction sliceSegmentText(seg: PreparedSegment, startG: number, endG: number): string {\n\tif (startG === 0 && endG < 0) return seg.text;\n\tconst graphemes = [...graphemeSegmenter().segment(seg.text)].map((g) => g.segment);\n\tconst stop = endG < 0 ? graphemes.length : endG;\n\treturn graphemes.slice(startG, stop).join(\"\");\n}\n\nfunction buildLineText(\n\tsegments: PreparedSegment[],\n\tstartSeg: number,\n\tstartG: number,\n\tendSeg: number,\n\tendG: number,\n\tappendHyphen: boolean,\n): string {\n\tlet text = \"\";\n\tfor (let i = startSeg; i < endSeg; i++) {\n\t\tconst seg = segments[i]!;\n\t\tif (seg.kind === \"soft-hyphen\" || seg.kind === \"hard-break\") continue;\n\t\tif (i === startSeg && startG > 0) {\n\t\t\ttext += sliceSegmentText(seg, startG, -1);\n\t\t} else {\n\t\t\ttext += seg.text;\n\t\t}\n\t}\n\tif (endG > 0 && endSeg < segments.length) {\n\t\tconst seg = segments[endSeg]!;\n\t\tconst from = startSeg === endSeg ? startG : 0;\n\t\ttext += sliceSegmentText(seg, from, endG);\n\t}\n\tif (appendHyphen) text += \"-\";\n\treturn text;\n}\n\nfunction resolveHyphenWidth(ctx: LayoutNextLineContext | undefined): number {\n\tif (!ctx || !ctx.adapter || !ctx.font) return 0;\n\tconst cache = ctx.cache;\n\tif (cache) {\n\t\tlet fc = cache.get(ctx.font);\n\t\tif (!fc) {\n\t\t\tfc = new Map<string, number>();\n\t\t\tcache.set(ctx.font, fc);\n\t\t}\n\t\tlet hw = fc.get(\"-\");\n\t\tif (hw === undefined) {\n\t\t\thw = ctx.adapter.measureSegment(\"-\", ctx.font).width;\n\t\t\tfc.set(\"-\", hw);\n\t\t}\n\t\treturn hw;\n\t}\n\treturn ctx.adapter.measureSegment(\"-\", ctx.font).width;\n}\n\n/**\n * Lay out the next single line starting from `cursor`, fitting into `slotWidth`.\n *\n * Unlike `computeLineBreaks`, which consumes whole text with one `maxWidth`,\n * this is the cursor-based primitive needed when successive lines have different\n * widths (multi-column flow, text wrapping around shape obstacles, mixed\n * column+pullquote layouts).\n *\n * Returns `null` when the cursor is past all segments (text exhausted).\n * At a hard-break with no preceding content, returns an empty line and advances\n * the cursor past the break so the caller can continue.\n *\n * ```ts\n * let cursor: LayoutCursor = { segmentIndex: 0, graphemeIndex: 0 };\n * while (true) {\n * const line = layoutNextLine(segments, cursor, availableWidth);\n * if (line === null) break;\n * render(line);\n * cursor = line.end;\n * }\n * ```\n */\nexport function layoutNextLine(\n\tsegments: PreparedSegment[],\n\tcursor: LayoutCursor,\n\tslotWidth: number,\n\tctx?: LayoutNextLineContext,\n): LayoutNextLineResult | null {\n\tlet i = cursor.segmentIndex;\n\tconst initialG = cursor.graphemeIndex;\n\n\tif (i >= segments.length) return null;\n\n\tif (initialG === 0) {\n\t\twhile (i < segments.length) {\n\t\t\tconst seg = segments[i]!;\n\t\t\tif (seg.kind === \"hard-break\") {\n\t\t\t\treturn {\n\t\t\t\t\ttext: \"\",\n\t\t\t\t\twidth: 0,\n\t\t\t\t\tstart: { segmentIndex: cursor.segmentIndex, graphemeIndex: 0 },\n\t\t\t\t\tend: { segmentIndex: i + 1, graphemeIndex: 0 },\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (seg.kind === \"space\" || seg.kind === \"zero-width-break\" || seg.kind === \"soft-hyphen\") {\n\t\t\t\ti += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (i >= segments.length) return null;\n\t}\n\n\tconst hyphenWidth = resolveHyphenWidth(ctx);\n\n\tconst startSeg = i;\n\tconst startG = i === cursor.segmentIndex ? initialG : 0;\n\n\tlet lineW = 0;\n\tlet lineEndSeg = startSeg;\n\tlet lineEndG = 0;\n\tlet hasContent = false;\n\tlet pendingBreakSeg = -1;\n\tlet pendingBreakG = 0;\n\tlet pendingBreakWidth = 0;\n\tlet pendingBreakSoftHyphen = false;\n\n\tconst recordPending = (\n\t\tsIdx: number,\n\t\tgIdx: number,\n\t\twidthAtBreak: number,\n\t\tkind: SegmentBreakKind,\n\t): void => {\n\t\tpendingBreakSeg = sIdx;\n\t\tpendingBreakG = gIdx;\n\t\tpendingBreakWidth = widthAtBreak;\n\t\tpendingBreakSoftHyphen = kind === \"soft-hyphen\";\n\t};\n\n\tconst consumeBreakable = (segIdx: number, gStart: number, gWidths: number[]): boolean => {\n\t\tfor (let g = gStart; g < gWidths.length; g++) {\n\t\t\tconst gw = gWidths[g]!;\n\t\t\tif (!hasContent) {\n\t\t\t\tlineW = gw;\n\t\t\t\tlineEndSeg = segIdx;\n\t\t\t\tlineEndG = g + 1;\n\t\t\t\thasContent = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (lineW + gw > slotWidth + 0.005) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tlineW += gw;\n\t\t\tlineEndSeg = segIdx;\n\t\t\tlineEndG = g + 1;\n\t\t}\n\t\tif (lineEndSeg === segIdx && lineEndG === gWidths.length) {\n\t\t\tlineEndSeg = segIdx + 1;\n\t\t\tlineEndG = 0;\n\t\t}\n\t\treturn false;\n\t};\n\n\tif (startG > 0 && startSeg < segments.length) {\n\t\tconst seg = segments[startSeg]!;\n\t\tif (seg.graphemeWidths) {\n\t\t\tconst overflowed = consumeBreakable(startSeg, startG, seg.graphemeWidths);\n\t\t\tif (overflowed) {\n\t\t\t\tconst text = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);\n\t\t\t\treturn {\n\t\t\t\t\ttext,\n\t\t\t\t\twidth: lineW,\n\t\t\t\t\tstart: { segmentIndex: startSeg, graphemeIndex: startG },\n\t\t\t\t\tend: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG },\n\t\t\t\t};\n\t\t\t}\n\t\t\ti = lineEndSeg;\n\t\t} else {\n\t\t\t// Mid-segment cursor on a non-breakable segment is an invariant\n\t\t\t// violation (cursor should only advance to a grapheme boundary via\n\t\t\t// `consumeBreakable` on a segment that HAS graphemeWidths). Treat as\n\t\t\t// segment-start so the caller gets well-formed output instead of\n\t\t\t// silently re-including the prefix.\n\t\t\t//\n\t\t\t// Not reachable through `computeFlowLines` but possible with\n\t\t\t// externally-constructed cursors.\n\t\t}\n\t}\n\n\tfor (; i < segments.length; ) {\n\t\tconst seg = segments[i]!;\n\n\t\tif (seg.kind === \"hard-break\") {\n\t\t\tif (hasContent) {\n\t\t\t\tconst endsAtSoftHyphen = lineEndSeg > 0 && segments[lineEndSeg - 1]?.kind === \"soft-hyphen\";\n\t\t\t\tconst text = buildLineText(\n\t\t\t\t\tsegments,\n\t\t\t\t\tstartSeg,\n\t\t\t\t\tstartG,\n\t\t\t\t\tlineEndSeg,\n\t\t\t\t\tlineEndG,\n\t\t\t\t\tendsAtSoftHyphen,\n\t\t\t\t);\n\t\t\t\treturn {\n\t\t\t\t\ttext,\n\t\t\t\t\twidth: lineW + (endsAtSoftHyphen ? hyphenWidth : 0),\n\t\t\t\t\tstart: { segmentIndex: startSeg, graphemeIndex: startG },\n\t\t\t\t\tend: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG },\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\ttext: \"\",\n\t\t\t\twidth: 0,\n\t\t\t\tstart: { segmentIndex: startSeg, graphemeIndex: startG },\n\t\t\t\tend: { segmentIndex: i + 1, graphemeIndex: 0 },\n\t\t\t};\n\t\t}\n\n\t\tconst w = seg.width;\n\n\t\tif (!hasContent) {\n\t\t\tif (w > slotWidth && seg.graphemeWidths) {\n\t\t\t\tconst overflowed = consumeBreakable(i, 0, seg.graphemeWidths);\n\t\t\t\tif (overflowed) {\n\t\t\t\t\tconst text = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttext,\n\t\t\t\t\t\twidth: lineW,\n\t\t\t\t\t\tstart: { segmentIndex: startSeg, graphemeIndex: startG },\n\t\t\t\t\t\tend: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG },\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\t// No `recordPending` here: segments with `graphemeWidths` are always\n\t\t\t\t// `kind === \"text\"` (see `analyzeAndMeasure`), which is not a break-\n\t\t\t\t// after kind, so the check would always fail.\n\t\t\t\ti = lineEndSeg;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlineW = w;\n\t\t\tlineEndSeg = i + 1;\n\t\t\tlineEndG = 0;\n\t\t\thasContent = true;\n\t\t\tif (canBreakAfter(seg.kind)) {\n\t\t\t\trecordPending(i + 1, 0, seg.kind === \"space\" ? lineW - w : lineW, seg.kind);\n\t\t\t}\n\t\t\ti += 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst newW = lineW + w;\n\n\t\tif (newW > slotWidth + 0.005) {\n\t\t\tif (canBreakAfter(seg.kind)) {\n\t\t\t\t// `lineW` is carried from before this segment — no mutation needed;\n\t\t\t\t// trailing space/soft-hyphen width is excluded from the line width.\n\t\t\t\tlineEndSeg = i + 1;\n\t\t\t\tlineEndG = 0;\n\t\t\t\tconst endsAtSoftHyphen = seg.kind === \"soft-hyphen\";\n\t\t\t\tconst finalWidth =\n\t\t\t\t\tseg.kind === \"space\" ? lineW : lineW + (endsAtSoftHyphen ? hyphenWidth : 0);\n\t\t\t\tconst text = buildLineText(\n\t\t\t\t\tsegments,\n\t\t\t\t\tstartSeg,\n\t\t\t\t\tstartG,\n\t\t\t\t\tlineEndSeg,\n\t\t\t\t\tlineEndG,\n\t\t\t\t\tendsAtSoftHyphen,\n\t\t\t\t);\n\t\t\t\treturn {\n\t\t\t\t\ttext,\n\t\t\t\t\twidth: finalWidth,\n\t\t\t\t\tstart: { segmentIndex: startSeg, graphemeIndex: startG },\n\t\t\t\t\tend: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (pendingBreakSeg >= 0) {\n\t\t\t\tconst text = buildLineText(\n\t\t\t\t\tsegments,\n\t\t\t\t\tstartSeg,\n\t\t\t\t\tstartG,\n\t\t\t\t\tpendingBreakSeg,\n\t\t\t\t\tpendingBreakG,\n\t\t\t\t\tpendingBreakSoftHyphen,\n\t\t\t\t);\n\t\t\t\treturn {\n\t\t\t\t\ttext,\n\t\t\t\t\twidth: pendingBreakWidth + (pendingBreakSoftHyphen ? hyphenWidth : 0),\n\t\t\t\t\tstart: { segmentIndex: startSeg, graphemeIndex: startG },\n\t\t\t\t\tend: { segmentIndex: pendingBreakSeg, graphemeIndex: pendingBreakG },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (w > slotWidth && seg.graphemeWidths) {\n\t\t\t\tconst text = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);\n\t\t\t\treturn {\n\t\t\t\t\ttext,\n\t\t\t\t\twidth: lineW,\n\t\t\t\t\tstart: { segmentIndex: startSeg, graphemeIndex: startG },\n\t\t\t\t\tend: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst text = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);\n\t\t\treturn {\n\t\t\t\ttext,\n\t\t\t\twidth: lineW,\n\t\t\t\tstart: { segmentIndex: startSeg, graphemeIndex: startG },\n\t\t\t\tend: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG },\n\t\t\t};\n\t\t}\n\n\t\tlineW = newW;\n\t\tlineEndSeg = i + 1;\n\t\tlineEndG = 0;\n\t\tif (canBreakAfter(seg.kind)) {\n\t\t\trecordPending(i + 1, 0, seg.kind === \"space\" ? lineW - w : lineW, seg.kind);\n\t\t}\n\t\ti += 1;\n\t}\n\n\tif (!hasContent) return null;\n\n\tconst endsAtSoftHyphen = lineEndSeg > 0 && segments[lineEndSeg - 1]?.kind === \"soft-hyphen\";\n\tconst text = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, endsAtSoftHyphen);\n\treturn {\n\t\ttext,\n\t\twidth: lineW + (endsAtSoftHyphen ? hyphenWidth : 0),\n\t\tstart: { segmentIndex: startSeg, graphemeIndex: startG },\n\t\tend: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG },\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Slot carving (flow-layout helper)\n// ---------------------------------------------------------------------------\n\n/**\n * Subtract blocked horizontal intervals from a base interval, producing\n * remaining ordered, non-overlapping slots wide enough to fit text.\n *\n * Pure geometry — no text dependency. Used by flow-layout to turn obstacle\n * intersections into per-line layout slots.\n *\n * ```ts\n * carveTextLineSlots({left: 0, right: 600}, [{left: 200, right: 280}])\n * // → [{left: 0, right: 200}, {left: 280, right: 600}]\n * ```\n */\nexport function carveTextLineSlots(\n\tbase: Interval,\n\tblocked: Interval[],\n\tminSlotWidth = 0,\n): Interval[] {\n\tlet slots: Interval[] = [base];\n\tfor (let bi = 0; bi < blocked.length; bi++) {\n\t\tconst block = blocked[bi]!;\n\t\tconst next: Interval[] = [];\n\t\tfor (let si = 0; si < slots.length; si++) {\n\t\t\tconst slot = slots[si]!;\n\t\t\tif (block.right <= slot.left || block.left >= slot.right) {\n\t\t\t\tnext.push(slot);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (block.left > slot.left) next.push({ left: slot.left, right: block.left });\n\t\t\tif (block.right < slot.right) next.push({ left: block.right, right: slot.right });\n\t\t}\n\t\tslots = next;\n\t}\n\tif (minSlotWidth > 0) {\n\t\treturn slots.filter((s) => s.right - s.left >= minSlotWidth);\n\t}\n\treturn slots;\n}\n\n// ---------------------------------------------------------------------------\n// Character positions\n// ---------------------------------------------------------------------------\n\n/** Compute per-character x,y positions from line breaks and segments. */\nexport function computeCharPositions(\n\tlineBreaks: LineBreaksResult,\n\tsegments: PreparedSegment[],\n\tlineHeight: number,\n): CharPosition[] {\n\tconst positions: CharPosition[] = [];\n\tconst graphemeSegmenter = new Intl.Segmenter(undefined, {\n\t\tgranularity: \"grapheme\",\n\t});\n\n\tfor (let lineIdx = 0; lineIdx < lineBreaks.lines.length; lineIdx++) {\n\t\tconst line = lineBreaks.lines[lineIdx]!;\n\t\tconst y = lineIdx * lineHeight;\n\t\tlet x = 0;\n\n\t\tfor (let si = line.startSegment; si < segments.length; si++) {\n\t\t\tconst seg = segments[si]!;\n\t\t\tif (seg.kind === \"soft-hyphen\" || seg.kind === \"hard-break\") {\n\t\t\t\t// Skip non-visual segments but stop if past the line\n\t\t\t\tif (si >= line.endSegment && line.endGrapheme === 0) break;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst graphemes = [...graphemeSegmenter.segment(seg.text)].map((g) => g.segment);\n\t\t\tif (graphemes.length === 0) continue;\n\t\t\tconst startG = si === line.startSegment ? line.startGrapheme : 0;\n\n\t\t\t// Determine how many graphemes of this segment belong to this line\n\t\t\tlet endG: number;\n\t\t\tif (si < line.endSegment) {\n\t\t\t\t// Full segment is on this line\n\t\t\t\tendG = graphemes.length;\n\t\t\t} else if (si === line.endSegment && line.endGrapheme > 0) {\n\t\t\t\t// Partial segment (grapheme-level break)\n\t\t\t\tendG = line.endGrapheme;\n\t\t\t} else {\n\t\t\t\t// Past the line's content\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tfor (let g = startG; g < endG; g++) {\n\t\t\t\tconst gWidth = seg.graphemeWidths ? seg.graphemeWidths[g]! : seg.width / graphemes.length;\n\t\t\t\tpositions.push({ x, y, width: gWidth, height: lineHeight, line: lineIdx });\n\t\t\t\tx += gWidth;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn positions;\n}\n\n// ---------------------------------------------------------------------------\n// Reactive graph factory\n// ---------------------------------------------------------------------------\n\nexport type ReactiveLayoutOptions = {\n\t/** Measurement backend (required). */\n\tadapter: MeasurementAdapter;\n\t/** Graph name (default: \"reactive-layout\"). */\n\tname?: string;\n\t/** Initial text. */\n\ttext?: string;\n\t/** Initial CSS font string. */\n\tfont?: string;\n\t/** Initial line height in px. */\n\tlineHeight?: number;\n\t/** Initial max width in px (clamped to ≥ 0). */\n\tmaxWidth?: number;\n};\n\n/**\n * Create a reactive text layout graph.\n *\n * ```\n * Graph(\"reactive-layout\")\n * ├── state(\"text\")\n * ├── state(\"font\")\n * ├── state(\"line-height\")\n * ├── state(\"max-width\")\n * ├── derived(\"segments\") — text + font → PreparedSegment[]\n * ├── derived(\"line-breaks\") — segments + max-width → LineBreaksResult\n * ├── derived(\"height\") — line-breaks → number\n * └── derived(\"char-positions\") — line-breaks + segments → CharPosition[]\n * ```\n */\nexport function reactiveLayout(opts: ReactiveLayoutOptions): ReactiveLayoutBundle {\n\tconst { adapter, name = \"reactive-layout\" } = opts;\n\tconst g = new Graph(name);\n\n\t// Shared measurement cache: Map<font, Map<segment, width>>\n\tconst measureCache = new Map<string, Map<string, number>>();\n\n\t// --- State nodes ---\n\tconst textNode = state<string>(opts.text ?? \"\", { name: \"text\" });\n\tconst fontNode = state<string>(opts.font ?? \"16px sans-serif\", {\n\t\tname: \"font\",\n\t});\n\tconst lineHeightNode = state<number>(opts.lineHeight ?? 20, {\n\t\tname: \"line-height\",\n\t});\n\tconst maxWidthNode = state<number>(Math.max(0, opts.maxWidth ?? 800), {\n\t\tname: \"max-width\",\n\t});\n\n\t// --- Derived: segments (text + font → PreparedSegment[]) ---\n\tfunction graphemeWidthsEqual(a: number[] | null, b: number[] | null): boolean {\n\t\tif (a === null || b === null) return a === b;\n\t\tif (a.length !== b.length) return false;\n\t\tfor (let i = 0; i < a.length; i++) {\n\t\t\tif (a[i] !== b[i]!) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t// Raw `node(...)` instead of `derived(...)` so the fn can return a\n\t// cleanup function. Core fires function-form cleanup on INVALIDATE (see\n\t// `node.ts:_updateState` INVALIDATE branch), which replaces the old v4\n\t// per-node `onMessage` hook that watched for INVALIDATE/TEARDOWN to flush\n\t// measurement caches.\n\tconst segmentsNode: Node<PreparedSegment[]> = node<PreparedSegment[]>(\n\t\t[textNode, fontNode],\n\t\t(data, actions, ctx) => {\n\t\t\tconst b0 = data[0];\n\t\t\tconst textVal = (b0 != null && b0.length > 0 ? b0.at(-1) : ctx.prevData[0]) as string;\n\t\t\tconst b1 = data[1];\n\t\t\tconst fontVal = (b1 != null && b1.length > 0 ? b1.at(-1) : ctx.prevData[1]) as string;\n\t\t\tconst t0 = monotonicNs();\n\t\t\tconst measureStats: SegmentMeasureStats = { hits: 0, misses: 0 };\n\t\t\tconst result = analyzeAndMeasure(\n\t\t\t\ttextVal as string,\n\t\t\t\tfontVal as string,\n\t\t\t\tadapter,\n\t\t\t\tmeasureCache,\n\t\t\t\tmeasureStats,\n\t\t\t);\n\t\t\tconst elapsed = monotonicNs() - t0;\n\n\t\t\tconst lookups = measureStats.hits + measureStats.misses;\n\t\t\tconst hitRate = lookups === 0 ? 1 : measureStats.hits / lookups;\n\n\t\t\t// After parent `segments` emits, deliver metrics via phase-3 deferral\n\t\t\t// so observers see the parent value first.\n\t\t\t// Forwards via `emitToMeta` (see `patterns/_internal.ts`).\n\t\t\tconst meta = segmentsNode.meta;\n\t\t\tif (meta) {\n\t\t\t\temitToMeta(meta[\"cache-hit-rate\"], hitRate);\n\t\t\t\temitToMeta(meta[\"segment-count\"], result.length);\n\t\t\t\temitToMeta(meta[\"layout-time-ns\"], elapsed);\n\t\t\t}\n\n\t\t\tactions.emit(result);\n\n\t\t\t// Function-form cleanup: fires before the next run AND on\n\t\t\t// deactivation AND on INVALIDATE. Flushes the shared measurement\n\t\t\t// cache + the adapter's own cache so stale measurements don't\n\t\t\t// persist across INVALIDATE broadcasts.\n\t\t\treturn () => {\n\t\t\t\tmeasureCache.clear();\n\t\t\t\tadapter.clearCache?.();\n\t\t\t};\n\t\t},\n\t\t{\n\t\t\tname: \"segments\",\n\t\t\tdescribeKind: \"derived\",\n\t\t\tmeta: {\n\t\t\t\t\"cache-hit-rate\": 0,\n\t\t\t\t\"segment-count\": 0,\n\t\t\t\t\"layout-time-ns\": 0,\n\t\t\t},\n\t\t\tequals: (a, b) => {\n\t\t\t\tconst sa = a as PreparedSegment[] | null;\n\t\t\t\tconst sb = b as PreparedSegment[] | null;\n\t\t\t\tif (sa == null || sb == null) return sa === sb;\n\t\t\t\tif (sa.length !== sb.length) return false;\n\t\t\t\tfor (let i = 0; i < sa.length; i++) {\n\t\t\t\t\tconst pa = sa[i]!;\n\t\t\t\t\tconst pb = sb[i]!;\n\t\t\t\t\tif (\n\t\t\t\t\t\tpa.text !== pb.text ||\n\t\t\t\t\t\tpa.width !== pb.width ||\n\t\t\t\t\t\tpa.kind !== pb.kind ||\n\t\t\t\t\t\t!graphemeWidthsEqual(pa.graphemeWidths ?? null, pb.graphemeWidths ?? null)\n\t\t\t\t\t)\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t},\n\t\t},\n\t);\n\n\t// --- Derived: line-breaks (segments + max-width + font → LineBreaksResult) ---\n\tconst lineBreaksNode = derived<LineBreaksResult>(\n\t\t[segmentsNode, maxWidthNode, fontNode],\n\t\t([segs, mw, font]) => {\n\t\t\treturn computeLineBreaks(\n\t\t\t\tsegs as PreparedSegment[],\n\t\t\t\tmw as number,\n\t\t\t\tadapter,\n\t\t\t\tfont as string,\n\t\t\t\tmeasureCache,\n\t\t\t);\n\t\t},\n\t\t{\n\t\t\tname: \"line-breaks\",\n\t\t\tequals: (a, b) => {\n\t\t\t\tconst la = a as LineBreaksResult | null;\n\t\t\t\tconst lb = b as LineBreaksResult | null;\n\t\t\t\tif (la == null || lb == null) return la === lb;\n\t\t\t\tif (la.lineCount !== lb.lineCount) return false;\n\t\t\t\tfor (let i = 0; i < la.lines.length; i++) {\n\t\t\t\t\tconst lineA = la.lines[i]!;\n\t\t\t\t\tconst lineB = lb.lines[i]!;\n\t\t\t\t\tif (\n\t\t\t\t\t\tlineA.text !== lineB.text ||\n\t\t\t\t\t\tlineA.width !== lineB.width ||\n\t\t\t\t\t\tlineA.startSegment !== lineB.startSegment ||\n\t\t\t\t\t\tlineA.startGrapheme !== lineB.startGrapheme ||\n\t\t\t\t\t\tlineA.endSegment !== lineB.endSegment ||\n\t\t\t\t\t\tlineA.endGrapheme !== lineB.endGrapheme\n\t\t\t\t\t)\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t},\n\t\t},\n\t);\n\n\t// --- Derived: height ---\n\tconst heightNode = derived<number>(\n\t\t[lineBreaksNode, lineHeightNode],\n\t\t([lb, lh]) => (lb as LineBreaksResult).lineCount * (lh as number),\n\t\t{ name: \"height\" },\n\t);\n\n\t// --- Derived: char-positions ---\n\tconst charPositionsNode = derived<CharPosition[]>(\n\t\t[lineBreaksNode, segmentsNode, lineHeightNode],\n\t\t([lb, segs, lh]) => {\n\t\t\treturn computeCharPositions(lb as LineBreaksResult, segs as PreparedSegment[], lh as number);\n\t\t},\n\t\t{\n\t\t\tname: \"char-positions\",\n\t\t\tequals: (a, b) => {\n\t\t\t\tconst ca = a as CharPosition[] | null;\n\t\t\t\tconst cb = b as CharPosition[] | null;\n\t\t\t\tif (ca == null || cb == null) return ca === cb;\n\t\t\t\tif (ca.length !== cb.length) return false;\n\t\t\t\tfor (let i = 0; i < ca.length; i++) {\n\t\t\t\t\tif (ca[i]!.x !== cb[i]!.x || ca[i]!.y !== cb[i]!.y || ca[i]!.width !== cb[i]!.width)\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t},\n\t\t},\n\t);\n\n\t// --- Register in graph ---\n\tg.add(\"text\", textNode);\n\tg.add(\"font\", fontNode);\n\tg.add(\"line-height\", lineHeightNode);\n\tg.add(\"max-width\", maxWidthNode);\n\tg.add(\"segments\", segmentsNode);\n\tg.add(\"line-breaks\", lineBreaksNode);\n\tg.add(\"height\", heightNode);\n\tg.add(\"char-positions\", charPositionsNode);\n\n\t// --- Edges (for describe() visibility) ---\n\n\treturn {\n\t\tgraph: g,\n\t\tsetText: (text: string) => g.set(\"text\", text),\n\t\tsetFont: (font: string) => g.set(\"font\", font),\n\t\tsetLineHeight: (lh: number) => g.set(\"line-height\", lh),\n\t\tsetMaxWidth: (mw: number) => g.set(\"max-width\", Math.max(0, mw)),\n\t\tsegments: segmentsNode,\n\t\tlineBreaks: lineBreaksNode,\n\t\theight: heightNode,\n\t\tcharPositions: charPositionsNode,\n\t};\n}\n"],"mappings":"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","names":["graphemeSegmenter","w","canBreakAfter","text","endsAtSoftHyphen"]}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DATA,
|
|
3
|
+
DIRTY,
|
|
4
|
+
__export
|
|
5
|
+
} from "./chunk-SX52TAR4.js";
|
|
6
|
+
|
|
7
|
+
// src/compat/vue/index.ts
|
|
8
|
+
var vue_exports = {};
|
|
9
|
+
__export(vue_exports, {
|
|
10
|
+
useStore: () => useStore,
|
|
11
|
+
useSubscribe: () => useSubscribe,
|
|
12
|
+
useSubscribeRecord: () => useSubscribeRecord
|
|
13
|
+
});
|
|
14
|
+
import {
|
|
15
|
+
computed,
|
|
16
|
+
getCurrentScope,
|
|
17
|
+
isRef,
|
|
18
|
+
onScopeDispose,
|
|
19
|
+
readonly,
|
|
20
|
+
shallowRef,
|
|
21
|
+
watch
|
|
22
|
+
} from "vue";
|
|
23
|
+
function useSubscribe(node) {
|
|
24
|
+
const ref = shallowRef(node.cache);
|
|
25
|
+
const unsub = node.subscribe(() => {
|
|
26
|
+
ref.value = node.cache;
|
|
27
|
+
});
|
|
28
|
+
if (getCurrentScope()) {
|
|
29
|
+
onScopeDispose(() => unsub());
|
|
30
|
+
} else if (typeof console !== "undefined") {
|
|
31
|
+
console.warn(
|
|
32
|
+
"[graphrefly-ts] useSubscribe called outside a Vue scope \u2014 subscription will not be auto-disposed."
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return readonly(ref);
|
|
36
|
+
}
|
|
37
|
+
function useStore(node) {
|
|
38
|
+
const inner = shallowRef(node.cache);
|
|
39
|
+
const unsub = node.subscribe(() => {
|
|
40
|
+
inner.value = node.cache;
|
|
41
|
+
});
|
|
42
|
+
if (getCurrentScope()) {
|
|
43
|
+
onScopeDispose(() => unsub());
|
|
44
|
+
} else if (typeof console !== "undefined") {
|
|
45
|
+
console.warn(
|
|
46
|
+
"[graphrefly-ts] useStore called outside a Vue scope \u2014 subscription will not be auto-disposed."
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
return computed({
|
|
50
|
+
get: () => inner.value,
|
|
51
|
+
set: (v) => {
|
|
52
|
+
node.down([[DIRTY], [DATA, v]]);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function useSubscribeRecord(keys, factory) {
|
|
57
|
+
const result = shallowRef({});
|
|
58
|
+
const activeSubs = /* @__PURE__ */ new Map();
|
|
59
|
+
function flushResult() {
|
|
60
|
+
const snap = {};
|
|
61
|
+
for (const [key, entry] of activeSubs) {
|
|
62
|
+
snap[key] = { ...entry.values };
|
|
63
|
+
}
|
|
64
|
+
result.value = snap;
|
|
65
|
+
}
|
|
66
|
+
function sync(newKeys) {
|
|
67
|
+
for (const entry of activeSubs.values()) {
|
|
68
|
+
for (const unsub of entry.subs) unsub();
|
|
69
|
+
}
|
|
70
|
+
activeSubs.clear();
|
|
71
|
+
for (const key of newKeys) {
|
|
72
|
+
const nodes = factory(key);
|
|
73
|
+
const fields = Object.keys(nodes);
|
|
74
|
+
const values = {};
|
|
75
|
+
const subs = [];
|
|
76
|
+
for (const field of fields) {
|
|
77
|
+
const node = nodes[field];
|
|
78
|
+
values[field] = node.cache;
|
|
79
|
+
const unsub = node.subscribe(() => {
|
|
80
|
+
values[field] = node.cache;
|
|
81
|
+
flushResult();
|
|
82
|
+
});
|
|
83
|
+
subs.push(unsub);
|
|
84
|
+
}
|
|
85
|
+
activeSubs.set(key, { subs, values });
|
|
86
|
+
}
|
|
87
|
+
const snap = {};
|
|
88
|
+
for (const [key, entry] of activeSubs) {
|
|
89
|
+
snap[key] = { ...entry.values };
|
|
90
|
+
}
|
|
91
|
+
result.value = snap;
|
|
92
|
+
}
|
|
93
|
+
const readKeys = () => {
|
|
94
|
+
const current = typeof keys === "function" ? keys() : isRef(keys) ? keys.value : keys;
|
|
95
|
+
return [...current ?? []];
|
|
96
|
+
};
|
|
97
|
+
watch(readKeys, (newKeys) => sync(newKeys ?? []), { immediate: true });
|
|
98
|
+
if (getCurrentScope()) {
|
|
99
|
+
onScopeDispose(() => {
|
|
100
|
+
for (const entry of activeSubs.values()) {
|
|
101
|
+
for (const unsub of entry.subs) unsub();
|
|
102
|
+
}
|
|
103
|
+
activeSubs.clear();
|
|
104
|
+
});
|
|
105
|
+
} else if (typeof console !== "undefined") {
|
|
106
|
+
console.warn(
|
|
107
|
+
"[graphrefly-ts] useSubscribeRecord called outside a Vue scope \u2014 subscription will not be auto-disposed."
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
return readonly(result);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export {
|
|
114
|
+
useSubscribe,
|
|
115
|
+
useStore,
|
|
116
|
+
useSubscribeRecord,
|
|
117
|
+
vue_exports
|
|
118
|
+
};
|
|
119
|
+
//# sourceMappingURL=chunk-MJ2NKQQL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/compat/vue/index.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Vue bindings — useStore / useSubscribe\n// ---------------------------------------------------------------------------\n// Bridges GraphReFly nodes into Vue reactivity. Works with any\n// Node<T>, including companion nodes (node.meta.status).\n//\n// Usage:\n// import { useStore, useSubscribe } from '@graphrefly/graphrefly-ts/compat/vue';\n// // Optional peer install (only for this adapter): pnpm add vue\n// const count = useStore(counterNode); // Ref<number | undefined> (read + write)\n// const status = useSubscribe(wsStatusNode); // Readonly<Ref<string | undefined>>\n// ---------------------------------------------------------------------------\n\nimport {\n\tcomputed,\n\tgetCurrentScope,\n\tisRef,\n\tonScopeDispose,\n\ttype Ref,\n\treadonly,\n\tshallowRef,\n\ttype WatchSource,\n\twatch,\n} from \"vue\";\nimport { DATA, DIRTY } from \"../../core/messages.js\";\nimport type { Node } from \"../../core/node.js\";\n\n/**\n * Subscribe to a read-only `Node<T>` as a Vue `Ref<T>`. Auto-unsubscribes on scope disposal.\n * Subscription lifecycle is tied to Vue scope disposal (not node terminal messages).\n */\nexport function useSubscribe<T>(node: Node<T>): Readonly<Ref<T | undefined | null>> {\n\tconst ref = shallowRef(node.cache) as Ref<T | undefined | null>;\n\n\tconst unsub = node.subscribe(() => {\n\t\tref.value = node.cache;\n\t});\n\n\tif (getCurrentScope()) {\n\t\tonScopeDispose(() => unsub());\n\t} else if (typeof console !== \"undefined\") {\n\t\tconsole.warn(\n\t\t\t\"[graphrefly-ts] useSubscribe called outside a Vue scope — subscription will not be auto-disposed.\",\n\t\t);\n\t}\n\n\treturn readonly(ref) as Readonly<Ref<T | undefined | null>>;\n}\n\n/**\n * Bind a writable `Node<T>` as a Vue `Ref<T>`. Reads and writes are bidirectional.\n * Value sets always dispatch `[[DIRTY], [DATA, value]]`, including `value === undefined`.\n * Subscription lifecycle is tied to Vue scope disposal (not node terminal messages).\n */\nexport function useStore<T>(node: Node<T>): Ref<T | undefined | null> {\n\tconst inner = shallowRef(node.cache) as Ref<T | undefined | null>;\n\n\tconst unsub = node.subscribe(() => {\n\t\tinner.value = node.cache;\n\t});\n\n\tif (getCurrentScope()) {\n\t\tonScopeDispose(() => unsub());\n\t} else if (typeof console !== \"undefined\") {\n\t\tconsole.warn(\n\t\t\t\"[graphrefly-ts] useStore called outside a Vue scope — subscription will not be auto-disposed.\",\n\t\t);\n\t}\n\n\treturn computed({\n\t\tget: () => inner.value,\n\t\tset: (v: T | undefined | null) => {\n\t\t\tnode.down([[DIRTY], [DATA, v]]);\n\t\t},\n\t});\n}\n\n/** Maps a key to an object of nodes. Used by `useSubscribeRecord` factory. */\nexport type NodeFactory<K, R extends Record<string, any>> = (key: K) => {\n\t[P in keyof R]: Node<R[P]>;\n};\n\n/**\n * Subscribe to a dynamic set of keyed node records. When keys change,\n * old subscriptions are torn down and new ones created automatically.\n * Must be called during Vue `setup()`.\n */\nexport function useSubscribeRecord<K extends string, R extends Record<string, any>>(\n\tkeys: WatchSource<K[] | undefined>,\n\tfactory: NodeFactory<K, R>,\n): Readonly<Ref<Record<K, R>>> {\n\tconst result = shallowRef<Record<K, R>>({} as Record<K, R>);\n\n\t// Track active subscriptions per key (strictly enclosed memory mapping)\n\tconst activeSubs = new Map<K, { subs: Array<() => void>; values: R }>();\n\tfunction flushResult() {\n\t\tconst snap = {} as Record<K, R>;\n\t\tfor (const [key, entry] of activeSubs) {\n\t\t\tsnap[key] = { ...entry.values };\n\t\t}\n\t\tresult.value = snap;\n\t}\n\n\tfunction sync(newKeys: K[]) {\n\t\tfor (const entry of activeSubs.values()) {\n\t\t\tfor (const unsub of entry.subs) unsub();\n\t\t}\n\t\tactiveSubs.clear();\n\n\t\tfor (const key of newKeys) {\n\t\t\tconst nodes = factory(key);\n\t\t\tconst fields = Object.keys(nodes) as (keyof R)[];\n\t\t\tconst values = {} as R;\n\t\t\tconst subs: Array<() => void> = [];\n\n\t\t\tfor (const field of fields) {\n\t\t\t\tconst node = nodes[field];\n\t\t\t\tvalues[field] = node.cache as R[keyof R];\n\t\t\t\tconst unsub = node.subscribe(() => {\n\t\t\t\t\tvalues[field] = node.cache as R[keyof R];\n\t\t\t\t\tflushResult();\n\t\t\t\t});\n\t\t\t\tsubs.push(unsub);\n\t\t\t}\n\n\t\t\tactiveSubs.set(key, { subs, values });\n\t\t}\n\n\t\tconst snap = {} as Record<K, R>;\n\t\tfor (const [key, entry] of activeSubs) {\n\t\t\tsnap[key] = { ...entry.values };\n\t\t}\n\t\tresult.value = snap;\n\t}\n\n\tconst readKeys = (): K[] => {\n\t\tconst current = typeof keys === \"function\" ? keys() : isRef(keys) ? keys.value : keys;\n\t\treturn [...(current ?? [])];\n\t};\n\n\twatch(readKeys, (newKeys) => sync(newKeys ?? []), { immediate: true });\n\n\tif (getCurrentScope()) {\n\t\tonScopeDispose(() => {\n\t\t\tfor (const entry of activeSubs.values()) {\n\t\t\t\tfor (const unsub of entry.subs) unsub();\n\t\t\t}\n\t\t\tactiveSubs.clear();\n\t\t});\n\t} else if (typeof console !== \"undefined\") {\n\t\tconsole.warn(\n\t\t\t\"[graphrefly-ts] useSubscribeRecord called outside a Vue scope — subscription will not be auto-disposed.\",\n\t\t);\n\t}\n\n\treturn readonly(result) as Readonly<Ref<Record<K, R>>>;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import {
|
|
2
|
+
batch,
|
|
3
|
+
dynamicNode,
|
|
4
|
+
state
|
|
5
|
+
} from "./chunk-PHOUUNK7.js";
|
|
6
|
+
import {
|
|
7
|
+
DATA,
|
|
8
|
+
ERROR,
|
|
9
|
+
__export
|
|
10
|
+
} from "./chunk-SX52TAR4.js";
|
|
11
|
+
|
|
12
|
+
// src/compat/nanostores/index.ts
|
|
13
|
+
var nanostores_exports = {};
|
|
14
|
+
__export(nanostores_exports, {
|
|
15
|
+
action: () => action,
|
|
16
|
+
atom: () => atom,
|
|
17
|
+
computed: () => computed,
|
|
18
|
+
getValue: () => getValue,
|
|
19
|
+
map: () => map,
|
|
20
|
+
onMount: () => onMount,
|
|
21
|
+
onStart: () => onStart,
|
|
22
|
+
onStop: () => onStop
|
|
23
|
+
});
|
|
24
|
+
var START_LISTENERS = /* @__PURE__ */ new WeakMap();
|
|
25
|
+
var STOP_LISTENERS = /* @__PURE__ */ new WeakMap();
|
|
26
|
+
function trigger(node, map2) {
|
|
27
|
+
const callbacks = map2.get(node);
|
|
28
|
+
if (callbacks) {
|
|
29
|
+
for (const cb of callbacks) cb();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function createStore(node, extra = {}) {
|
|
33
|
+
let listeners = 0;
|
|
34
|
+
const store = {
|
|
35
|
+
...extra,
|
|
36
|
+
get: () => getVal(node),
|
|
37
|
+
subscribe: (cb) => {
|
|
38
|
+
if (listeners === 0) trigger(node, START_LISTENERS);
|
|
39
|
+
listeners++;
|
|
40
|
+
const sub = node.subscribe((msgs) => {
|
|
41
|
+
for (const [t, v] of msgs) {
|
|
42
|
+
if (t === DATA) cb(v);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
return () => {
|
|
46
|
+
sub();
|
|
47
|
+
listeners--;
|
|
48
|
+
if (listeners === 0) trigger(node, STOP_LISTENERS);
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
listen: (cb) => {
|
|
52
|
+
if (listeners === 0) trigger(node, START_LISTENERS);
|
|
53
|
+
listeners++;
|
|
54
|
+
let initial = true;
|
|
55
|
+
const sub = node.subscribe((msgs) => {
|
|
56
|
+
for (const [t, v] of msgs) {
|
|
57
|
+
if (t === DATA) {
|
|
58
|
+
if (initial) {
|
|
59
|
+
initial = false;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
cb(v);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
return () => {
|
|
67
|
+
sub();
|
|
68
|
+
listeners--;
|
|
69
|
+
if (listeners === 0) trigger(node, STOP_LISTENERS);
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
_node: node
|
|
73
|
+
};
|
|
74
|
+
return store;
|
|
75
|
+
}
|
|
76
|
+
function pull(n) {
|
|
77
|
+
let val = n.cache;
|
|
78
|
+
let err;
|
|
79
|
+
const unsub = n.subscribe((msgs) => {
|
|
80
|
+
for (const [t, v] of msgs) {
|
|
81
|
+
if (t === DATA) val = v;
|
|
82
|
+
if (t === ERROR) err = v;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
unsub();
|
|
86
|
+
if (err) throw err;
|
|
87
|
+
return val;
|
|
88
|
+
}
|
|
89
|
+
function getVal(n) {
|
|
90
|
+
if (n.status === "sentinel") {
|
|
91
|
+
return pull(n);
|
|
92
|
+
}
|
|
93
|
+
return n.cache;
|
|
94
|
+
}
|
|
95
|
+
function atom(initial) {
|
|
96
|
+
const n = state(initial, {
|
|
97
|
+
resubscribable: true,
|
|
98
|
+
resetOnTeardown: true
|
|
99
|
+
});
|
|
100
|
+
return createStore(n, {
|
|
101
|
+
// `n.emit` routes through the framed pipeline: `bundle()` auto-
|
|
102
|
+
// prefixes `[DIRTY]` (diamond-safe wave coordination) and the
|
|
103
|
+
// node's `equals` (default `Object.is`) folds same-value writes
|
|
104
|
+
// into `RESOLVED`. Matches nanostores' documented Object.is
|
|
105
|
+
// dedup semantics without any custom check.
|
|
106
|
+
set: (value) => n.emit(value)
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function computed(stores, fn) {
|
|
110
|
+
const storeArray = Array.isArray(stores) ? stores : [stores];
|
|
111
|
+
const depNodes = storeArray.map((s) => s._node);
|
|
112
|
+
const n = dynamicNode(
|
|
113
|
+
depNodes,
|
|
114
|
+
(track) => {
|
|
115
|
+
const vals = storeArray.map((s) => {
|
|
116
|
+
const node = s._node;
|
|
117
|
+
if (node.status === "sentinel") {
|
|
118
|
+
pull(node);
|
|
119
|
+
}
|
|
120
|
+
return track(node);
|
|
121
|
+
});
|
|
122
|
+
return fn(...vals);
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
resubscribable: true,
|
|
126
|
+
resetOnTeardown: true,
|
|
127
|
+
equals: Object.is
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
return createStore(n);
|
|
131
|
+
}
|
|
132
|
+
function map(initial) {
|
|
133
|
+
const n = state(initial, {
|
|
134
|
+
resubscribable: true,
|
|
135
|
+
resetOnTeardown: true,
|
|
136
|
+
equals: () => false
|
|
137
|
+
});
|
|
138
|
+
return createStore(n, {
|
|
139
|
+
// `map`'s state node is configured with `equals: () => false`
|
|
140
|
+
// above, so every `emit` produces DATA (even for same-key same-
|
|
141
|
+
// value sets). The `emit` path is still required for diamond
|
|
142
|
+
// coordination (`[DIRTY]` auto-prefix via `bundle()`).
|
|
143
|
+
set: (value) => n.emit(value),
|
|
144
|
+
setKey: (key, value) => {
|
|
145
|
+
const current = getVal(n);
|
|
146
|
+
n.emit({ ...current, [key]: value });
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
function getValue(store) {
|
|
151
|
+
return store.get();
|
|
152
|
+
}
|
|
153
|
+
function onStart(store, cb) {
|
|
154
|
+
const node = store._node;
|
|
155
|
+
let callbacks = START_LISTENERS.get(node);
|
|
156
|
+
if (!callbacks) {
|
|
157
|
+
callbacks = /* @__PURE__ */ new Set();
|
|
158
|
+
START_LISTENERS.set(node, callbacks);
|
|
159
|
+
}
|
|
160
|
+
callbacks.add(cb);
|
|
161
|
+
}
|
|
162
|
+
function onStop(store, cb) {
|
|
163
|
+
const node = store._node;
|
|
164
|
+
let callbacks = STOP_LISTENERS.get(node);
|
|
165
|
+
if (!callbacks) {
|
|
166
|
+
callbacks = /* @__PURE__ */ new Set();
|
|
167
|
+
STOP_LISTENERS.set(node, callbacks);
|
|
168
|
+
}
|
|
169
|
+
callbacks.add(cb);
|
|
170
|
+
}
|
|
171
|
+
function onMount(store, cb) {
|
|
172
|
+
onStart(store, () => {
|
|
173
|
+
const stop = cb();
|
|
174
|
+
if (typeof stop === "function") onStop(store, stop);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
function action(_store, _name, fn) {
|
|
178
|
+
return (...args) => {
|
|
179
|
+
let result;
|
|
180
|
+
batch(() => {
|
|
181
|
+
result = fn(...args);
|
|
182
|
+
});
|
|
183
|
+
return result;
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export {
|
|
188
|
+
atom,
|
|
189
|
+
computed,
|
|
190
|
+
map,
|
|
191
|
+
getValue,
|
|
192
|
+
onStart,
|
|
193
|
+
onStop,
|
|
194
|
+
onMount,
|
|
195
|
+
action,
|
|
196
|
+
nanostores_exports
|
|
197
|
+
};
|
|
198
|
+
//# sourceMappingURL=chunk-N6UR7YVY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/compat/nanostores/index.ts"],"sourcesContent":["import { batch } from \"../../core/batch.js\";\nimport { DATA, ERROR, type Messages } from \"../../core/messages.js\";\nimport type { Node } from \"../../core/node.js\";\nimport { dynamicNode, state, type TrackFn } from \"../../core/sugar.js\";\n\n/**\n * A Nanostores-compatible atom.\n *\n * @category compat\n */\nexport interface NanoAtom<T> {\n\t/** Get current value. */\n\tget(): T;\n\t/** Set a new value (writable atoms only). */\n\tset(value: T): void;\n\t/** Subscribe to value changes. Callback receives the new value.\n\t * Returns unsubscribe function. Called immediately with current value. */\n\tsubscribe(cb: (value: T) => void): () => void;\n\t/** Listen to value changes (no immediate call). Returns unsubscribe. */\n\tlisten(cb: (value: T) => void): () => void;\n\t/** The underlying GraphReFly node. */\n\treadonly _node: Node<T>;\n}\n\n/**\n * A Nanostores-compatible computed store.\n *\n * @category compat\n */\nexport interface NanoComputed<T> {\n\t/** Get current value. */\n\tget(): T;\n\t/** Subscribe to value changes. Called immediately with current value.\n\t * Returns unsubscribe function. */\n\tsubscribe(cb: (value: T) => void): () => void;\n\t/** Listen to value changes (no immediate call). Returns unsubscribe. */\n\tlisten(cb: (value: T) => void): () => void;\n\t/** The underlying GraphReFly node. */\n\treadonly _node: Node<T>;\n}\n\n/**\n * A Nanostores-compatible map.\n *\n * @category compat\n */\nexport interface NanoMap<T extends Record<string, unknown>> extends NanoAtom<T> {\n\t/** Set a single key. */\n\tsetKey<K extends keyof T>(key: K, value: T[K]): void;\n}\n\nconst START_LISTENERS = new WeakMap<Node<any>, Set<() => void>>();\nconst STOP_LISTENERS = new WeakMap<Node<any>, Set<() => void>>();\n\nfunction trigger(node: Node<any>, map: WeakMap<Node<any>, Set<() => void>>) {\n\tconst callbacks = map.get(node);\n\tif (callbacks) {\n\t\tfor (const cb of callbacks) cb();\n\t}\n}\n\nfunction createStore<T>(node: Node<T>, extra: any = {}): any {\n\tlet listeners = 0;\n\tconst store = {\n\t\t...extra,\n\t\tget: () => getVal(node),\n\t\tsubscribe: (cb: (value: T) => void) => {\n\t\t\tif (listeners === 0) trigger(node, START_LISTENERS);\n\t\t\tlisteners++;\n\t\t\t// Push-on-subscribe delivers the initial value via DATA — no explicit cb() needed.\n\t\t\tconst sub = node.subscribe((msgs: Messages) => {\n\t\t\t\tfor (const [t, v] of msgs) {\n\t\t\t\t\tif (t === DATA) cb(v as T);\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn () => {\n\t\t\t\tsub();\n\t\t\t\tlisteners--;\n\t\t\t\tif (listeners === 0) trigger(node, STOP_LISTENERS);\n\t\t\t};\n\t\t},\n\t\tlisten: (cb: (value: T) => void) => {\n\t\t\tif (listeners === 0) trigger(node, START_LISTENERS);\n\t\t\tlisteners++;\n\t\t\t// Skip the initial push-on-subscribe DATA — listen() fires on changes only.\n\t\t\tlet initial = true;\n\t\t\tconst sub = node.subscribe((msgs: Messages) => {\n\t\t\t\tfor (const [t, v] of msgs) {\n\t\t\t\t\tif (t === DATA) {\n\t\t\t\t\t\tif (initial) {\n\t\t\t\t\t\t\tinitial = false;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcb(v as T);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn () => {\n\t\t\t\tsub();\n\t\t\t\tlisteners--;\n\t\t\t\tif (listeners === 0) trigger(node, STOP_LISTENERS);\n\t\t\t};\n\t\t},\n\t\t_node: node,\n\t};\n\treturn store;\n}\n\nfunction pull<T>(n: Node<T>): T {\n\tlet val: T | undefined | null = n.cache;\n\tlet err: any;\n\tconst unsub = n.subscribe((msgs: Messages) => {\n\t\tfor (const [t, v] of msgs) {\n\t\t\tif (t === DATA) val = v as T;\n\t\t\tif (t === ERROR) err = v;\n\t\t}\n\t});\n\tunsub();\n\tif (err) throw err;\n\treturn val as T;\n}\n\nfunction getVal<T>(n: Node<T>): T {\n\tif (n.status === \"sentinel\") {\n\t\treturn pull(n);\n\t}\n\treturn n.cache as T;\n}\n\n/**\n * Creates a nanostores-compatible atom.\n *\n * @param initial - Initial value.\n * @returns `NanoAtom<T>`\n *\n * @category compat\n */\nexport function atom<T>(initial: T): NanoAtom<T> {\n\tconst n = state<T>(initial, {\n\t\tresubscribable: true,\n\t\tresetOnTeardown: true,\n\t});\n\n\treturn createStore(n, {\n\t\t// `n.emit` routes through the framed pipeline: `bundle()` auto-\n\t\t// prefixes `[DIRTY]` (diamond-safe wave coordination) and the\n\t\t// node's `equals` (default `Object.is`) folds same-value writes\n\t\t// into `RESOLVED`. Matches nanostores' documented Object.is\n\t\t// dedup semantics without any custom check.\n\t\tset: (value: T) => n.emit(value),\n\t});\n}\n\n/**\n * Creates a nanostores-compatible computed store.\n *\n * @param stores - One or more atoms/computed stores.\n * @param fn - Compute function.\n * @returns `NanoComputed<T>`\n *\n * @category compat\n */\nexport function computed<T, A>(\n\tstoreA: NanoAtom<A> | NanoComputed<A>,\n\tfn: (a: A) => T,\n): NanoComputed<T>;\nexport function computed<T, A, B>(\n\tstores: [NanoAtom<A> | NanoComputed<A>, NanoAtom<B> | NanoComputed<B>],\n\tfn: (a: A, b: B) => T,\n): NanoComputed<T>;\nexport function computed<T, A, B, C>(\n\tstores: [\n\t\tNanoAtom<A> | NanoComputed<A>,\n\t\tNanoAtom<B> | NanoComputed<B>,\n\t\tNanoAtom<C> | NanoComputed<C>,\n\t],\n\tfn: (a: A, b: B, c: C) => T,\n): NanoComputed<T>;\nexport function computed<T>(stores: any, fn: (...args: any[]) => T): NanoComputed<T> {\n\tconst storeArray: Array<NanoAtom<any> | NanoComputed<any>> = Array.isArray(stores)\n\t\t? stores\n\t\t: [stores];\n\n\tconst depNodes = storeArray.map((s) => s._node);\n\tconst n = dynamicNode(\n\t\tdepNodes,\n\t\t(track: TrackFn) => {\n\t\t\tconst vals = storeArray.map((s) => {\n\t\t\t\tconst node = s._node;\n\t\t\t\tif (node.status === \"sentinel\") {\n\t\t\t\t\tpull(node);\n\t\t\t\t}\n\t\t\t\treturn track(node);\n\t\t\t});\n\t\t\treturn fn(...vals);\n\t\t},\n\t\t{\n\t\t\tresubscribable: true,\n\t\t\tresetOnTeardown: true,\n\t\t\tequals: Object.is as any,\n\t\t},\n\t);\n\n\treturn createStore(n);\n}\n\n/**\n * Creates a nanostores-compatible map.\n *\n * @param initial - Initial object value.\n * @returns `NanoMap<T>`\n *\n * @category compat\n */\nexport function map<T extends Record<string, unknown>>(initial: T): NanoMap<T> {\n\tconst n = state<T>(initial, {\n\t\tresubscribable: true,\n\t\tresetOnTeardown: true,\n\t\tequals: () => false,\n\t});\n\n\treturn createStore(n, {\n\t\t// `map`'s state node is configured with `equals: () => false`\n\t\t// above, so every `emit` produces DATA (even for same-key same-\n\t\t// value sets). The `emit` path is still required for diamond\n\t\t// coordination (`[DIRTY]` auto-prefix via `bundle()`).\n\t\tset: (value: T) => n.emit(value),\n\t\tsetKey: <K extends keyof T>(key: K, value: T[K]) => {\n\t\t\tconst current = getVal(n);\n\t\t\tn.emit({ ...current, [key]: value });\n\t\t},\n\t});\n}\n\n/**\n * Returns the current value of the store.\n *\n * @category compat\n */\nexport function getValue<T>(store: NanoAtom<T> | NanoComputed<T>): T {\n\treturn store.get();\n}\n\n/**\n * Adds a listener for the store start (first listener connected).\n *\n * @category compat\n */\nexport function onStart(store: NanoAtom<any> | NanoComputed<any>, cb: () => void): void {\n\tconst node = store._node;\n\tlet callbacks = START_LISTENERS.get(node);\n\tif (!callbacks) {\n\t\tcallbacks = new Set();\n\t\tSTART_LISTENERS.set(node, callbacks);\n\t}\n\tcallbacks.add(cb);\n}\n\n/**\n * Adds a listener for the store stop (last listener disconnected).\n *\n * @category compat\n */\nexport function onStop(store: NanoAtom<any> | NanoComputed<any>, cb: () => void): void {\n\tconst node = store._node;\n\tlet callbacks = STOP_LISTENERS.get(node);\n\tif (!callbacks) {\n\t\tcallbacks = new Set();\n\t\tSTOP_LISTENERS.set(node, callbacks);\n\t}\n\tcallbacks.add(cb);\n}\n\n/**\n * Adds a listener for the store mount (first listener connected).\n *\n * @returns A cleanup function called when the last listener is removed.\n * @category compat\n */\nexport function onMount(\n\tstore: NanoAtom<any> | NanoComputed<any>,\n\tcb: () => (() => void) | undefined,\n): void {\n\tonStart(store, () => {\n\t\tconst stop = cb();\n\t\tif (typeof stop === \"function\") onStop(store, stop);\n\t});\n}\n\n/**\n * Batches multiple store updates.\n *\n * @category compat\n */\nexport function action<Args extends any[], Return>(\n\t_store: NanoAtom<any> | NanoComputed<any>,\n\t_name: string,\n\tfn: (...args: Args) => Return,\n): (...args: Args) => Return {\n\treturn (...args: Args) => {\n\t\tlet result: any;\n\t\tbatch(() => {\n\t\t\tresult = fn(...args);\n\t\t});\n\t\treturn result as Return;\n\t};\n}\n"],"mappings":"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","names":["map"]}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DATA,
|
|
3
|
+
DIRTY,
|
|
4
|
+
RESOLVED,
|
|
5
|
+
__export
|
|
6
|
+
} from "./chunk-SX52TAR4.js";
|
|
7
|
+
|
|
8
|
+
// src/compat/svelte/index.ts
|
|
9
|
+
var svelte_exports = {};
|
|
10
|
+
__export(svelte_exports, {
|
|
11
|
+
useStore: () => useStore,
|
|
12
|
+
useSubscribe: () => useSubscribe,
|
|
13
|
+
useSubscribeRecord: () => useSubscribeRecord
|
|
14
|
+
});
|
|
15
|
+
function useSubscribe(node) {
|
|
16
|
+
return {
|
|
17
|
+
subscribe(run) {
|
|
18
|
+
const unsub = node.subscribe(() => {
|
|
19
|
+
run(node.cache);
|
|
20
|
+
});
|
|
21
|
+
run(node.cache);
|
|
22
|
+
return unsub;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function useStore(node) {
|
|
27
|
+
return {
|
|
28
|
+
subscribe(run) {
|
|
29
|
+
const unsub = node.subscribe(() => {
|
|
30
|
+
run(node.cache);
|
|
31
|
+
});
|
|
32
|
+
run(node.cache);
|
|
33
|
+
return unsub;
|
|
34
|
+
},
|
|
35
|
+
set(value) {
|
|
36
|
+
node.down([[DIRTY], [DATA, value]]);
|
|
37
|
+
},
|
|
38
|
+
update(updater) {
|
|
39
|
+
const next = updater(node.cache);
|
|
40
|
+
node.down([[DIRTY], [DATA, next]]);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function useSubscribeRecord(keysNode, factory) {
|
|
45
|
+
return {
|
|
46
|
+
subscribe(run) {
|
|
47
|
+
let entrySubs = [];
|
|
48
|
+
const cleanupEntries = () => {
|
|
49
|
+
for (const unsub of entrySubs) unsub();
|
|
50
|
+
entrySubs = [];
|
|
51
|
+
};
|
|
52
|
+
const buildSnapshot = () => {
|
|
53
|
+
const snap = {};
|
|
54
|
+
for (const key of keysNode.cache ?? []) {
|
|
55
|
+
const nodes = factory(key);
|
|
56
|
+
const values = {};
|
|
57
|
+
for (const field of Object.keys(nodes)) {
|
|
58
|
+
values[field] = nodes[field].cache;
|
|
59
|
+
}
|
|
60
|
+
snap[key] = values;
|
|
61
|
+
}
|
|
62
|
+
return snap;
|
|
63
|
+
};
|
|
64
|
+
const sync = (nextKeys) => {
|
|
65
|
+
cleanupEntries();
|
|
66
|
+
for (const key of nextKeys) {
|
|
67
|
+
const nodes = factory(key);
|
|
68
|
+
for (const field of Object.keys(nodes)) {
|
|
69
|
+
const unsub = nodes[field].subscribe(() => {
|
|
70
|
+
run(buildSnapshot());
|
|
71
|
+
});
|
|
72
|
+
entrySubs.push(unsub);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
run(buildSnapshot());
|
|
76
|
+
};
|
|
77
|
+
const keysUnsub = keysNode.subscribe((msgs) => {
|
|
78
|
+
if (msgs.some((m) => m[0] === DATA || m[0] === RESOLVED)) {
|
|
79
|
+
sync(keysNode.cache ?? []);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
sync(keysNode.cache ?? []);
|
|
83
|
+
return () => {
|
|
84
|
+
keysUnsub();
|
|
85
|
+
cleanupEntries();
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export {
|
|
92
|
+
useSubscribe,
|
|
93
|
+
useStore,
|
|
94
|
+
useSubscribeRecord,
|
|
95
|
+
svelte_exports
|
|
96
|
+
};
|
|
97
|
+
//# sourceMappingURL=chunk-OHISZPOJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/compat/svelte/index.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Svelte bindings — useSubscribe / useStore\n// ---------------------------------------------------------------------------\n// Bridges GraphReFly nodes into Svelte's store contract. Works with any\n// Node<T>, including companion nodes (node.meta.status).\n//\n// Usage:\n// import { useSubscribe, useStore } from '@graphrefly/graphrefly-ts/compat/svelte';\n// // Optional peer install (only for this adapter): pnpm add svelte\n// const status = useSubscribe(wsStatusNode); // Svelte readable store\n// const count = useStore(countNode); // Svelte writable store\n// // In template: $status, $count\n// // $count = 42\n// ---------------------------------------------------------------------------\n\nimport { DATA, DIRTY, type Messages, RESOLVED } from \"../../core/messages.js\";\nimport type { Node } from \"../../core/node.js\";\n\n/** Svelte store contract — implements the minimal `subscribe` method. */\nexport interface SvelteReadable<T> {\n\tsubscribe(run: (value: T) => void): () => void;\n}\n\n/** Svelte writable store contract. */\nexport interface SvelteWritable<T> extends SvelteReadable<T> {\n\tset(value: T): void;\n\tupdate(updater: (value: T) => T): void;\n}\n\n/**\n * Subscribe to a `Node<T>` as a Svelte readable store (implements Svelte store contract).\n * Subscription lifecycle is tied to Svelte store unsubscription (not node terminal messages).\n */\nexport function useSubscribe<T>(node: Node<T>): SvelteReadable<T | undefined | null> {\n\treturn {\n\t\tsubscribe(run: (value: T | undefined | null) => void): () => void {\n\t\t\tconst unsub = node.subscribe(() => {\n\t\t\t\trun(node.cache);\n\t\t\t});\n\t\t\trun(node.cache);\n\t\t\treturn unsub;\n\t\t},\n\t};\n}\n\n/**\n * Bind a writable `Node<T>` as a Svelte writable store.\n * Reads and writes adapt seamlessly.\n * Setter/update always forward `[[DIRTY], [DATA, value]]`, including `value === undefined`.\n * Subscription lifecycle is tied to Svelte store unsubscription (not node terminal messages).\n */\nexport function useStore<T>(node: Node<T>): SvelteWritable<T | undefined | null> {\n\treturn {\n\t\tsubscribe(run: (value: T | undefined | null) => void): () => void {\n\t\t\tconst unsub = node.subscribe(() => {\n\t\t\t\trun(node.cache);\n\t\t\t});\n\t\t\trun(node.cache);\n\t\t\treturn unsub;\n\t\t},\n\t\tset(value: T | undefined | null) {\n\t\t\tnode.down([[DIRTY], [DATA, value]]);\n\t\t},\n\t\tupdate(updater: (value: T | undefined | null) => T | undefined | null) {\n\t\t\tconst next = updater(node.cache);\n\t\t\tnode.down([[DIRTY], [DATA, next]]);\n\t\t},\n\t};\n}\n\n/** Maps a key to an object of nodes. Used by `useSubscribeRecord`. */\nexport type NodeFactory<K, R extends Record<string, any>> = (key: K) => {\n\t[P in keyof R]: Node<R[P]>;\n};\n\n/**\n * Subscribe to a dynamic keyed record of nodes as a Svelte readable store.\n * Re-subscribes all per-key fields whenever `keysNode` changes.\n * Key re-sync is gated to settled batches (`messageTier >= 3`) to avoid DIRTY-phase churn.\n */\nexport function useSubscribeRecord<K extends string, R extends Record<string, any>>(\n\tkeysNode: Node<K[]>,\n\tfactory: NodeFactory<K, R>,\n): SvelteReadable<Record<K, R>> {\n\treturn {\n\t\tsubscribe(run: (value: Record<K, R>) => void): () => void {\n\t\t\tlet entrySubs: Array<() => void> = [];\n\n\t\t\tconst cleanupEntries = () => {\n\t\t\t\tfor (const unsub of entrySubs) unsub();\n\t\t\t\tentrySubs = [];\n\t\t\t};\n\n\t\t\tconst buildSnapshot = (): Record<K, R> => {\n\t\t\t\tconst snap = {} as Record<K, R>;\n\t\t\t\tfor (const key of keysNode.cache ?? []) {\n\t\t\t\t\tconst nodes = factory(key);\n\t\t\t\t\tconst values = {} as R;\n\t\t\t\t\tfor (const field of Object.keys(nodes) as (keyof R)[]) {\n\t\t\t\t\t\tvalues[field] = nodes[field].cache as R[keyof R];\n\t\t\t\t\t}\n\t\t\t\t\tsnap[key] = values;\n\t\t\t\t}\n\t\t\t\treturn snap;\n\t\t\t};\n\n\t\t\tconst sync = (nextKeys: K[]) => {\n\t\t\t\tcleanupEntries();\n\t\t\t\tfor (const key of nextKeys) {\n\t\t\t\t\tconst nodes = factory(key);\n\t\t\t\t\tfor (const field of Object.keys(nodes) as (keyof R)[]) {\n\t\t\t\t\t\tconst unsub = nodes[field].subscribe(() => {\n\t\t\t\t\t\t\trun(buildSnapshot());\n\t\t\t\t\t\t});\n\t\t\t\t\t\tentrySubs.push(unsub);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trun(buildSnapshot());\n\t\t\t};\n\n\t\t\tconst keysUnsub = keysNode.subscribe((msgs: Messages) => {\n\t\t\t\tif (msgs.some((m) => m[0] === DATA || m[0] === RESOLVED)) {\n\t\t\t\t\tsync(keysNode.cache ?? []);\n\t\t\t\t}\n\t\t\t});\n\t\t\tsync(keysNode.cache ?? []);\n\n\t\t\treturn () => {\n\t\t\t\tkeysUnsub();\n\t\t\t\tcleanupEntries();\n\t\t\t};\n\t\t},\n\t};\n}\n"],"mappings":";;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiCO,SAAS,aAAgB,MAAqD;AACpF,SAAO;AAAA,IACN,UAAU,KAAwD;AACjE,YAAM,QAAQ,KAAK,UAAU,MAAM;AAClC,YAAI,KAAK,KAAK;AAAA,MACf,CAAC;AACD,UAAI,KAAK,KAAK;AACd,aAAO;AAAA,IACR;AAAA,EACD;AACD;AAQO,SAAS,SAAY,MAAqD;AAChF,SAAO;AAAA,IACN,UAAU,KAAwD;AACjE,YAAM,QAAQ,KAAK,UAAU,MAAM;AAClC,YAAI,KAAK,KAAK;AAAA,MACf,CAAC;AACD,UAAI,KAAK,KAAK;AACd,aAAO;AAAA,IACR;AAAA,IACA,IAAI,OAA6B;AAChC,WAAK,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC;AAAA,IACnC;AAAA,IACA,OAAO,SAAgE;AACtE,YAAM,OAAO,QAAQ,KAAK,KAAK;AAC/B,WAAK,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC;AAAA,IAClC;AAAA,EACD;AACD;AAYO,SAAS,mBACf,UACA,SAC+B;AAC/B,SAAO;AAAA,IACN,UAAU,KAAgD;AACzD,UAAI,YAA+B,CAAC;AAEpC,YAAM,iBAAiB,MAAM;AAC5B,mBAAW,SAAS,UAAW,OAAM;AACrC,oBAAY,CAAC;AAAA,MACd;AAEA,YAAM,gBAAgB,MAAoB;AACzC,cAAM,OAAO,CAAC;AACd,mBAAW,OAAO,SAAS,SAAS,CAAC,GAAG;AACvC,gBAAM,QAAQ,QAAQ,GAAG;AACzB,gBAAM,SAAS,CAAC;AAChB,qBAAW,SAAS,OAAO,KAAK,KAAK,GAAkB;AACtD,mBAAO,KAAK,IAAI,MAAM,KAAK,EAAE;AAAA,UAC9B;AACA,eAAK,GAAG,IAAI;AAAA,QACb;AACA,eAAO;AAAA,MACR;AAEA,YAAM,OAAO,CAAC,aAAkB;AAC/B,uBAAe;AACf,mBAAW,OAAO,UAAU;AAC3B,gBAAM,QAAQ,QAAQ,GAAG;AACzB,qBAAW,SAAS,OAAO,KAAK,KAAK,GAAkB;AACtD,kBAAM,QAAQ,MAAM,KAAK,EAAE,UAAU,MAAM;AAC1C,kBAAI,cAAc,CAAC;AAAA,YACpB,CAAC;AACD,sBAAU,KAAK,KAAK;AAAA,UACrB;AAAA,QACD;AACA,YAAI,cAAc,CAAC;AAAA,MACpB;AAEA,YAAM,YAAY,SAAS,UAAU,CAAC,SAAmB;AACxD,YAAI,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC,MAAM,QAAQ,EAAE,CAAC,MAAM,QAAQ,GAAG;AACzD,eAAK,SAAS,SAAS,CAAC,CAAC;AAAA,QAC1B;AAAA,MACD,CAAC;AACD,WAAK,SAAS,SAAS,CAAC,CAAC;AAEzB,aAAO,MAAM;AACZ,kBAAU;AACV,uBAAe;AAAA,MAChB;AAAA,IACD;AAAA,EACD;AACD;","names":[]}
|