@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.
- package/dist/{chunk-IPLKX3L2.js → chunk-EVR6UFUV.js} +2 -2
- package/dist/{chunk-5WGT55R4.js → chunk-IAHGTNOZ.js} +4 -2
- package/dist/{chunk-5WGT55R4.js.map → chunk-IAHGTNOZ.js.map} +1 -1
- package/dist/{chunk-AOCBDH4T.js → chunk-L2GLW2U7.js} +68 -1
- package/dist/chunk-L2GLW2U7.js.map +1 -0
- package/dist/{chunk-TDEXAMGO.js → chunk-TKE3JGOH.js} +488 -16
- package/dist/chunk-TKE3JGOH.js.map +1 -0
- package/dist/compat/nestjs/index.cjs.map +1 -1
- package/dist/compat/nestjs/index.js +2 -2
- package/dist/extra/index.cjs +68 -0
- package/dist/extra/index.cjs.map +1 -1
- package/dist/extra/index.d.cts +1 -1
- package/dist/extra/index.d.ts +1 -1
- package/dist/extra/index.js +4 -2
- package/dist/{index-1z8vRTCt.d.cts → index-Ch0IpIO0.d.cts} +29 -2
- package/dist/{index-b5BYtczN.d.cts → index-DKE1EATr.d.cts} +222 -2
- package/dist/{index-BysCTzJz.d.ts → index-Ds23Wvou.d.ts} +29 -2
- package/dist/{index-D7XgsUt7.d.ts → index-OXImXMq6.d.ts} +222 -2
- package/dist/index.cjs +550 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/patterns/reactive-layout/index.cjs +488 -16
- package/dist/patterns/reactive-layout/index.cjs.map +1 -1
- package/dist/patterns/reactive-layout/index.d.cts +1 -1
- package/dist/patterns/reactive-layout/index.d.ts +1 -1
- package/dist/patterns/reactive-layout/index.js +16 -4
- package/package.json +1 -1
- package/dist/chunk-AOCBDH4T.js.map +0 -1
- package/dist/chunk-TDEXAMGO.js.map +0 -1
- /package/dist/{chunk-IPLKX3L2.js.map → chunk-EVR6UFUV.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
|
|
|
@@ -4958,7 +4964,7 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
|
|
|
4958
4964
|
const normalized = normalizeWhitespace(text);
|
|
4959
4965
|
if (normalized.length === 0) return [];
|
|
4960
4966
|
const pieces = segmentText(normalized);
|
|
4961
|
-
const
|
|
4967
|
+
const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
|
|
4962
4968
|
granularity: "grapheme"
|
|
4963
4969
|
});
|
|
4964
4970
|
const rawTexts = [];
|
|
@@ -5002,7 +5008,8 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
|
|
|
5002
5008
|
let w = fontCache.get(seg);
|
|
5003
5009
|
if (w === void 0) {
|
|
5004
5010
|
if (stats) stats.misses += 1;
|
|
5005
|
-
|
|
5011
|
+
const raw = adapter.measureSegment(seg, font).width;
|
|
5012
|
+
w = Number.isFinite(raw) && raw >= 0 ? raw : 0;
|
|
5006
5013
|
fontCache.set(seg, w);
|
|
5007
5014
|
} else if (stats) {
|
|
5008
5015
|
stats.hits += 1;
|
|
@@ -5024,7 +5031,7 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
|
|
|
5024
5031
|
}
|
|
5025
5032
|
if (isCJK(t)) {
|
|
5026
5033
|
let unitText = "";
|
|
5027
|
-
for (const gs of
|
|
5034
|
+
for (const gs of graphemeSegmenter2.segment(t)) {
|
|
5028
5035
|
const grapheme = gs.segment;
|
|
5029
5036
|
if (unitText.length > 0 && kinsokuStart.has(grapheme)) {
|
|
5030
5037
|
unitText += grapheme;
|
|
@@ -5056,7 +5063,7 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
|
|
|
5056
5063
|
let graphemeWidths = null;
|
|
5057
5064
|
if (mergedWordLike[i] && t.length > 1) {
|
|
5058
5065
|
const gWidths = [];
|
|
5059
|
-
for (const gs of
|
|
5066
|
+
for (const gs of graphemeSegmenter2.segment(t)) {
|
|
5060
5067
|
gWidths.push(measureCached(gs.segment));
|
|
5061
5068
|
}
|
|
5062
5069
|
if (gWidths.length > 1) {
|
|
@@ -5096,10 +5103,10 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5096
5103
|
const seg = segments[i];
|
|
5097
5104
|
if (seg.kind === "soft-hyphen" || seg.kind === "hard-break") continue;
|
|
5098
5105
|
if (i === lineStartSeg && lineStartGrapheme > 0 && seg.graphemeWidths) {
|
|
5099
|
-
const
|
|
5106
|
+
const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
|
|
5100
5107
|
granularity: "grapheme"
|
|
5101
5108
|
});
|
|
5102
|
-
const graphemes = [...
|
|
5109
|
+
const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
|
|
5103
5110
|
text += graphemes.slice(lineStartGrapheme).join("");
|
|
5104
5111
|
} else {
|
|
5105
5112
|
text += seg.text;
|
|
@@ -5107,10 +5114,10 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5107
5114
|
}
|
|
5108
5115
|
if (endGrapheme > 0 && endSeg < segments.length) {
|
|
5109
5116
|
const seg = segments[endSeg];
|
|
5110
|
-
const
|
|
5117
|
+
const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
|
|
5111
5118
|
granularity: "grapheme"
|
|
5112
5119
|
});
|
|
5113
|
-
const graphemes = [...
|
|
5120
|
+
const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
|
|
5114
5121
|
const startG = lineStartSeg === endSeg ? lineStartGrapheme : 0;
|
|
5115
5122
|
text += graphemes.slice(startG, endGrapheme).join("");
|
|
5116
5123
|
}
|
|
@@ -5130,7 +5137,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5130
5137
|
pendingBreakSeg = -1;
|
|
5131
5138
|
pendingBreakWidth = 0;
|
|
5132
5139
|
}
|
|
5133
|
-
function
|
|
5140
|
+
function canBreakAfter2(kind) {
|
|
5134
5141
|
return kind === "space" || kind === "zero-width-break" || kind === "soft-hyphen";
|
|
5135
5142
|
}
|
|
5136
5143
|
function startLine(segIdx, graphemeIdx, width) {
|
|
@@ -5175,7 +5182,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5175
5182
|
} else {
|
|
5176
5183
|
startLine(i, 0, w);
|
|
5177
5184
|
}
|
|
5178
|
-
if (
|
|
5185
|
+
if (canBreakAfter2(seg.kind)) {
|
|
5179
5186
|
pendingBreakSeg = i + 1;
|
|
5180
5187
|
pendingBreakWidth = seg.kind === "space" ? lineW - w : lineW;
|
|
5181
5188
|
}
|
|
@@ -5183,7 +5190,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5183
5190
|
}
|
|
5184
5191
|
const newW = lineW + w;
|
|
5185
5192
|
if (newW > maxWidth + 5e-3) {
|
|
5186
|
-
if (
|
|
5193
|
+
if (canBreakAfter2(seg.kind)) {
|
|
5187
5194
|
lineW += w;
|
|
5188
5195
|
lineEndSeg = i + 1;
|
|
5189
5196
|
lineEndGrapheme = 0;
|
|
@@ -5207,7 +5214,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5207
5214
|
lineW = newW;
|
|
5208
5215
|
lineEndSeg = i + 1;
|
|
5209
5216
|
lineEndGrapheme = 0;
|
|
5210
|
-
if (
|
|
5217
|
+
if (canBreakAfter2(seg.kind)) {
|
|
5211
5218
|
pendingBreakSeg = i + 1;
|
|
5212
5219
|
pendingBreakWidth = seg.kind === "space" ? lineW - w : lineW;
|
|
5213
5220
|
}
|
|
@@ -5238,9 +5245,289 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5238
5245
|
}
|
|
5239
5246
|
}
|
|
5240
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
|
+
}
|
|
5241
5528
|
function computeCharPositions(lineBreaks, segments, lineHeight) {
|
|
5242
5529
|
const positions = [];
|
|
5243
|
-
const
|
|
5530
|
+
const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
|
|
5244
5531
|
granularity: "grapheme"
|
|
5245
5532
|
});
|
|
5246
5533
|
for (let lineIdx = 0; lineIdx < lineBreaks.lines.length; lineIdx++) {
|
|
@@ -5253,7 +5540,7 @@ function computeCharPositions(lineBreaks, segments, lineHeight) {
|
|
|
5253
5540
|
if (si >= line.endSegment && line.endGrapheme === 0) break;
|
|
5254
5541
|
continue;
|
|
5255
5542
|
}
|
|
5256
|
-
const graphemes = [...
|
|
5543
|
+
const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
|
|
5257
5544
|
if (graphemes.length === 0) continue;
|
|
5258
5545
|
const startG = si === line.startSegment ? line.startGrapheme : 0;
|
|
5259
5546
|
let endG;
|
|
@@ -5609,6 +5896,185 @@ function reactiveBlockLayout(opts) {
|
|
|
5609
5896
|
totalHeight: totalHeightNode
|
|
5610
5897
|
};
|
|
5611
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
|
+
}
|
|
5612
6078
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5613
6079
|
0 && (module.exports = {
|
|
5614
6080
|
CanvasMeasureAdapter,
|
|
@@ -5618,13 +6084,19 @@ function reactiveBlockLayout(opts) {
|
|
|
5618
6084
|
PrecomputedAdapter,
|
|
5619
6085
|
SvgBoundsAdapter,
|
|
5620
6086
|
analyzeAndMeasure,
|
|
6087
|
+
carveTextLineSlots,
|
|
6088
|
+
circleIntervalForBand,
|
|
5621
6089
|
computeBlockFlow,
|
|
5622
6090
|
computeCharPositions,
|
|
6091
|
+
computeFlowLines,
|
|
5623
6092
|
computeLineBreaks,
|
|
5624
6093
|
computeTotalHeight,
|
|
6094
|
+
layoutNextLine,
|
|
5625
6095
|
measureBlock,
|
|
5626
6096
|
measureBlocks,
|
|
5627
6097
|
reactiveBlockLayout,
|
|
5628
|
-
|
|
6098
|
+
reactiveFlowLayout,
|
|
6099
|
+
reactiveLayout,
|
|
6100
|
+
rectIntervalForBand
|
|
5629
6101
|
});
|
|
5630
6102
|
//# sourceMappingURL=index.cjs.map
|