@helping-ai-workflow/md2doc 3.12.1 → 3.13.1
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/bin/md2doc.js +6 -0
- package/lib/editor/client.js +1015 -194
- package/lib/editor/inline-md.js +45 -1
- package/lib/editor/list-md.js +8 -1
- package/lib/editor/server.js +5 -0
- package/lib/editor/wave-canvas.js +301 -0
- package/lib/editor/wave-codec.js +417 -11
- package/lib/editor/wave-draw.js +209 -1202
- package/lib/editor/wave-geometry.js +145 -10
- package/lib/editor/wave-panels.js +907 -918
- package/lib/editor/wave-store.js +486 -91
- package/lib/editor/wave-ui.js +3588 -2232
- package/lib/md2doc.js +765 -534
- package/lib/theme/runtime.js +296 -29
- package/lib/theme/tokens.js +3 -0
- package/lib/wave-overflow.js +107 -0
- package/package.json +3 -3
- package/scripts/release-check.js +60 -0
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* wave-geometry — where every cycle of a WaveJSON document sits, and which
|
|
5
|
-
* cycle a point lands in.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* milliseconds.
|
|
5
|
+
* cycle a point lands in. Arithmetic, no mutation, and no globals: every size
|
|
6
|
+
* arrives as a parameter, so the layer above can hand in whatever the
|
|
7
|
+
* rendered SVG actually measures and this file stays testable in
|
|
8
|
+
* milliseconds. The one function that reads a DOM is `measureEngineSvg`, and
|
|
9
|
+
* it reads only the engine SVG it is handed (getScreenCTM, text lengths) —
|
|
10
|
+
* never a page global, which the purity test in wave-geometry.test.js keeps
|
|
11
|
+
* true.
|
|
9
12
|
*
|
|
10
13
|
* It lives on its own for one reason: the hit test's boundary rule — a cell's
|
|
11
14
|
* LEFT edge belongs to it, its RIGHT edge belongs to the next cell — is the
|
|
@@ -315,6 +318,10 @@ function layoutOf(doc, opts) {
|
|
|
315
318
|
const laneHeight = size(o.laneHeight, DEFAULTS.laneHeight, 'laneHeight', false);
|
|
316
319
|
const cycleWidth = size(o.cycleWidth, DEFAULTS.cycleWidth, 'cycleWidth', false);
|
|
317
320
|
const nameColWidth = size(o.nameColWidth, DEFAULTS.nameColWidth, 'nameColWidth', true);
|
|
321
|
+
// wave-redesign P1 Task 4: the engine canvas measures where lane 0's row
|
|
322
|
+
// starts (`measureEngineSvg(...).laneTops[0]`) and passes it here, which
|
|
323
|
+
// replaces the editor-only band constants below. Absent, nothing changes.
|
|
324
|
+
const originYOverride = size(o.originY, undefined, 'originY', true);
|
|
318
325
|
|
|
319
326
|
// `config.hscale` scales every lane's cycle width the same way the engine's
|
|
320
327
|
// own `lane.hscale` does — see `hscaleOf` for the exact rounding/clamping
|
|
@@ -328,7 +335,7 @@ function layoutOf(doc, opts) {
|
|
|
328
335
|
const headTextHeight = (doc !== null && typeof doc === 'object' &&
|
|
329
336
|
doc.head !== null && typeof doc.head === 'object' &&
|
|
330
337
|
doc.head.text !== undefined) ? HEAD_TEXT_HEIGHT : 0;
|
|
331
|
-
const originY = headTextHeight + RULER_HEIGHT;
|
|
338
|
+
const originY = originYOverride !== undefined ? originYOverride : headTextHeight + RULER_HEIGHT;
|
|
332
339
|
const footHeight = hasBanner(doc, 'foot') ? FOOT_HEIGHT : 0;
|
|
333
340
|
// v3.6.0 Task 9 fix round 1 (R29): stacked BELOW `footHeight`, mirroring
|
|
334
341
|
// `headTextHeight` stacked ABOVE `RULER_HEIGHT` — `footHeight` alone still
|
|
@@ -424,7 +431,8 @@ function layoutOf(doc, opts) {
|
|
|
424
431
|
* A group title occupies NO ROW — measured in Task 2 against the pinned
|
|
425
432
|
* engine, and `wave-geometry` is built on it. What it occupies is horizontal
|
|
426
433
|
* space in the name column, so that is where this puts it: a label in the
|
|
427
|
-
* lane list's rail,
|
|
434
|
+
* name column (the lane list's rail, until the redesign removed it),
|
|
435
|
+
* spanning its lanes' rows. Drawing it as a row of its own
|
|
428
436
|
* would put the lane list one row out of step with the waveform beside it for
|
|
429
437
|
* every lane below the group, which is exactly the class of silent
|
|
430
438
|
* disagreement this batch keeps producing.
|
|
@@ -434,8 +442,10 @@ function layoutOf(doc, opts) {
|
|
|
434
442
|
* shown here; it is not addressable through the bridge either.
|
|
435
443
|
*
|
|
436
444
|
* v3.6.0 Task 2: moved here from `lib/editor/wave-ui.js` per ruling R9 — both
|
|
437
|
-
* `wave-panels.js` (the lane rail) and `wave-draw.js` (the canvas)
|
|
438
|
-
* and this file is the one place both already
|
|
445
|
+
* `wave-panels.js` (the lane rail) and `wave-draw.js` (the canvas) needed it,
|
|
446
|
+
* and this file is the one place both already depended on. Since the wave
|
|
447
|
+
* redesign its caller is `wave-ui.js` (the name column's group ⠿ and the
|
|
448
|
+
* group-row hover). Behaviour
|
|
439
449
|
* unchanged; it now reads this file's own `valueAt` instead of a copy that
|
|
440
450
|
* used to live in `wave-ui.js`.
|
|
441
451
|
*/
|
|
@@ -643,11 +653,21 @@ const EDGE_HANDLE = 12;
|
|
|
643
653
|
* 一個答案,`cellRect`/`reachOf` 已經是它,錨點只是在那個答案上再加一個
|
|
644
654
|
* 固定比例的偏移。
|
|
645
655
|
*/
|
|
656
|
+
/*
|
|
657
|
+
* wave-redesign P1 Task 4: the offset is `anchorRatio` of the BASE cycle
|
|
658
|
+
* (`layout.cycleWidth`), not of this lane's own cell width. The engine's
|
|
659
|
+
* `render-arcs.js` places a letter at `xs * (2*pos*period*hscale - phase) +
|
|
660
|
+
* xlabel`, and `xlabel` (the skin's `socket.x`, 6 of a 40-unit cycle) is not
|
|
661
|
+
* multiplied by `period` or `hscale`. With the cell width a `period: 2` lane
|
|
662
|
+
* put every anchor 6 engine units right of the engine's, which the engine
|
|
663
|
+
* canvas test (test/wave-canvas.test.js) measures against the drawn
|
|
664
|
+
* `gmark_` paths. For `period` 1 and `hscale` 1 the two are the same number.
|
|
665
|
+
*/
|
|
646
666
|
function anchorOfCell(layout, laneIndex, cell) {
|
|
647
667
|
const r = cellRect(layout, laneIndex, cell);
|
|
648
668
|
if (r === null) return null;
|
|
649
669
|
return {
|
|
650
|
-
x: r.x + SKIN_METRICS.anchorRatio *
|
|
670
|
+
x: r.x + SKIN_METRICS.anchorRatio * layout.cycleWidth,
|
|
651
671
|
y: r.y + r.height / 2,
|
|
652
672
|
};
|
|
653
673
|
}
|
|
@@ -697,7 +717,8 @@ function boundaryAt(layout, x, y) {
|
|
|
697
717
|
const row = layout.lanes[laneIndex];
|
|
698
718
|
const reach = reachOf(row.originX, row.cycleWidth, layout.width);
|
|
699
719
|
if (reach === 0) return null;
|
|
700
|
-
|
|
720
|
+
// Same anchor offset as `anchorOfCell`: a fraction of the BASE cycle.
|
|
721
|
+
const raw = (x - row.originX - SKIN_METRICS.anchorRatio * layout.cycleWidth) / row.cycleWidth;
|
|
701
722
|
let cell = Math.round(raw);
|
|
702
723
|
if (cell < 0) cell = 0;
|
|
703
724
|
if (cell > reach - 1) cell = reach - 1;
|
|
@@ -823,6 +844,8 @@ function edgeLayoutDetail(doc, layout) {
|
|
|
823
844
|
for (let i = 0; i < doc.edge.length; i += 1) {
|
|
824
845
|
const edge = codec.parseEdge(doc.edge[i]);
|
|
825
846
|
if (edge === null) {
|
|
847
|
+
// 單點標註(`<字母> <文字>`)由標註層畫,不算畫不出來的 edge。
|
|
848
|
+
if (codec.parseNote(doc.edge[i]) !== null) continue;
|
|
826
849
|
skipped.push({ index: i, edge: null, reason: 'unparseable' });
|
|
827
850
|
continue;
|
|
828
851
|
}
|
|
@@ -933,6 +956,117 @@ function edgeHandleAt(edges, x, y) {
|
|
|
933
956
|
return null;
|
|
934
957
|
}
|
|
935
958
|
|
|
959
|
+
/**
|
|
960
|
+
* What the WaveDrom engine actually drew, read off its own SVG — the half of
|
|
961
|
+
* the editor canvas's geometry that `layoutOf` cannot know (wave-redesign
|
|
962
|
+
* spec §3.1). `layoutOf` stays the source of every lane's horizontal cell
|
|
963
|
+
* arithmetic (period, phase, hscale); this supplies the three things only the
|
|
964
|
+
* rendered picture has:
|
|
965
|
+
*
|
|
966
|
+
* - `xg`: the name column, from the `lanes_<i>` group's translate, which the
|
|
967
|
+
* engine writes as `lane.xg + 0.5` (insert-svg-template.js). Callers pass
|
|
968
|
+
* `xg + 0.5` to `layoutOf` as `nameColWidth`, because that half unit is
|
|
969
|
+
* part of where every cell starts.
|
|
970
|
+
* - `laneTops[j]`: the top of lane j's 30-unit row band. Each lane is a
|
|
971
|
+
* `wavelane_<j>_<i>` group translated by `lane.y0 + j * lane.yo` inside
|
|
972
|
+
* `lanes_<i>` (5 + 30j); the 20-unit brick is drawn from that y down, so
|
|
973
|
+
* the band that has the brick in its middle starts y0 above the group.
|
|
974
|
+
* y0 is measured as wavelane_0's offset from `lanes_<i>`, not pinned.
|
|
975
|
+
* With the band so placed, a row's vertical centre is exactly the y the
|
|
976
|
+
* engine anchors a `node` letter at (`i * yo + y0 + ys / 2`, render-arcs.js).
|
|
977
|
+
* - `labels`: every data label `text` in each lane's `wavelane_draw_` group,
|
|
978
|
+
* with its centre `x`, its advance `width` (`getComputedTextLength`, the
|
|
979
|
+
* same quantity lib/wave-overflow.js estimates), its box `y` / `height`,
|
|
980
|
+
* and — when the document is passed — which `data` slot it belongs to
|
|
981
|
+
* and how many cycles that segment spans (`cells`). The pairing is by
|
|
982
|
+
* position, never by text (two segments may carry the same label): the
|
|
983
|
+
* label belongs to the segment of the same lane whose x range contains
|
|
984
|
+
* its centre.
|
|
985
|
+
*
|
|
986
|
+
* Every number is in ROOT engine units (the svg's viewBox), converted through
|
|
987
|
+
* `getScreenCTM`, so the canvas can scale the svg without this changing.
|
|
988
|
+
* Grid lines and lane contents carry coordinates LOCAL to translated groups;
|
|
989
|
+
* reading their raw attributes is how a prototype ended up half a cycle off.
|
|
990
|
+
* `originY` is the `lanes_` group's own y, the fallback when there is no lane.
|
|
991
|
+
*/
|
|
992
|
+
function measureEngineSvg(svg, doc) {
|
|
993
|
+
const out = { xg: 0, originY: 0, laneTops: [], laneHeight: DEFAULTS.laneHeight, labels: [] };
|
|
994
|
+
const rootCtm = svg && typeof svg.getScreenCTM === 'function' ? svg.getScreenCTM() : null;
|
|
995
|
+
if (rootCtm === null) return out;
|
|
996
|
+
const rootInv = rootCtm.inverse();
|
|
997
|
+
function toRoot(el) { return rootInv.multiply(el.getScreenCTM()); }
|
|
998
|
+
const lanesG = svg.querySelector('g[id^="lanes_"]');
|
|
999
|
+
if (lanesG === null) return out;
|
|
1000
|
+
const lm = toRoot(lanesG);
|
|
1001
|
+
out.xg = lm.e - 0.5;
|
|
1002
|
+
out.originY = lm.f;
|
|
1003
|
+
|
|
1004
|
+
const groups = [];
|
|
1005
|
+
for (let k = 0; k < lanesG.children.length; k++) {
|
|
1006
|
+
const g = lanesG.children[k];
|
|
1007
|
+
const m = /^wavelane_(\d+)_\d+$/.exec(g.id || '');
|
|
1008
|
+
if (m !== null) groups[Number(m[1])] = g;
|
|
1009
|
+
}
|
|
1010
|
+
const tops = [];
|
|
1011
|
+
let y0 = null;
|
|
1012
|
+
for (let j = 0; j < groups.length; j++) {
|
|
1013
|
+
if (groups[j] === undefined) break;
|
|
1014
|
+
const gy = toRoot(groups[j]).f;
|
|
1015
|
+
if (y0 === null) y0 = gy - lm.f;
|
|
1016
|
+
tops.push(gy - y0);
|
|
1017
|
+
}
|
|
1018
|
+
out.laneTops = tops;
|
|
1019
|
+
if (tops.length > 1) out.laneHeight = tops[1] - tops[0];
|
|
1020
|
+
|
|
1021
|
+
const layout = (doc !== null && typeof doc === 'object' && tops.length > 0)
|
|
1022
|
+
? layoutOf(doc, { laneHeight: out.laneHeight, nameColWidth: out.xg + 0.5, originY: tops[0] })
|
|
1023
|
+
: null;
|
|
1024
|
+
|
|
1025
|
+
for (let j = 0; j < tops.length; j++) {
|
|
1026
|
+
let draw = null;
|
|
1027
|
+
for (let k = 0; k < groups[j].children.length; k++) {
|
|
1028
|
+
if (/^wavelane_draw_/.test(groups[j].children[k].id || '')) { draw = groups[j].children[k]; break; }
|
|
1029
|
+
}
|
|
1030
|
+
if (draw === null) continue;
|
|
1031
|
+
const segs = layout !== null && j < layout.lanes.length ? segmentsOf(layout.lanes[j]) : [];
|
|
1032
|
+
for (let k = 0; k < draw.children.length; k++) {
|
|
1033
|
+
const t = draw.children[k];
|
|
1034
|
+
if (String(t.tagName).toLowerCase() !== 'text') continue;
|
|
1035
|
+
const m = toRoot(t);
|
|
1036
|
+
const box = t.getBBox();
|
|
1037
|
+
const x = m.a * (box.x + box.width / 2) + m.c * (box.y + box.height / 2) + m.e;
|
|
1038
|
+
const top = m.b * box.x + m.d * box.y + m.f;
|
|
1039
|
+
let slot = null;
|
|
1040
|
+
let cells = null;
|
|
1041
|
+
for (const s of segs) {
|
|
1042
|
+
if (x >= s.from && x < s.to) { slot = s.slot; cells = s.cells; break; }
|
|
1043
|
+
}
|
|
1044
|
+
out.labels.push({
|
|
1045
|
+
laneIndex: j, slot: slot, x: x, width: t.getComputedTextLength() * m.a,
|
|
1046
|
+
text: t.textContent, y: top, height: box.height * m.d, cells: cells,
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
return out;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
/** Each data segment of one laid-out lane: its slot, cycle count and x range. */
|
|
1054
|
+
function segmentsOf(row) {
|
|
1055
|
+
const lane = row.lane;
|
|
1056
|
+
if (lane === null || typeof lane !== 'object' || typeof lane.wave !== 'string') return [];
|
|
1057
|
+
const cells = codec.expandWave(lane.wave);
|
|
1058
|
+
const out = [];
|
|
1059
|
+
for (let i = 0; i < cells.length; i++) {
|
|
1060
|
+
const slot = codec.dataSlotOf(lane, i);
|
|
1061
|
+
if (slot === null) continue;
|
|
1062
|
+
let n = 1;
|
|
1063
|
+
while (i + n < cells.length && cells[i + n].held) n++;
|
|
1064
|
+
out.push({ slot: slot, cells: n, from: row.originX + i * row.cycleWidth,
|
|
1065
|
+
to: row.originX + (i + n) * row.cycleWidth });
|
|
1066
|
+
}
|
|
1067
|
+
return out;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
936
1070
|
module.exports = {
|
|
937
1071
|
layoutOf: layoutOf,
|
|
938
1072
|
valueAt: valueAt,
|
|
@@ -946,6 +1080,7 @@ module.exports = {
|
|
|
946
1080
|
edgeLayoutDetail: edgeLayoutDetail,
|
|
947
1081
|
edgeHandleRect: edgeHandleRect,
|
|
948
1082
|
edgeHandleAt: edgeHandleAt,
|
|
1083
|
+
measureEngineSvg: measureEngineSvg,
|
|
949
1084
|
EDGE_HANDLE: EDGE_HANDLE,
|
|
950
1085
|
DEFAULTS: DEFAULTS,
|
|
951
1086
|
MAX_LANES: MAX_LANES,
|