@graphrefly/graphrefly 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/{chunk-QOWVNWOC.js → chunk-3ZWCKRHX.js} +27 -25
- package/dist/{chunk-QOWVNWOC.js.map → chunk-3ZWCKRHX.js.map} +1 -1
- package/dist/chunk-6LDQFTYS.js +102 -0
- package/dist/chunk-6LDQFTYS.js.map +1 -0
- package/dist/{chunk-5WGT55R4.js → chunk-AMCG74RZ.js} +195 -24
- package/dist/chunk-AMCG74RZ.js.map +1 -0
- package/dist/{chunk-AOCBDH4T.js → chunk-BVZYTZ5H.js} +76 -103
- package/dist/chunk-BVZYTZ5H.js.map +1 -0
- package/dist/chunk-FQMKGR6L.js +330 -0
- package/dist/chunk-FQMKGR6L.js.map +1 -0
- package/dist/chunk-HXZEYDUR.js +94 -0
- package/dist/chunk-HXZEYDUR.js.map +1 -0
- package/dist/{chunk-IPLKX3L2.js → chunk-IZYUSJC7.js} +16 -14
- package/dist/{chunk-IPLKX3L2.js.map → chunk-IZYUSJC7.js.map} +1 -1
- package/dist/chunk-J22W6HV3.js +107 -0
- package/dist/chunk-J22W6HV3.js.map +1 -0
- package/dist/{chunk-HWPIFSW2.js → chunk-JSCT3CR4.js} +6 -4
- package/dist/{chunk-HWPIFSW2.js.map → chunk-JSCT3CR4.js.map} +1 -1
- package/dist/chunk-JYXEWPH4.js +62 -0
- package/dist/chunk-JYXEWPH4.js.map +1 -0
- package/dist/chunk-LCE3GF5P.js +866 -0
- package/dist/chunk-LCE3GF5P.js.map +1 -0
- package/dist/chunk-MJ2NKQQL.js +119 -0
- package/dist/chunk-MJ2NKQQL.js.map +1 -0
- package/dist/chunk-N6UR7YVY.js +198 -0
- package/dist/chunk-N6UR7YVY.js.map +1 -0
- package/dist/chunk-OHISZPOJ.js +97 -0
- package/dist/chunk-OHISZPOJ.js.map +1 -0
- package/dist/{chunk-5DJTTKX3.js → chunk-PHOUUNK7.js} +74 -111
- package/dist/chunk-PHOUUNK7.js.map +1 -0
- package/dist/{chunk-PY4XCDLR.js → chunk-RB6QPHJ7.js} +8 -6
- package/dist/{chunk-PY4XCDLR.js.map → chunk-RB6QPHJ7.js.map} +1 -1
- package/dist/chunk-SN4YWWYO.js +171 -0
- package/dist/chunk-SN4YWWYO.js.map +1 -0
- package/dist/chunk-SX52TAR4.js +110 -0
- package/dist/chunk-SX52TAR4.js.map +1 -0
- package/dist/{chunk-XOFWRC73.js → chunk-THTWHNU4.js} +319 -24
- package/dist/chunk-THTWHNU4.js.map +1 -0
- package/dist/{chunk-H4RVA4VE.js → chunk-VYPWMZ6H.js} +2 -2
- package/dist/chunk-XGPU467M.js +136 -0
- package/dist/chunk-XGPU467M.js.map +1 -0
- package/dist/{chunk-TDEXAMGO.js → chunk-ZQMEI34O.js} +206 -574
- package/dist/chunk-ZQMEI34O.js.map +1 -0
- package/dist/compat/index.cjs +7656 -0
- package/dist/compat/index.cjs.map +1 -0
- package/dist/compat/index.d.cts +18 -0
- package/dist/compat/index.d.ts +18 -0
- package/dist/compat/index.js +49 -0
- package/dist/compat/index.js.map +1 -0
- package/dist/compat/jotai/index.cjs +2048 -0
- package/dist/compat/jotai/index.cjs.map +1 -0
- package/dist/compat/jotai/index.d.cts +2 -0
- package/dist/compat/jotai/index.d.ts +2 -0
- package/dist/compat/jotai/index.js +9 -0
- package/dist/compat/jotai/index.js.map +1 -0
- package/dist/compat/nanostores/index.cjs +2175 -0
- package/dist/compat/nanostores/index.cjs.map +1 -0
- package/dist/compat/nanostores/index.d.cts +2 -0
- package/dist/compat/nanostores/index.d.ts +2 -0
- package/dist/compat/nanostores/index.js +23 -0
- package/dist/compat/nanostores/index.js.map +1 -0
- package/dist/compat/nestjs/index.cjs +350 -16
- 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 +10 -9
- package/dist/compat/react/index.cjs +141 -0
- package/dist/compat/react/index.cjs.map +1 -0
- package/dist/compat/react/index.d.cts +2 -0
- package/dist/compat/react/index.d.ts +2 -0
- package/dist/compat/react/index.js +12 -0
- package/dist/compat/react/index.js.map +1 -0
- package/dist/compat/solid/index.cjs +128 -0
- package/dist/compat/solid/index.cjs.map +1 -0
- package/dist/compat/solid/index.d.cts +2 -0
- package/dist/compat/solid/index.d.ts +2 -0
- package/dist/compat/solid/index.js +12 -0
- package/dist/compat/solid/index.js.map +1 -0
- package/dist/compat/svelte/index.cjs +131 -0
- package/dist/compat/svelte/index.cjs.map +1 -0
- package/dist/compat/svelte/index.d.cts +2 -0
- package/dist/compat/svelte/index.d.ts +2 -0
- package/dist/compat/svelte/index.js +12 -0
- package/dist/compat/svelte/index.js.map +1 -0
- package/dist/compat/vue/index.cjs +146 -0
- package/dist/compat/vue/index.cjs.map +1 -0
- package/dist/compat/vue/index.d.cts +3 -0
- package/dist/compat/vue/index.d.ts +3 -0
- package/dist/compat/vue/index.js +12 -0
- package/dist/compat/vue/index.js.map +1 -0
- package/dist/compat/zustand/index.cjs +4931 -0
- package/dist/compat/zustand/index.cjs.map +1 -0
- package/dist/compat/zustand/index.d.cts +5 -0
- package/dist/compat/zustand/index.d.ts +5 -0
- package/dist/compat/zustand/index.js +12 -0
- package/dist/compat/zustand/index.js.map +1 -0
- package/dist/core/index.cjs +53 -4
- 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 +26 -24
- package/dist/demo-shell-26p5fVxn.d.cts +102 -0
- package/dist/demo-shell-DEp-nMTl.d.ts +102 -0
- package/dist/extra/index.cjs +290 -110
- package/dist/extra/index.cjs.map +1 -1
- package/dist/extra/index.d.cts +5 -4
- package/dist/extra/index.d.ts +5 -4
- package/dist/extra/index.js +8 -5
- package/dist/extra/sources.cjs +2486 -0
- package/dist/extra/sources.cjs.map +1 -0
- package/dist/extra/sources.d.cts +465 -0
- package/dist/extra/sources.d.ts +465 -0
- package/dist/extra/sources.js +57 -0
- package/dist/extra/sources.js.map +1 -0
- package/dist/graph/index.cjs +408 -14
- 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 +13 -5
- package/dist/{graph-D-3JIQme.d.cts → graph-6tZ5jEzr.d.cts} +195 -4
- package/dist/{graph-B6NFqv3z.d.ts → graph-DQ69XU0g.d.ts} +195 -4
- package/dist/index-B4MP_8V_.d.cts +37 -0
- package/dist/index-BEfE8H_G.d.cts +121 -0
- package/dist/{index-D7XgsUt7.d.ts → index-BW1z3BN9.d.ts} +169 -127
- package/dist/index-BYOHF0zP.d.ts +34 -0
- package/dist/index-B_IP40nB.d.cts +36 -0
- package/dist/index-Bd_fwmLf.d.cts +45 -0
- package/dist/{index-BysCTzJz.d.ts → index-BeIdBfcb.d.cts} +121 -547
- package/dist/index-BjI6ty9z.d.ts +121 -0
- package/dist/index-Bxb5ZYc9.d.cts +34 -0
- package/dist/{index-BJB7t9gg.d.cts → index-C0ZXMaXO.d.cts} +2 -2
- package/dist/{index-b5BYtczN.d.cts → index-C8mdwMXc.d.cts} +169 -127
- package/dist/index-CDAjUFIv.d.ts +36 -0
- package/dist/index-CPgZ5wRl.d.ts +44 -0
- package/dist/{index-AMWewNDe.d.cts → index-CUwyr1Kk.d.cts} +33 -4
- package/dist/index-CUyrtuOf.d.cts +127 -0
- package/dist/{index-C-TXEa7C.d.ts → index-CY2TljO4.d.ts} +2 -2
- package/dist/index-CmnuOibw.d.ts +37 -0
- package/dist/{index-DiobMNwE.d.ts → index-CuYwdKO-.d.ts} +3 -3
- package/dist/index-DFhjO4Gg.d.cts +44 -0
- package/dist/{index-1z8vRTCt.d.cts → index-DdD5MVDL.d.ts} +121 -547
- package/dist/index-DrISNAOm.d.ts +45 -0
- package/dist/index-QBpffFW-.d.cts +86 -0
- package/dist/{index-J7Kc0oIQ.d.cts → index-_oMEWlDq.d.cts} +3 -3
- package/dist/{index-CYkjxu3s.d.ts → index-eJ6T_qGM.d.ts} +33 -4
- package/dist/index-qldRdbQw.d.ts +86 -0
- package/dist/index-xdGjv0nO.d.ts +127 -0
- package/dist/index.cjs +2334 -195
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1007 -648
- package/dist/index.d.ts +1007 -648
- package/dist/index.js +1204 -1172
- package/dist/index.js.map +1 -1
- package/dist/{meta-CnkLA_43.d.ts → meta-BGqSZ7mt.d.ts} +1 -1
- package/dist/{meta-DWbkoq1s.d.cts → meta-C0-8XW6Q.d.cts} +1 -1
- package/dist/{node-B-f-Lu-k.d.cts → node-C_IBuvX2.d.cts} +26 -1
- package/dist/{node-B-f-Lu-k.d.ts → node-C_IBuvX2.d.ts} +26 -1
- package/dist/{observable-DBnrwcar.d.cts → observable-Crr1jgzx.d.cts} +1 -1
- package/dist/{observable-uP-wy_uK.d.ts → observable-DCk45RH5.d.ts} +1 -1
- package/dist/patterns/demo-shell.cjs +5604 -0
- package/dist/patterns/demo-shell.cjs.map +1 -0
- package/dist/patterns/demo-shell.d.cts +6 -0
- package/dist/patterns/demo-shell.d.ts +6 -0
- package/dist/patterns/demo-shell.js +15 -0
- package/dist/patterns/demo-shell.js.map +1 -0
- package/dist/patterns/reactive-layout/index.cjs +843 -29
- package/dist/patterns/reactive-layout/index.cjs.map +1 -1
- package/dist/patterns/reactive-layout/index.d.cts +6 -5
- package/dist/patterns/reactive-layout/index.d.ts +6 -5
- package/dist/patterns/reactive-layout/index.js +25 -10
- package/dist/reactive-layout-BaOQefHu.d.cts +183 -0
- package/dist/reactive-layout-D9gejYXE.d.ts +183 -0
- package/dist/{storage-BuTdpCI1.d.cts → storage-BMycWEh2.d.ts} +9 -1
- package/dist/{storage-F2X1U1x0.d.ts → storage-DiqWHzVI.d.cts} +9 -1
- package/package.json +32 -2
- package/dist/chunk-5DJTTKX3.js.map +0 -1
- package/dist/chunk-5WGT55R4.js.map +0 -1
- package/dist/chunk-AOCBDH4T.js.map +0 -1
- package/dist/chunk-MW4VAKAO.js +0 -47
- package/dist/chunk-MW4VAKAO.js.map +0 -1
- package/dist/chunk-TDEXAMGO.js.map +0 -1
- package/dist/chunk-XOFWRC73.js.map +0 -1
- /package/dist/{chunk-H4RVA4VE.js.map → chunk-VYPWMZ6H.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
|
|
|
@@ -232,7 +238,7 @@ var CanvasMeasureAdapter = class {
|
|
|
232
238
|
this.currentFont = font;
|
|
233
239
|
}
|
|
234
240
|
let width = ctx.measureText(text).width;
|
|
235
|
-
if (this.emojiCorrection !== 1 &&
|
|
241
|
+
if (this.emojiCorrection !== 1 && new RegExp("\\p{Emoji_Presentation}", "u").test(text)) {
|
|
236
242
|
width *= this.emojiCorrection;
|
|
237
243
|
}
|
|
238
244
|
return { width };
|
|
@@ -1159,6 +1165,12 @@ var NodeImpl = class _NodeImpl {
|
|
|
1159
1165
|
_autoError;
|
|
1160
1166
|
_pausable;
|
|
1161
1167
|
_guard;
|
|
1168
|
+
/**
|
|
1169
|
+
* @internal Additional guards stacked at runtime via {@link NodeImpl._pushGuard}
|
|
1170
|
+
* (e.g. by `policyEnforcer({ mode: "enforce" })`, roadmap §9.2). Effective
|
|
1171
|
+
* write/signal/observe checks AND the original `_guard` with every entry here.
|
|
1172
|
+
*/
|
|
1173
|
+
_extraGuards;
|
|
1162
1174
|
_hashFn;
|
|
1163
1175
|
_versioning;
|
|
1164
1176
|
/**
|
|
@@ -1332,18 +1344,61 @@ var NodeImpl = class _NodeImpl {
|
|
|
1332
1344
|
if (this._inspectorHooks?.size === 0) this._inspectorHooks = void 0;
|
|
1333
1345
|
};
|
|
1334
1346
|
}
|
|
1347
|
+
/**
|
|
1348
|
+
* @internal Push an additional guard onto this node. Effective enforcement
|
|
1349
|
+
* is the AND of `_guard` and every guard pushed via this hook — any one
|
|
1350
|
+
* rejecting throws {@link GuardDenied}. Returns a disposer that removes
|
|
1351
|
+
* the pushed guard. Multiple guards may be stacked simultaneously.
|
|
1352
|
+
*
|
|
1353
|
+
* Used by `policyEnforcer({ mode: "enforce" })` (roadmap §9.2) to overlay
|
|
1354
|
+
* runtime constraint enforcement onto an existing graph without rebuilding
|
|
1355
|
+
* its nodes. Pre-1.0 internal API; not part of the public surface.
|
|
1356
|
+
*
|
|
1357
|
+
* **Identity semantics:** guards are tracked in a `Set`, so pushing the
|
|
1358
|
+
* same `NodeGuard` reference twice is a single registration. Wrap each
|
|
1359
|
+
* push in a unique closure if independent stacking is needed.
|
|
1360
|
+
*
|
|
1361
|
+
* **Iteration order:** insertion-ordered (`Set` semantics). Determinism
|
|
1362
|
+
* follows from single-threaded JS execution; nested re-entry from inside
|
|
1363
|
+
* a guard body (push/pop while iterating) is undefined-but-survivable.
|
|
1364
|
+
*/
|
|
1365
|
+
_pushGuard(guard) {
|
|
1366
|
+
if (this._extraGuards == null) this._extraGuards = /* @__PURE__ */ new Set();
|
|
1367
|
+
this._extraGuards.add(guard);
|
|
1368
|
+
return () => {
|
|
1369
|
+
this._extraGuards?.delete(guard);
|
|
1370
|
+
if (this._extraGuards?.size === 0) this._extraGuards = void 0;
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1335
1373
|
allowsObserve(actor) {
|
|
1336
|
-
if (this._guard == null) return true;
|
|
1337
|
-
|
|
1374
|
+
if (this._guard == null && this._extraGuards == null) return true;
|
|
1375
|
+
const a = normalizeActor(actor);
|
|
1376
|
+
if (this._guard != null && !this._guard(a, "observe")) return false;
|
|
1377
|
+
if (this._extraGuards != null) {
|
|
1378
|
+
for (const eg of this._extraGuards) {
|
|
1379
|
+
if (!eg(a, "observe")) return false;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
return true;
|
|
1338
1383
|
}
|
|
1339
1384
|
// --- Guard helper ---
|
|
1340
1385
|
_checkGuard(options) {
|
|
1341
|
-
if (options?.internal
|
|
1386
|
+
if (options?.internal) return;
|
|
1387
|
+
const hasGuard = this._guard != null || this._extraGuards != null;
|
|
1388
|
+
const hasActor = options?.actor != null;
|
|
1389
|
+
if (!hasGuard && !hasActor) return;
|
|
1342
1390
|
const actor = normalizeActor(options?.actor);
|
|
1343
1391
|
const action = options?.delivery === "signal" ? "signal" : "write";
|
|
1344
|
-
if (!this._guard(actor, action)) {
|
|
1392
|
+
if (this._guard != null && !this._guard(actor, action)) {
|
|
1345
1393
|
throw new GuardDenied({ actor, action, nodeName: this.name });
|
|
1346
1394
|
}
|
|
1395
|
+
if (this._extraGuards != null) {
|
|
1396
|
+
for (const eg of this._extraGuards) {
|
|
1397
|
+
if (!eg(actor, action)) {
|
|
1398
|
+
throw new GuardDenied({ actor, action, nodeName: this.name });
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1347
1402
|
this._lastMutation = { actor, timestamp_ns: wallClockNs() };
|
|
1348
1403
|
}
|
|
1349
1404
|
// --- Public transport ---
|
|
@@ -2275,6 +2330,10 @@ function sentinelGuard(batchData, ctx, allowPartial) {
|
|
|
2275
2330
|
function state(initial, opts) {
|
|
2276
2331
|
return node([], { ...opts, initial });
|
|
2277
2332
|
}
|
|
2333
|
+
function producer(fn, opts) {
|
|
2334
|
+
const wrapped = (_data, actions, ctx) => fn(actions, ctx) ?? void 0;
|
|
2335
|
+
return node(wrapped, { describeKind: "producer", ...opts });
|
|
2336
|
+
}
|
|
2278
2337
|
function derived(deps, fn, opts) {
|
|
2279
2338
|
const allowPartial = opts?.partial ?? false;
|
|
2280
2339
|
const wrapped = (batchData, actions, ctx) => {
|
|
@@ -2477,6 +2536,200 @@ var RingBuffer = class {
|
|
|
2477
2536
|
}
|
|
2478
2537
|
};
|
|
2479
2538
|
|
|
2539
|
+
// src/graph/explain.ts
|
|
2540
|
+
function explainPath(described, from, to, opts = {}) {
|
|
2541
|
+
const fromExists = from in described.nodes;
|
|
2542
|
+
const toExists = to in described.nodes;
|
|
2543
|
+
if (!fromExists) return makeFailure(from, to, "no-such-from");
|
|
2544
|
+
if (!toExists) return makeFailure(from, to, "no-such-to");
|
|
2545
|
+
const maxDepth = opts.maxDepth;
|
|
2546
|
+
if (maxDepth != null && (!Number.isInteger(maxDepth) || maxDepth < 0)) {
|
|
2547
|
+
throw new Error(`explainPath: maxDepth must be an integer >= 0`);
|
|
2548
|
+
}
|
|
2549
|
+
if (from === to) {
|
|
2550
|
+
if (opts.findCycle === true) {
|
|
2551
|
+
const cycle = findShortestCycle(described, from, opts);
|
|
2552
|
+
if (cycle != null) return cycle;
|
|
2553
|
+
}
|
|
2554
|
+
const step = buildStep(from, described.nodes[from], 0, opts);
|
|
2555
|
+
return makeSuccess(from, to, [step]);
|
|
2556
|
+
}
|
|
2557
|
+
if (maxDepth === 0) return makeFailure(from, to, "no-path");
|
|
2558
|
+
const result = bfsShortestPath(described, from, to, maxDepth);
|
|
2559
|
+
if (!result.found) {
|
|
2560
|
+
return makeFailure(from, to, result.truncated ? "max-depth-exceeded" : "no-path");
|
|
2561
|
+
}
|
|
2562
|
+
return makeSuccess(from, to, materializeSteps(described, result.pathOrder, opts));
|
|
2563
|
+
}
|
|
2564
|
+
function bfsShortestPath(described, from, to, maxDepth) {
|
|
2565
|
+
const pred = /* @__PURE__ */ new Map();
|
|
2566
|
+
const queue = [{ path: to, depth: 0 }];
|
|
2567
|
+
const visited = /* @__PURE__ */ new Set([to]);
|
|
2568
|
+
let head = 0;
|
|
2569
|
+
let truncated = false;
|
|
2570
|
+
while (head < queue.length) {
|
|
2571
|
+
const cur = queue[head++];
|
|
2572
|
+
if (cur.path === from) break;
|
|
2573
|
+
if (maxDepth != null && cur.depth >= maxDepth) {
|
|
2574
|
+
const node3 = described.nodes[cur.path];
|
|
2575
|
+
if (node3?.deps && node3.deps.length > 0) truncated = true;
|
|
2576
|
+
continue;
|
|
2577
|
+
}
|
|
2578
|
+
const node2 = described.nodes[cur.path];
|
|
2579
|
+
if (node2 == null) continue;
|
|
2580
|
+
const deps = node2.deps ?? [];
|
|
2581
|
+
const slots = /* @__PURE__ */ new Map();
|
|
2582
|
+
for (let i = 0; i < deps.length; i++) {
|
|
2583
|
+
const dep = deps[i];
|
|
2584
|
+
if (!dep) continue;
|
|
2585
|
+
let arr = slots.get(dep);
|
|
2586
|
+
if (arr == null) {
|
|
2587
|
+
arr = [];
|
|
2588
|
+
slots.set(dep, arr);
|
|
2589
|
+
}
|
|
2590
|
+
arr.push(i);
|
|
2591
|
+
}
|
|
2592
|
+
for (const [dep, indices] of slots) {
|
|
2593
|
+
if (visited.has(dep)) continue;
|
|
2594
|
+
visited.add(dep);
|
|
2595
|
+
pred.set(dep, { from: cur.path, depIndices: indices });
|
|
2596
|
+
queue.push({ path: dep, depth: cur.depth + 1 });
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
if (!pred.has(from)) {
|
|
2600
|
+
return { found: false, pathOrder: [], truncated };
|
|
2601
|
+
}
|
|
2602
|
+
const pathOrder = [{ path: from }];
|
|
2603
|
+
let cursor = from;
|
|
2604
|
+
while (cursor !== to) {
|
|
2605
|
+
const p = pred.get(cursor);
|
|
2606
|
+
if (p == null) return { found: false, pathOrder: [], truncated: false };
|
|
2607
|
+
pathOrder[pathOrder.length - 1].depIndices = p.depIndices;
|
|
2608
|
+
pathOrder.push({ path: p.from });
|
|
2609
|
+
cursor = p.from;
|
|
2610
|
+
}
|
|
2611
|
+
return { found: true, pathOrder, truncated: false };
|
|
2612
|
+
}
|
|
2613
|
+
function findShortestCycle(described, start, opts) {
|
|
2614
|
+
const startNode = described.nodes[start];
|
|
2615
|
+
if (startNode == null) return null;
|
|
2616
|
+
const startDeps = startNode.deps ?? [];
|
|
2617
|
+
const selfSlots = [];
|
|
2618
|
+
for (let i = 0; i < startDeps.length; i++) if (startDeps[i] === start) selfSlots.push(i);
|
|
2619
|
+
if (selfSlots.length > 0) {
|
|
2620
|
+
const step0 = buildStep(start, startNode, 0, opts);
|
|
2621
|
+
step0.dep_index = selfSlots[0];
|
|
2622
|
+
const step1 = buildStep(start, startNode, 1, opts);
|
|
2623
|
+
return makeSuccess(start, start, [step0, step1]);
|
|
2624
|
+
}
|
|
2625
|
+
let best = null;
|
|
2626
|
+
for (let i = 0; i < startDeps.length; i++) {
|
|
2627
|
+
const dep = startDeps[i];
|
|
2628
|
+
if (!dep || dep === start) continue;
|
|
2629
|
+
const sub = bfsShortestPath(described, dep, start, opts.maxDepth);
|
|
2630
|
+
if (!sub.found) continue;
|
|
2631
|
+
if (best == null || sub.pathOrder.length < best.pathOrder.length) {
|
|
2632
|
+
best = sub;
|
|
2633
|
+
best = {
|
|
2634
|
+
found: true,
|
|
2635
|
+
pathOrder: [{ path: start, depIndices: [i] }, ...sub.pathOrder],
|
|
2636
|
+
truncated: false
|
|
2637
|
+
};
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
if (best == null) return null;
|
|
2641
|
+
return makeSuccess(start, start, materializeSteps(described, best.pathOrder, opts));
|
|
2642
|
+
}
|
|
2643
|
+
function materializeSteps(described, pathOrder, opts) {
|
|
2644
|
+
return pathOrder.map((entry, i) => {
|
|
2645
|
+
const node2 = described.nodes[entry.path];
|
|
2646
|
+
const step = buildStep(entry.path, node2, i, opts);
|
|
2647
|
+
if (entry.depIndices != null && entry.depIndices.length > 0) {
|
|
2648
|
+
step.dep_index = entry.depIndices[0];
|
|
2649
|
+
if (entry.depIndices.length > 1) step.dep_indices = [...entry.depIndices];
|
|
2650
|
+
}
|
|
2651
|
+
return step;
|
|
2652
|
+
});
|
|
2653
|
+
}
|
|
2654
|
+
function buildStep(path, node2, hop, opts) {
|
|
2655
|
+
const step = {
|
|
2656
|
+
path,
|
|
2657
|
+
type: node2.type,
|
|
2658
|
+
hop
|
|
2659
|
+
};
|
|
2660
|
+
if (node2.status !== void 0) step.status = node2.status;
|
|
2661
|
+
if ("value" in node2) step.value = node2.value;
|
|
2662
|
+
if (node2.v != null) step.v = node2.v;
|
|
2663
|
+
const annotation = opts.annotations?.get(path) ?? node2.reason;
|
|
2664
|
+
if (annotation != null) step.reason = annotation;
|
|
2665
|
+
const lastMutation = opts.lastMutations?.get(path) ?? node2.lastMutation;
|
|
2666
|
+
if (lastMutation != null) step.lastMutation = lastMutation;
|
|
2667
|
+
return step;
|
|
2668
|
+
}
|
|
2669
|
+
function makeSuccess(from, to, steps) {
|
|
2670
|
+
return finalize(from, to, true, "ok", steps);
|
|
2671
|
+
}
|
|
2672
|
+
function makeFailure(from, to, reason) {
|
|
2673
|
+
return finalize(from, to, false, reason, []);
|
|
2674
|
+
}
|
|
2675
|
+
function finalize(from, to, found, reason, steps) {
|
|
2676
|
+
const text = renderChain(from, to, found, reason, steps);
|
|
2677
|
+
return {
|
|
2678
|
+
from,
|
|
2679
|
+
to,
|
|
2680
|
+
found,
|
|
2681
|
+
reason,
|
|
2682
|
+
steps,
|
|
2683
|
+
text,
|
|
2684
|
+
toJSON() {
|
|
2685
|
+
return { from, to, found, reason, steps };
|
|
2686
|
+
}
|
|
2687
|
+
};
|
|
2688
|
+
}
|
|
2689
|
+
function renderChain(from, to, found, reason, steps) {
|
|
2690
|
+
if (!found) {
|
|
2691
|
+
switch (reason) {
|
|
2692
|
+
case "no-such-from":
|
|
2693
|
+
return `explainPath: no node named "${from}"`;
|
|
2694
|
+
case "no-such-to":
|
|
2695
|
+
return `explainPath: no node named "${to}"`;
|
|
2696
|
+
case "max-depth-exceeded":
|
|
2697
|
+
return `explainPath: no path from "${from}" to "${to}" within maxDepth`;
|
|
2698
|
+
default:
|
|
2699
|
+
return `explainPath: no path from "${from}" to "${to}"`;
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
const lines = [`Causal path: ${from} \u2192 ${to} (${steps.length} step(s))`];
|
|
2703
|
+
for (const step of steps) {
|
|
2704
|
+
const arrow = step.hop === 0 ? "\xB7" : "\u2193";
|
|
2705
|
+
const head = ` ${arrow} ${step.path} (${step.type}${step.status ? `/${step.status}` : ""})`;
|
|
2706
|
+
lines.push(head);
|
|
2707
|
+
if ("value" in step) {
|
|
2708
|
+
lines.push(` value: ${formatValue(step.value)}`);
|
|
2709
|
+
}
|
|
2710
|
+
if (step.reason != null) {
|
|
2711
|
+
lines.push(` reason: ${step.reason}`);
|
|
2712
|
+
}
|
|
2713
|
+
if (step.lastMutation != null) {
|
|
2714
|
+
const a = step.lastMutation.actor;
|
|
2715
|
+
lines.push(` actor: ${a.type}${a.id ? `:${a.id}` : ""}`);
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
return lines.join("\n");
|
|
2719
|
+
}
|
|
2720
|
+
function formatValue(v) {
|
|
2721
|
+
if (v === void 0) return "<sentinel>";
|
|
2722
|
+
if (v === null) return "null";
|
|
2723
|
+
if (typeof v === "string") return JSON.stringify(v);
|
|
2724
|
+
if (typeof v === "number" || typeof v === "boolean" || typeof v === "bigint") return String(v);
|
|
2725
|
+
try {
|
|
2726
|
+
const s = JSON.stringify(v);
|
|
2727
|
+
return s.length > 80 ? `${s.slice(0, 77)}...` : s;
|
|
2728
|
+
} catch {
|
|
2729
|
+
return String(v);
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2480
2733
|
// src/extra/utils/sizeof.ts
|
|
2481
2734
|
var OVERHEAD = {
|
|
2482
2735
|
object: 56,
|
|
@@ -3110,6 +3363,20 @@ var Graph = class _Graph {
|
|
|
3110
3363
|
_parent = void 0;
|
|
3111
3364
|
_storageDisposers = /* @__PURE__ */ new Set();
|
|
3112
3365
|
_disposers = /* @__PURE__ */ new Set();
|
|
3366
|
+
/**
|
|
3367
|
+
* @internal Lazy `TopologyEvent` producer. Created on first `.topology`
|
|
3368
|
+
* access. Zero cost until something subscribes — producer fn only runs when
|
|
3369
|
+
* the first sink attaches, registering one handler into
|
|
3370
|
+
* {@link Graph._topologyEmitters}.
|
|
3371
|
+
*/
|
|
3372
|
+
_topology;
|
|
3373
|
+
/**
|
|
3374
|
+
* @internal Active emit handlers for the topology producer. Each entry is
|
|
3375
|
+
* the closure registered by the producer fn on activation; cleared on
|
|
3376
|
+
* deactivation. `_emitTopology` broadcasts through every entry (there is at
|
|
3377
|
+
* most one per activation cycle of the producer).
|
|
3378
|
+
*/
|
|
3379
|
+
_topologyEmitters = /* @__PURE__ */ new Set();
|
|
3113
3380
|
/**
|
|
3114
3381
|
* @param name - Non-empty graph id (must not contain `::` and must not
|
|
3115
3382
|
* equal the reserved meta segment `__meta__`).
|
|
@@ -3149,6 +3416,55 @@ var Graph = class _Graph {
|
|
|
3149
3416
|
return out;
|
|
3150
3417
|
}
|
|
3151
3418
|
// ——————————————————————————————————————————————————————————————
|
|
3419
|
+
// Topology companion (structural-change event stream)
|
|
3420
|
+
// ——————————————————————————————————————————————————————————————
|
|
3421
|
+
/**
|
|
3422
|
+
* Reactive stream of structural changes to this graph's own registry
|
|
3423
|
+
* (add / mount / remove). Value mutations live on `observe()`; this
|
|
3424
|
+
* companion only fires when the topology shape changes.
|
|
3425
|
+
*
|
|
3426
|
+
* Lazy: the underlying node is created on first access and activates when
|
|
3427
|
+
* something subscribes. No emission replay — late subscribers do not
|
|
3428
|
+
* receive historical events and should snapshot via {@link Graph.describe}
|
|
3429
|
+
* before listening for incremental changes. Events that fire while the
|
|
3430
|
+
* producer has zero subscribers are dropped (no retention).
|
|
3431
|
+
*
|
|
3432
|
+
* Own-graph only: a parent's `topology` does NOT emit for structural
|
|
3433
|
+
* changes inside a mounted child. Transitive consumers subscribe to each
|
|
3434
|
+
* child's topology separately (recurse through `topology`'s own "added"
|
|
3435
|
+
* events with `nodeKind: "mount"` to discover new children).
|
|
3436
|
+
*
|
|
3437
|
+
* See {@link TopologyEvent} for payload shape.
|
|
3438
|
+
*
|
|
3439
|
+
* @category observability
|
|
3440
|
+
*/
|
|
3441
|
+
get topology() {
|
|
3442
|
+
if (this._topology == null) {
|
|
3443
|
+
this._topology = producer(
|
|
3444
|
+
(actions) => {
|
|
3445
|
+
const handler = (event) => {
|
|
3446
|
+
actions.emit(event);
|
|
3447
|
+
};
|
|
3448
|
+
this._topologyEmitters.add(handler);
|
|
3449
|
+
return () => {
|
|
3450
|
+
this._topologyEmitters.delete(handler);
|
|
3451
|
+
};
|
|
3452
|
+
},
|
|
3453
|
+
{ name: `${this.name}_topology` }
|
|
3454
|
+
);
|
|
3455
|
+
}
|
|
3456
|
+
return this._topology;
|
|
3457
|
+
}
|
|
3458
|
+
/**
|
|
3459
|
+
* @internal Fire a {@link TopologyEvent} to every active subscriber of
|
|
3460
|
+
* `this.topology`. No-op when the topology node has never been accessed or
|
|
3461
|
+
* currently has no sinks — zero cost for graphs nobody observes.
|
|
3462
|
+
*/
|
|
3463
|
+
_emitTopology(event) {
|
|
3464
|
+
if (this._topology == null || this._topologyEmitters.size === 0) return;
|
|
3465
|
+
for (const h of this._topologyEmitters) h(event);
|
|
3466
|
+
}
|
|
3467
|
+
// ——————————————————————————————————————————————————————————————
|
|
3152
3468
|
// Node registry
|
|
3153
3469
|
// ——————————————————————————————————————————————————————————————
|
|
3154
3470
|
/**
|
|
@@ -3178,6 +3494,7 @@ var Graph = class _Graph {
|
|
|
3178
3494
|
}
|
|
3179
3495
|
this._nodes.set(name, node2);
|
|
3180
3496
|
this._nodeToName.set(node2, name);
|
|
3497
|
+
this._emitTopology({ kind: "added", name, nodeKind: "node" });
|
|
3181
3498
|
return node2;
|
|
3182
3499
|
}
|
|
3183
3500
|
/**
|
|
@@ -3218,22 +3535,23 @@ var Graph = class _Graph {
|
|
|
3218
3535
|
assertRegisterableName(name, this.name, "remove");
|
|
3219
3536
|
const child = this._mounts.get(name);
|
|
3220
3537
|
if (child) {
|
|
3221
|
-
const
|
|
3538
|
+
const audit2 = { kind: "mount", nodes: [], mounts: [] };
|
|
3222
3539
|
const targets = [];
|
|
3223
3540
|
child._collectObserveTargets("", targets);
|
|
3224
3541
|
for (const [p, n] of targets) {
|
|
3225
3542
|
if (!p.includes(`${PATH_SEP}${GRAPH_META_SEGMENT}${PATH_SEP}`)) {
|
|
3226
|
-
|
|
3543
|
+
audit2.nodes.push(p);
|
|
3227
3544
|
}
|
|
3228
3545
|
void n;
|
|
3229
3546
|
}
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3547
|
+
audit2.nodes.sort();
|
|
3548
|
+
audit2.mounts.push(name);
|
|
3549
|
+
audit2.mounts.push(...child._collectSubgraphs(`${name}${PATH_SEP}`));
|
|
3233
3550
|
this._mounts.delete(name);
|
|
3234
3551
|
child._parent = void 0;
|
|
3235
3552
|
teardownMountedGraph(child);
|
|
3236
|
-
|
|
3553
|
+
this._emitTopology({ kind: "removed", name, nodeKind: "mount", audit: audit2 });
|
|
3554
|
+
return audit2;
|
|
3237
3555
|
}
|
|
3238
3556
|
const node2 = this._nodes.get(name);
|
|
3239
3557
|
if (!node2) {
|
|
@@ -3242,7 +3560,9 @@ var Graph = class _Graph {
|
|
|
3242
3560
|
this._nodes.delete(name);
|
|
3243
3561
|
this._nodeToName.delete(node2);
|
|
3244
3562
|
node2.down([[TEARDOWN]], { internal: true });
|
|
3245
|
-
|
|
3563
|
+
const audit = { kind: "node", nodes: [name], mounts: [] };
|
|
3564
|
+
this._emitTopology({ kind: "removed", name, nodeKind: "node", audit });
|
|
3565
|
+
return audit;
|
|
3246
3566
|
}
|
|
3247
3567
|
/**
|
|
3248
3568
|
* Bulk remove — invokes {@link Graph.remove} for every local name matching
|
|
@@ -3471,6 +3791,7 @@ var Graph = class _Graph {
|
|
|
3471
3791
|
}
|
|
3472
3792
|
this._mounts.set(name, child);
|
|
3473
3793
|
child._parent = this;
|
|
3794
|
+
this._emitTopology({ kind: "added", name, nodeKind: "mount" });
|
|
3474
3795
|
return child;
|
|
3475
3796
|
}
|
|
3476
3797
|
/**
|
|
@@ -3761,6 +4082,33 @@ var Graph = class _Graph {
|
|
|
3761
4082
|
}
|
|
3762
4083
|
return reachable(this.describe(), from, direction, opts);
|
|
3763
4084
|
}
|
|
4085
|
+
/**
|
|
4086
|
+
* Causal walkback: shortest dep-chain from `from` to `to`, enriched with
|
|
4087
|
+
* each node's value, status, last-mutation actor, and reasoning annotation
|
|
4088
|
+
* from {@link Graph.trace}. Wraps {@link explainPath} (roadmap §9.2).
|
|
4089
|
+
*
|
|
4090
|
+
* @param from - Upstream node (the cause).
|
|
4091
|
+
* @param to - Downstream node (the effect).
|
|
4092
|
+
* @param opts - Optional `maxDepth` and `findCycle`. When `findCycle:true`
|
|
4093
|
+
* and `from === to`, returns the shortest cycle through other nodes
|
|
4094
|
+
* (useful for diagnosing feedback loops, COMPOSITION-GUIDE §7).
|
|
4095
|
+
* Annotations and lastMutations are collected automatically from the
|
|
4096
|
+
* live graph.
|
|
4097
|
+
*/
|
|
4098
|
+
explain(from, to, opts) {
|
|
4099
|
+
const described = this.describe({ detail: "full" });
|
|
4100
|
+
const annotations = new Map(this._annotations);
|
|
4101
|
+
const lastMutations = /* @__PURE__ */ new Map();
|
|
4102
|
+
for (const [path, n] of Object.entries(described.nodes)) {
|
|
4103
|
+
if (n.lastMutation != null) lastMutations.set(path, n.lastMutation);
|
|
4104
|
+
}
|
|
4105
|
+
return explainPath(described, from, to, {
|
|
4106
|
+
...opts?.maxDepth != null ? { maxDepth: opts.maxDepth } : {},
|
|
4107
|
+
...opts?.findCycle === true ? { findCycle: true } : {},
|
|
4108
|
+
annotations,
|
|
4109
|
+
lastMutations
|
|
4110
|
+
});
|
|
4111
|
+
}
|
|
3764
4112
|
/**
|
|
3765
4113
|
* @internal Collect all qualified paths in this graph tree matching a
|
|
3766
4114
|
* glob pattern. Used by scoped autoCheckpoint subscription.
|
|
@@ -4439,7 +4787,7 @@ var Graph = class _Graph {
|
|
|
4439
4787
|
return;
|
|
4440
4788
|
}
|
|
4441
4789
|
const nextSeq = s.seq + 1;
|
|
4442
|
-
const timestamp_ns =
|
|
4790
|
+
const timestamp_ns = wallClockNs();
|
|
4443
4791
|
const isFirst = s.lastSnapshot == null;
|
|
4444
4792
|
const shouldCompact = isFirst || nextSeq % s.compactEvery === 0;
|
|
4445
4793
|
const record = shouldCompact ? {
|
|
@@ -4958,7 +5306,7 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
|
|
|
4958
5306
|
const normalized = normalizeWhitespace(text);
|
|
4959
5307
|
if (normalized.length === 0) return [];
|
|
4960
5308
|
const pieces = segmentText(normalized);
|
|
4961
|
-
const
|
|
5309
|
+
const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
|
|
4962
5310
|
granularity: "grapheme"
|
|
4963
5311
|
});
|
|
4964
5312
|
const rawTexts = [];
|
|
@@ -5002,7 +5350,8 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
|
|
|
5002
5350
|
let w = fontCache.get(seg);
|
|
5003
5351
|
if (w === void 0) {
|
|
5004
5352
|
if (stats) stats.misses += 1;
|
|
5005
|
-
|
|
5353
|
+
const raw = adapter.measureSegment(seg, font).width;
|
|
5354
|
+
w = Number.isFinite(raw) && raw >= 0 ? raw : 0;
|
|
5006
5355
|
fontCache.set(seg, w);
|
|
5007
5356
|
} else if (stats) {
|
|
5008
5357
|
stats.hits += 1;
|
|
@@ -5024,7 +5373,7 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
|
|
|
5024
5373
|
}
|
|
5025
5374
|
if (isCJK(t)) {
|
|
5026
5375
|
let unitText = "";
|
|
5027
|
-
for (const gs of
|
|
5376
|
+
for (const gs of graphemeSegmenter2.segment(t)) {
|
|
5028
5377
|
const grapheme = gs.segment;
|
|
5029
5378
|
if (unitText.length > 0 && kinsokuStart.has(grapheme)) {
|
|
5030
5379
|
unitText += grapheme;
|
|
@@ -5056,7 +5405,7 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
|
|
|
5056
5405
|
let graphemeWidths = null;
|
|
5057
5406
|
if (mergedWordLike[i] && t.length > 1) {
|
|
5058
5407
|
const gWidths = [];
|
|
5059
|
-
for (const gs of
|
|
5408
|
+
for (const gs of graphemeSegmenter2.segment(t)) {
|
|
5060
5409
|
gWidths.push(measureCached(gs.segment));
|
|
5061
5410
|
}
|
|
5062
5411
|
if (gWidths.length > 1) {
|
|
@@ -5096,10 +5445,10 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5096
5445
|
const seg = segments[i];
|
|
5097
5446
|
if (seg.kind === "soft-hyphen" || seg.kind === "hard-break") continue;
|
|
5098
5447
|
if (i === lineStartSeg && lineStartGrapheme > 0 && seg.graphemeWidths) {
|
|
5099
|
-
const
|
|
5448
|
+
const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
|
|
5100
5449
|
granularity: "grapheme"
|
|
5101
5450
|
});
|
|
5102
|
-
const graphemes = [...
|
|
5451
|
+
const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
|
|
5103
5452
|
text += graphemes.slice(lineStartGrapheme).join("");
|
|
5104
5453
|
} else {
|
|
5105
5454
|
text += seg.text;
|
|
@@ -5107,10 +5456,10 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5107
5456
|
}
|
|
5108
5457
|
if (endGrapheme > 0 && endSeg < segments.length) {
|
|
5109
5458
|
const seg = segments[endSeg];
|
|
5110
|
-
const
|
|
5459
|
+
const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
|
|
5111
5460
|
granularity: "grapheme"
|
|
5112
5461
|
});
|
|
5113
|
-
const graphemes = [...
|
|
5462
|
+
const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
|
|
5114
5463
|
const startG = lineStartSeg === endSeg ? lineStartGrapheme : 0;
|
|
5115
5464
|
text += graphemes.slice(startG, endGrapheme).join("");
|
|
5116
5465
|
}
|
|
@@ -5130,7 +5479,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5130
5479
|
pendingBreakSeg = -1;
|
|
5131
5480
|
pendingBreakWidth = 0;
|
|
5132
5481
|
}
|
|
5133
|
-
function
|
|
5482
|
+
function canBreakAfter2(kind) {
|
|
5134
5483
|
return kind === "space" || kind === "zero-width-break" || kind === "soft-hyphen";
|
|
5135
5484
|
}
|
|
5136
5485
|
function startLine(segIdx, graphemeIdx, width) {
|
|
@@ -5175,7 +5524,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5175
5524
|
} else {
|
|
5176
5525
|
startLine(i, 0, w);
|
|
5177
5526
|
}
|
|
5178
|
-
if (
|
|
5527
|
+
if (canBreakAfter2(seg.kind)) {
|
|
5179
5528
|
pendingBreakSeg = i + 1;
|
|
5180
5529
|
pendingBreakWidth = seg.kind === "space" ? lineW - w : lineW;
|
|
5181
5530
|
}
|
|
@@ -5183,7 +5532,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5183
5532
|
}
|
|
5184
5533
|
const newW = lineW + w;
|
|
5185
5534
|
if (newW > maxWidth + 5e-3) {
|
|
5186
|
-
if (
|
|
5535
|
+
if (canBreakAfter2(seg.kind)) {
|
|
5187
5536
|
lineW += w;
|
|
5188
5537
|
lineEndSeg = i + 1;
|
|
5189
5538
|
lineEndGrapheme = 0;
|
|
@@ -5207,7 +5556,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5207
5556
|
lineW = newW;
|
|
5208
5557
|
lineEndSeg = i + 1;
|
|
5209
5558
|
lineEndGrapheme = 0;
|
|
5210
|
-
if (
|
|
5559
|
+
if (canBreakAfter2(seg.kind)) {
|
|
5211
5560
|
pendingBreakSeg = i + 1;
|
|
5212
5561
|
pendingBreakWidth = seg.kind === "space" ? lineW - w : lineW;
|
|
5213
5562
|
}
|
|
@@ -5238,9 +5587,289 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
5238
5587
|
}
|
|
5239
5588
|
}
|
|
5240
5589
|
}
|
|
5590
|
+
function canBreakAfter(kind) {
|
|
5591
|
+
return kind === "space" || kind === "zero-width-break" || kind === "soft-hyphen";
|
|
5592
|
+
}
|
|
5593
|
+
var _graphemeSegmenter = null;
|
|
5594
|
+
function graphemeSegmenter() {
|
|
5595
|
+
if (_graphemeSegmenter === null) {
|
|
5596
|
+
_graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
5597
|
+
}
|
|
5598
|
+
return _graphemeSegmenter;
|
|
5599
|
+
}
|
|
5600
|
+
function sliceSegmentText(seg, startG, endG) {
|
|
5601
|
+
if (startG === 0 && endG < 0) return seg.text;
|
|
5602
|
+
const graphemes = [...graphemeSegmenter().segment(seg.text)].map((g) => g.segment);
|
|
5603
|
+
const stop = endG < 0 ? graphemes.length : endG;
|
|
5604
|
+
return graphemes.slice(startG, stop).join("");
|
|
5605
|
+
}
|
|
5606
|
+
function buildLineText(segments, startSeg, startG, endSeg, endG, appendHyphen) {
|
|
5607
|
+
let text = "";
|
|
5608
|
+
for (let i = startSeg; i < endSeg; i++) {
|
|
5609
|
+
const seg = segments[i];
|
|
5610
|
+
if (seg.kind === "soft-hyphen" || seg.kind === "hard-break") continue;
|
|
5611
|
+
if (i === startSeg && startG > 0) {
|
|
5612
|
+
text += sliceSegmentText(seg, startG, -1);
|
|
5613
|
+
} else {
|
|
5614
|
+
text += seg.text;
|
|
5615
|
+
}
|
|
5616
|
+
}
|
|
5617
|
+
if (endG > 0 && endSeg < segments.length) {
|
|
5618
|
+
const seg = segments[endSeg];
|
|
5619
|
+
const from = startSeg === endSeg ? startG : 0;
|
|
5620
|
+
text += sliceSegmentText(seg, from, endG);
|
|
5621
|
+
}
|
|
5622
|
+
if (appendHyphen) text += "-";
|
|
5623
|
+
return text;
|
|
5624
|
+
}
|
|
5625
|
+
function resolveHyphenWidth(ctx) {
|
|
5626
|
+
if (!ctx || !ctx.adapter || !ctx.font) return 0;
|
|
5627
|
+
const cache = ctx.cache;
|
|
5628
|
+
if (cache) {
|
|
5629
|
+
let fc = cache.get(ctx.font);
|
|
5630
|
+
if (!fc) {
|
|
5631
|
+
fc = /* @__PURE__ */ new Map();
|
|
5632
|
+
cache.set(ctx.font, fc);
|
|
5633
|
+
}
|
|
5634
|
+
let hw = fc.get("-");
|
|
5635
|
+
if (hw === void 0) {
|
|
5636
|
+
hw = ctx.adapter.measureSegment("-", ctx.font).width;
|
|
5637
|
+
fc.set("-", hw);
|
|
5638
|
+
}
|
|
5639
|
+
return hw;
|
|
5640
|
+
}
|
|
5641
|
+
return ctx.adapter.measureSegment("-", ctx.font).width;
|
|
5642
|
+
}
|
|
5643
|
+
function layoutNextLine(segments, cursor, slotWidth, ctx) {
|
|
5644
|
+
let i = cursor.segmentIndex;
|
|
5645
|
+
const initialG = cursor.graphemeIndex;
|
|
5646
|
+
if (i >= segments.length) return null;
|
|
5647
|
+
if (initialG === 0) {
|
|
5648
|
+
while (i < segments.length) {
|
|
5649
|
+
const seg = segments[i];
|
|
5650
|
+
if (seg.kind === "hard-break") {
|
|
5651
|
+
return {
|
|
5652
|
+
text: "",
|
|
5653
|
+
width: 0,
|
|
5654
|
+
start: { segmentIndex: cursor.segmentIndex, graphemeIndex: 0 },
|
|
5655
|
+
end: { segmentIndex: i + 1, graphemeIndex: 0 }
|
|
5656
|
+
};
|
|
5657
|
+
}
|
|
5658
|
+
if (seg.kind === "space" || seg.kind === "zero-width-break" || seg.kind === "soft-hyphen") {
|
|
5659
|
+
i += 1;
|
|
5660
|
+
continue;
|
|
5661
|
+
}
|
|
5662
|
+
break;
|
|
5663
|
+
}
|
|
5664
|
+
if (i >= segments.length) return null;
|
|
5665
|
+
}
|
|
5666
|
+
const hyphenWidth = resolveHyphenWidth(ctx);
|
|
5667
|
+
const startSeg = i;
|
|
5668
|
+
const startG = i === cursor.segmentIndex ? initialG : 0;
|
|
5669
|
+
let lineW = 0;
|
|
5670
|
+
let lineEndSeg = startSeg;
|
|
5671
|
+
let lineEndG = 0;
|
|
5672
|
+
let hasContent = false;
|
|
5673
|
+
let pendingBreakSeg = -1;
|
|
5674
|
+
let pendingBreakG = 0;
|
|
5675
|
+
let pendingBreakWidth = 0;
|
|
5676
|
+
let pendingBreakSoftHyphen = false;
|
|
5677
|
+
const recordPending = (sIdx, gIdx, widthAtBreak, kind) => {
|
|
5678
|
+
pendingBreakSeg = sIdx;
|
|
5679
|
+
pendingBreakG = gIdx;
|
|
5680
|
+
pendingBreakWidth = widthAtBreak;
|
|
5681
|
+
pendingBreakSoftHyphen = kind === "soft-hyphen";
|
|
5682
|
+
};
|
|
5683
|
+
const consumeBreakable = (segIdx, gStart, gWidths) => {
|
|
5684
|
+
for (let g = gStart; g < gWidths.length; g++) {
|
|
5685
|
+
const gw = gWidths[g];
|
|
5686
|
+
if (!hasContent) {
|
|
5687
|
+
lineW = gw;
|
|
5688
|
+
lineEndSeg = segIdx;
|
|
5689
|
+
lineEndG = g + 1;
|
|
5690
|
+
hasContent = true;
|
|
5691
|
+
continue;
|
|
5692
|
+
}
|
|
5693
|
+
if (lineW + gw > slotWidth + 5e-3) {
|
|
5694
|
+
return true;
|
|
5695
|
+
}
|
|
5696
|
+
lineW += gw;
|
|
5697
|
+
lineEndSeg = segIdx;
|
|
5698
|
+
lineEndG = g + 1;
|
|
5699
|
+
}
|
|
5700
|
+
if (lineEndSeg === segIdx && lineEndG === gWidths.length) {
|
|
5701
|
+
lineEndSeg = segIdx + 1;
|
|
5702
|
+
lineEndG = 0;
|
|
5703
|
+
}
|
|
5704
|
+
return false;
|
|
5705
|
+
};
|
|
5706
|
+
if (startG > 0 && startSeg < segments.length) {
|
|
5707
|
+
const seg = segments[startSeg];
|
|
5708
|
+
if (seg.graphemeWidths) {
|
|
5709
|
+
const overflowed = consumeBreakable(startSeg, startG, seg.graphemeWidths);
|
|
5710
|
+
if (overflowed) {
|
|
5711
|
+
const text2 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
|
|
5712
|
+
return {
|
|
5713
|
+
text: text2,
|
|
5714
|
+
width: lineW,
|
|
5715
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
5716
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
5717
|
+
};
|
|
5718
|
+
}
|
|
5719
|
+
i = lineEndSeg;
|
|
5720
|
+
} else {
|
|
5721
|
+
}
|
|
5722
|
+
}
|
|
5723
|
+
for (; i < segments.length; ) {
|
|
5724
|
+
const seg = segments[i];
|
|
5725
|
+
if (seg.kind === "hard-break") {
|
|
5726
|
+
if (hasContent) {
|
|
5727
|
+
const endsAtSoftHyphen2 = lineEndSeg > 0 && segments[lineEndSeg - 1]?.kind === "soft-hyphen";
|
|
5728
|
+
const text2 = buildLineText(
|
|
5729
|
+
segments,
|
|
5730
|
+
startSeg,
|
|
5731
|
+
startG,
|
|
5732
|
+
lineEndSeg,
|
|
5733
|
+
lineEndG,
|
|
5734
|
+
endsAtSoftHyphen2
|
|
5735
|
+
);
|
|
5736
|
+
return {
|
|
5737
|
+
text: text2,
|
|
5738
|
+
width: lineW + (endsAtSoftHyphen2 ? hyphenWidth : 0),
|
|
5739
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
5740
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
5741
|
+
};
|
|
5742
|
+
}
|
|
5743
|
+
return {
|
|
5744
|
+
text: "",
|
|
5745
|
+
width: 0,
|
|
5746
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
5747
|
+
end: { segmentIndex: i + 1, graphemeIndex: 0 }
|
|
5748
|
+
};
|
|
5749
|
+
}
|
|
5750
|
+
const w = seg.width;
|
|
5751
|
+
if (!hasContent) {
|
|
5752
|
+
if (w > slotWidth && seg.graphemeWidths) {
|
|
5753
|
+
const overflowed = consumeBreakable(i, 0, seg.graphemeWidths);
|
|
5754
|
+
if (overflowed) {
|
|
5755
|
+
const text2 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
|
|
5756
|
+
return {
|
|
5757
|
+
text: text2,
|
|
5758
|
+
width: lineW,
|
|
5759
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
5760
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
5761
|
+
};
|
|
5762
|
+
}
|
|
5763
|
+
i = lineEndSeg;
|
|
5764
|
+
continue;
|
|
5765
|
+
}
|
|
5766
|
+
lineW = w;
|
|
5767
|
+
lineEndSeg = i + 1;
|
|
5768
|
+
lineEndG = 0;
|
|
5769
|
+
hasContent = true;
|
|
5770
|
+
if (canBreakAfter(seg.kind)) {
|
|
5771
|
+
recordPending(i + 1, 0, seg.kind === "space" ? lineW - w : lineW, seg.kind);
|
|
5772
|
+
}
|
|
5773
|
+
i += 1;
|
|
5774
|
+
continue;
|
|
5775
|
+
}
|
|
5776
|
+
const newW = lineW + w;
|
|
5777
|
+
if (newW > slotWidth + 5e-3) {
|
|
5778
|
+
if (canBreakAfter(seg.kind)) {
|
|
5779
|
+
lineEndSeg = i + 1;
|
|
5780
|
+
lineEndG = 0;
|
|
5781
|
+
const endsAtSoftHyphen2 = seg.kind === "soft-hyphen";
|
|
5782
|
+
const finalWidth = seg.kind === "space" ? lineW : lineW + (endsAtSoftHyphen2 ? hyphenWidth : 0);
|
|
5783
|
+
const text3 = buildLineText(
|
|
5784
|
+
segments,
|
|
5785
|
+
startSeg,
|
|
5786
|
+
startG,
|
|
5787
|
+
lineEndSeg,
|
|
5788
|
+
lineEndG,
|
|
5789
|
+
endsAtSoftHyphen2
|
|
5790
|
+
);
|
|
5791
|
+
return {
|
|
5792
|
+
text: text3,
|
|
5793
|
+
width: finalWidth,
|
|
5794
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
5795
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
5796
|
+
};
|
|
5797
|
+
}
|
|
5798
|
+
if (pendingBreakSeg >= 0) {
|
|
5799
|
+
const text3 = buildLineText(
|
|
5800
|
+
segments,
|
|
5801
|
+
startSeg,
|
|
5802
|
+
startG,
|
|
5803
|
+
pendingBreakSeg,
|
|
5804
|
+
pendingBreakG,
|
|
5805
|
+
pendingBreakSoftHyphen
|
|
5806
|
+
);
|
|
5807
|
+
return {
|
|
5808
|
+
text: text3,
|
|
5809
|
+
width: pendingBreakWidth + (pendingBreakSoftHyphen ? hyphenWidth : 0),
|
|
5810
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
5811
|
+
end: { segmentIndex: pendingBreakSeg, graphemeIndex: pendingBreakG }
|
|
5812
|
+
};
|
|
5813
|
+
}
|
|
5814
|
+
if (w > slotWidth && seg.graphemeWidths) {
|
|
5815
|
+
const text3 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
|
|
5816
|
+
return {
|
|
5817
|
+
text: text3,
|
|
5818
|
+
width: lineW,
|
|
5819
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
5820
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
5821
|
+
};
|
|
5822
|
+
}
|
|
5823
|
+
const text2 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
|
|
5824
|
+
return {
|
|
5825
|
+
text: text2,
|
|
5826
|
+
width: lineW,
|
|
5827
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
5828
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
5829
|
+
};
|
|
5830
|
+
}
|
|
5831
|
+
lineW = newW;
|
|
5832
|
+
lineEndSeg = i + 1;
|
|
5833
|
+
lineEndG = 0;
|
|
5834
|
+
if (canBreakAfter(seg.kind)) {
|
|
5835
|
+
recordPending(i + 1, 0, seg.kind === "space" ? lineW - w : lineW, seg.kind);
|
|
5836
|
+
}
|
|
5837
|
+
i += 1;
|
|
5838
|
+
}
|
|
5839
|
+
if (!hasContent) return null;
|
|
5840
|
+
const endsAtSoftHyphen = lineEndSeg > 0 && segments[lineEndSeg - 1]?.kind === "soft-hyphen";
|
|
5841
|
+
const text = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, endsAtSoftHyphen);
|
|
5842
|
+
return {
|
|
5843
|
+
text,
|
|
5844
|
+
width: lineW + (endsAtSoftHyphen ? hyphenWidth : 0),
|
|
5845
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
5846
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
5847
|
+
};
|
|
5848
|
+
}
|
|
5849
|
+
function carveTextLineSlots(base, blocked, minSlotWidth = 0) {
|
|
5850
|
+
let slots = [base];
|
|
5851
|
+
for (let bi = 0; bi < blocked.length; bi++) {
|
|
5852
|
+
const block = blocked[bi];
|
|
5853
|
+
const next = [];
|
|
5854
|
+
for (let si = 0; si < slots.length; si++) {
|
|
5855
|
+
const slot = slots[si];
|
|
5856
|
+
if (block.right <= slot.left || block.left >= slot.right) {
|
|
5857
|
+
next.push(slot);
|
|
5858
|
+
continue;
|
|
5859
|
+
}
|
|
5860
|
+
if (block.left > slot.left) next.push({ left: slot.left, right: block.left });
|
|
5861
|
+
if (block.right < slot.right) next.push({ left: block.right, right: slot.right });
|
|
5862
|
+
}
|
|
5863
|
+
slots = next;
|
|
5864
|
+
}
|
|
5865
|
+
if (minSlotWidth > 0) {
|
|
5866
|
+
return slots.filter((s) => s.right - s.left >= minSlotWidth);
|
|
5867
|
+
}
|
|
5868
|
+
return slots;
|
|
5869
|
+
}
|
|
5241
5870
|
function computeCharPositions(lineBreaks, segments, lineHeight) {
|
|
5242
5871
|
const positions = [];
|
|
5243
|
-
const
|
|
5872
|
+
const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
|
|
5244
5873
|
granularity: "grapheme"
|
|
5245
5874
|
});
|
|
5246
5875
|
for (let lineIdx = 0; lineIdx < lineBreaks.lines.length; lineIdx++) {
|
|
@@ -5253,7 +5882,7 @@ function computeCharPositions(lineBreaks, segments, lineHeight) {
|
|
|
5253
5882
|
if (si >= line.endSegment && line.endGrapheme === 0) break;
|
|
5254
5883
|
continue;
|
|
5255
5884
|
}
|
|
5256
|
-
const graphemes = [...
|
|
5885
|
+
const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
|
|
5257
5886
|
if (graphemes.length === 0) continue;
|
|
5258
5887
|
const startG = si === line.startSegment ? line.startGrapheme : 0;
|
|
5259
5888
|
let endG;
|
|
@@ -5609,6 +6238,185 @@ function reactiveBlockLayout(opts) {
|
|
|
5609
6238
|
totalHeight: totalHeightNode
|
|
5610
6239
|
};
|
|
5611
6240
|
}
|
|
6241
|
+
|
|
6242
|
+
// src/patterns/reactive-layout/reactive-flow-layout.ts
|
|
6243
|
+
function circleIntervalForBand(o, bandTop, bandBottom) {
|
|
6244
|
+
const hPad = o.hPad ?? 0;
|
|
6245
|
+
const vPad = o.vPad ?? 0;
|
|
6246
|
+
const top = bandTop - vPad;
|
|
6247
|
+
const bottom = bandBottom + vPad;
|
|
6248
|
+
if (top >= o.cy + o.r || bottom <= o.cy - o.r) return null;
|
|
6249
|
+
const minDy = o.cy >= top && o.cy <= bottom ? 0 : o.cy < top ? top - o.cy : o.cy - bottom;
|
|
6250
|
+
if (minDy >= o.r) return null;
|
|
6251
|
+
const maxDx = Math.sqrt(o.r * o.r - minDy * minDy);
|
|
6252
|
+
return { left: o.cx - maxDx - hPad, right: o.cx + maxDx + hPad };
|
|
6253
|
+
}
|
|
6254
|
+
function rectIntervalForBand(o, bandTop, bandBottom) {
|
|
6255
|
+
const hPad = o.hPad ?? 0;
|
|
6256
|
+
const vPad = o.vPad ?? 0;
|
|
6257
|
+
if (bandBottom <= o.y - vPad) return null;
|
|
6258
|
+
if (bandTop >= o.y + o.h + vPad) return null;
|
|
6259
|
+
return { left: o.x - hPad, right: o.x + o.w + hPad };
|
|
6260
|
+
}
|
|
6261
|
+
function obstacleIntervalForBand(o, bandTop, bandBottom) {
|
|
6262
|
+
return o.kind === "circle" ? circleIntervalForBand(o, bandTop, bandBottom) : rectIntervalForBand(o, bandTop, bandBottom);
|
|
6263
|
+
}
|
|
6264
|
+
function computeFlowLines(segments, container, columns, obstacles, lineHeight, minSlotWidth) {
|
|
6265
|
+
const lines = [];
|
|
6266
|
+
let cursor = { segmentIndex: 0, graphemeIndex: 0 };
|
|
6267
|
+
if (segments.length === 0 || columns.count <= 0 || lineHeight <= 0) {
|
|
6268
|
+
return { lines, cursor };
|
|
6269
|
+
}
|
|
6270
|
+
const padX = container.paddingX ?? 0;
|
|
6271
|
+
const padY = container.paddingY ?? 0;
|
|
6272
|
+
const availWidth = Math.max(0, container.width - padX * 2);
|
|
6273
|
+
const availHeight = Math.max(0, container.height - padY * 2);
|
|
6274
|
+
const gapTotal = columns.gap * Math.max(0, columns.count - 1);
|
|
6275
|
+
const colWidth = Math.max(0, (availWidth - gapTotal) / columns.count);
|
|
6276
|
+
if (colWidth <= 0) return { lines, cursor };
|
|
6277
|
+
outerCol: for (let col = 0; col < columns.count; col++) {
|
|
6278
|
+
const colLeft = padX + col * (colWidth + columns.gap);
|
|
6279
|
+
const colRight = colLeft + colWidth;
|
|
6280
|
+
let bandTop = padY;
|
|
6281
|
+
while (bandTop + lineHeight <= padY + availHeight) {
|
|
6282
|
+
const bandBottom = bandTop + lineHeight;
|
|
6283
|
+
const blocked = [];
|
|
6284
|
+
for (let oi = 0; oi < obstacles.length; oi++) {
|
|
6285
|
+
const iv = obstacleIntervalForBand(obstacles[oi], bandTop, bandBottom);
|
|
6286
|
+
if (iv !== null) blocked.push(iv);
|
|
6287
|
+
}
|
|
6288
|
+
const slots = carveTextLineSlots({ left: colLeft, right: colRight }, blocked, minSlotWidth);
|
|
6289
|
+
if (slots.length === 0) {
|
|
6290
|
+
bandTop += lineHeight;
|
|
6291
|
+
continue;
|
|
6292
|
+
}
|
|
6293
|
+
let hardBreakThisBand = false;
|
|
6294
|
+
for (let si = 0; si < slots.length; si++) {
|
|
6295
|
+
const slot = slots[si];
|
|
6296
|
+
const slotW = slot.right - slot.left;
|
|
6297
|
+
const line = layoutNextLine(segments, cursor, slotW);
|
|
6298
|
+
if (line === null) {
|
|
6299
|
+
return { lines, cursor };
|
|
6300
|
+
}
|
|
6301
|
+
if (line.text.length === 0 && line.width === 0) {
|
|
6302
|
+
cursor = line.end;
|
|
6303
|
+
hardBreakThisBand = true;
|
|
6304
|
+
break;
|
|
6305
|
+
}
|
|
6306
|
+
lines.push({
|
|
6307
|
+
x: slot.left,
|
|
6308
|
+
y: bandTop,
|
|
6309
|
+
width: line.width,
|
|
6310
|
+
slotWidth: slotW,
|
|
6311
|
+
text: line.text,
|
|
6312
|
+
columnIndex: col,
|
|
6313
|
+
flushToRight: slot.right < colRight - 0.5
|
|
6314
|
+
});
|
|
6315
|
+
cursor = line.end;
|
|
6316
|
+
}
|
|
6317
|
+
bandTop += lineHeight;
|
|
6318
|
+
if (hardBreakThisBand) continue;
|
|
6319
|
+
if (cursor.segmentIndex >= segments.length) break outerCol;
|
|
6320
|
+
}
|
|
6321
|
+
if (cursor.segmentIndex >= segments.length) break;
|
|
6322
|
+
}
|
|
6323
|
+
return { lines, cursor };
|
|
6324
|
+
}
|
|
6325
|
+
function reactiveFlowLayout(opts) {
|
|
6326
|
+
const { adapter, name = "reactive-flow-layout", minSlotWidth = 20 } = opts;
|
|
6327
|
+
const g = new Graph(name);
|
|
6328
|
+
const measureCache = /* @__PURE__ */ new Map();
|
|
6329
|
+
const textNode = state(opts.text ?? "", { name: "text" });
|
|
6330
|
+
const fontNode = state(opts.font ?? "16px sans-serif", { name: "font" });
|
|
6331
|
+
const lineHeightNode = state(opts.lineHeight ?? 20, { name: "line-height" });
|
|
6332
|
+
const containerNode = state(
|
|
6333
|
+
opts.container ?? { width: 800, height: 600, paddingX: 0, paddingY: 0 },
|
|
6334
|
+
{ name: "container" }
|
|
6335
|
+
);
|
|
6336
|
+
const columnsNode = state(opts.columns ?? { count: 1, gap: 0 }, {
|
|
6337
|
+
name: "columns"
|
|
6338
|
+
});
|
|
6339
|
+
const obstaclesNode = state(opts.obstacles ?? [], { name: "obstacles" });
|
|
6340
|
+
const segmentsNode = node(
|
|
6341
|
+
[textNode, fontNode],
|
|
6342
|
+
(data, actions, ctx) => {
|
|
6343
|
+
const b0 = data[0];
|
|
6344
|
+
const textVal = b0 != null && b0.length > 0 ? b0.at(-1) : ctx.prevData[0];
|
|
6345
|
+
const b1 = data[1];
|
|
6346
|
+
const fontVal = b1 != null && b1.length > 0 ? b1.at(-1) : ctx.prevData[1];
|
|
6347
|
+
const result = analyzeAndMeasure(textVal, fontVal, adapter, measureCache);
|
|
6348
|
+
actions.emit(result);
|
|
6349
|
+
return () => {
|
|
6350
|
+
measureCache.clear();
|
|
6351
|
+
adapter.clearCache?.();
|
|
6352
|
+
};
|
|
6353
|
+
},
|
|
6354
|
+
{ name: "segments", describeKind: "derived" }
|
|
6355
|
+
);
|
|
6356
|
+
const flowLinesNode = derived(
|
|
6357
|
+
[segmentsNode, containerNode, columnsNode, obstaclesNode, lineHeightNode],
|
|
6358
|
+
([segs, cont, cols, obs, lh]) => {
|
|
6359
|
+
const segments = segs;
|
|
6360
|
+
const t0 = monotonicNs();
|
|
6361
|
+
const { lines: result, cursor } = computeFlowLines(
|
|
6362
|
+
segments,
|
|
6363
|
+
cont,
|
|
6364
|
+
cols,
|
|
6365
|
+
obs,
|
|
6366
|
+
lh,
|
|
6367
|
+
minSlotWidth
|
|
6368
|
+
);
|
|
6369
|
+
const elapsed = monotonicNs() - t0;
|
|
6370
|
+
const overflow = Math.max(0, segments.length - cursor.segmentIndex);
|
|
6371
|
+
const meta = flowLinesNode.meta;
|
|
6372
|
+
if (meta) {
|
|
6373
|
+
emitToMeta(meta["line-count"], result.length);
|
|
6374
|
+
emitToMeta(meta["layout-time-ns"], elapsed);
|
|
6375
|
+
emitToMeta(meta["overflow-segments"], overflow);
|
|
6376
|
+
}
|
|
6377
|
+
return result;
|
|
6378
|
+
},
|
|
6379
|
+
{
|
|
6380
|
+
name: "flow-lines",
|
|
6381
|
+
meta: {
|
|
6382
|
+
"line-count": 0,
|
|
6383
|
+
"layout-time-ns": 0,
|
|
6384
|
+
"overflow-segments": 0
|
|
6385
|
+
},
|
|
6386
|
+
equals: (a, b) => {
|
|
6387
|
+
const la = a;
|
|
6388
|
+
const lb = b;
|
|
6389
|
+
if (la.length !== lb.length) return false;
|
|
6390
|
+
for (let i = 0; i < la.length; i++) {
|
|
6391
|
+
const pa = la[i];
|
|
6392
|
+
const pb = lb[i];
|
|
6393
|
+
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)
|
|
6394
|
+
return false;
|
|
6395
|
+
}
|
|
6396
|
+
return true;
|
|
6397
|
+
}
|
|
6398
|
+
}
|
|
6399
|
+
);
|
|
6400
|
+
g.add("text", textNode);
|
|
6401
|
+
g.add("font", fontNode);
|
|
6402
|
+
g.add("line-height", lineHeightNode);
|
|
6403
|
+
g.add("container", containerNode);
|
|
6404
|
+
g.add("columns", columnsNode);
|
|
6405
|
+
g.add("obstacles", obstaclesNode);
|
|
6406
|
+
g.add("segments", segmentsNode);
|
|
6407
|
+
g.add("flow-lines", flowLinesNode);
|
|
6408
|
+
return {
|
|
6409
|
+
graph: g,
|
|
6410
|
+
setText: (t) => g.set("text", t),
|
|
6411
|
+
setFont: (f) => g.set("font", f),
|
|
6412
|
+
setLineHeight: (lh) => g.set("line-height", lh),
|
|
6413
|
+
setContainer: (c) => g.set("container", c),
|
|
6414
|
+
setColumns: (c) => g.set("columns", c),
|
|
6415
|
+
setObstacles: (o) => g.set("obstacles", o),
|
|
6416
|
+
segments: segmentsNode,
|
|
6417
|
+
flowLines: flowLinesNode
|
|
6418
|
+
};
|
|
6419
|
+
}
|
|
5612
6420
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5613
6421
|
0 && (module.exports = {
|
|
5614
6422
|
CanvasMeasureAdapter,
|
|
@@ -5618,13 +6426,19 @@ function reactiveBlockLayout(opts) {
|
|
|
5618
6426
|
PrecomputedAdapter,
|
|
5619
6427
|
SvgBoundsAdapter,
|
|
5620
6428
|
analyzeAndMeasure,
|
|
6429
|
+
carveTextLineSlots,
|
|
6430
|
+
circleIntervalForBand,
|
|
5621
6431
|
computeBlockFlow,
|
|
5622
6432
|
computeCharPositions,
|
|
6433
|
+
computeFlowLines,
|
|
5623
6434
|
computeLineBreaks,
|
|
5624
6435
|
computeTotalHeight,
|
|
6436
|
+
layoutNextLine,
|
|
5625
6437
|
measureBlock,
|
|
5626
6438
|
measureBlocks,
|
|
5627
6439
|
reactiveBlockLayout,
|
|
5628
|
-
|
|
6440
|
+
reactiveFlowLayout,
|
|
6441
|
+
reactiveLayout,
|
|
6442
|
+
rectIntervalForBand
|
|
5629
6443
|
});
|
|
5630
6444
|
//# sourceMappingURL=index.cjs.map
|