@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.
- package/dist/{chunk-NZMBRXQV.js → chunk-5DJTTKX3.js} +11 -13
- package/dist/chunk-5DJTTKX3.js.map +1 -0
- package/dist/{chunk-PNUZM7PC.js → chunk-EVR6UFUV.js} +6 -6
- package/dist/{chunk-HVBX5KIW.js → chunk-H4RVA4VE.js} +2 -2
- package/dist/{chunk-32N5A454.js → chunk-HWPIFSW2.js} +2 -2
- package/dist/chunk-HWPIFSW2.js.map +1 -0
- package/dist/{chunk-XTLYW4FR.js → chunk-IAHGTNOZ.js} +6 -4
- package/dist/{chunk-XTLYW4FR.js.map → chunk-IAHGTNOZ.js.map} +1 -1
- package/dist/{chunk-CWYPA63G.js → chunk-L2GLW2U7.js} +69 -2
- package/dist/chunk-L2GLW2U7.js.map +1 -0
- package/dist/{chunk-2GQLMQVJ.js → chunk-MW4VAKAO.js} +3 -3
- package/dist/{chunk-JFONSPNF.js → chunk-PY4XCDLR.js} +2 -2
- package/dist/{chunk-263BEJJO.js → chunk-QOWVNWOC.js} +3 -3
- package/dist/{chunk-PX6PDUJ5.js → chunk-TKE3JGOH.js} +491 -19
- package/dist/chunk-TKE3JGOH.js.map +1 -0
- package/dist/{chunk-XRFJJ2IU.js → chunk-XOFWRC73.js} +3 -3
- package/dist/compat/nestjs/index.cjs +10 -12
- package/dist/compat/nestjs/index.cjs.map +1 -1
- package/dist/compat/nestjs/index.d.cts +6 -6
- package/dist/compat/nestjs/index.d.ts +6 -6
- package/dist/compat/nestjs/index.js +9 -9
- package/dist/core/index.cjs +10 -12
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +3 -3
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.js +3 -3
- package/dist/extra/index.cjs +78 -12
- package/dist/extra/index.cjs.map +1 -1
- package/dist/extra/index.d.cts +4 -4
- package/dist/extra/index.d.ts +4 -4
- package/dist/extra/index.js +6 -4
- package/dist/graph/index.cjs +10 -12
- package/dist/graph/index.cjs.map +1 -1
- package/dist/graph/index.d.cts +5 -5
- package/dist/graph/index.d.ts +5 -5
- package/dist/graph/index.js +4 -4
- package/dist/{graph-CEO2FkLY.d.ts → graph-B6NFqv3z.d.ts} +3 -3
- package/dist/{graph-BtdSRHUc.d.cts → graph-D-3JIQme.d.cts} +3 -3
- package/dist/{index-BFGjXbiP.d.cts → index-AMWewNDe.d.cts} +2 -2
- package/dist/{index-BUj3ASVe.d.cts → index-BJB7t9gg.d.cts} +10 -24
- package/dist/{index-DSPc5rkv.d.ts → index-C-TXEa7C.d.ts} +10 -24
- package/dist/{index-CkElcUY6.d.ts → index-CYkjxu3s.d.ts} +2 -2
- package/dist/{index-B0tfuXwV.d.cts → index-Ch0IpIO0.d.cts} +32 -5
- package/dist/{index-C59uSJAH.d.cts → index-DKE1EATr.d.cts} +224 -4
- package/dist/{index-DgscL7v0.d.ts → index-DiobMNwE.d.ts} +3 -3
- package/dist/{index-RXN94sHK.d.ts → index-Ds23Wvou.d.ts} +32 -5
- package/dist/{index-BPlWVAKY.d.cts → index-J7Kc0oIQ.d.cts} +3 -3
- package/dist/{index-jEtF4N7L.d.ts → index-OXImXMq6.d.ts} +224 -4
- package/dist/index.cjs +570 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -15
- package/dist/index.d.ts +15 -15
- package/dist/index.js +23 -31
- package/dist/index.js.map +1 -1
- package/dist/{meta-3QjzotRv.d.ts → meta-CnkLA_43.d.ts} +1 -1
- package/dist/{meta-B-Lbs4-O.d.cts → meta-DWbkoq1s.d.cts} +1 -1
- package/dist/{node-C7PD3sn9.d.cts → node-B-f-Lu-k.d.cts} +15 -13
- package/dist/{node-C7PD3sn9.d.ts → node-B-f-Lu-k.d.ts} +15 -13
- package/dist/{observable-axpzv1K2.d.cts → observable-DBnrwcar.d.cts} +1 -1
- package/dist/{observable-EyO-moQY.d.ts → observable-uP-wy_uK.d.ts} +1 -1
- package/dist/patterns/reactive-layout/index.cjs +498 -28
- package/dist/patterns/reactive-layout/index.cjs.map +1 -1
- package/dist/patterns/reactive-layout/index.d.cts +5 -5
- package/dist/patterns/reactive-layout/index.d.ts +5 -5
- package/dist/patterns/reactive-layout/index.js +20 -8
- package/dist/{storage-DIgAr7M_.d.cts → storage-BuTdpCI1.d.cts} +1 -1
- package/dist/{storage-CHT5WE9m.d.ts → storage-F2X1U1x0.d.ts} +1 -1
- package/package.json +2 -2
- package/dist/chunk-32N5A454.js.map +0 -1
- package/dist/chunk-CWYPA63G.js.map +0 -1
- package/dist/chunk-NZMBRXQV.js.map +0 -1
- package/dist/chunk-PX6PDUJ5.js.map +0 -1
- /package/dist/{chunk-PNUZM7PC.js.map → chunk-EVR6UFUV.js.map} +0 -0
- /package/dist/{chunk-HVBX5KIW.js.map → chunk-H4RVA4VE.js.map} +0 -0
- /package/dist/{chunk-2GQLMQVJ.js.map → chunk-MW4VAKAO.js.map} +0 -0
- /package/dist/{chunk-JFONSPNF.js.map → chunk-PY4XCDLR.js.map} +0 -0
- /package/dist/{chunk-263BEJJO.js.map → chunk-QOWVNWOC.js.map} +0 -0
- /package/dist/{chunk-XRFJJ2IU.js.map → chunk-XOFWRC73.js.map} +0 -0
|
@@ -27,14 +27,20 @@ __export(reactive_layout_exports, {
|
|
|
27
27
|
PrecomputedAdapter: () => PrecomputedAdapter,
|
|
28
28
|
SvgBoundsAdapter: () => SvgBoundsAdapter,
|
|
29
29
|
analyzeAndMeasure: () => analyzeAndMeasure,
|
|
30
|
+
carveTextLineSlots: () => carveTextLineSlots,
|
|
31
|
+
circleIntervalForBand: () => circleIntervalForBand,
|
|
30
32
|
computeBlockFlow: () => computeBlockFlow,
|
|
31
33
|
computeCharPositions: () => computeCharPositions,
|
|
34
|
+
computeFlowLines: () => computeFlowLines,
|
|
32
35
|
computeLineBreaks: () => computeLineBreaks,
|
|
33
36
|
computeTotalHeight: () => computeTotalHeight,
|
|
37
|
+
layoutNextLine: () => layoutNextLine,
|
|
34
38
|
measureBlock: () => measureBlock,
|
|
35
39
|
measureBlocks: () => measureBlocks,
|
|
36
40
|
reactiveBlockLayout: () => reactiveBlockLayout,
|
|
37
|
-
|
|
41
|
+
reactiveFlowLayout: () => reactiveFlowLayout,
|
|
42
|
+
reactiveLayout: () => reactiveLayout,
|
|
43
|
+
rectIntervalForBand: () => rectIntervalForBand
|
|
38
44
|
});
|
|
39
45
|
module.exports = __toCommonJS(reactive_layout_exports);
|
|
40
46
|
|
|
@@ -1954,37 +1960,35 @@ var NodeImpl = class _NodeImpl {
|
|
|
1954
1960
|
// --- Emit pipeline ---
|
|
1955
1961
|
/**
|
|
1956
1962
|
* @internal The unified dispatch waist — one call = one wave.
|
|
1963
|
+
* See `GRAPHREFLY-SPEC.md` §1.3.1 for protocol context — the stages
|
|
1964
|
+
* below are the implementation order.
|
|
1957
1965
|
*
|
|
1958
1966
|
* Pipeline stages, in order:
|
|
1959
1967
|
*
|
|
1960
|
-
* 1.
|
|
1961
|
-
* 2. Terminal filter — post-COMPLETE/ERROR only TEARDOWN/INVALIDATE
|
|
1968
|
+
* 1. Terminal filter — post-COMPLETE/ERROR only TEARDOWN/INVALIDATE
|
|
1962
1969
|
* still propagate so graph teardown and cache-clear still work.
|
|
1963
|
-
*
|
|
1970
|
+
* 2. Tier sort (stable) — the batch can be in any order when it
|
|
1964
1971
|
* arrives; the walker downstream (`downWithBatch`) assumes
|
|
1965
1972
|
* ascending tier monotone, and so does `_updateState`'s tier-3
|
|
1966
1973
|
* slice walk. This is the single source of truth for ordering.
|
|
1967
|
-
*
|
|
1974
|
+
* 3. Synthetic DIRTY prefix — if a tier-3 payload is present, no
|
|
1968
1975
|
* DIRTY is already in the batch, and the node isn't already in
|
|
1969
1976
|
* `"dirty"` status, prepend `[DIRTY]` after any tier-0 START
|
|
1970
1977
|
* entries. Guarantees spec §1.3.1 (DIRTY precedes DATA within
|
|
1971
1978
|
* the same batch) uniformly across every entry point.
|
|
1972
|
-
*
|
|
1979
|
+
* 4. PAUSE/RESUME lock bookkeeping (C0) — update `_pauseLocks`,
|
|
1973
1980
|
* derive `_paused`, filter unknown-lockId RESUME, replay
|
|
1974
1981
|
* bufferAll buffer on final lock release.
|
|
1975
|
-
*
|
|
1982
|
+
* 5. Meta TEARDOWN fan-out — notify meta children before
|
|
1976
1983
|
* `_updateState`'s TEARDOWN branch calls `_deactivate`. Hoisted
|
|
1977
1984
|
* out of the walk to keep `_updateState` re-entrance-free.
|
|
1978
|
-
*
|
|
1985
|
+
* 6. `_updateState` — walk the batch in tier order, advancing
|
|
1979
1986
|
* `_cached` / `_status` / `_versioning` and running equals
|
|
1980
1987
|
* substitution on tier-3 DATA (§3.5.1). Returns
|
|
1981
1988
|
* `{finalMessages, equalsError?}`.
|
|
1982
|
-
*
|
|
1989
|
+
* 7. `downWithBatch` dispatch (or bufferAll capture if paused with
|
|
1983
1990
|
* `pausable: "resumeAll"`).
|
|
1984
|
-
*
|
|
1985
|
-
*
|
|
1986
|
-
* `node.down` / `node.emit` / `actions.down` / `actions.emit` all
|
|
1987
|
-
* converge here — the unified `_emit` waist (spec §1.3.1).
|
|
1991
|
+
* 8. Recursive ERROR emission if equals threw mid-walk.
|
|
1988
1992
|
*/
|
|
1989
1993
|
_emit(messages) {
|
|
1990
1994
|
if (messages.length === 0) return;
|
|
@@ -4960,7 +4964,7 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
|
|
|
4960
4964
|
const normalized = normalizeWhitespace(text);
|
|
4961
4965
|
if (normalized.length === 0) return [];
|
|
4962
4966
|
const pieces = segmentText(normalized);
|
|
4963
|
-
const
|
|
4967
|
+
const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
|
|
4964
4968
|
granularity: "grapheme"
|
|
4965
4969
|
});
|
|
4966
4970
|
const rawTexts = [];
|
|
@@ -5004,7 +5008,8 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
|
|
|
5004
5008
|
let w = fontCache.get(seg);
|
|
5005
5009
|
if (w === void 0) {
|
|
5006
5010
|
if (stats) stats.misses += 1;
|
|
5007
|
-
|
|
5011
|
+
const raw = adapter.measureSegment(seg, font).width;
|
|
5012
|
+
w = Number.isFinite(raw) && raw >= 0 ? raw : 0;
|
|
5008
5013
|
fontCache.set(seg, w);
|
|
5009
5014
|
} else if (stats) {
|
|
5010
5015
|
stats.hits += 1;
|
|
@@ -5026,7 +5031,7 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
|
|
|
5026
5031
|
}
|
|
5027
5032
|
if (isCJK(t)) {
|
|
5028
5033
|
let unitText = "";
|
|
5029
|
-
for (const gs of
|
|
5034
|
+
for (const gs of graphemeSegmenter2.segment(t)) {
|
|
5030
5035
|
const grapheme = gs.segment;
|
|
5031
5036
|
if (unitText.length > 0 && kinsokuStart.has(grapheme)) {
|
|
5032
5037
|
unitText += grapheme;
|
|
@@ -5058,7 +5063,7 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
|
|
|
5058
5063
|
let graphemeWidths = null;
|
|
5059
5064
|
if (mergedWordLike[i] && t.length > 1) {
|
|
5060
5065
|
const gWidths = [];
|
|
5061
|
-
for (const gs of
|
|
5066
|
+
for (const gs of graphemeSegmenter2.segment(t)) {
|
|
5062
5067
|
gWidths.push(measureCached(gs.segment));
|
|
5063
5068
|
}
|
|
5064
5069
|
if (gWidths.length > 1) {
|
|
@@ -5098,10 +5103,10 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5098
5103
|
const seg = segments[i];
|
|
5099
5104
|
if (seg.kind === "soft-hyphen" || seg.kind === "hard-break") continue;
|
|
5100
5105
|
if (i === lineStartSeg && lineStartGrapheme > 0 && seg.graphemeWidths) {
|
|
5101
|
-
const
|
|
5106
|
+
const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
|
|
5102
5107
|
granularity: "grapheme"
|
|
5103
5108
|
});
|
|
5104
|
-
const graphemes = [...
|
|
5109
|
+
const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
|
|
5105
5110
|
text += graphemes.slice(lineStartGrapheme).join("");
|
|
5106
5111
|
} else {
|
|
5107
5112
|
text += seg.text;
|
|
@@ -5109,10 +5114,10 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5109
5114
|
}
|
|
5110
5115
|
if (endGrapheme > 0 && endSeg < segments.length) {
|
|
5111
5116
|
const seg = segments[endSeg];
|
|
5112
|
-
const
|
|
5117
|
+
const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
|
|
5113
5118
|
granularity: "grapheme"
|
|
5114
5119
|
});
|
|
5115
|
-
const graphemes = [...
|
|
5120
|
+
const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
|
|
5116
5121
|
const startG = lineStartSeg === endSeg ? lineStartGrapheme : 0;
|
|
5117
5122
|
text += graphemes.slice(startG, endGrapheme).join("");
|
|
5118
5123
|
}
|
|
@@ -5132,7 +5137,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5132
5137
|
pendingBreakSeg = -1;
|
|
5133
5138
|
pendingBreakWidth = 0;
|
|
5134
5139
|
}
|
|
5135
|
-
function
|
|
5140
|
+
function canBreakAfter2(kind) {
|
|
5136
5141
|
return kind === "space" || kind === "zero-width-break" || kind === "soft-hyphen";
|
|
5137
5142
|
}
|
|
5138
5143
|
function startLine(segIdx, graphemeIdx, width) {
|
|
@@ -5177,7 +5182,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5177
5182
|
} else {
|
|
5178
5183
|
startLine(i, 0, w);
|
|
5179
5184
|
}
|
|
5180
|
-
if (
|
|
5185
|
+
if (canBreakAfter2(seg.kind)) {
|
|
5181
5186
|
pendingBreakSeg = i + 1;
|
|
5182
5187
|
pendingBreakWidth = seg.kind === "space" ? lineW - w : lineW;
|
|
5183
5188
|
}
|
|
@@ -5185,7 +5190,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5185
5190
|
}
|
|
5186
5191
|
const newW = lineW + w;
|
|
5187
5192
|
if (newW > maxWidth + 5e-3) {
|
|
5188
|
-
if (
|
|
5193
|
+
if (canBreakAfter2(seg.kind)) {
|
|
5189
5194
|
lineW += w;
|
|
5190
5195
|
lineEndSeg = i + 1;
|
|
5191
5196
|
lineEndGrapheme = 0;
|
|
@@ -5209,7 +5214,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5209
5214
|
lineW = newW;
|
|
5210
5215
|
lineEndSeg = i + 1;
|
|
5211
5216
|
lineEndGrapheme = 0;
|
|
5212
|
-
if (
|
|
5217
|
+
if (canBreakAfter2(seg.kind)) {
|
|
5213
5218
|
pendingBreakSeg = i + 1;
|
|
5214
5219
|
pendingBreakWidth = seg.kind === "space" ? lineW - w : lineW;
|
|
5215
5220
|
}
|
|
@@ -5240,9 +5245,289 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5240
5245
|
}
|
|
5241
5246
|
}
|
|
5242
5247
|
}
|
|
5248
|
+
function canBreakAfter(kind) {
|
|
5249
|
+
return kind === "space" || kind === "zero-width-break" || kind === "soft-hyphen";
|
|
5250
|
+
}
|
|
5251
|
+
var _graphemeSegmenter = null;
|
|
5252
|
+
function graphemeSegmenter() {
|
|
5253
|
+
if (_graphemeSegmenter === null) {
|
|
5254
|
+
_graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
5255
|
+
}
|
|
5256
|
+
return _graphemeSegmenter;
|
|
5257
|
+
}
|
|
5258
|
+
function sliceSegmentText(seg, startG, endG) {
|
|
5259
|
+
if (startG === 0 && endG < 0) return seg.text;
|
|
5260
|
+
const graphemes = [...graphemeSegmenter().segment(seg.text)].map((g) => g.segment);
|
|
5261
|
+
const stop = endG < 0 ? graphemes.length : endG;
|
|
5262
|
+
return graphemes.slice(startG, stop).join("");
|
|
5263
|
+
}
|
|
5264
|
+
function buildLineText(segments, startSeg, startG, endSeg, endG, appendHyphen) {
|
|
5265
|
+
let text = "";
|
|
5266
|
+
for (let i = startSeg; i < endSeg; i++) {
|
|
5267
|
+
const seg = segments[i];
|
|
5268
|
+
if (seg.kind === "soft-hyphen" || seg.kind === "hard-break") continue;
|
|
5269
|
+
if (i === startSeg && startG > 0) {
|
|
5270
|
+
text += sliceSegmentText(seg, startG, -1);
|
|
5271
|
+
} else {
|
|
5272
|
+
text += seg.text;
|
|
5273
|
+
}
|
|
5274
|
+
}
|
|
5275
|
+
if (endG > 0 && endSeg < segments.length) {
|
|
5276
|
+
const seg = segments[endSeg];
|
|
5277
|
+
const from = startSeg === endSeg ? startG : 0;
|
|
5278
|
+
text += sliceSegmentText(seg, from, endG);
|
|
5279
|
+
}
|
|
5280
|
+
if (appendHyphen) text += "-";
|
|
5281
|
+
return text;
|
|
5282
|
+
}
|
|
5283
|
+
function resolveHyphenWidth(ctx) {
|
|
5284
|
+
if (!ctx || !ctx.adapter || !ctx.font) return 0;
|
|
5285
|
+
const cache = ctx.cache;
|
|
5286
|
+
if (cache) {
|
|
5287
|
+
let fc = cache.get(ctx.font);
|
|
5288
|
+
if (!fc) {
|
|
5289
|
+
fc = /* @__PURE__ */ new Map();
|
|
5290
|
+
cache.set(ctx.font, fc);
|
|
5291
|
+
}
|
|
5292
|
+
let hw = fc.get("-");
|
|
5293
|
+
if (hw === void 0) {
|
|
5294
|
+
hw = ctx.adapter.measureSegment("-", ctx.font).width;
|
|
5295
|
+
fc.set("-", hw);
|
|
5296
|
+
}
|
|
5297
|
+
return hw;
|
|
5298
|
+
}
|
|
5299
|
+
return ctx.adapter.measureSegment("-", ctx.font).width;
|
|
5300
|
+
}
|
|
5301
|
+
function layoutNextLine(segments, cursor, slotWidth, ctx) {
|
|
5302
|
+
let i = cursor.segmentIndex;
|
|
5303
|
+
const initialG = cursor.graphemeIndex;
|
|
5304
|
+
if (i >= segments.length) return null;
|
|
5305
|
+
if (initialG === 0) {
|
|
5306
|
+
while (i < segments.length) {
|
|
5307
|
+
const seg = segments[i];
|
|
5308
|
+
if (seg.kind === "hard-break") {
|
|
5309
|
+
return {
|
|
5310
|
+
text: "",
|
|
5311
|
+
width: 0,
|
|
5312
|
+
start: { segmentIndex: cursor.segmentIndex, graphemeIndex: 0 },
|
|
5313
|
+
end: { segmentIndex: i + 1, graphemeIndex: 0 }
|
|
5314
|
+
};
|
|
5315
|
+
}
|
|
5316
|
+
if (seg.kind === "space" || seg.kind === "zero-width-break" || seg.kind === "soft-hyphen") {
|
|
5317
|
+
i += 1;
|
|
5318
|
+
continue;
|
|
5319
|
+
}
|
|
5320
|
+
break;
|
|
5321
|
+
}
|
|
5322
|
+
if (i >= segments.length) return null;
|
|
5323
|
+
}
|
|
5324
|
+
const hyphenWidth = resolveHyphenWidth(ctx);
|
|
5325
|
+
const startSeg = i;
|
|
5326
|
+
const startG = i === cursor.segmentIndex ? initialG : 0;
|
|
5327
|
+
let lineW = 0;
|
|
5328
|
+
let lineEndSeg = startSeg;
|
|
5329
|
+
let lineEndG = 0;
|
|
5330
|
+
let hasContent = false;
|
|
5331
|
+
let pendingBreakSeg = -1;
|
|
5332
|
+
let pendingBreakG = 0;
|
|
5333
|
+
let pendingBreakWidth = 0;
|
|
5334
|
+
let pendingBreakSoftHyphen = false;
|
|
5335
|
+
const recordPending = (sIdx, gIdx, widthAtBreak, kind) => {
|
|
5336
|
+
pendingBreakSeg = sIdx;
|
|
5337
|
+
pendingBreakG = gIdx;
|
|
5338
|
+
pendingBreakWidth = widthAtBreak;
|
|
5339
|
+
pendingBreakSoftHyphen = kind === "soft-hyphen";
|
|
5340
|
+
};
|
|
5341
|
+
const consumeBreakable = (segIdx, gStart, gWidths) => {
|
|
5342
|
+
for (let g = gStart; g < gWidths.length; g++) {
|
|
5343
|
+
const gw = gWidths[g];
|
|
5344
|
+
if (!hasContent) {
|
|
5345
|
+
lineW = gw;
|
|
5346
|
+
lineEndSeg = segIdx;
|
|
5347
|
+
lineEndG = g + 1;
|
|
5348
|
+
hasContent = true;
|
|
5349
|
+
continue;
|
|
5350
|
+
}
|
|
5351
|
+
if (lineW + gw > slotWidth + 5e-3) {
|
|
5352
|
+
return true;
|
|
5353
|
+
}
|
|
5354
|
+
lineW += gw;
|
|
5355
|
+
lineEndSeg = segIdx;
|
|
5356
|
+
lineEndG = g + 1;
|
|
5357
|
+
}
|
|
5358
|
+
if (lineEndSeg === segIdx && lineEndG === gWidths.length) {
|
|
5359
|
+
lineEndSeg = segIdx + 1;
|
|
5360
|
+
lineEndG = 0;
|
|
5361
|
+
}
|
|
5362
|
+
return false;
|
|
5363
|
+
};
|
|
5364
|
+
if (startG > 0 && startSeg < segments.length) {
|
|
5365
|
+
const seg = segments[startSeg];
|
|
5366
|
+
if (seg.graphemeWidths) {
|
|
5367
|
+
const overflowed = consumeBreakable(startSeg, startG, seg.graphemeWidths);
|
|
5368
|
+
if (overflowed) {
|
|
5369
|
+
const text2 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
|
|
5370
|
+
return {
|
|
5371
|
+
text: text2,
|
|
5372
|
+
width: lineW,
|
|
5373
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
5374
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
5375
|
+
};
|
|
5376
|
+
}
|
|
5377
|
+
i = lineEndSeg;
|
|
5378
|
+
} else {
|
|
5379
|
+
}
|
|
5380
|
+
}
|
|
5381
|
+
for (; i < segments.length; ) {
|
|
5382
|
+
const seg = segments[i];
|
|
5383
|
+
if (seg.kind === "hard-break") {
|
|
5384
|
+
if (hasContent) {
|
|
5385
|
+
const endsAtSoftHyphen2 = lineEndSeg > 0 && segments[lineEndSeg - 1]?.kind === "soft-hyphen";
|
|
5386
|
+
const text2 = buildLineText(
|
|
5387
|
+
segments,
|
|
5388
|
+
startSeg,
|
|
5389
|
+
startG,
|
|
5390
|
+
lineEndSeg,
|
|
5391
|
+
lineEndG,
|
|
5392
|
+
endsAtSoftHyphen2
|
|
5393
|
+
);
|
|
5394
|
+
return {
|
|
5395
|
+
text: text2,
|
|
5396
|
+
width: lineW + (endsAtSoftHyphen2 ? hyphenWidth : 0),
|
|
5397
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
5398
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
5399
|
+
};
|
|
5400
|
+
}
|
|
5401
|
+
return {
|
|
5402
|
+
text: "",
|
|
5403
|
+
width: 0,
|
|
5404
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
5405
|
+
end: { segmentIndex: i + 1, graphemeIndex: 0 }
|
|
5406
|
+
};
|
|
5407
|
+
}
|
|
5408
|
+
const w = seg.width;
|
|
5409
|
+
if (!hasContent) {
|
|
5410
|
+
if (w > slotWidth && seg.graphemeWidths) {
|
|
5411
|
+
const overflowed = consumeBreakable(i, 0, seg.graphemeWidths);
|
|
5412
|
+
if (overflowed) {
|
|
5413
|
+
const text2 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
|
|
5414
|
+
return {
|
|
5415
|
+
text: text2,
|
|
5416
|
+
width: lineW,
|
|
5417
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
5418
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
5419
|
+
};
|
|
5420
|
+
}
|
|
5421
|
+
i = lineEndSeg;
|
|
5422
|
+
continue;
|
|
5423
|
+
}
|
|
5424
|
+
lineW = w;
|
|
5425
|
+
lineEndSeg = i + 1;
|
|
5426
|
+
lineEndG = 0;
|
|
5427
|
+
hasContent = true;
|
|
5428
|
+
if (canBreakAfter(seg.kind)) {
|
|
5429
|
+
recordPending(i + 1, 0, seg.kind === "space" ? lineW - w : lineW, seg.kind);
|
|
5430
|
+
}
|
|
5431
|
+
i += 1;
|
|
5432
|
+
continue;
|
|
5433
|
+
}
|
|
5434
|
+
const newW = lineW + w;
|
|
5435
|
+
if (newW > slotWidth + 5e-3) {
|
|
5436
|
+
if (canBreakAfter(seg.kind)) {
|
|
5437
|
+
lineEndSeg = i + 1;
|
|
5438
|
+
lineEndG = 0;
|
|
5439
|
+
const endsAtSoftHyphen2 = seg.kind === "soft-hyphen";
|
|
5440
|
+
const finalWidth = seg.kind === "space" ? lineW : lineW + (endsAtSoftHyphen2 ? hyphenWidth : 0);
|
|
5441
|
+
const text3 = buildLineText(
|
|
5442
|
+
segments,
|
|
5443
|
+
startSeg,
|
|
5444
|
+
startG,
|
|
5445
|
+
lineEndSeg,
|
|
5446
|
+
lineEndG,
|
|
5447
|
+
endsAtSoftHyphen2
|
|
5448
|
+
);
|
|
5449
|
+
return {
|
|
5450
|
+
text: text3,
|
|
5451
|
+
width: finalWidth,
|
|
5452
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
5453
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
5454
|
+
};
|
|
5455
|
+
}
|
|
5456
|
+
if (pendingBreakSeg >= 0) {
|
|
5457
|
+
const text3 = buildLineText(
|
|
5458
|
+
segments,
|
|
5459
|
+
startSeg,
|
|
5460
|
+
startG,
|
|
5461
|
+
pendingBreakSeg,
|
|
5462
|
+
pendingBreakG,
|
|
5463
|
+
pendingBreakSoftHyphen
|
|
5464
|
+
);
|
|
5465
|
+
return {
|
|
5466
|
+
text: text3,
|
|
5467
|
+
width: pendingBreakWidth + (pendingBreakSoftHyphen ? hyphenWidth : 0),
|
|
5468
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
5469
|
+
end: { segmentIndex: pendingBreakSeg, graphemeIndex: pendingBreakG }
|
|
5470
|
+
};
|
|
5471
|
+
}
|
|
5472
|
+
if (w > slotWidth && seg.graphemeWidths) {
|
|
5473
|
+
const text3 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
|
|
5474
|
+
return {
|
|
5475
|
+
text: text3,
|
|
5476
|
+
width: lineW,
|
|
5477
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
5478
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
5479
|
+
};
|
|
5480
|
+
}
|
|
5481
|
+
const text2 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
|
|
5482
|
+
return {
|
|
5483
|
+
text: text2,
|
|
5484
|
+
width: lineW,
|
|
5485
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
5486
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
5487
|
+
};
|
|
5488
|
+
}
|
|
5489
|
+
lineW = newW;
|
|
5490
|
+
lineEndSeg = i + 1;
|
|
5491
|
+
lineEndG = 0;
|
|
5492
|
+
if (canBreakAfter(seg.kind)) {
|
|
5493
|
+
recordPending(i + 1, 0, seg.kind === "space" ? lineW - w : lineW, seg.kind);
|
|
5494
|
+
}
|
|
5495
|
+
i += 1;
|
|
5496
|
+
}
|
|
5497
|
+
if (!hasContent) return null;
|
|
5498
|
+
const endsAtSoftHyphen = lineEndSeg > 0 && segments[lineEndSeg - 1]?.kind === "soft-hyphen";
|
|
5499
|
+
const text = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, endsAtSoftHyphen);
|
|
5500
|
+
return {
|
|
5501
|
+
text,
|
|
5502
|
+
width: lineW + (endsAtSoftHyphen ? hyphenWidth : 0),
|
|
5503
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
5504
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
5505
|
+
};
|
|
5506
|
+
}
|
|
5507
|
+
function carveTextLineSlots(base, blocked, minSlotWidth = 0) {
|
|
5508
|
+
let slots = [base];
|
|
5509
|
+
for (let bi = 0; bi < blocked.length; bi++) {
|
|
5510
|
+
const block = blocked[bi];
|
|
5511
|
+
const next = [];
|
|
5512
|
+
for (let si = 0; si < slots.length; si++) {
|
|
5513
|
+
const slot = slots[si];
|
|
5514
|
+
if (block.right <= slot.left || block.left >= slot.right) {
|
|
5515
|
+
next.push(slot);
|
|
5516
|
+
continue;
|
|
5517
|
+
}
|
|
5518
|
+
if (block.left > slot.left) next.push({ left: slot.left, right: block.left });
|
|
5519
|
+
if (block.right < slot.right) next.push({ left: block.right, right: slot.right });
|
|
5520
|
+
}
|
|
5521
|
+
slots = next;
|
|
5522
|
+
}
|
|
5523
|
+
if (minSlotWidth > 0) {
|
|
5524
|
+
return slots.filter((s) => s.right - s.left >= minSlotWidth);
|
|
5525
|
+
}
|
|
5526
|
+
return slots;
|
|
5527
|
+
}
|
|
5243
5528
|
function computeCharPositions(lineBreaks, segments, lineHeight) {
|
|
5244
5529
|
const positions = [];
|
|
5245
|
-
const
|
|
5530
|
+
const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
|
|
5246
5531
|
granularity: "grapheme"
|
|
5247
5532
|
});
|
|
5248
5533
|
for (let lineIdx = 0; lineIdx < lineBreaks.lines.length; lineIdx++) {
|
|
@@ -5255,7 +5540,7 @@ function computeCharPositions(lineBreaks, segments, lineHeight) {
|
|
|
5255
5540
|
if (si >= line.endSegment && line.endGrapheme === 0) break;
|
|
5256
5541
|
continue;
|
|
5257
5542
|
}
|
|
5258
|
-
const graphemes = [...
|
|
5543
|
+
const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
|
|
5259
5544
|
if (graphemes.length === 0) continue;
|
|
5260
5545
|
const startG = si === line.startSegment ? line.startGrapheme : 0;
|
|
5261
5546
|
let endG;
|
|
@@ -5611,6 +5896,185 @@ function reactiveBlockLayout(opts) {
|
|
|
5611
5896
|
totalHeight: totalHeightNode
|
|
5612
5897
|
};
|
|
5613
5898
|
}
|
|
5899
|
+
|
|
5900
|
+
// src/patterns/reactive-layout/reactive-flow-layout.ts
|
|
5901
|
+
function circleIntervalForBand(o, bandTop, bandBottom) {
|
|
5902
|
+
const hPad = o.hPad ?? 0;
|
|
5903
|
+
const vPad = o.vPad ?? 0;
|
|
5904
|
+
const top = bandTop - vPad;
|
|
5905
|
+
const bottom = bandBottom + vPad;
|
|
5906
|
+
if (top >= o.cy + o.r || bottom <= o.cy - o.r) return null;
|
|
5907
|
+
const minDy = o.cy >= top && o.cy <= bottom ? 0 : o.cy < top ? top - o.cy : o.cy - bottom;
|
|
5908
|
+
if (minDy >= o.r) return null;
|
|
5909
|
+
const maxDx = Math.sqrt(o.r * o.r - minDy * minDy);
|
|
5910
|
+
return { left: o.cx - maxDx - hPad, right: o.cx + maxDx + hPad };
|
|
5911
|
+
}
|
|
5912
|
+
function rectIntervalForBand(o, bandTop, bandBottom) {
|
|
5913
|
+
const hPad = o.hPad ?? 0;
|
|
5914
|
+
const vPad = o.vPad ?? 0;
|
|
5915
|
+
if (bandBottom <= o.y - vPad) return null;
|
|
5916
|
+
if (bandTop >= o.y + o.h + vPad) return null;
|
|
5917
|
+
return { left: o.x - hPad, right: o.x + o.w + hPad };
|
|
5918
|
+
}
|
|
5919
|
+
function obstacleIntervalForBand(o, bandTop, bandBottom) {
|
|
5920
|
+
return o.kind === "circle" ? circleIntervalForBand(o, bandTop, bandBottom) : rectIntervalForBand(o, bandTop, bandBottom);
|
|
5921
|
+
}
|
|
5922
|
+
function computeFlowLines(segments, container, columns, obstacles, lineHeight, minSlotWidth) {
|
|
5923
|
+
const lines = [];
|
|
5924
|
+
let cursor = { segmentIndex: 0, graphemeIndex: 0 };
|
|
5925
|
+
if (segments.length === 0 || columns.count <= 0 || lineHeight <= 0) {
|
|
5926
|
+
return { lines, cursor };
|
|
5927
|
+
}
|
|
5928
|
+
const padX = container.paddingX ?? 0;
|
|
5929
|
+
const padY = container.paddingY ?? 0;
|
|
5930
|
+
const availWidth = Math.max(0, container.width - padX * 2);
|
|
5931
|
+
const availHeight = Math.max(0, container.height - padY * 2);
|
|
5932
|
+
const gapTotal = columns.gap * Math.max(0, columns.count - 1);
|
|
5933
|
+
const colWidth = Math.max(0, (availWidth - gapTotal) / columns.count);
|
|
5934
|
+
if (colWidth <= 0) return { lines, cursor };
|
|
5935
|
+
outerCol: for (let col = 0; col < columns.count; col++) {
|
|
5936
|
+
const colLeft = padX + col * (colWidth + columns.gap);
|
|
5937
|
+
const colRight = colLeft + colWidth;
|
|
5938
|
+
let bandTop = padY;
|
|
5939
|
+
while (bandTop + lineHeight <= padY + availHeight) {
|
|
5940
|
+
const bandBottom = bandTop + lineHeight;
|
|
5941
|
+
const blocked = [];
|
|
5942
|
+
for (let oi = 0; oi < obstacles.length; oi++) {
|
|
5943
|
+
const iv = obstacleIntervalForBand(obstacles[oi], bandTop, bandBottom);
|
|
5944
|
+
if (iv !== null) blocked.push(iv);
|
|
5945
|
+
}
|
|
5946
|
+
const slots = carveTextLineSlots({ left: colLeft, right: colRight }, blocked, minSlotWidth);
|
|
5947
|
+
if (slots.length === 0) {
|
|
5948
|
+
bandTop += lineHeight;
|
|
5949
|
+
continue;
|
|
5950
|
+
}
|
|
5951
|
+
let hardBreakThisBand = false;
|
|
5952
|
+
for (let si = 0; si < slots.length; si++) {
|
|
5953
|
+
const slot = slots[si];
|
|
5954
|
+
const slotW = slot.right - slot.left;
|
|
5955
|
+
const line = layoutNextLine(segments, cursor, slotW);
|
|
5956
|
+
if (line === null) {
|
|
5957
|
+
return { lines, cursor };
|
|
5958
|
+
}
|
|
5959
|
+
if (line.text.length === 0 && line.width === 0) {
|
|
5960
|
+
cursor = line.end;
|
|
5961
|
+
hardBreakThisBand = true;
|
|
5962
|
+
break;
|
|
5963
|
+
}
|
|
5964
|
+
lines.push({
|
|
5965
|
+
x: slot.left,
|
|
5966
|
+
y: bandTop,
|
|
5967
|
+
width: line.width,
|
|
5968
|
+
slotWidth: slotW,
|
|
5969
|
+
text: line.text,
|
|
5970
|
+
columnIndex: col,
|
|
5971
|
+
flushToRight: slot.right < colRight - 0.5
|
|
5972
|
+
});
|
|
5973
|
+
cursor = line.end;
|
|
5974
|
+
}
|
|
5975
|
+
bandTop += lineHeight;
|
|
5976
|
+
if (hardBreakThisBand) continue;
|
|
5977
|
+
if (cursor.segmentIndex >= segments.length) break outerCol;
|
|
5978
|
+
}
|
|
5979
|
+
if (cursor.segmentIndex >= segments.length) break;
|
|
5980
|
+
}
|
|
5981
|
+
return { lines, cursor };
|
|
5982
|
+
}
|
|
5983
|
+
function reactiveFlowLayout(opts) {
|
|
5984
|
+
const { adapter, name = "reactive-flow-layout", minSlotWidth = 20 } = opts;
|
|
5985
|
+
const g = new Graph(name);
|
|
5986
|
+
const measureCache = /* @__PURE__ */ new Map();
|
|
5987
|
+
const textNode = state(opts.text ?? "", { name: "text" });
|
|
5988
|
+
const fontNode = state(opts.font ?? "16px sans-serif", { name: "font" });
|
|
5989
|
+
const lineHeightNode = state(opts.lineHeight ?? 20, { name: "line-height" });
|
|
5990
|
+
const containerNode = state(
|
|
5991
|
+
opts.container ?? { width: 800, height: 600, paddingX: 0, paddingY: 0 },
|
|
5992
|
+
{ name: "container" }
|
|
5993
|
+
);
|
|
5994
|
+
const columnsNode = state(opts.columns ?? { count: 1, gap: 0 }, {
|
|
5995
|
+
name: "columns"
|
|
5996
|
+
});
|
|
5997
|
+
const obstaclesNode = state(opts.obstacles ?? [], { name: "obstacles" });
|
|
5998
|
+
const segmentsNode = node(
|
|
5999
|
+
[textNode, fontNode],
|
|
6000
|
+
(data, actions, ctx) => {
|
|
6001
|
+
const b0 = data[0];
|
|
6002
|
+
const textVal = b0 != null && b0.length > 0 ? b0.at(-1) : ctx.prevData[0];
|
|
6003
|
+
const b1 = data[1];
|
|
6004
|
+
const fontVal = b1 != null && b1.length > 0 ? b1.at(-1) : ctx.prevData[1];
|
|
6005
|
+
const result = analyzeAndMeasure(textVal, fontVal, adapter, measureCache);
|
|
6006
|
+
actions.emit(result);
|
|
6007
|
+
return () => {
|
|
6008
|
+
measureCache.clear();
|
|
6009
|
+
adapter.clearCache?.();
|
|
6010
|
+
};
|
|
6011
|
+
},
|
|
6012
|
+
{ name: "segments", describeKind: "derived" }
|
|
6013
|
+
);
|
|
6014
|
+
const flowLinesNode = derived(
|
|
6015
|
+
[segmentsNode, containerNode, columnsNode, obstaclesNode, lineHeightNode],
|
|
6016
|
+
([segs, cont, cols, obs, lh]) => {
|
|
6017
|
+
const segments = segs;
|
|
6018
|
+
const t0 = monotonicNs();
|
|
6019
|
+
const { lines: result, cursor } = computeFlowLines(
|
|
6020
|
+
segments,
|
|
6021
|
+
cont,
|
|
6022
|
+
cols,
|
|
6023
|
+
obs,
|
|
6024
|
+
lh,
|
|
6025
|
+
minSlotWidth
|
|
6026
|
+
);
|
|
6027
|
+
const elapsed = monotonicNs() - t0;
|
|
6028
|
+
const overflow = Math.max(0, segments.length - cursor.segmentIndex);
|
|
6029
|
+
const meta = flowLinesNode.meta;
|
|
6030
|
+
if (meta) {
|
|
6031
|
+
emitToMeta(meta["line-count"], result.length);
|
|
6032
|
+
emitToMeta(meta["layout-time-ns"], elapsed);
|
|
6033
|
+
emitToMeta(meta["overflow-segments"], overflow);
|
|
6034
|
+
}
|
|
6035
|
+
return result;
|
|
6036
|
+
},
|
|
6037
|
+
{
|
|
6038
|
+
name: "flow-lines",
|
|
6039
|
+
meta: {
|
|
6040
|
+
"line-count": 0,
|
|
6041
|
+
"layout-time-ns": 0,
|
|
6042
|
+
"overflow-segments": 0
|
|
6043
|
+
},
|
|
6044
|
+
equals: (a, b) => {
|
|
6045
|
+
const la = a;
|
|
6046
|
+
const lb = b;
|
|
6047
|
+
if (la.length !== lb.length) return false;
|
|
6048
|
+
for (let i = 0; i < la.length; i++) {
|
|
6049
|
+
const pa = la[i];
|
|
6050
|
+
const pb = lb[i];
|
|
6051
|
+
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)
|
|
6052
|
+
return false;
|
|
6053
|
+
}
|
|
6054
|
+
return true;
|
|
6055
|
+
}
|
|
6056
|
+
}
|
|
6057
|
+
);
|
|
6058
|
+
g.add("text", textNode);
|
|
6059
|
+
g.add("font", fontNode);
|
|
6060
|
+
g.add("line-height", lineHeightNode);
|
|
6061
|
+
g.add("container", containerNode);
|
|
6062
|
+
g.add("columns", columnsNode);
|
|
6063
|
+
g.add("obstacles", obstaclesNode);
|
|
6064
|
+
g.add("segments", segmentsNode);
|
|
6065
|
+
g.add("flow-lines", flowLinesNode);
|
|
6066
|
+
return {
|
|
6067
|
+
graph: g,
|
|
6068
|
+
setText: (t) => g.set("text", t),
|
|
6069
|
+
setFont: (f) => g.set("font", f),
|
|
6070
|
+
setLineHeight: (lh) => g.set("line-height", lh),
|
|
6071
|
+
setContainer: (c) => g.set("container", c),
|
|
6072
|
+
setColumns: (c) => g.set("columns", c),
|
|
6073
|
+
setObstacles: (o) => g.set("obstacles", o),
|
|
6074
|
+
segments: segmentsNode,
|
|
6075
|
+
flowLines: flowLinesNode
|
|
6076
|
+
};
|
|
6077
|
+
}
|
|
5614
6078
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5615
6079
|
0 && (module.exports = {
|
|
5616
6080
|
CanvasMeasureAdapter,
|
|
@@ -5620,13 +6084,19 @@ function reactiveBlockLayout(opts) {
|
|
|
5620
6084
|
PrecomputedAdapter,
|
|
5621
6085
|
SvgBoundsAdapter,
|
|
5622
6086
|
analyzeAndMeasure,
|
|
6087
|
+
carveTextLineSlots,
|
|
6088
|
+
circleIntervalForBand,
|
|
5623
6089
|
computeBlockFlow,
|
|
5624
6090
|
computeCharPositions,
|
|
6091
|
+
computeFlowLines,
|
|
5625
6092
|
computeLineBreaks,
|
|
5626
6093
|
computeTotalHeight,
|
|
6094
|
+
layoutNextLine,
|
|
5627
6095
|
measureBlock,
|
|
5628
6096
|
measureBlocks,
|
|
5629
6097
|
reactiveBlockLayout,
|
|
5630
|
-
|
|
6098
|
+
reactiveFlowLayout,
|
|
6099
|
+
reactiveLayout,
|
|
6100
|
+
rectIntervalForBand
|
|
5631
6101
|
});
|
|
5632
6102
|
//# sourceMappingURL=index.cjs.map
|