@graphrefly/graphrefly 0.23.0 → 0.25.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 (78) hide show
  1. package/dist/{chunk-NZMBRXQV.js → chunk-5DJTTKX3.js} +11 -13
  2. package/dist/chunk-5DJTTKX3.js.map +1 -0
  3. package/dist/{chunk-PNUZM7PC.js → chunk-EVR6UFUV.js} +6 -6
  4. package/dist/{chunk-HVBX5KIW.js → chunk-H4RVA4VE.js} +2 -2
  5. package/dist/{chunk-32N5A454.js → chunk-HWPIFSW2.js} +2 -2
  6. package/dist/chunk-HWPIFSW2.js.map +1 -0
  7. package/dist/{chunk-XTLYW4FR.js → chunk-IAHGTNOZ.js} +6 -4
  8. package/dist/{chunk-XTLYW4FR.js.map → chunk-IAHGTNOZ.js.map} +1 -1
  9. package/dist/{chunk-CWYPA63G.js → chunk-L2GLW2U7.js} +69 -2
  10. package/dist/chunk-L2GLW2U7.js.map +1 -0
  11. package/dist/{chunk-2GQLMQVJ.js → chunk-MW4VAKAO.js} +3 -3
  12. package/dist/{chunk-JFONSPNF.js → chunk-PY4XCDLR.js} +2 -2
  13. package/dist/{chunk-263BEJJO.js → chunk-QOWVNWOC.js} +3 -3
  14. package/dist/{chunk-PX6PDUJ5.js → chunk-TKE3JGOH.js} +491 -19
  15. package/dist/chunk-TKE3JGOH.js.map +1 -0
  16. package/dist/{chunk-XRFJJ2IU.js → chunk-XOFWRC73.js} +3 -3
  17. package/dist/compat/nestjs/index.cjs +10 -12
  18. package/dist/compat/nestjs/index.cjs.map +1 -1
  19. package/dist/compat/nestjs/index.d.cts +6 -6
  20. package/dist/compat/nestjs/index.d.ts +6 -6
  21. package/dist/compat/nestjs/index.js +9 -9
  22. package/dist/core/index.cjs +10 -12
  23. package/dist/core/index.cjs.map +1 -1
  24. package/dist/core/index.d.cts +3 -3
  25. package/dist/core/index.d.ts +3 -3
  26. package/dist/core/index.js +3 -3
  27. package/dist/extra/index.cjs +78 -12
  28. package/dist/extra/index.cjs.map +1 -1
  29. package/dist/extra/index.d.cts +4 -4
  30. package/dist/extra/index.d.ts +4 -4
  31. package/dist/extra/index.js +6 -4
  32. package/dist/graph/index.cjs +10 -12
  33. package/dist/graph/index.cjs.map +1 -1
  34. package/dist/graph/index.d.cts +5 -5
  35. package/dist/graph/index.d.ts +5 -5
  36. package/dist/graph/index.js +4 -4
  37. package/dist/{graph-CEO2FkLY.d.ts → graph-B6NFqv3z.d.ts} +3 -3
  38. package/dist/{graph-BtdSRHUc.d.cts → graph-D-3JIQme.d.cts} +3 -3
  39. package/dist/{index-BFGjXbiP.d.cts → index-AMWewNDe.d.cts} +2 -2
  40. package/dist/{index-BUj3ASVe.d.cts → index-BJB7t9gg.d.cts} +10 -24
  41. package/dist/{index-DSPc5rkv.d.ts → index-C-TXEa7C.d.ts} +10 -24
  42. package/dist/{index-CkElcUY6.d.ts → index-CYkjxu3s.d.ts} +2 -2
  43. package/dist/{index-B0tfuXwV.d.cts → index-Ch0IpIO0.d.cts} +32 -5
  44. package/dist/{index-C59uSJAH.d.cts → index-DKE1EATr.d.cts} +224 -4
  45. package/dist/{index-DgscL7v0.d.ts → index-DiobMNwE.d.ts} +3 -3
  46. package/dist/{index-RXN94sHK.d.ts → index-Ds23Wvou.d.ts} +32 -5
  47. package/dist/{index-BPlWVAKY.d.cts → index-J7Kc0oIQ.d.cts} +3 -3
  48. package/dist/{index-jEtF4N7L.d.ts → index-OXImXMq6.d.ts} +224 -4
  49. package/dist/index.cjs +570 -47
  50. package/dist/index.cjs.map +1 -1
  51. package/dist/index.d.cts +15 -15
  52. package/dist/index.d.ts +15 -15
  53. package/dist/index.js +23 -31
  54. package/dist/index.js.map +1 -1
  55. package/dist/{meta-3QjzotRv.d.ts → meta-CnkLA_43.d.ts} +1 -1
  56. package/dist/{meta-B-Lbs4-O.d.cts → meta-DWbkoq1s.d.cts} +1 -1
  57. package/dist/{node-C7PD3sn9.d.cts → node-B-f-Lu-k.d.cts} +15 -13
  58. package/dist/{node-C7PD3sn9.d.ts → node-B-f-Lu-k.d.ts} +15 -13
  59. package/dist/{observable-axpzv1K2.d.cts → observable-DBnrwcar.d.cts} +1 -1
  60. package/dist/{observable-EyO-moQY.d.ts → observable-uP-wy_uK.d.ts} +1 -1
  61. package/dist/patterns/reactive-layout/index.cjs +498 -28
  62. package/dist/patterns/reactive-layout/index.cjs.map +1 -1
  63. package/dist/patterns/reactive-layout/index.d.cts +5 -5
  64. package/dist/patterns/reactive-layout/index.d.ts +5 -5
  65. package/dist/patterns/reactive-layout/index.js +20 -8
  66. package/dist/{storage-DIgAr7M_.d.cts → storage-BuTdpCI1.d.cts} +1 -1
  67. package/dist/{storage-CHT5WE9m.d.ts → storage-F2X1U1x0.d.ts} +1 -1
  68. package/package.json +2 -2
  69. package/dist/chunk-32N5A454.js.map +0 -1
  70. package/dist/chunk-CWYPA63G.js.map +0 -1
  71. package/dist/chunk-NZMBRXQV.js.map +0 -1
  72. package/dist/chunk-PX6PDUJ5.js.map +0 -1
  73. /package/dist/{chunk-PNUZM7PC.js.map → chunk-EVR6UFUV.js.map} +0 -0
  74. /package/dist/{chunk-HVBX5KIW.js.map → chunk-H4RVA4VE.js.map} +0 -0
  75. /package/dist/{chunk-2GQLMQVJ.js.map → chunk-MW4VAKAO.js.map} +0 -0
  76. /package/dist/{chunk-JFONSPNF.js.map → chunk-PY4XCDLR.js.map} +0 -0
  77. /package/dist/{chunk-263BEJJO.js.map → chunk-QOWVNWOC.js.map} +0 -0
  78. /package/dist/{chunk-XRFJJ2IU.js.map → chunk-XOFWRC73.js.map} +0 -0
package/dist/index.cjs CHANGED
@@ -200,6 +200,7 @@ __export(index_exports, {
200
200
  fromPromise: () => fromPromise,
201
201
  fromPulsar: () => fromPulsar,
202
202
  fromRabbitMQ: () => fromRabbitMQ,
203
+ fromRaf: () => fromRaf,
203
204
  fromRedisStream: () => fromRedisStream,
204
205
  fromSSE: () => fromSSE,
205
206
  fromSqlite: () => fromSqlite,
@@ -2132,37 +2133,35 @@ var NodeImpl = class _NodeImpl {
2132
2133
  // --- Emit pipeline ---
2133
2134
  /**
2134
2135
  * @internal The unified dispatch waist — one call = one wave.
2136
+ * See `GRAPHREFLY-SPEC.md` §1.3.1 for protocol context — the stages
2137
+ * below are the implementation order.
2135
2138
  *
2136
2139
  * Pipeline stages, in order:
2137
2140
  *
2138
- * 1. Early-return on empty batch.
2139
- * 2. Terminal filter — post-COMPLETE/ERROR only TEARDOWN/INVALIDATE
2141
+ * 1. Terminal filter — post-COMPLETE/ERROR only TEARDOWN/INVALIDATE
2140
2142
  * still propagate so graph teardown and cache-clear still work.
2141
- * 3. Tier sort (stable) — the batch can be in any order when it
2143
+ * 2. Tier sort (stable) — the batch can be in any order when it
2142
2144
  * arrives; the walker downstream (`downWithBatch`) assumes
2143
2145
  * ascending tier monotone, and so does `_updateState`'s tier-3
2144
2146
  * slice walk. This is the single source of truth for ordering.
2145
- * 4. Synthetic DIRTY prefix — if a tier-3 payload is present, no
2147
+ * 3. Synthetic DIRTY prefix — if a tier-3 payload is present, no
2146
2148
  * DIRTY is already in the batch, and the node isn't already in
2147
2149
  * `"dirty"` status, prepend `[DIRTY]` after any tier-0 START
2148
2150
  * entries. Guarantees spec §1.3.1 (DIRTY precedes DATA within
2149
2151
  * the same batch) uniformly across every entry point.
2150
- * 5. PAUSE/RESUME lock bookkeeping (C0) — update `_pauseLocks`,
2152
+ * 4. PAUSE/RESUME lock bookkeeping (C0) — update `_pauseLocks`,
2151
2153
  * derive `_paused`, filter unknown-lockId RESUME, replay
2152
2154
  * bufferAll buffer on final lock release.
2153
- * 6. Meta TEARDOWN fan-out — notify meta children before
2155
+ * 5. Meta TEARDOWN fan-out — notify meta children before
2154
2156
  * `_updateState`'s TEARDOWN branch calls `_deactivate`. Hoisted
2155
2157
  * out of the walk to keep `_updateState` re-entrance-free.
2156
- * 7. `_updateState` — walk the batch in tier order, advancing
2158
+ * 6. `_updateState` — walk the batch in tier order, advancing
2157
2159
  * `_cached` / `_status` / `_versioning` and running equals
2158
2160
  * substitution on tier-3 DATA (§3.5.1). Returns
2159
2161
  * `{finalMessages, equalsError?}`.
2160
- * 8. `downWithBatch` dispatch (or bufferAll capture if paused with
2162
+ * 7. `downWithBatch` dispatch (or bufferAll capture if paused with
2161
2163
  * `pausable: "resumeAll"`).
2162
- * 9. Recursive ERROR emission if equals threw mid-walk.
2163
- *
2164
- * `node.down` / `node.emit` / `actions.down` / `actions.emit` all
2165
- * converge here — the unified `_emit` waist (spec §1.3.1).
2164
+ * 8. Recursive ERROR emission if equals threw mid-walk.
2166
2165
  */
2167
2166
  _emit(messages) {
2168
2167
  if (messages.length === 0) return;
@@ -3188,6 +3187,72 @@ function fromTimer(ms, opts) {
3188
3187
  return cleanup;
3189
3188
  }, sourceOpts(rest));
3190
3189
  }
3190
+ function fromRaf(opts) {
3191
+ const { signal, ...rest } = opts ?? {};
3192
+ return producer((a) => {
3193
+ let done = false;
3194
+ let rafId;
3195
+ let fallbackTimer;
3196
+ let abortListenerAdded = false;
3197
+ let visibilityListenerAdded = false;
3198
+ const raf = typeof requestAnimationFrame === "function" ? requestAnimationFrame : void 0;
3199
+ const caf = typeof cancelAnimationFrame === "function" ? cancelAnimationFrame : void 0;
3200
+ const doc = typeof document !== "undefined" ? document : void 0;
3201
+ const clearPending = () => {
3202
+ if (rafId !== void 0 && caf) caf(rafId);
3203
+ if (fallbackTimer !== void 0) clearTimeout(fallbackTimer);
3204
+ rafId = void 0;
3205
+ fallbackTimer = void 0;
3206
+ };
3207
+ const cleanup = () => {
3208
+ done = true;
3209
+ clearPending();
3210
+ if (abortListenerAdded) {
3211
+ signal?.removeEventListener("abort", onAbort);
3212
+ abortListenerAdded = false;
3213
+ }
3214
+ if (visibilityListenerAdded && doc) {
3215
+ doc.removeEventListener("visibilitychange", onVisibilityChange);
3216
+ visibilityListenerAdded = false;
3217
+ }
3218
+ };
3219
+ const onAbort = () => {
3220
+ if (done) return;
3221
+ cleanup();
3222
+ a.down([[ERROR, signal.reason]]);
3223
+ };
3224
+ const tick = (now) => {
3225
+ if (done) return;
3226
+ a.emit(now);
3227
+ scheduleNext();
3228
+ };
3229
+ const scheduleNext = () => {
3230
+ if (done) return;
3231
+ if (raf && (!doc || doc.visibilityState !== "hidden")) {
3232
+ rafId = raf(tick);
3233
+ } else {
3234
+ fallbackTimer = setTimeout(() => tick(performance.now()), 16);
3235
+ }
3236
+ };
3237
+ const onVisibilityChange = () => {
3238
+ if (done) return;
3239
+ clearPending();
3240
+ scheduleNext();
3241
+ };
3242
+ if (signal?.aborted) {
3243
+ onAbort();
3244
+ return cleanup;
3245
+ }
3246
+ signal?.addEventListener("abort", onAbort, { once: true });
3247
+ abortListenerAdded = signal !== void 0;
3248
+ if (doc && raf) {
3249
+ doc.addEventListener("visibilitychange", onVisibilityChange);
3250
+ visibilityListenerAdded = true;
3251
+ }
3252
+ scheduleNext();
3253
+ return cleanup;
3254
+ }, sourceOpts(rest));
3255
+ }
3191
3256
  function fromCron(expr, opts) {
3192
3257
  const schedule = parseCron(expr);
3193
3258
  const { tickMs: tickOpt, output, ...rest } = opts ?? {};
@@ -8187,20 +8252,12 @@ function useStore4(node2) {
8187
8252
  function useSubscribeRecord4(keys, factory) {
8188
8253
  const result = (0, import_vue.shallowRef)({});
8189
8254
  const activeSubs = /* @__PURE__ */ new Map();
8190
- let disposed = false;
8191
- let batchPending = false;
8192
- function scheduleBatch() {
8193
- if (batchPending) return;
8194
- batchPending = true;
8195
- queueMicrotask(() => {
8196
- if (disposed) return;
8197
- batchPending = false;
8198
- const snap = {};
8199
- for (const [key, entry] of activeSubs) {
8200
- snap[key] = { ...entry.values };
8201
- }
8202
- result.value = snap;
8203
- });
8255
+ function flushResult() {
8256
+ const snap = {};
8257
+ for (const [key, entry] of activeSubs) {
8258
+ snap[key] = { ...entry.values };
8259
+ }
8260
+ result.value = snap;
8204
8261
  }
8205
8262
  function sync(newKeys) {
8206
8263
  for (const entry of activeSubs.values()) {
@@ -8217,7 +8274,7 @@ function useSubscribeRecord4(keys, factory) {
8217
8274
  values[field] = node2.cache;
8218
8275
  const unsub = node2.subscribe(() => {
8219
8276
  values[field] = node2.cache;
8220
- scheduleBatch();
8277
+ flushResult();
8221
8278
  });
8222
8279
  subs.push(unsub);
8223
8280
  }
@@ -8236,7 +8293,6 @@ function useSubscribeRecord4(keys, factory) {
8236
8293
  (0, import_vue.watch)(readKeys, (newKeys) => sync(newKeys ?? []), { immediate: true });
8237
8294
  if ((0, import_vue.getCurrentScope)()) {
8238
8295
  (0, import_vue.onScopeDispose)(() => {
8239
- disposed = true;
8240
8296
  for (const entry of activeSubs.values()) {
8241
8297
  for (const unsub of entry.subs) unsub();
8242
8298
  }
@@ -8265,18 +8321,17 @@ function create(initializer) {
8265
8321
  g.add("state", s);
8266
8322
  const getState = () => s.cache;
8267
8323
  const setState = (partial, replace) => {
8268
- const prev = getState();
8324
+ const prev = s.cache;
8269
8325
  const next = typeof partial === "function" ? partial(prev) : partial;
8270
- const nextState = replace ? next : { ...prev, ...next };
8271
- s.emit(nextState);
8326
+ s.emit(replace ? next : { ...prev, ...next });
8272
8327
  };
8273
8328
  const api = {
8274
8329
  getState,
8275
8330
  setState,
8276
8331
  getInitialState: () => initialValue,
8277
8332
  subscribe: (listener) => {
8278
- let prev = getState();
8279
8333
  let initial = true;
8334
+ let prev = s.cache;
8280
8335
  return s.subscribe((msgs) => {
8281
8336
  for (const [t, v] of msgs) {
8282
8337
  if (t === DATA) {
@@ -8380,6 +8435,7 @@ __export(extra_exports, {
8380
8435
  fromPromise: () => fromPromise,
8381
8436
  fromPulsar: () => fromPulsar,
8382
8437
  fromRabbitMQ: () => fromRabbitMQ,
8438
+ fromRaf: () => fromRaf,
8383
8439
  fromRedisStream: () => fromRedisStream,
8384
8440
  fromSSE: () => fromSSE,
8385
8441
  fromSqlite: () => fromSqlite,
@@ -18066,7 +18122,7 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
18066
18122
  const normalized = normalizeWhitespace(text);
18067
18123
  if (normalized.length === 0) return [];
18068
18124
  const pieces = segmentText(normalized);
18069
- const graphemeSegmenter = new Intl.Segmenter(void 0, {
18125
+ const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
18070
18126
  granularity: "grapheme"
18071
18127
  });
18072
18128
  const rawTexts = [];
@@ -18110,7 +18166,8 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
18110
18166
  let w = fontCache.get(seg);
18111
18167
  if (w === void 0) {
18112
18168
  if (stats) stats.misses += 1;
18113
- w = adapter.measureSegment(seg, font).width;
18169
+ const raw = adapter.measureSegment(seg, font).width;
18170
+ w = Number.isFinite(raw) && raw >= 0 ? raw : 0;
18114
18171
  fontCache.set(seg, w);
18115
18172
  } else if (stats) {
18116
18173
  stats.hits += 1;
@@ -18132,7 +18189,7 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
18132
18189
  }
18133
18190
  if (isCJK(t)) {
18134
18191
  let unitText = "";
18135
- for (const gs of graphemeSegmenter.segment(t)) {
18192
+ for (const gs of graphemeSegmenter2.segment(t)) {
18136
18193
  const grapheme = gs.segment;
18137
18194
  if (unitText.length > 0 && kinsokuStart.has(grapheme)) {
18138
18195
  unitText += grapheme;
@@ -18164,7 +18221,7 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
18164
18221
  let graphemeWidths = null;
18165
18222
  if (mergedWordLike[i] && t.length > 1) {
18166
18223
  const gWidths = [];
18167
- for (const gs of graphemeSegmenter.segment(t)) {
18224
+ for (const gs of graphemeSegmenter2.segment(t)) {
18168
18225
  gWidths.push(measureCached(gs.segment));
18169
18226
  }
18170
18227
  if (gWidths.length > 1) {
@@ -18204,10 +18261,10 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
18204
18261
  const seg = segments[i];
18205
18262
  if (seg.kind === "soft-hyphen" || seg.kind === "hard-break") continue;
18206
18263
  if (i === lineStartSeg && lineStartGrapheme > 0 && seg.graphemeWidths) {
18207
- const graphemeSegmenter = new Intl.Segmenter(void 0, {
18264
+ const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
18208
18265
  granularity: "grapheme"
18209
18266
  });
18210
- const graphemes = [...graphemeSegmenter.segment(seg.text)].map((g) => g.segment);
18267
+ const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
18211
18268
  text += graphemes.slice(lineStartGrapheme).join("");
18212
18269
  } else {
18213
18270
  text += seg.text;
@@ -18215,10 +18272,10 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
18215
18272
  }
18216
18273
  if (endGrapheme > 0 && endSeg < segments.length) {
18217
18274
  const seg = segments[endSeg];
18218
- const graphemeSegmenter = new Intl.Segmenter(void 0, {
18275
+ const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
18219
18276
  granularity: "grapheme"
18220
18277
  });
18221
- const graphemes = [...graphemeSegmenter.segment(seg.text)].map((g) => g.segment);
18278
+ const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
18222
18279
  const startG = lineStartSeg === endSeg ? lineStartGrapheme : 0;
18223
18280
  text += graphemes.slice(startG, endGrapheme).join("");
18224
18281
  }
@@ -18238,7 +18295,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
18238
18295
  pendingBreakSeg = -1;
18239
18296
  pendingBreakWidth = 0;
18240
18297
  }
18241
- function canBreakAfter(kind) {
18298
+ function canBreakAfter2(kind) {
18242
18299
  return kind === "space" || kind === "zero-width-break" || kind === "soft-hyphen";
18243
18300
  }
18244
18301
  function startLine(segIdx, graphemeIdx, width) {
@@ -18283,7 +18340,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
18283
18340
  } else {
18284
18341
  startLine(i, 0, w);
18285
18342
  }
18286
- if (canBreakAfter(seg.kind)) {
18343
+ if (canBreakAfter2(seg.kind)) {
18287
18344
  pendingBreakSeg = i + 1;
18288
18345
  pendingBreakWidth = seg.kind === "space" ? lineW - w : lineW;
18289
18346
  }
@@ -18291,7 +18348,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
18291
18348
  }
18292
18349
  const newW = lineW + w;
18293
18350
  if (newW > maxWidth + 5e-3) {
18294
- if (canBreakAfter(seg.kind)) {
18351
+ if (canBreakAfter2(seg.kind)) {
18295
18352
  lineW += w;
18296
18353
  lineEndSeg = i + 1;
18297
18354
  lineEndGrapheme = 0;
@@ -18315,7 +18372,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
18315
18372
  lineW = newW;
18316
18373
  lineEndSeg = i + 1;
18317
18374
  lineEndGrapheme = 0;
18318
- if (canBreakAfter(seg.kind)) {
18375
+ if (canBreakAfter2(seg.kind)) {
18319
18376
  pendingBreakSeg = i + 1;
18320
18377
  pendingBreakWidth = seg.kind === "space" ? lineW - w : lineW;
18321
18378
  }
@@ -18346,9 +18403,289 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
18346
18403
  }
18347
18404
  }
18348
18405
  }
18406
+ function canBreakAfter(kind) {
18407
+ return kind === "space" || kind === "zero-width-break" || kind === "soft-hyphen";
18408
+ }
18409
+ var _graphemeSegmenter = null;
18410
+ function graphemeSegmenter() {
18411
+ if (_graphemeSegmenter === null) {
18412
+ _graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
18413
+ }
18414
+ return _graphemeSegmenter;
18415
+ }
18416
+ function sliceSegmentText(seg, startG, endG) {
18417
+ if (startG === 0 && endG < 0) return seg.text;
18418
+ const graphemes = [...graphemeSegmenter().segment(seg.text)].map((g) => g.segment);
18419
+ const stop = endG < 0 ? graphemes.length : endG;
18420
+ return graphemes.slice(startG, stop).join("");
18421
+ }
18422
+ function buildLineText(segments, startSeg, startG, endSeg, endG, appendHyphen) {
18423
+ let text = "";
18424
+ for (let i = startSeg; i < endSeg; i++) {
18425
+ const seg = segments[i];
18426
+ if (seg.kind === "soft-hyphen" || seg.kind === "hard-break") continue;
18427
+ if (i === startSeg && startG > 0) {
18428
+ text += sliceSegmentText(seg, startG, -1);
18429
+ } else {
18430
+ text += seg.text;
18431
+ }
18432
+ }
18433
+ if (endG > 0 && endSeg < segments.length) {
18434
+ const seg = segments[endSeg];
18435
+ const from = startSeg === endSeg ? startG : 0;
18436
+ text += sliceSegmentText(seg, from, endG);
18437
+ }
18438
+ if (appendHyphen) text += "-";
18439
+ return text;
18440
+ }
18441
+ function resolveHyphenWidth(ctx) {
18442
+ if (!ctx || !ctx.adapter || !ctx.font) return 0;
18443
+ const cache = ctx.cache;
18444
+ if (cache) {
18445
+ let fc = cache.get(ctx.font);
18446
+ if (!fc) {
18447
+ fc = /* @__PURE__ */ new Map();
18448
+ cache.set(ctx.font, fc);
18449
+ }
18450
+ let hw = fc.get("-");
18451
+ if (hw === void 0) {
18452
+ hw = ctx.adapter.measureSegment("-", ctx.font).width;
18453
+ fc.set("-", hw);
18454
+ }
18455
+ return hw;
18456
+ }
18457
+ return ctx.adapter.measureSegment("-", ctx.font).width;
18458
+ }
18459
+ function layoutNextLine(segments, cursor, slotWidth, ctx) {
18460
+ let i = cursor.segmentIndex;
18461
+ const initialG = cursor.graphemeIndex;
18462
+ if (i >= segments.length) return null;
18463
+ if (initialG === 0) {
18464
+ while (i < segments.length) {
18465
+ const seg = segments[i];
18466
+ if (seg.kind === "hard-break") {
18467
+ return {
18468
+ text: "",
18469
+ width: 0,
18470
+ start: { segmentIndex: cursor.segmentIndex, graphemeIndex: 0 },
18471
+ end: { segmentIndex: i + 1, graphemeIndex: 0 }
18472
+ };
18473
+ }
18474
+ if (seg.kind === "space" || seg.kind === "zero-width-break" || seg.kind === "soft-hyphen") {
18475
+ i += 1;
18476
+ continue;
18477
+ }
18478
+ break;
18479
+ }
18480
+ if (i >= segments.length) return null;
18481
+ }
18482
+ const hyphenWidth = resolveHyphenWidth(ctx);
18483
+ const startSeg = i;
18484
+ const startG = i === cursor.segmentIndex ? initialG : 0;
18485
+ let lineW = 0;
18486
+ let lineEndSeg = startSeg;
18487
+ let lineEndG = 0;
18488
+ let hasContent = false;
18489
+ let pendingBreakSeg = -1;
18490
+ let pendingBreakG = 0;
18491
+ let pendingBreakWidth = 0;
18492
+ let pendingBreakSoftHyphen = false;
18493
+ const recordPending = (sIdx, gIdx, widthAtBreak, kind) => {
18494
+ pendingBreakSeg = sIdx;
18495
+ pendingBreakG = gIdx;
18496
+ pendingBreakWidth = widthAtBreak;
18497
+ pendingBreakSoftHyphen = kind === "soft-hyphen";
18498
+ };
18499
+ const consumeBreakable = (segIdx, gStart, gWidths) => {
18500
+ for (let g = gStart; g < gWidths.length; g++) {
18501
+ const gw = gWidths[g];
18502
+ if (!hasContent) {
18503
+ lineW = gw;
18504
+ lineEndSeg = segIdx;
18505
+ lineEndG = g + 1;
18506
+ hasContent = true;
18507
+ continue;
18508
+ }
18509
+ if (lineW + gw > slotWidth + 5e-3) {
18510
+ return true;
18511
+ }
18512
+ lineW += gw;
18513
+ lineEndSeg = segIdx;
18514
+ lineEndG = g + 1;
18515
+ }
18516
+ if (lineEndSeg === segIdx && lineEndG === gWidths.length) {
18517
+ lineEndSeg = segIdx + 1;
18518
+ lineEndG = 0;
18519
+ }
18520
+ return false;
18521
+ };
18522
+ if (startG > 0 && startSeg < segments.length) {
18523
+ const seg = segments[startSeg];
18524
+ if (seg.graphemeWidths) {
18525
+ const overflowed = consumeBreakable(startSeg, startG, seg.graphemeWidths);
18526
+ if (overflowed) {
18527
+ const text2 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
18528
+ return {
18529
+ text: text2,
18530
+ width: lineW,
18531
+ start: { segmentIndex: startSeg, graphemeIndex: startG },
18532
+ end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
18533
+ };
18534
+ }
18535
+ i = lineEndSeg;
18536
+ } else {
18537
+ }
18538
+ }
18539
+ for (; i < segments.length; ) {
18540
+ const seg = segments[i];
18541
+ if (seg.kind === "hard-break") {
18542
+ if (hasContent) {
18543
+ const endsAtSoftHyphen2 = lineEndSeg > 0 && segments[lineEndSeg - 1]?.kind === "soft-hyphen";
18544
+ const text2 = buildLineText(
18545
+ segments,
18546
+ startSeg,
18547
+ startG,
18548
+ lineEndSeg,
18549
+ lineEndG,
18550
+ endsAtSoftHyphen2
18551
+ );
18552
+ return {
18553
+ text: text2,
18554
+ width: lineW + (endsAtSoftHyphen2 ? hyphenWidth : 0),
18555
+ start: { segmentIndex: startSeg, graphemeIndex: startG },
18556
+ end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
18557
+ };
18558
+ }
18559
+ return {
18560
+ text: "",
18561
+ width: 0,
18562
+ start: { segmentIndex: startSeg, graphemeIndex: startG },
18563
+ end: { segmentIndex: i + 1, graphemeIndex: 0 }
18564
+ };
18565
+ }
18566
+ const w = seg.width;
18567
+ if (!hasContent) {
18568
+ if (w > slotWidth && seg.graphemeWidths) {
18569
+ const overflowed = consumeBreakable(i, 0, seg.graphemeWidths);
18570
+ if (overflowed) {
18571
+ const text2 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
18572
+ return {
18573
+ text: text2,
18574
+ width: lineW,
18575
+ start: { segmentIndex: startSeg, graphemeIndex: startG },
18576
+ end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
18577
+ };
18578
+ }
18579
+ i = lineEndSeg;
18580
+ continue;
18581
+ }
18582
+ lineW = w;
18583
+ lineEndSeg = i + 1;
18584
+ lineEndG = 0;
18585
+ hasContent = true;
18586
+ if (canBreakAfter(seg.kind)) {
18587
+ recordPending(i + 1, 0, seg.kind === "space" ? lineW - w : lineW, seg.kind);
18588
+ }
18589
+ i += 1;
18590
+ continue;
18591
+ }
18592
+ const newW = lineW + w;
18593
+ if (newW > slotWidth + 5e-3) {
18594
+ if (canBreakAfter(seg.kind)) {
18595
+ lineEndSeg = i + 1;
18596
+ lineEndG = 0;
18597
+ const endsAtSoftHyphen2 = seg.kind === "soft-hyphen";
18598
+ const finalWidth = seg.kind === "space" ? lineW : lineW + (endsAtSoftHyphen2 ? hyphenWidth : 0);
18599
+ const text3 = buildLineText(
18600
+ segments,
18601
+ startSeg,
18602
+ startG,
18603
+ lineEndSeg,
18604
+ lineEndG,
18605
+ endsAtSoftHyphen2
18606
+ );
18607
+ return {
18608
+ text: text3,
18609
+ width: finalWidth,
18610
+ start: { segmentIndex: startSeg, graphemeIndex: startG },
18611
+ end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
18612
+ };
18613
+ }
18614
+ if (pendingBreakSeg >= 0) {
18615
+ const text3 = buildLineText(
18616
+ segments,
18617
+ startSeg,
18618
+ startG,
18619
+ pendingBreakSeg,
18620
+ pendingBreakG,
18621
+ pendingBreakSoftHyphen
18622
+ );
18623
+ return {
18624
+ text: text3,
18625
+ width: pendingBreakWidth + (pendingBreakSoftHyphen ? hyphenWidth : 0),
18626
+ start: { segmentIndex: startSeg, graphemeIndex: startG },
18627
+ end: { segmentIndex: pendingBreakSeg, graphemeIndex: pendingBreakG }
18628
+ };
18629
+ }
18630
+ if (w > slotWidth && seg.graphemeWidths) {
18631
+ const text3 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
18632
+ return {
18633
+ text: text3,
18634
+ width: lineW,
18635
+ start: { segmentIndex: startSeg, graphemeIndex: startG },
18636
+ end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
18637
+ };
18638
+ }
18639
+ const text2 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
18640
+ return {
18641
+ text: text2,
18642
+ width: lineW,
18643
+ start: { segmentIndex: startSeg, graphemeIndex: startG },
18644
+ end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
18645
+ };
18646
+ }
18647
+ lineW = newW;
18648
+ lineEndSeg = i + 1;
18649
+ lineEndG = 0;
18650
+ if (canBreakAfter(seg.kind)) {
18651
+ recordPending(i + 1, 0, seg.kind === "space" ? lineW - w : lineW, seg.kind);
18652
+ }
18653
+ i += 1;
18654
+ }
18655
+ if (!hasContent) return null;
18656
+ const endsAtSoftHyphen = lineEndSeg > 0 && segments[lineEndSeg - 1]?.kind === "soft-hyphen";
18657
+ const text = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, endsAtSoftHyphen);
18658
+ return {
18659
+ text,
18660
+ width: lineW + (endsAtSoftHyphen ? hyphenWidth : 0),
18661
+ start: { segmentIndex: startSeg, graphemeIndex: startG },
18662
+ end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
18663
+ };
18664
+ }
18665
+ function carveTextLineSlots(base, blocked, minSlotWidth = 0) {
18666
+ let slots = [base];
18667
+ for (let bi = 0; bi < blocked.length; bi++) {
18668
+ const block = blocked[bi];
18669
+ const next = [];
18670
+ for (let si = 0; si < slots.length; si++) {
18671
+ const slot = slots[si];
18672
+ if (block.right <= slot.left || block.left >= slot.right) {
18673
+ next.push(slot);
18674
+ continue;
18675
+ }
18676
+ if (block.left > slot.left) next.push({ left: slot.left, right: block.left });
18677
+ if (block.right < slot.right) next.push({ left: block.right, right: slot.right });
18678
+ }
18679
+ slots = next;
18680
+ }
18681
+ if (minSlotWidth > 0) {
18682
+ return slots.filter((s) => s.right - s.left >= minSlotWidth);
18683
+ }
18684
+ return slots;
18685
+ }
18349
18686
  function computeCharPositions(lineBreaks, segments, lineHeight) {
18350
18687
  const positions = [];
18351
- const graphemeSegmenter = new Intl.Segmenter(void 0, {
18688
+ const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
18352
18689
  granularity: "grapheme"
18353
18690
  });
18354
18691
  for (let lineIdx = 0; lineIdx < lineBreaks.lines.length; lineIdx++) {
@@ -18361,7 +18698,7 @@ function computeCharPositions(lineBreaks, segments, lineHeight) {
18361
18698
  if (si >= line.endSegment && line.endGrapheme === 0) break;
18362
18699
  continue;
18363
18700
  }
18364
- const graphemes = [...graphemeSegmenter.segment(seg.text)].map((g) => g.segment);
18701
+ const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
18365
18702
  if (graphemes.length === 0) continue;
18366
18703
  const startG = si === line.startSegment ? line.startGrapheme : 0;
18367
18704
  let endG;
@@ -21352,14 +21689,20 @@ __export(reactive_layout_exports, {
21352
21689
  PrecomputedAdapter: () => PrecomputedAdapter,
21353
21690
  SvgBoundsAdapter: () => SvgBoundsAdapter,
21354
21691
  analyzeAndMeasure: () => analyzeAndMeasure,
21692
+ carveTextLineSlots: () => carveTextLineSlots,
21693
+ circleIntervalForBand: () => circleIntervalForBand,
21355
21694
  computeBlockFlow: () => computeBlockFlow,
21356
21695
  computeCharPositions: () => computeCharPositions,
21696
+ computeFlowLines: () => computeFlowLines,
21357
21697
  computeLineBreaks: () => computeLineBreaks,
21358
21698
  computeTotalHeight: () => computeTotalHeight,
21699
+ layoutNextLine: () => layoutNextLine,
21359
21700
  measureBlock: () => measureBlock,
21360
21701
  measureBlocks: () => measureBlocks,
21361
21702
  reactiveBlockLayout: () => reactiveBlockLayout,
21362
- reactiveLayout: () => reactiveLayout
21703
+ reactiveFlowLayout: () => reactiveFlowLayout,
21704
+ reactiveLayout: () => reactiveLayout,
21705
+ rectIntervalForBand: () => rectIntervalForBand
21363
21706
  });
21364
21707
 
21365
21708
  // src/patterns/reactive-layout/measurement-adapters.ts
@@ -21827,6 +22170,185 @@ function reactiveBlockLayout(opts) {
21827
22170
  };
21828
22171
  }
21829
22172
 
22173
+ // src/patterns/reactive-layout/reactive-flow-layout.ts
22174
+ function circleIntervalForBand(o, bandTop, bandBottom) {
22175
+ const hPad = o.hPad ?? 0;
22176
+ const vPad = o.vPad ?? 0;
22177
+ const top = bandTop - vPad;
22178
+ const bottom = bandBottom + vPad;
22179
+ if (top >= o.cy + o.r || bottom <= o.cy - o.r) return null;
22180
+ const minDy = o.cy >= top && o.cy <= bottom ? 0 : o.cy < top ? top - o.cy : o.cy - bottom;
22181
+ if (minDy >= o.r) return null;
22182
+ const maxDx = Math.sqrt(o.r * o.r - minDy * minDy);
22183
+ return { left: o.cx - maxDx - hPad, right: o.cx + maxDx + hPad };
22184
+ }
22185
+ function rectIntervalForBand(o, bandTop, bandBottom) {
22186
+ const hPad = o.hPad ?? 0;
22187
+ const vPad = o.vPad ?? 0;
22188
+ if (bandBottom <= o.y - vPad) return null;
22189
+ if (bandTop >= o.y + o.h + vPad) return null;
22190
+ return { left: o.x - hPad, right: o.x + o.w + hPad };
22191
+ }
22192
+ function obstacleIntervalForBand(o, bandTop, bandBottom) {
22193
+ return o.kind === "circle" ? circleIntervalForBand(o, bandTop, bandBottom) : rectIntervalForBand(o, bandTop, bandBottom);
22194
+ }
22195
+ function computeFlowLines(segments, container, columns, obstacles, lineHeight, minSlotWidth) {
22196
+ const lines = [];
22197
+ let cursor = { segmentIndex: 0, graphemeIndex: 0 };
22198
+ if (segments.length === 0 || columns.count <= 0 || lineHeight <= 0) {
22199
+ return { lines, cursor };
22200
+ }
22201
+ const padX = container.paddingX ?? 0;
22202
+ const padY = container.paddingY ?? 0;
22203
+ const availWidth = Math.max(0, container.width - padX * 2);
22204
+ const availHeight = Math.max(0, container.height - padY * 2);
22205
+ const gapTotal = columns.gap * Math.max(0, columns.count - 1);
22206
+ const colWidth = Math.max(0, (availWidth - gapTotal) / columns.count);
22207
+ if (colWidth <= 0) return { lines, cursor };
22208
+ outerCol: for (let col = 0; col < columns.count; col++) {
22209
+ const colLeft = padX + col * (colWidth + columns.gap);
22210
+ const colRight = colLeft + colWidth;
22211
+ let bandTop = padY;
22212
+ while (bandTop + lineHeight <= padY + availHeight) {
22213
+ const bandBottom = bandTop + lineHeight;
22214
+ const blocked = [];
22215
+ for (let oi = 0; oi < obstacles.length; oi++) {
22216
+ const iv = obstacleIntervalForBand(obstacles[oi], bandTop, bandBottom);
22217
+ if (iv !== null) blocked.push(iv);
22218
+ }
22219
+ const slots = carveTextLineSlots({ left: colLeft, right: colRight }, blocked, minSlotWidth);
22220
+ if (slots.length === 0) {
22221
+ bandTop += lineHeight;
22222
+ continue;
22223
+ }
22224
+ let hardBreakThisBand = false;
22225
+ for (let si = 0; si < slots.length; si++) {
22226
+ const slot = slots[si];
22227
+ const slotW = slot.right - slot.left;
22228
+ const line = layoutNextLine(segments, cursor, slotW);
22229
+ if (line === null) {
22230
+ return { lines, cursor };
22231
+ }
22232
+ if (line.text.length === 0 && line.width === 0) {
22233
+ cursor = line.end;
22234
+ hardBreakThisBand = true;
22235
+ break;
22236
+ }
22237
+ lines.push({
22238
+ x: slot.left,
22239
+ y: bandTop,
22240
+ width: line.width,
22241
+ slotWidth: slotW,
22242
+ text: line.text,
22243
+ columnIndex: col,
22244
+ flushToRight: slot.right < colRight - 0.5
22245
+ });
22246
+ cursor = line.end;
22247
+ }
22248
+ bandTop += lineHeight;
22249
+ if (hardBreakThisBand) continue;
22250
+ if (cursor.segmentIndex >= segments.length) break outerCol;
22251
+ }
22252
+ if (cursor.segmentIndex >= segments.length) break;
22253
+ }
22254
+ return { lines, cursor };
22255
+ }
22256
+ function reactiveFlowLayout(opts) {
22257
+ const { adapter, name = "reactive-flow-layout", minSlotWidth = 20 } = opts;
22258
+ const g = new Graph(name);
22259
+ const measureCache = /* @__PURE__ */ new Map();
22260
+ const textNode = state(opts.text ?? "", { name: "text" });
22261
+ const fontNode = state(opts.font ?? "16px sans-serif", { name: "font" });
22262
+ const lineHeightNode = state(opts.lineHeight ?? 20, { name: "line-height" });
22263
+ const containerNode = state(
22264
+ opts.container ?? { width: 800, height: 600, paddingX: 0, paddingY: 0 },
22265
+ { name: "container" }
22266
+ );
22267
+ const columnsNode = state(opts.columns ?? { count: 1, gap: 0 }, {
22268
+ name: "columns"
22269
+ });
22270
+ const obstaclesNode = state(opts.obstacles ?? [], { name: "obstacles" });
22271
+ const segmentsNode = node(
22272
+ [textNode, fontNode],
22273
+ (data, actions, ctx) => {
22274
+ const b0 = data[0];
22275
+ const textVal = b0 != null && b0.length > 0 ? b0.at(-1) : ctx.prevData[0];
22276
+ const b1 = data[1];
22277
+ const fontVal = b1 != null && b1.length > 0 ? b1.at(-1) : ctx.prevData[1];
22278
+ const result = analyzeAndMeasure(textVal, fontVal, adapter, measureCache);
22279
+ actions.emit(result);
22280
+ return () => {
22281
+ measureCache.clear();
22282
+ adapter.clearCache?.();
22283
+ };
22284
+ },
22285
+ { name: "segments", describeKind: "derived" }
22286
+ );
22287
+ const flowLinesNode = derived(
22288
+ [segmentsNode, containerNode, columnsNode, obstaclesNode, lineHeightNode],
22289
+ ([segs, cont, cols, obs, lh]) => {
22290
+ const segments = segs;
22291
+ const t0 = monotonicNs();
22292
+ const { lines: result, cursor } = computeFlowLines(
22293
+ segments,
22294
+ cont,
22295
+ cols,
22296
+ obs,
22297
+ lh,
22298
+ minSlotWidth
22299
+ );
22300
+ const elapsed = monotonicNs() - t0;
22301
+ const overflow = Math.max(0, segments.length - cursor.segmentIndex);
22302
+ const meta = flowLinesNode.meta;
22303
+ if (meta) {
22304
+ emitToMeta(meta["line-count"], result.length);
22305
+ emitToMeta(meta["layout-time-ns"], elapsed);
22306
+ emitToMeta(meta["overflow-segments"], overflow);
22307
+ }
22308
+ return result;
22309
+ },
22310
+ {
22311
+ name: "flow-lines",
22312
+ meta: {
22313
+ "line-count": 0,
22314
+ "layout-time-ns": 0,
22315
+ "overflow-segments": 0
22316
+ },
22317
+ equals: (a, b) => {
22318
+ const la = a;
22319
+ const lb = b;
22320
+ if (la.length !== lb.length) return false;
22321
+ for (let i = 0; i < la.length; i++) {
22322
+ const pa = la[i];
22323
+ const pb = lb[i];
22324
+ if (pa.x !== pb.x || pa.y !== pb.y || pa.width !== pb.width || pa.slotWidth !== pb.slotWidth || pa.text !== pb.text || pa.columnIndex !== pb.columnIndex || pa.flushToRight !== pb.flushToRight)
22325
+ return false;
22326
+ }
22327
+ return true;
22328
+ }
22329
+ }
22330
+ );
22331
+ g.add("text", textNode);
22332
+ g.add("font", fontNode);
22333
+ g.add("line-height", lineHeightNode);
22334
+ g.add("container", containerNode);
22335
+ g.add("columns", columnsNode);
22336
+ g.add("obstacles", obstaclesNode);
22337
+ g.add("segments", segmentsNode);
22338
+ g.add("flow-lines", flowLinesNode);
22339
+ return {
22340
+ graph: g,
22341
+ setText: (t) => g.set("text", t),
22342
+ setFont: (f) => g.set("font", f),
22343
+ setLineHeight: (lh) => g.set("line-height", lh),
22344
+ setContainer: (c) => g.set("container", c),
22345
+ setColumns: (c) => g.set("columns", c),
22346
+ setObstacles: (o) => g.set("obstacles", o),
22347
+ segments: segmentsNode,
22348
+ flowLines: flowLinesNode
22349
+ };
22350
+ }
22351
+
21830
22352
  // src/index.ts
21831
22353
  var version = "0.0.0";
21832
22354
  // Annotate the CommonJS export names for ESM import in node:
@@ -21966,6 +22488,7 @@ var version = "0.0.0";
21966
22488
  fromPromise,
21967
22489
  fromPulsar,
21968
22490
  fromRabbitMQ,
22491
+ fromRaf,
21969
22492
  fromRedisStream,
21970
22493
  fromSSE,
21971
22494
  fromSqlite,