@graphrefly/graphrefly 0.24.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 (33) hide show
  1. package/dist/{chunk-IPLKX3L2.js → chunk-EVR6UFUV.js} +2 -2
  2. package/dist/{chunk-5WGT55R4.js → chunk-IAHGTNOZ.js} +4 -2
  3. package/dist/{chunk-5WGT55R4.js.map → chunk-IAHGTNOZ.js.map} +1 -1
  4. package/dist/{chunk-AOCBDH4T.js → chunk-L2GLW2U7.js} +68 -1
  5. package/dist/chunk-L2GLW2U7.js.map +1 -0
  6. package/dist/{chunk-TDEXAMGO.js → chunk-TKE3JGOH.js} +488 -16
  7. package/dist/chunk-TKE3JGOH.js.map +1 -0
  8. package/dist/compat/nestjs/index.cjs.map +1 -1
  9. package/dist/compat/nestjs/index.js +2 -2
  10. package/dist/extra/index.cjs +68 -0
  11. package/dist/extra/index.cjs.map +1 -1
  12. package/dist/extra/index.d.cts +1 -1
  13. package/dist/extra/index.d.ts +1 -1
  14. package/dist/extra/index.js +4 -2
  15. package/dist/{index-1z8vRTCt.d.cts → index-Ch0IpIO0.d.cts} +29 -2
  16. package/dist/{index-b5BYtczN.d.cts → index-DKE1EATr.d.cts} +222 -2
  17. package/dist/{index-BysCTzJz.d.ts → index-Ds23Wvou.d.ts} +29 -2
  18. package/dist/{index-D7XgsUt7.d.ts → index-OXImXMq6.d.ts} +222 -2
  19. package/dist/index.cjs +550 -15
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +3 -3
  22. package/dist/index.d.ts +3 -3
  23. package/dist/index.js +6 -4
  24. package/dist/index.js.map +1 -1
  25. package/dist/patterns/reactive-layout/index.cjs +488 -16
  26. package/dist/patterns/reactive-layout/index.cjs.map +1 -1
  27. package/dist/patterns/reactive-layout/index.d.cts +1 -1
  28. package/dist/patterns/reactive-layout/index.d.ts +1 -1
  29. package/dist/patterns/reactive-layout/index.js +16 -4
  30. package/package.json +1 -1
  31. package/dist/chunk-AOCBDH4T.js.map +0 -1
  32. package/dist/chunk-TDEXAMGO.js.map +0 -1
  33. /package/dist/{chunk-IPLKX3L2.js.map → chunk-EVR6UFUV.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,
@@ -3186,6 +3187,72 @@ function fromTimer(ms, opts) {
3186
3187
  return cleanup;
3187
3188
  }, sourceOpts(rest));
3188
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
+ }
3189
3256
  function fromCron(expr, opts) {
3190
3257
  const schedule = parseCron(expr);
3191
3258
  const { tickMs: tickOpt, output, ...rest } = opts ?? {};
@@ -8368,6 +8435,7 @@ __export(extra_exports, {
8368
8435
  fromPromise: () => fromPromise,
8369
8436
  fromPulsar: () => fromPulsar,
8370
8437
  fromRabbitMQ: () => fromRabbitMQ,
8438
+ fromRaf: () => fromRaf,
8371
8439
  fromRedisStream: () => fromRedisStream,
8372
8440
  fromSSE: () => fromSSE,
8373
8441
  fromSqlite: () => fromSqlite,
@@ -18054,7 +18122,7 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
18054
18122
  const normalized = normalizeWhitespace(text);
18055
18123
  if (normalized.length === 0) return [];
18056
18124
  const pieces = segmentText(normalized);
18057
- const graphemeSegmenter = new Intl.Segmenter(void 0, {
18125
+ const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
18058
18126
  granularity: "grapheme"
18059
18127
  });
18060
18128
  const rawTexts = [];
@@ -18098,7 +18166,8 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
18098
18166
  let w = fontCache.get(seg);
18099
18167
  if (w === void 0) {
18100
18168
  if (stats) stats.misses += 1;
18101
- w = adapter.measureSegment(seg, font).width;
18169
+ const raw = adapter.measureSegment(seg, font).width;
18170
+ w = Number.isFinite(raw) && raw >= 0 ? raw : 0;
18102
18171
  fontCache.set(seg, w);
18103
18172
  } else if (stats) {
18104
18173
  stats.hits += 1;
@@ -18120,7 +18189,7 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
18120
18189
  }
18121
18190
  if (isCJK(t)) {
18122
18191
  let unitText = "";
18123
- for (const gs of graphemeSegmenter.segment(t)) {
18192
+ for (const gs of graphemeSegmenter2.segment(t)) {
18124
18193
  const grapheme = gs.segment;
18125
18194
  if (unitText.length > 0 && kinsokuStart.has(grapheme)) {
18126
18195
  unitText += grapheme;
@@ -18152,7 +18221,7 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
18152
18221
  let graphemeWidths = null;
18153
18222
  if (mergedWordLike[i] && t.length > 1) {
18154
18223
  const gWidths = [];
18155
- for (const gs of graphemeSegmenter.segment(t)) {
18224
+ for (const gs of graphemeSegmenter2.segment(t)) {
18156
18225
  gWidths.push(measureCached(gs.segment));
18157
18226
  }
18158
18227
  if (gWidths.length > 1) {
@@ -18192,10 +18261,10 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
18192
18261
  const seg = segments[i];
18193
18262
  if (seg.kind === "soft-hyphen" || seg.kind === "hard-break") continue;
18194
18263
  if (i === lineStartSeg && lineStartGrapheme > 0 && seg.graphemeWidths) {
18195
- const graphemeSegmenter = new Intl.Segmenter(void 0, {
18264
+ const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
18196
18265
  granularity: "grapheme"
18197
18266
  });
18198
- const graphemes = [...graphemeSegmenter.segment(seg.text)].map((g) => g.segment);
18267
+ const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
18199
18268
  text += graphemes.slice(lineStartGrapheme).join("");
18200
18269
  } else {
18201
18270
  text += seg.text;
@@ -18203,10 +18272,10 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
18203
18272
  }
18204
18273
  if (endGrapheme > 0 && endSeg < segments.length) {
18205
18274
  const seg = segments[endSeg];
18206
- const graphemeSegmenter = new Intl.Segmenter(void 0, {
18275
+ const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
18207
18276
  granularity: "grapheme"
18208
18277
  });
18209
- const graphemes = [...graphemeSegmenter.segment(seg.text)].map((g) => g.segment);
18278
+ const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
18210
18279
  const startG = lineStartSeg === endSeg ? lineStartGrapheme : 0;
18211
18280
  text += graphemes.slice(startG, endGrapheme).join("");
18212
18281
  }
@@ -18226,7 +18295,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
18226
18295
  pendingBreakSeg = -1;
18227
18296
  pendingBreakWidth = 0;
18228
18297
  }
18229
- function canBreakAfter(kind) {
18298
+ function canBreakAfter2(kind) {
18230
18299
  return kind === "space" || kind === "zero-width-break" || kind === "soft-hyphen";
18231
18300
  }
18232
18301
  function startLine(segIdx, graphemeIdx, width) {
@@ -18271,7 +18340,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
18271
18340
  } else {
18272
18341
  startLine(i, 0, w);
18273
18342
  }
18274
- if (canBreakAfter(seg.kind)) {
18343
+ if (canBreakAfter2(seg.kind)) {
18275
18344
  pendingBreakSeg = i + 1;
18276
18345
  pendingBreakWidth = seg.kind === "space" ? lineW - w : lineW;
18277
18346
  }
@@ -18279,7 +18348,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
18279
18348
  }
18280
18349
  const newW = lineW + w;
18281
18350
  if (newW > maxWidth + 5e-3) {
18282
- if (canBreakAfter(seg.kind)) {
18351
+ if (canBreakAfter2(seg.kind)) {
18283
18352
  lineW += w;
18284
18353
  lineEndSeg = i + 1;
18285
18354
  lineEndGrapheme = 0;
@@ -18303,7 +18372,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
18303
18372
  lineW = newW;
18304
18373
  lineEndSeg = i + 1;
18305
18374
  lineEndGrapheme = 0;
18306
- if (canBreakAfter(seg.kind)) {
18375
+ if (canBreakAfter2(seg.kind)) {
18307
18376
  pendingBreakSeg = i + 1;
18308
18377
  pendingBreakWidth = seg.kind === "space" ? lineW - w : lineW;
18309
18378
  }
@@ -18334,9 +18403,289 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
18334
18403
  }
18335
18404
  }
18336
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
+ }
18337
18686
  function computeCharPositions(lineBreaks, segments, lineHeight) {
18338
18687
  const positions = [];
18339
- const graphemeSegmenter = new Intl.Segmenter(void 0, {
18688
+ const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
18340
18689
  granularity: "grapheme"
18341
18690
  });
18342
18691
  for (let lineIdx = 0; lineIdx < lineBreaks.lines.length; lineIdx++) {
@@ -18349,7 +18698,7 @@ function computeCharPositions(lineBreaks, segments, lineHeight) {
18349
18698
  if (si >= line.endSegment && line.endGrapheme === 0) break;
18350
18699
  continue;
18351
18700
  }
18352
- const graphemes = [...graphemeSegmenter.segment(seg.text)].map((g) => g.segment);
18701
+ const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
18353
18702
  if (graphemes.length === 0) continue;
18354
18703
  const startG = si === line.startSegment ? line.startGrapheme : 0;
18355
18704
  let endG;
@@ -21340,14 +21689,20 @@ __export(reactive_layout_exports, {
21340
21689
  PrecomputedAdapter: () => PrecomputedAdapter,
21341
21690
  SvgBoundsAdapter: () => SvgBoundsAdapter,
21342
21691
  analyzeAndMeasure: () => analyzeAndMeasure,
21692
+ carveTextLineSlots: () => carveTextLineSlots,
21693
+ circleIntervalForBand: () => circleIntervalForBand,
21343
21694
  computeBlockFlow: () => computeBlockFlow,
21344
21695
  computeCharPositions: () => computeCharPositions,
21696
+ computeFlowLines: () => computeFlowLines,
21345
21697
  computeLineBreaks: () => computeLineBreaks,
21346
21698
  computeTotalHeight: () => computeTotalHeight,
21699
+ layoutNextLine: () => layoutNextLine,
21347
21700
  measureBlock: () => measureBlock,
21348
21701
  measureBlocks: () => measureBlocks,
21349
21702
  reactiveBlockLayout: () => reactiveBlockLayout,
21350
- reactiveLayout: () => reactiveLayout
21703
+ reactiveFlowLayout: () => reactiveFlowLayout,
21704
+ reactiveLayout: () => reactiveLayout,
21705
+ rectIntervalForBand: () => rectIntervalForBand
21351
21706
  });
21352
21707
 
21353
21708
  // src/patterns/reactive-layout/measurement-adapters.ts
@@ -21815,6 +22170,185 @@ function reactiveBlockLayout(opts) {
21815
22170
  };
21816
22171
  }
21817
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
+
21818
22352
  // src/index.ts
21819
22353
  var version = "0.0.0";
21820
22354
  // Annotate the CommonJS export names for ESM import in node:
@@ -21954,6 +22488,7 @@ var version = "0.0.0";
21954
22488
  fromPromise,
21955
22489
  fromPulsar,
21956
22490
  fromRabbitMQ,
22491
+ fromRaf,
21957
22492
  fromRedisStream,
21958
22493
  fromSSE,
21959
22494
  fromSqlite,