@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.
Files changed (184) hide show
  1. package/README.md +8 -0
  2. package/dist/{chunk-QOWVNWOC.js → chunk-3ZWCKRHX.js} +27 -25
  3. package/dist/{chunk-QOWVNWOC.js.map → chunk-3ZWCKRHX.js.map} +1 -1
  4. package/dist/chunk-6LDQFTYS.js +102 -0
  5. package/dist/chunk-6LDQFTYS.js.map +1 -0
  6. package/dist/{chunk-5WGT55R4.js → chunk-AMCG74RZ.js} +195 -24
  7. package/dist/chunk-AMCG74RZ.js.map +1 -0
  8. package/dist/{chunk-AOCBDH4T.js → chunk-BVZYTZ5H.js} +76 -103
  9. package/dist/chunk-BVZYTZ5H.js.map +1 -0
  10. package/dist/chunk-FQMKGR6L.js +330 -0
  11. package/dist/chunk-FQMKGR6L.js.map +1 -0
  12. package/dist/chunk-HXZEYDUR.js +94 -0
  13. package/dist/chunk-HXZEYDUR.js.map +1 -0
  14. package/dist/{chunk-IPLKX3L2.js → chunk-IZYUSJC7.js} +16 -14
  15. package/dist/{chunk-IPLKX3L2.js.map → chunk-IZYUSJC7.js.map} +1 -1
  16. package/dist/chunk-J22W6HV3.js +107 -0
  17. package/dist/chunk-J22W6HV3.js.map +1 -0
  18. package/dist/{chunk-HWPIFSW2.js → chunk-JSCT3CR4.js} +6 -4
  19. package/dist/{chunk-HWPIFSW2.js.map → chunk-JSCT3CR4.js.map} +1 -1
  20. package/dist/chunk-JYXEWPH4.js +62 -0
  21. package/dist/chunk-JYXEWPH4.js.map +1 -0
  22. package/dist/chunk-LCE3GF5P.js +866 -0
  23. package/dist/chunk-LCE3GF5P.js.map +1 -0
  24. package/dist/chunk-MJ2NKQQL.js +119 -0
  25. package/dist/chunk-MJ2NKQQL.js.map +1 -0
  26. package/dist/chunk-N6UR7YVY.js +198 -0
  27. package/dist/chunk-N6UR7YVY.js.map +1 -0
  28. package/dist/chunk-OHISZPOJ.js +97 -0
  29. package/dist/chunk-OHISZPOJ.js.map +1 -0
  30. package/dist/{chunk-5DJTTKX3.js → chunk-PHOUUNK7.js} +74 -111
  31. package/dist/chunk-PHOUUNK7.js.map +1 -0
  32. package/dist/{chunk-PY4XCDLR.js → chunk-RB6QPHJ7.js} +8 -6
  33. package/dist/{chunk-PY4XCDLR.js.map → chunk-RB6QPHJ7.js.map} +1 -1
  34. package/dist/chunk-SN4YWWYO.js +171 -0
  35. package/dist/chunk-SN4YWWYO.js.map +1 -0
  36. package/dist/chunk-SX52TAR4.js +110 -0
  37. package/dist/chunk-SX52TAR4.js.map +1 -0
  38. package/dist/{chunk-XOFWRC73.js → chunk-THTWHNU4.js} +319 -24
  39. package/dist/chunk-THTWHNU4.js.map +1 -0
  40. package/dist/{chunk-H4RVA4VE.js → chunk-VYPWMZ6H.js} +2 -2
  41. package/dist/chunk-XGPU467M.js +136 -0
  42. package/dist/chunk-XGPU467M.js.map +1 -0
  43. package/dist/{chunk-TDEXAMGO.js → chunk-ZQMEI34O.js} +206 -574
  44. package/dist/chunk-ZQMEI34O.js.map +1 -0
  45. package/dist/compat/index.cjs +7656 -0
  46. package/dist/compat/index.cjs.map +1 -0
  47. package/dist/compat/index.d.cts +18 -0
  48. package/dist/compat/index.d.ts +18 -0
  49. package/dist/compat/index.js +49 -0
  50. package/dist/compat/index.js.map +1 -0
  51. package/dist/compat/jotai/index.cjs +2048 -0
  52. package/dist/compat/jotai/index.cjs.map +1 -0
  53. package/dist/compat/jotai/index.d.cts +2 -0
  54. package/dist/compat/jotai/index.d.ts +2 -0
  55. package/dist/compat/jotai/index.js +9 -0
  56. package/dist/compat/jotai/index.js.map +1 -0
  57. package/dist/compat/nanostores/index.cjs +2175 -0
  58. package/dist/compat/nanostores/index.cjs.map +1 -0
  59. package/dist/compat/nanostores/index.d.cts +2 -0
  60. package/dist/compat/nanostores/index.d.ts +2 -0
  61. package/dist/compat/nanostores/index.js +23 -0
  62. package/dist/compat/nanostores/index.js.map +1 -0
  63. package/dist/compat/nestjs/index.cjs +350 -16
  64. package/dist/compat/nestjs/index.cjs.map +1 -1
  65. package/dist/compat/nestjs/index.d.cts +6 -6
  66. package/dist/compat/nestjs/index.d.ts +6 -6
  67. package/dist/compat/nestjs/index.js +10 -9
  68. package/dist/compat/react/index.cjs +141 -0
  69. package/dist/compat/react/index.cjs.map +1 -0
  70. package/dist/compat/react/index.d.cts +2 -0
  71. package/dist/compat/react/index.d.ts +2 -0
  72. package/dist/compat/react/index.js +12 -0
  73. package/dist/compat/react/index.js.map +1 -0
  74. package/dist/compat/solid/index.cjs +128 -0
  75. package/dist/compat/solid/index.cjs.map +1 -0
  76. package/dist/compat/solid/index.d.cts +2 -0
  77. package/dist/compat/solid/index.d.ts +2 -0
  78. package/dist/compat/solid/index.js +12 -0
  79. package/dist/compat/solid/index.js.map +1 -0
  80. package/dist/compat/svelte/index.cjs +131 -0
  81. package/dist/compat/svelte/index.cjs.map +1 -0
  82. package/dist/compat/svelte/index.d.cts +2 -0
  83. package/dist/compat/svelte/index.d.ts +2 -0
  84. package/dist/compat/svelte/index.js +12 -0
  85. package/dist/compat/svelte/index.js.map +1 -0
  86. package/dist/compat/vue/index.cjs +146 -0
  87. package/dist/compat/vue/index.cjs.map +1 -0
  88. package/dist/compat/vue/index.d.cts +3 -0
  89. package/dist/compat/vue/index.d.ts +3 -0
  90. package/dist/compat/vue/index.js +12 -0
  91. package/dist/compat/vue/index.js.map +1 -0
  92. package/dist/compat/zustand/index.cjs +4931 -0
  93. package/dist/compat/zustand/index.cjs.map +1 -0
  94. package/dist/compat/zustand/index.d.cts +5 -0
  95. package/dist/compat/zustand/index.d.ts +5 -0
  96. package/dist/compat/zustand/index.js +12 -0
  97. package/dist/compat/zustand/index.js.map +1 -0
  98. package/dist/core/index.cjs +53 -4
  99. package/dist/core/index.cjs.map +1 -1
  100. package/dist/core/index.d.cts +3 -3
  101. package/dist/core/index.d.ts +3 -3
  102. package/dist/core/index.js +26 -24
  103. package/dist/demo-shell-26p5fVxn.d.cts +102 -0
  104. package/dist/demo-shell-DEp-nMTl.d.ts +102 -0
  105. package/dist/extra/index.cjs +290 -110
  106. package/dist/extra/index.cjs.map +1 -1
  107. package/dist/extra/index.d.cts +5 -4
  108. package/dist/extra/index.d.ts +5 -4
  109. package/dist/extra/index.js +8 -5
  110. package/dist/extra/sources.cjs +2486 -0
  111. package/dist/extra/sources.cjs.map +1 -0
  112. package/dist/extra/sources.d.cts +465 -0
  113. package/dist/extra/sources.d.ts +465 -0
  114. package/dist/extra/sources.js +57 -0
  115. package/dist/extra/sources.js.map +1 -0
  116. package/dist/graph/index.cjs +408 -14
  117. package/dist/graph/index.cjs.map +1 -1
  118. package/dist/graph/index.d.cts +5 -5
  119. package/dist/graph/index.d.ts +5 -5
  120. package/dist/graph/index.js +13 -5
  121. package/dist/{graph-D-3JIQme.d.cts → graph-6tZ5jEzr.d.cts} +195 -4
  122. package/dist/{graph-B6NFqv3z.d.ts → graph-DQ69XU0g.d.ts} +195 -4
  123. package/dist/index-B4MP_8V_.d.cts +37 -0
  124. package/dist/index-BEfE8H_G.d.cts +121 -0
  125. package/dist/{index-D7XgsUt7.d.ts → index-BW1z3BN9.d.ts} +169 -127
  126. package/dist/index-BYOHF0zP.d.ts +34 -0
  127. package/dist/index-B_IP40nB.d.cts +36 -0
  128. package/dist/index-Bd_fwmLf.d.cts +45 -0
  129. package/dist/{index-BysCTzJz.d.ts → index-BeIdBfcb.d.cts} +121 -547
  130. package/dist/index-BjI6ty9z.d.ts +121 -0
  131. package/dist/index-Bxb5ZYc9.d.cts +34 -0
  132. package/dist/{index-BJB7t9gg.d.cts → index-C0ZXMaXO.d.cts} +2 -2
  133. package/dist/{index-b5BYtczN.d.cts → index-C8mdwMXc.d.cts} +169 -127
  134. package/dist/index-CDAjUFIv.d.ts +36 -0
  135. package/dist/index-CPgZ5wRl.d.ts +44 -0
  136. package/dist/{index-AMWewNDe.d.cts → index-CUwyr1Kk.d.cts} +33 -4
  137. package/dist/index-CUyrtuOf.d.cts +127 -0
  138. package/dist/{index-C-TXEa7C.d.ts → index-CY2TljO4.d.ts} +2 -2
  139. package/dist/index-CmnuOibw.d.ts +37 -0
  140. package/dist/{index-DiobMNwE.d.ts → index-CuYwdKO-.d.ts} +3 -3
  141. package/dist/index-DFhjO4Gg.d.cts +44 -0
  142. package/dist/{index-1z8vRTCt.d.cts → index-DdD5MVDL.d.ts} +121 -547
  143. package/dist/index-DrISNAOm.d.ts +45 -0
  144. package/dist/index-QBpffFW-.d.cts +86 -0
  145. package/dist/{index-J7Kc0oIQ.d.cts → index-_oMEWlDq.d.cts} +3 -3
  146. package/dist/{index-CYkjxu3s.d.ts → index-eJ6T_qGM.d.ts} +33 -4
  147. package/dist/index-qldRdbQw.d.ts +86 -0
  148. package/dist/index-xdGjv0nO.d.ts +127 -0
  149. package/dist/index.cjs +2334 -195
  150. package/dist/index.cjs.map +1 -1
  151. package/dist/index.d.cts +1007 -648
  152. package/dist/index.d.ts +1007 -648
  153. package/dist/index.js +1204 -1172
  154. package/dist/index.js.map +1 -1
  155. package/dist/{meta-CnkLA_43.d.ts → meta-BGqSZ7mt.d.ts} +1 -1
  156. package/dist/{meta-DWbkoq1s.d.cts → meta-C0-8XW6Q.d.cts} +1 -1
  157. package/dist/{node-B-f-Lu-k.d.cts → node-C_IBuvX2.d.cts} +26 -1
  158. package/dist/{node-B-f-Lu-k.d.ts → node-C_IBuvX2.d.ts} +26 -1
  159. package/dist/{observable-DBnrwcar.d.cts → observable-Crr1jgzx.d.cts} +1 -1
  160. package/dist/{observable-uP-wy_uK.d.ts → observable-DCk45RH5.d.ts} +1 -1
  161. package/dist/patterns/demo-shell.cjs +5604 -0
  162. package/dist/patterns/demo-shell.cjs.map +1 -0
  163. package/dist/patterns/demo-shell.d.cts +6 -0
  164. package/dist/patterns/demo-shell.d.ts +6 -0
  165. package/dist/patterns/demo-shell.js +15 -0
  166. package/dist/patterns/demo-shell.js.map +1 -0
  167. package/dist/patterns/reactive-layout/index.cjs +843 -29
  168. package/dist/patterns/reactive-layout/index.cjs.map +1 -1
  169. package/dist/patterns/reactive-layout/index.d.cts +6 -5
  170. package/dist/patterns/reactive-layout/index.d.ts +6 -5
  171. package/dist/patterns/reactive-layout/index.js +25 -10
  172. package/dist/reactive-layout-BaOQefHu.d.cts +183 -0
  173. package/dist/reactive-layout-D9gejYXE.d.ts +183 -0
  174. package/dist/{storage-BuTdpCI1.d.cts → storage-BMycWEh2.d.ts} +9 -1
  175. package/dist/{storage-F2X1U1x0.d.ts → storage-DiqWHzVI.d.cts} +9 -1
  176. package/package.json +32 -2
  177. package/dist/chunk-5DJTTKX3.js.map +0 -1
  178. package/dist/chunk-5WGT55R4.js.map +0 -1
  179. package/dist/chunk-AOCBDH4T.js.map +0 -1
  180. package/dist/chunk-MW4VAKAO.js +0 -47
  181. package/dist/chunk-MW4VAKAO.js.map +0 -1
  182. package/dist/chunk-TDEXAMGO.js.map +0 -1
  183. package/dist/chunk-XOFWRC73.js.map +0 -1
  184. /package/dist/{chunk-H4RVA4VE.js.map → chunk-VYPWMZ6H.js.map} +0 -0
@@ -0,0 +1,866 @@
1
+ import {
2
+ emitToMeta
3
+ } from "./chunk-JSCT3CR4.js";
4
+ import {
5
+ Graph
6
+ } from "./chunk-THTWHNU4.js";
7
+ import {
8
+ derived,
9
+ monotonicNs,
10
+ node,
11
+ state
12
+ } from "./chunk-PHOUUNK7.js";
13
+
14
+ // src/patterns/reactive-layout/reactive-layout.ts
15
+ function isCJK(s) {
16
+ for (const ch of s) {
17
+ const c = ch.codePointAt(0);
18
+ if (c >= 19968 && c <= 40959 || // CJK Unified Ideographs
19
+ c >= 13312 && c <= 19903 || // CJK Extension A
20
+ c >= 12288 && c <= 12351 || // CJK Symbols and Punctuation
21
+ c >= 12352 && c <= 12447 || // Hiragana
22
+ c >= 12448 && c <= 12543 || // Katakana
23
+ c >= 44032 && c <= 55215 || // Hangul
24
+ c >= 65280 && c <= 65519) {
25
+ return true;
26
+ }
27
+ }
28
+ return false;
29
+ }
30
+ var kinsokuStart = /* @__PURE__ */ new Set([
31
+ "\uFF0C",
32
+ "\uFF0E",
33
+ "\uFF01",
34
+ "\uFF1A",
35
+ "\uFF1B",
36
+ "\uFF1F",
37
+ "\u3001",
38
+ "\u3002",
39
+ "\u30FB",
40
+ "\uFF09",
41
+ "\u3015",
42
+ "\u3009",
43
+ "\u300B",
44
+ "\u300D",
45
+ "\u300F",
46
+ "\u3011"
47
+ ]);
48
+ var leftStickyPunctuation = /* @__PURE__ */ new Set([
49
+ ".",
50
+ ",",
51
+ "!",
52
+ "?",
53
+ ":",
54
+ ";",
55
+ ")",
56
+ "]",
57
+ "}",
58
+ "%",
59
+ '"',
60
+ "\u201D",
61
+ "\u2019",
62
+ "\xBB",
63
+ "\u203A",
64
+ "\u2026"
65
+ ]);
66
+ function normalizeWhitespace(text) {
67
+ return text.replace(/[\t\n\r\f ]+/g, " ").replace(/^ | $/g, "");
68
+ }
69
+ function segmentText(normalized) {
70
+ const wordSegmenter = new Intl.Segmenter(void 0, { granularity: "word" });
71
+ const pieces = [];
72
+ for (const s of wordSegmenter.segment(normalized)) {
73
+ const text = s.segment;
74
+ const isWordLike = s.isWordLike ?? false;
75
+ const texts = [];
76
+ const wordLikes = [];
77
+ const kinds = [];
78
+ let currentText = "";
79
+ let currentKind = null;
80
+ for (const ch of text) {
81
+ let kind;
82
+ if (ch === " ") kind = "space";
83
+ else if (ch === "\u200B") kind = "zero-width-break";
84
+ else if (ch === "\xAD") kind = "soft-hyphen";
85
+ else if (ch === "\n") kind = "hard-break";
86
+ else kind = "text";
87
+ if (currentKind !== null && kind === currentKind) {
88
+ currentText += ch;
89
+ } else {
90
+ if (currentKind !== null) {
91
+ texts.push(currentText);
92
+ wordLikes.push(currentKind === "text" && isWordLike);
93
+ kinds.push(currentKind);
94
+ }
95
+ currentText = ch;
96
+ currentKind = kind;
97
+ }
98
+ }
99
+ if (currentKind !== null) {
100
+ texts.push(currentText);
101
+ wordLikes.push(currentKind === "text" && isWordLike);
102
+ kinds.push(currentKind);
103
+ }
104
+ pieces.push({ texts, isWordLike: wordLikes, kinds });
105
+ }
106
+ return pieces;
107
+ }
108
+ function analyzeAndMeasure(text, font, adapter, cache, stats) {
109
+ const normalized = normalizeWhitespace(text);
110
+ if (normalized.length === 0) return [];
111
+ const pieces = segmentText(normalized);
112
+ const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
113
+ granularity: "grapheme"
114
+ });
115
+ const rawTexts = [];
116
+ const rawKinds = [];
117
+ const rawWordLike = [];
118
+ for (const piece of pieces) {
119
+ for (let i = 0; i < piece.texts.length; i++) {
120
+ rawTexts.push(piece.texts[i]);
121
+ rawKinds.push(piece.kinds[i]);
122
+ rawWordLike.push(piece.isWordLike[i]);
123
+ }
124
+ }
125
+ const mergedTexts = [];
126
+ const mergedKinds = [];
127
+ const mergedWordLike = [];
128
+ for (let i = 0; i < rawTexts.length; i++) {
129
+ const t = rawTexts[i];
130
+ const k = rawKinds[i];
131
+ const wl = rawWordLike[i];
132
+ if (k === "text" && !wl && mergedTexts.length > 0 && mergedKinds[mergedKinds.length - 1] === "text") {
133
+ const isSticky = t.length === 1 && (leftStickyPunctuation.has(t) || kinsokuStart.has(t));
134
+ if (isSticky) {
135
+ mergedTexts[mergedTexts.length - 1] += t;
136
+ continue;
137
+ }
138
+ }
139
+ if (t === "-" && mergedTexts.length > 0 && mergedKinds[mergedKinds.length - 1] === "text" && mergedWordLike[mergedWordLike.length - 1]) {
140
+ mergedTexts[mergedTexts.length - 1] += t;
141
+ continue;
142
+ }
143
+ mergedTexts.push(t);
144
+ mergedKinds.push(k);
145
+ mergedWordLike.push(wl);
146
+ }
147
+ let fontCache = cache.get(font);
148
+ if (!fontCache) {
149
+ fontCache = /* @__PURE__ */ new Map();
150
+ cache.set(font, fontCache);
151
+ }
152
+ function measureCached(seg) {
153
+ let w = fontCache.get(seg);
154
+ if (w === void 0) {
155
+ if (stats) stats.misses += 1;
156
+ const raw = adapter.measureSegment(seg, font).width;
157
+ w = Number.isFinite(raw) && raw >= 0 ? raw : 0;
158
+ fontCache.set(seg, w);
159
+ } else if (stats) {
160
+ stats.hits += 1;
161
+ }
162
+ return w;
163
+ }
164
+ const segments = [];
165
+ for (let i = 0; i < mergedTexts.length; i++) {
166
+ const t = mergedTexts[i];
167
+ const k = mergedKinds[i];
168
+ if (k !== "text") {
169
+ segments.push({
170
+ text: t,
171
+ width: k === "space" ? measureCached(" ") * t.length : 0,
172
+ kind: k,
173
+ graphemeWidths: null
174
+ });
175
+ continue;
176
+ }
177
+ if (isCJK(t)) {
178
+ let unitText = "";
179
+ for (const gs of graphemeSegmenter2.segment(t)) {
180
+ const grapheme = gs.segment;
181
+ if (unitText.length > 0 && kinsokuStart.has(grapheme)) {
182
+ unitText += grapheme;
183
+ continue;
184
+ }
185
+ if (unitText.length > 0) {
186
+ const w2 = measureCached(unitText);
187
+ segments.push({
188
+ text: unitText,
189
+ width: w2,
190
+ kind: "text",
191
+ graphemeWidths: null
192
+ });
193
+ }
194
+ unitText = grapheme;
195
+ }
196
+ if (unitText.length > 0) {
197
+ const w2 = measureCached(unitText);
198
+ segments.push({
199
+ text: unitText,
200
+ width: w2,
201
+ kind: "text",
202
+ graphemeWidths: null
203
+ });
204
+ }
205
+ continue;
206
+ }
207
+ const w = measureCached(t);
208
+ let graphemeWidths = null;
209
+ if (mergedWordLike[i] && t.length > 1) {
210
+ const gWidths = [];
211
+ for (const gs of graphemeSegmenter2.segment(t)) {
212
+ gWidths.push(measureCached(gs.segment));
213
+ }
214
+ if (gWidths.length > 1) {
215
+ graphemeWidths = gWidths;
216
+ }
217
+ }
218
+ segments.push({ text: t, width: w, kind: "text", graphemeWidths });
219
+ }
220
+ return segments;
221
+ }
222
+ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
223
+ if (segments.length === 0) {
224
+ return { lines: [], lineCount: 0 };
225
+ }
226
+ const lines = [];
227
+ let lineW = 0;
228
+ let hasContent = false;
229
+ let lineStartSeg = 0;
230
+ let lineStartGrapheme = 0;
231
+ let lineEndSeg = 0;
232
+ let lineEndGrapheme = 0;
233
+ let pendingBreakSeg = -1;
234
+ let pendingBreakWidth = 0;
235
+ let fontCache = cache.get(font);
236
+ if (!fontCache) {
237
+ fontCache = /* @__PURE__ */ new Map();
238
+ cache.set(font, fontCache);
239
+ }
240
+ let hyphenWidth = fontCache.get("-");
241
+ if (hyphenWidth === void 0) {
242
+ hyphenWidth = adapter.measureSegment("-", font).width;
243
+ fontCache.set("-", hyphenWidth);
244
+ }
245
+ function flushLine(endSeg = lineEndSeg, endGrapheme = lineEndGrapheme, width = lineW) {
246
+ let text = "";
247
+ for (let i = lineStartSeg; i < endSeg; i++) {
248
+ const seg = segments[i];
249
+ if (seg.kind === "soft-hyphen" || seg.kind === "hard-break") continue;
250
+ if (i === lineStartSeg && lineStartGrapheme > 0 && seg.graphemeWidths) {
251
+ const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
252
+ granularity: "grapheme"
253
+ });
254
+ const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
255
+ text += graphemes.slice(lineStartGrapheme).join("");
256
+ } else {
257
+ text += seg.text;
258
+ }
259
+ }
260
+ if (endGrapheme > 0 && endSeg < segments.length) {
261
+ const seg = segments[endSeg];
262
+ const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
263
+ granularity: "grapheme"
264
+ });
265
+ const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
266
+ const startG = lineStartSeg === endSeg ? lineStartGrapheme : 0;
267
+ text += graphemes.slice(startG, endGrapheme).join("");
268
+ }
269
+ if (endSeg > 0 && segments[endSeg - 1]?.kind === "soft-hyphen" && !(lineStartSeg === endSeg && lineStartGrapheme > 0)) {
270
+ text += "-";
271
+ }
272
+ lines.push({
273
+ text,
274
+ width,
275
+ startSegment: lineStartSeg,
276
+ startGrapheme: lineStartGrapheme,
277
+ endSegment: endSeg,
278
+ endGrapheme
279
+ });
280
+ lineW = 0;
281
+ hasContent = false;
282
+ pendingBreakSeg = -1;
283
+ pendingBreakWidth = 0;
284
+ }
285
+ function canBreakAfter2(kind) {
286
+ return kind === "space" || kind === "zero-width-break" || kind === "soft-hyphen";
287
+ }
288
+ function startLine(segIdx, graphemeIdx, width) {
289
+ hasContent = true;
290
+ lineStartSeg = segIdx;
291
+ lineStartGrapheme = graphemeIdx;
292
+ lineEndSeg = segIdx + 1;
293
+ lineEndGrapheme = 0;
294
+ lineW = width;
295
+ }
296
+ function startLineAtGrapheme(segIdx, graphemeIdx, width) {
297
+ hasContent = true;
298
+ lineStartSeg = segIdx;
299
+ lineStartGrapheme = graphemeIdx;
300
+ lineEndSeg = segIdx;
301
+ lineEndGrapheme = graphemeIdx + 1;
302
+ lineW = width;
303
+ }
304
+ for (let i = 0; i < segments.length; i++) {
305
+ const seg = segments[i];
306
+ if (seg.kind === "hard-break") {
307
+ if (hasContent) {
308
+ flushLine();
309
+ } else {
310
+ lines.push({
311
+ text: "",
312
+ width: 0,
313
+ startSegment: i,
314
+ startGrapheme: 0,
315
+ endSegment: i,
316
+ endGrapheme: 0
317
+ });
318
+ }
319
+ lineStartSeg = i + 1;
320
+ lineStartGrapheme = 0;
321
+ continue;
322
+ }
323
+ const w = seg.width;
324
+ if (!hasContent) {
325
+ if (w > maxWidth && seg.graphemeWidths) {
326
+ appendBreakableSegment(i, 0, seg.graphemeWidths);
327
+ } else {
328
+ startLine(i, 0, w);
329
+ }
330
+ if (canBreakAfter2(seg.kind)) {
331
+ pendingBreakSeg = i + 1;
332
+ pendingBreakWidth = seg.kind === "space" ? lineW - w : lineW;
333
+ }
334
+ continue;
335
+ }
336
+ const newW = lineW + w;
337
+ if (newW > maxWidth + 5e-3) {
338
+ if (canBreakAfter2(seg.kind)) {
339
+ lineW += w;
340
+ lineEndSeg = i + 1;
341
+ lineEndGrapheme = 0;
342
+ flushLine(i + 1, 0, seg.kind === "space" ? lineW - w : lineW);
343
+ continue;
344
+ }
345
+ if (pendingBreakSeg >= 0) {
346
+ flushLine(pendingBreakSeg, 0, pendingBreakWidth);
347
+ i--;
348
+ continue;
349
+ }
350
+ if (w > maxWidth && seg.graphemeWidths) {
351
+ flushLine();
352
+ appendBreakableSegment(i, 0, seg.graphemeWidths);
353
+ continue;
354
+ }
355
+ flushLine();
356
+ i--;
357
+ continue;
358
+ }
359
+ lineW = newW;
360
+ lineEndSeg = i + 1;
361
+ lineEndGrapheme = 0;
362
+ if (canBreakAfter2(seg.kind)) {
363
+ pendingBreakSeg = i + 1;
364
+ pendingBreakWidth = seg.kind === "space" ? lineW - w : lineW;
365
+ }
366
+ }
367
+ if (hasContent) {
368
+ flushLine();
369
+ }
370
+ return { lines, lineCount: lines.length };
371
+ function appendBreakableSegment(segIdx, startG, gWidths) {
372
+ for (let g = startG; g < gWidths.length; g++) {
373
+ const gw = gWidths[g];
374
+ if (!hasContent) {
375
+ startLineAtGrapheme(segIdx, g, gw);
376
+ continue;
377
+ }
378
+ if (lineW + gw > maxWidth + 5e-3) {
379
+ flushLine();
380
+ startLineAtGrapheme(segIdx, g, gw);
381
+ } else {
382
+ lineW += gw;
383
+ lineEndSeg = segIdx;
384
+ lineEndGrapheme = g + 1;
385
+ }
386
+ }
387
+ if (hasContent && lineEndSeg === segIdx && lineEndGrapheme === gWidths.length) {
388
+ lineEndSeg = segIdx + 1;
389
+ lineEndGrapheme = 0;
390
+ }
391
+ }
392
+ }
393
+ function canBreakAfter(kind) {
394
+ return kind === "space" || kind === "zero-width-break" || kind === "soft-hyphen";
395
+ }
396
+ var _graphemeSegmenter = null;
397
+ function graphemeSegmenter() {
398
+ if (_graphemeSegmenter === null) {
399
+ _graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
400
+ }
401
+ return _graphemeSegmenter;
402
+ }
403
+ function sliceSegmentText(seg, startG, endG) {
404
+ if (startG === 0 && endG < 0) return seg.text;
405
+ const graphemes = [...graphemeSegmenter().segment(seg.text)].map((g) => g.segment);
406
+ const stop = endG < 0 ? graphemes.length : endG;
407
+ return graphemes.slice(startG, stop).join("");
408
+ }
409
+ function buildLineText(segments, startSeg, startG, endSeg, endG, appendHyphen) {
410
+ let text = "";
411
+ for (let i = startSeg; i < endSeg; i++) {
412
+ const seg = segments[i];
413
+ if (seg.kind === "soft-hyphen" || seg.kind === "hard-break") continue;
414
+ if (i === startSeg && startG > 0) {
415
+ text += sliceSegmentText(seg, startG, -1);
416
+ } else {
417
+ text += seg.text;
418
+ }
419
+ }
420
+ if (endG > 0 && endSeg < segments.length) {
421
+ const seg = segments[endSeg];
422
+ const from = startSeg === endSeg ? startG : 0;
423
+ text += sliceSegmentText(seg, from, endG);
424
+ }
425
+ if (appendHyphen) text += "-";
426
+ return text;
427
+ }
428
+ function resolveHyphenWidth(ctx) {
429
+ if (!ctx || !ctx.adapter || !ctx.font) return 0;
430
+ const cache = ctx.cache;
431
+ if (cache) {
432
+ let fc = cache.get(ctx.font);
433
+ if (!fc) {
434
+ fc = /* @__PURE__ */ new Map();
435
+ cache.set(ctx.font, fc);
436
+ }
437
+ let hw = fc.get("-");
438
+ if (hw === void 0) {
439
+ hw = ctx.adapter.measureSegment("-", ctx.font).width;
440
+ fc.set("-", hw);
441
+ }
442
+ return hw;
443
+ }
444
+ return ctx.adapter.measureSegment("-", ctx.font).width;
445
+ }
446
+ function layoutNextLine(segments, cursor, slotWidth, ctx) {
447
+ let i = cursor.segmentIndex;
448
+ const initialG = cursor.graphemeIndex;
449
+ if (i >= segments.length) return null;
450
+ if (initialG === 0) {
451
+ while (i < segments.length) {
452
+ const seg = segments[i];
453
+ if (seg.kind === "hard-break") {
454
+ return {
455
+ text: "",
456
+ width: 0,
457
+ start: { segmentIndex: cursor.segmentIndex, graphemeIndex: 0 },
458
+ end: { segmentIndex: i + 1, graphemeIndex: 0 }
459
+ };
460
+ }
461
+ if (seg.kind === "space" || seg.kind === "zero-width-break" || seg.kind === "soft-hyphen") {
462
+ i += 1;
463
+ continue;
464
+ }
465
+ break;
466
+ }
467
+ if (i >= segments.length) return null;
468
+ }
469
+ const hyphenWidth = resolveHyphenWidth(ctx);
470
+ const startSeg = i;
471
+ const startG = i === cursor.segmentIndex ? initialG : 0;
472
+ let lineW = 0;
473
+ let lineEndSeg = startSeg;
474
+ let lineEndG = 0;
475
+ let hasContent = false;
476
+ let pendingBreakSeg = -1;
477
+ let pendingBreakG = 0;
478
+ let pendingBreakWidth = 0;
479
+ let pendingBreakSoftHyphen = false;
480
+ const recordPending = (sIdx, gIdx, widthAtBreak, kind) => {
481
+ pendingBreakSeg = sIdx;
482
+ pendingBreakG = gIdx;
483
+ pendingBreakWidth = widthAtBreak;
484
+ pendingBreakSoftHyphen = kind === "soft-hyphen";
485
+ };
486
+ const consumeBreakable = (segIdx, gStart, gWidths) => {
487
+ for (let g = gStart; g < gWidths.length; g++) {
488
+ const gw = gWidths[g];
489
+ if (!hasContent) {
490
+ lineW = gw;
491
+ lineEndSeg = segIdx;
492
+ lineEndG = g + 1;
493
+ hasContent = true;
494
+ continue;
495
+ }
496
+ if (lineW + gw > slotWidth + 5e-3) {
497
+ return true;
498
+ }
499
+ lineW += gw;
500
+ lineEndSeg = segIdx;
501
+ lineEndG = g + 1;
502
+ }
503
+ if (lineEndSeg === segIdx && lineEndG === gWidths.length) {
504
+ lineEndSeg = segIdx + 1;
505
+ lineEndG = 0;
506
+ }
507
+ return false;
508
+ };
509
+ if (startG > 0 && startSeg < segments.length) {
510
+ const seg = segments[startSeg];
511
+ if (seg.graphemeWidths) {
512
+ const overflowed = consumeBreakable(startSeg, startG, seg.graphemeWidths);
513
+ if (overflowed) {
514
+ const text2 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
515
+ return {
516
+ text: text2,
517
+ width: lineW,
518
+ start: { segmentIndex: startSeg, graphemeIndex: startG },
519
+ end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
520
+ };
521
+ }
522
+ i = lineEndSeg;
523
+ } else {
524
+ }
525
+ }
526
+ for (; i < segments.length; ) {
527
+ const seg = segments[i];
528
+ if (seg.kind === "hard-break") {
529
+ if (hasContent) {
530
+ const endsAtSoftHyphen2 = lineEndSeg > 0 && segments[lineEndSeg - 1]?.kind === "soft-hyphen";
531
+ const text2 = buildLineText(
532
+ segments,
533
+ startSeg,
534
+ startG,
535
+ lineEndSeg,
536
+ lineEndG,
537
+ endsAtSoftHyphen2
538
+ );
539
+ return {
540
+ text: text2,
541
+ width: lineW + (endsAtSoftHyphen2 ? hyphenWidth : 0),
542
+ start: { segmentIndex: startSeg, graphemeIndex: startG },
543
+ end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
544
+ };
545
+ }
546
+ return {
547
+ text: "",
548
+ width: 0,
549
+ start: { segmentIndex: startSeg, graphemeIndex: startG },
550
+ end: { segmentIndex: i + 1, graphemeIndex: 0 }
551
+ };
552
+ }
553
+ const w = seg.width;
554
+ if (!hasContent) {
555
+ if (w > slotWidth && seg.graphemeWidths) {
556
+ const overflowed = consumeBreakable(i, 0, seg.graphemeWidths);
557
+ if (overflowed) {
558
+ const text2 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
559
+ return {
560
+ text: text2,
561
+ width: lineW,
562
+ start: { segmentIndex: startSeg, graphemeIndex: startG },
563
+ end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
564
+ };
565
+ }
566
+ i = lineEndSeg;
567
+ continue;
568
+ }
569
+ lineW = w;
570
+ lineEndSeg = i + 1;
571
+ lineEndG = 0;
572
+ hasContent = true;
573
+ if (canBreakAfter(seg.kind)) {
574
+ recordPending(i + 1, 0, seg.kind === "space" ? lineW - w : lineW, seg.kind);
575
+ }
576
+ i += 1;
577
+ continue;
578
+ }
579
+ const newW = lineW + w;
580
+ if (newW > slotWidth + 5e-3) {
581
+ if (canBreakAfter(seg.kind)) {
582
+ lineEndSeg = i + 1;
583
+ lineEndG = 0;
584
+ const endsAtSoftHyphen2 = seg.kind === "soft-hyphen";
585
+ const finalWidth = seg.kind === "space" ? lineW : lineW + (endsAtSoftHyphen2 ? hyphenWidth : 0);
586
+ const text3 = buildLineText(
587
+ segments,
588
+ startSeg,
589
+ startG,
590
+ lineEndSeg,
591
+ lineEndG,
592
+ endsAtSoftHyphen2
593
+ );
594
+ return {
595
+ text: text3,
596
+ width: finalWidth,
597
+ start: { segmentIndex: startSeg, graphemeIndex: startG },
598
+ end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
599
+ };
600
+ }
601
+ if (pendingBreakSeg >= 0) {
602
+ const text3 = buildLineText(
603
+ segments,
604
+ startSeg,
605
+ startG,
606
+ pendingBreakSeg,
607
+ pendingBreakG,
608
+ pendingBreakSoftHyphen
609
+ );
610
+ return {
611
+ text: text3,
612
+ width: pendingBreakWidth + (pendingBreakSoftHyphen ? hyphenWidth : 0),
613
+ start: { segmentIndex: startSeg, graphemeIndex: startG },
614
+ end: { segmentIndex: pendingBreakSeg, graphemeIndex: pendingBreakG }
615
+ };
616
+ }
617
+ if (w > slotWidth && seg.graphemeWidths) {
618
+ const text3 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
619
+ return {
620
+ text: text3,
621
+ width: lineW,
622
+ start: { segmentIndex: startSeg, graphemeIndex: startG },
623
+ end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
624
+ };
625
+ }
626
+ const text2 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
627
+ return {
628
+ text: text2,
629
+ width: lineW,
630
+ start: { segmentIndex: startSeg, graphemeIndex: startG },
631
+ end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
632
+ };
633
+ }
634
+ lineW = newW;
635
+ lineEndSeg = i + 1;
636
+ lineEndG = 0;
637
+ if (canBreakAfter(seg.kind)) {
638
+ recordPending(i + 1, 0, seg.kind === "space" ? lineW - w : lineW, seg.kind);
639
+ }
640
+ i += 1;
641
+ }
642
+ if (!hasContent) return null;
643
+ const endsAtSoftHyphen = lineEndSeg > 0 && segments[lineEndSeg - 1]?.kind === "soft-hyphen";
644
+ const text = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, endsAtSoftHyphen);
645
+ return {
646
+ text,
647
+ width: lineW + (endsAtSoftHyphen ? hyphenWidth : 0),
648
+ start: { segmentIndex: startSeg, graphemeIndex: startG },
649
+ end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
650
+ };
651
+ }
652
+ function carveTextLineSlots(base, blocked, minSlotWidth = 0) {
653
+ let slots = [base];
654
+ for (let bi = 0; bi < blocked.length; bi++) {
655
+ const block = blocked[bi];
656
+ const next = [];
657
+ for (let si = 0; si < slots.length; si++) {
658
+ const slot = slots[si];
659
+ if (block.right <= slot.left || block.left >= slot.right) {
660
+ next.push(slot);
661
+ continue;
662
+ }
663
+ if (block.left > slot.left) next.push({ left: slot.left, right: block.left });
664
+ if (block.right < slot.right) next.push({ left: block.right, right: slot.right });
665
+ }
666
+ slots = next;
667
+ }
668
+ if (minSlotWidth > 0) {
669
+ return slots.filter((s) => s.right - s.left >= minSlotWidth);
670
+ }
671
+ return slots;
672
+ }
673
+ function computeCharPositions(lineBreaks, segments, lineHeight) {
674
+ const positions = [];
675
+ const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
676
+ granularity: "grapheme"
677
+ });
678
+ for (let lineIdx = 0; lineIdx < lineBreaks.lines.length; lineIdx++) {
679
+ const line = lineBreaks.lines[lineIdx];
680
+ const y = lineIdx * lineHeight;
681
+ let x = 0;
682
+ for (let si = line.startSegment; si < segments.length; si++) {
683
+ const seg = segments[si];
684
+ if (seg.kind === "soft-hyphen" || seg.kind === "hard-break") {
685
+ if (si >= line.endSegment && line.endGrapheme === 0) break;
686
+ continue;
687
+ }
688
+ const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
689
+ if (graphemes.length === 0) continue;
690
+ const startG = si === line.startSegment ? line.startGrapheme : 0;
691
+ let endG;
692
+ if (si < line.endSegment) {
693
+ endG = graphemes.length;
694
+ } else if (si === line.endSegment && line.endGrapheme > 0) {
695
+ endG = line.endGrapheme;
696
+ } else {
697
+ break;
698
+ }
699
+ for (let g = startG; g < endG; g++) {
700
+ const gWidth = seg.graphemeWidths ? seg.graphemeWidths[g] : seg.width / graphemes.length;
701
+ positions.push({ x, y, width: gWidth, height: lineHeight, line: lineIdx });
702
+ x += gWidth;
703
+ }
704
+ }
705
+ }
706
+ return positions;
707
+ }
708
+ function reactiveLayout(opts) {
709
+ const { adapter, name = "reactive-layout" } = opts;
710
+ const g = new Graph(name);
711
+ const measureCache = /* @__PURE__ */ new Map();
712
+ const textNode = state(opts.text ?? "", { name: "text" });
713
+ const fontNode = state(opts.font ?? "16px sans-serif", {
714
+ name: "font"
715
+ });
716
+ const lineHeightNode = state(opts.lineHeight ?? 20, {
717
+ name: "line-height"
718
+ });
719
+ const maxWidthNode = state(Math.max(0, opts.maxWidth ?? 800), {
720
+ name: "max-width"
721
+ });
722
+ function graphemeWidthsEqual(a, b) {
723
+ if (a === null || b === null) return a === b;
724
+ if (a.length !== b.length) return false;
725
+ for (let i = 0; i < a.length; i++) {
726
+ if (a[i] !== b[i]) return false;
727
+ }
728
+ return true;
729
+ }
730
+ const segmentsNode = node(
731
+ [textNode, fontNode],
732
+ (data, actions, ctx) => {
733
+ const b0 = data[0];
734
+ const textVal = b0 != null && b0.length > 0 ? b0.at(-1) : ctx.prevData[0];
735
+ const b1 = data[1];
736
+ const fontVal = b1 != null && b1.length > 0 ? b1.at(-1) : ctx.prevData[1];
737
+ const t0 = monotonicNs();
738
+ const measureStats = { hits: 0, misses: 0 };
739
+ const result = analyzeAndMeasure(
740
+ textVal,
741
+ fontVal,
742
+ adapter,
743
+ measureCache,
744
+ measureStats
745
+ );
746
+ const elapsed = monotonicNs() - t0;
747
+ const lookups = measureStats.hits + measureStats.misses;
748
+ const hitRate = lookups === 0 ? 1 : measureStats.hits / lookups;
749
+ const meta = segmentsNode.meta;
750
+ if (meta) {
751
+ emitToMeta(meta["cache-hit-rate"], hitRate);
752
+ emitToMeta(meta["segment-count"], result.length);
753
+ emitToMeta(meta["layout-time-ns"], elapsed);
754
+ }
755
+ actions.emit(result);
756
+ return () => {
757
+ measureCache.clear();
758
+ adapter.clearCache?.();
759
+ };
760
+ },
761
+ {
762
+ name: "segments",
763
+ describeKind: "derived",
764
+ meta: {
765
+ "cache-hit-rate": 0,
766
+ "segment-count": 0,
767
+ "layout-time-ns": 0
768
+ },
769
+ equals: (a, b) => {
770
+ const sa = a;
771
+ const sb = b;
772
+ if (sa == null || sb == null) return sa === sb;
773
+ if (sa.length !== sb.length) return false;
774
+ for (let i = 0; i < sa.length; i++) {
775
+ const pa = sa[i];
776
+ const pb = sb[i];
777
+ if (pa.text !== pb.text || pa.width !== pb.width || pa.kind !== pb.kind || !graphemeWidthsEqual(pa.graphemeWidths ?? null, pb.graphemeWidths ?? null))
778
+ return false;
779
+ }
780
+ return true;
781
+ }
782
+ }
783
+ );
784
+ const lineBreaksNode = derived(
785
+ [segmentsNode, maxWidthNode, fontNode],
786
+ ([segs, mw, font]) => {
787
+ return computeLineBreaks(
788
+ segs,
789
+ mw,
790
+ adapter,
791
+ font,
792
+ measureCache
793
+ );
794
+ },
795
+ {
796
+ name: "line-breaks",
797
+ equals: (a, b) => {
798
+ const la = a;
799
+ const lb = b;
800
+ if (la == null || lb == null) return la === lb;
801
+ if (la.lineCount !== lb.lineCount) return false;
802
+ for (let i = 0; i < la.lines.length; i++) {
803
+ const lineA = la.lines[i];
804
+ const lineB = lb.lines[i];
805
+ if (lineA.text !== lineB.text || lineA.width !== lineB.width || lineA.startSegment !== lineB.startSegment || lineA.startGrapheme !== lineB.startGrapheme || lineA.endSegment !== lineB.endSegment || lineA.endGrapheme !== lineB.endGrapheme)
806
+ return false;
807
+ }
808
+ return true;
809
+ }
810
+ }
811
+ );
812
+ const heightNode = derived(
813
+ [lineBreaksNode, lineHeightNode],
814
+ ([lb, lh]) => lb.lineCount * lh,
815
+ { name: "height" }
816
+ );
817
+ const charPositionsNode = derived(
818
+ [lineBreaksNode, segmentsNode, lineHeightNode],
819
+ ([lb, segs, lh]) => {
820
+ return computeCharPositions(lb, segs, lh);
821
+ },
822
+ {
823
+ name: "char-positions",
824
+ equals: (a, b) => {
825
+ const ca = a;
826
+ const cb = b;
827
+ if (ca == null || cb == null) return ca === cb;
828
+ if (ca.length !== cb.length) return false;
829
+ for (let i = 0; i < ca.length; i++) {
830
+ if (ca[i].x !== cb[i].x || ca[i].y !== cb[i].y || ca[i].width !== cb[i].width)
831
+ return false;
832
+ }
833
+ return true;
834
+ }
835
+ }
836
+ );
837
+ g.add("text", textNode);
838
+ g.add("font", fontNode);
839
+ g.add("line-height", lineHeightNode);
840
+ g.add("max-width", maxWidthNode);
841
+ g.add("segments", segmentsNode);
842
+ g.add("line-breaks", lineBreaksNode);
843
+ g.add("height", heightNode);
844
+ g.add("char-positions", charPositionsNode);
845
+ return {
846
+ graph: g,
847
+ setText: (text) => g.set("text", text),
848
+ setFont: (font) => g.set("font", font),
849
+ setLineHeight: (lh) => g.set("line-height", lh),
850
+ setMaxWidth: (mw) => g.set("max-width", Math.max(0, mw)),
851
+ segments: segmentsNode,
852
+ lineBreaks: lineBreaksNode,
853
+ height: heightNode,
854
+ charPositions: charPositionsNode
855
+ };
856
+ }
857
+
858
+ export {
859
+ analyzeAndMeasure,
860
+ computeLineBreaks,
861
+ layoutNextLine,
862
+ carveTextLineSlots,
863
+ computeCharPositions,
864
+ reactiveLayout
865
+ };
866
+ //# sourceMappingURL=chunk-LCE3GF5P.js.map