@helping-ai-workflow/md2doc 3.12.1 → 3.13.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/bin/md2doc.js +6 -0
- package/lib/editor/client.js +356 -180
- 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 +342 -460
- package/lib/theme/runtime.js +18 -3
- package/lib/wave-overflow.js +107 -0
- package/package.json +3 -3
- package/scripts/release-check.js +60 -0
package/lib/md2doc.js
CHANGED
|
@@ -23,6 +23,8 @@ const path = require('path');
|
|
|
23
23
|
const drawio = require('./drawio.js');
|
|
24
24
|
const { applyReaderTheme } = require('./theme/tokens');
|
|
25
25
|
const { highlightCode } = require('./highlight');
|
|
26
|
+
const waveCodec = require('./editor/wave-codec.js');
|
|
27
|
+
const waveOverflow = require('./wave-overflow.js');
|
|
26
28
|
|
|
27
29
|
// How long to let client-side WaveDrom / Mermaid scripts render before we
|
|
28
30
|
// snapshot the DOM (PDF print, or --bake-svg inert-SVG bake).
|
|
@@ -1004,6 +1006,7 @@ ${itemsHtml}
|
|
|
1004
1006
|
renderer.br = function() { return '<br data-hard-break="1">'; };
|
|
1005
1007
|
}
|
|
1006
1008
|
|
|
1009
|
+
let waveBlockCount = 0;
|
|
1007
1010
|
renderer.code = function(token) {
|
|
1008
1011
|
// token is either a string (old API) or {text, lang} object (new API)
|
|
1009
1012
|
const lang = (typeof token === 'object') ? (token.lang || '') : (arguments[1] || '');
|
|
@@ -1011,6 +1014,29 @@ ${itemsHtml}
|
|
|
1011
1014
|
|
|
1012
1015
|
if (lang === 'wavedrom') {
|
|
1013
1016
|
usesWaveDrom = true;
|
|
1017
|
+
waveBlockCount++;
|
|
1018
|
+
// CLI-only: the CLI passes its source path as opts.warnWaveOverflow;
|
|
1019
|
+
// library and --edit renders leave it unset and stay silent.
|
|
1020
|
+
// One line per diagram, never one per label (final review M1): the
|
|
1021
|
+
// per-label lines of one diagram each named their own scale, and
|
|
1022
|
+
// following the first ("set to 2") left the next one ("to 4") still
|
|
1023
|
+
// overflowing. The line names the count, the label that needs the
|
|
1024
|
+
// most, and the one scale that fits them all (suggestScale).
|
|
1025
|
+
if (opts.warnWaveOverflow) {
|
|
1026
|
+
try {
|
|
1027
|
+
const parsed = waveCodec.parseSource(code);
|
|
1028
|
+
const list = parsed.ok ? waveOverflow.overflowOf(parsed.doc) : [];
|
|
1029
|
+
if (list.length > 0) {
|
|
1030
|
+
let worst = list[0];
|
|
1031
|
+
for (const o of list) if (o.needScale > worst.needScale) worst = o;
|
|
1032
|
+
const named = '"' + worst.label + '" in ' + worst.name;
|
|
1033
|
+
const what = list.length === 1
|
|
1034
|
+
? '1 data label is wider than its segment (' + named + ')'
|
|
1035
|
+
: list.length + ' data labels are wider than their segments (worst: ' + named + ')';
|
|
1036
|
+
console.error(`md2doc: ${opts.warnWaveOverflow}: wavedrom #${waveBlockCount}: ${what}; set config.hscale to ${waveOverflow.suggestScale(list)} (estimated)`);
|
|
1037
|
+
}
|
|
1038
|
+
} catch (e) { /* a warning must never break a render */ }
|
|
1039
|
+
}
|
|
1014
1040
|
return `\n<script type="WaveDrom">\n${code}\n</script>\n`;
|
|
1015
1041
|
}
|
|
1016
1042
|
if (lang === 'mermaid') {
|
|
@@ -1787,488 +1813,341 @@ ${edUlMarkerCss}${edOlCounterCss}
|
|
|
1787
1813
|
position: fixed; inset: 0; z-index: 998; background: rgba(0,0,0,.45);
|
|
1788
1814
|
display: flex; align-items: center; justify-content: center;
|
|
1789
1815
|
}
|
|
1790
|
-
/*
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
user nothing"). 1920px is chosen so 94vw is the actual governor at
|
|
1797
|
-
every one of those four measured sizes (94% of 1920 is 1804.8, still
|
|
1798
|
-
under the new cap) — this number is a ceiling for a screen wider than
|
|
1799
|
-
that, not a value that binds within the measured range. At 800x600,
|
|
1800
|
-
94vw (752px) was already narrower than either cap, so that viewport is
|
|
1801
|
-
unchanged by this. See task-13-report.md for the re-measured
|
|
1802
|
-
toolbar/canvas table. */
|
|
1816
|
+
/* Wave redesign Task 6b (spec section 4.1): one column, top to bottom:
|
|
1817
|
+
title bar, toolbar, the notice card, the stage, the hint bar. The side
|
|
1818
|
+
rail is gone and the stage takes the whole width. The width cap of
|
|
1819
|
+
1920px is v3.6.0 Task 13's: below it, 94vw governs at every measured
|
|
1820
|
+
screen size. position: relative because the toolbar's popovers are
|
|
1821
|
+
placed against the panel. */
|
|
1803
1822
|
.ed-wave-panel {
|
|
1804
|
-
background: #fff; border-radius: 6px; width: min(1920px, 94vw);
|
|
1823
|
+
position: relative; background: #fff; border-radius: 6px; width: min(1920px, 94vw);
|
|
1805
1824
|
height: min(760px, 90vh); display: flex; flex-direction: column;
|
|
1806
1825
|
box-shadow: 0 6px 28px rgba(0,0,0,.35); overflow: hidden;
|
|
1807
1826
|
}
|
|
1808
1827
|
.ed-wave-head {
|
|
1809
|
-
display: flex; align-items: center;
|
|
1810
|
-
padding: 8px 12px; border-bottom: 1px solid #e2e2e2;
|
|
1811
|
-
}
|
|
1828
|
+
display: flex; align-items: center; gap: 12px;
|
|
1829
|
+
padding: 8px 12px; border-bottom: 1px solid #e2e2e2;
|
|
1830
|
+
}
|
|
1831
|
+
.ed-wave-title { font-weight: bold; }
|
|
1832
|
+
/* The FILE's save state (spec section 4.8), the same two states and
|
|
1833
|
+
colours as the edit toolbar's own status slot. */
|
|
1834
|
+
.ed-wave-save { margin-left: auto; font-size: 12px; color: var(--md-ed-ok); }
|
|
1835
|
+
.ed-wave-save[data-dirty="1"] { color: var(--md-ed-dirty); }
|
|
1812
1836
|
.ed-wave-close {
|
|
1813
|
-
border:
|
|
1814
|
-
|
|
1815
|
-
}
|
|
1816
|
-
/* Which way out keeps the work and which throws it away. Escape is a modal's
|
|
1817
|
-
universal close key and here it discards a whole session, so the header
|
|
1818
|
-
says so next to the button that does not. Normal weight against the bar's
|
|
1819
|
-
bold heading: it is a caption, not a warning the user has to clear. */
|
|
1820
|
-
.ed-wave-escape-hint {
|
|
1821
|
-
margin-left: auto; margin-right: 12px; font-weight: normal;
|
|
1822
|
-
font-size: 11px; color: #7a4b00;
|
|
1837
|
+
border: 0; border-radius: 4px; background: transparent; color: #555;
|
|
1838
|
+
width: 28px; height: 28px; font-size: 15px; line-height: 1; cursor: pointer;
|
|
1823
1839
|
}
|
|
1840
|
+
.ed-wave-close:hover { background: var(--md-ed-hover); }
|
|
1841
|
+
/* Spec section 4.2: one row; groups are divided by a thin rule and 完成
|
|
1842
|
+
sits on the right. Buttons are quiet until hovered or switched on. */
|
|
1824
1843
|
.ed-wave-toolbar {
|
|
1825
|
-
display: flex; flex-wrap: wrap; gap: 12px; align-items: center;
|
|
1826
|
-
padding: 8px 12px; border-bottom: 1px solid #e2e2e2; background: #fafafa;
|
|
1827
|
-
}
|
|
1828
|
-
/* v3.6.0 Task 12: the eight captioned sections themselves. .ed-wave-tools
|
|
1829
|
-
is the row that holds them (the SAME element as .ed-wave-toolbar
|
|
1830
|
-
above -- that class still owns the bar's padding/border/background,
|
|
1831
|
-
this one owns only the group row's own gap). Each .ed-wave-tool-group
|
|
1832
|
-
is a bordered chip so eight adjacent sections read as eight things, not
|
|
1833
|
-
one longer wall of buttons; the caption sits to its buttons' LEFT (see
|
|
1834
|
-
wave-panels.js's toolGroup() for why not above). */
|
|
1835
|
-
.ed-wave-tools { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
|
|
1836
|
-
.ed-wave-tool-group {
|
|
1837
|
-
display: flex; align-items: center; gap: 4px;
|
|
1838
|
-
padding: 2px 6px; border: 1px solid #ececec; border-radius: 6px;
|
|
1839
|
-
}
|
|
1840
|
-
.ed-wave-tool-caption {
|
|
1841
|
-
font-size: 10px; color: #8a8a8a; letter-spacing: .5px;
|
|
1842
|
-
white-space: nowrap; margin-right: 2px;
|
|
1843
|
-
}
|
|
1844
|
-
.ed-wave-brushes, .ed-wave-meta {
|
|
1845
1844
|
display: flex; flex-wrap: wrap; gap: 4px; align-items: center;
|
|
1845
|
+
padding: 6px 12px; border-bottom: 1px solid #e2e2e2; background: #fafafa;
|
|
1846
1846
|
}
|
|
1847
|
-
/* v3.6.0 Task 10: widened from a 28x28 bare-character square to a small
|
|
1848
|
-
column (glyph on top, the character kept as a caption underneath) so
|
|
1849
|
-
the 22-brush palette says what each brush paints instead of relying on
|
|
1850
|
-
the letter alone -- nine of those 22 (P, N, u, d, h, l, 2, 6-9) have no
|
|
1851
|
-
obvious on-canvas picture from the character by itself. */
|
|
1852
|
-
/* '.ed-wave-brushes .ed-wave-brush', not the bare class: '.ed-wave-toolbar
|
|
1853
|
-
button' below outranks a single class on specificity (class+type beats
|
|
1854
|
-
class alone) and would otherwise win the padding fight and squeeze the
|
|
1855
|
-
24px-wide icon into a content box narrower than it is. */
|
|
1856
|
-
.ed-wave-brushes .ed-wave-brush {
|
|
1857
|
-
width: 34px; height: 40px; border: 1px solid #c4c4c4; border-radius: 4px;
|
|
1858
|
-
background: #fff; font-family: monospace; cursor: pointer;
|
|
1859
|
-
display: flex; flex-direction: column; align-items: center;
|
|
1860
|
-
justify-content: center; gap: 1px; padding: 2px 0; box-sizing: border-box;
|
|
1861
|
-
}
|
|
1862
|
-
.ed-wave-brush.is-on { background: #1a73e8; color: #fff; border-color: #1a73e8; }
|
|
1863
|
-
.ed-wave-brush-char { font-size: 11px; line-height: 1; }
|
|
1864
|
-
/* The glyph itself. Its shapes are drawn by wave-draw.js's levelIcon,
|
|
1865
|
-
which reuses renderCanvas's own drawing functions (bandOf, brickPath,
|
|
1866
|
-
the bus/clock/gap helpers) -- never a second hand-drawn set, so a
|
|
1867
|
-
change to how a brush paints on the real canvas cannot leave the
|
|
1868
|
-
toolbar showing the old picture. */
|
|
1869
|
-
.ed-wave-level-icon { display: block; overflow: visible; }
|
|
1870
1847
|
.ed-wave-toolbar button {
|
|
1871
|
-
border: 1px solid
|
|
1872
|
-
padding:
|
|
1873
|
-
}
|
|
1874
|
-
.ed-wave-toolbar button:
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
}
|
|
1900
|
-
/* The
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
.ed-wave-nolanes { color: #7a1f00; background: #ffe9e0; border-color: #e8a987; }
|
|
1905
|
-
/* v3.6.0 Task 13 fix round 1 (Critical 2): position: relative added.
|
|
1906
|
-
.ed-wave-side[data-expanded] below is position: absolute, and neither
|
|
1907
|
-
.ed-wave-panel nor (until this line) this element had a position of
|
|
1908
|
-
their own — so its containing block was .ed-wave-overlay
|
|
1909
|
-
(position: fixed; inset: 0), the WHOLE viewport, not the dialog.
|
|
1910
|
-
.ed-wave-panel's own overflow: hidden did not clip it either: clipping
|
|
1911
|
-
follows the containing-block chain, not DOM nesting, so the rail
|
|
1912
|
-
rendered against viewport corners at a narrow window and covered
|
|
1913
|
-
.ed-wave-head's 關閉 button — the only discoverable way to close the
|
|
1914
|
-
dialog (a hit-test at 關閉's own rectangle returned the edge panel's
|
|
1915
|
-
delete button instead; Escape still worked but was not discoverable).
|
|
1916
|
-
Anchoring the rail to THIS element rather than to .ed-wave-panel itself
|
|
1917
|
-
is deliberate, not incidental: .ed-wave-body sits below .ed-wave-head
|
|
1918
|
-
and above .ed-wave-status, so top: 0 / bottom: 0 on the rail now lands
|
|
1919
|
-
at body's own top/bottom — never over the header (關閉) or the status
|
|
1920
|
-
line — without this rule having to know either one's height. */
|
|
1921
|
-
.ed-wave-body { display: flex; flex: 1 1 auto; min-height: 0; position: relative; }
|
|
1922
|
-
/* v3.6.0 Task 13: the old comment here hand-computed the canvas's leftover
|
|
1923
|
-
width from the (now gone) .ed-wave-preview column's own flex-basis;
|
|
1924
|
-
that arithmetic no longer applies — the third column is .ed-wave-side
|
|
1925
|
-
below, which collapses to a 28px rail under 1100px specifically so the
|
|
1926
|
-
canvas is not squeezed the way a fixed 160px minimum used to squeeze it
|
|
1927
|
-
at 800x600 (MEASURED before this task: canvas width there was only
|
|
1928
|
-
282px). Re-measured after this task at four real viewports; see
|
|
1929
|
-
task-13-report.md rather than a formula here — a number derived by hand
|
|
1930
|
-
drifts the moment any sibling column's own width changes, which is
|
|
1931
|
-
exactly what this task did. */
|
|
1932
|
-
.ed-wave-lanes {
|
|
1933
|
-
flex: 0 0 200px; border-right: 1px solid #e2e2e2; overflow: auto;
|
|
1934
|
-
padding: 8px 0 8px 0;
|
|
1935
|
-
}
|
|
1936
|
-
/* The rows are absolutely positioned at index * laneHeight so they line up
|
|
1937
|
-
with the drawing beside them cell for cell. A group title is NOT a row —
|
|
1938
|
-
measured against the pinned engine in Task 2 — so it is drawn as a label in
|
|
1939
|
-
the rail spanning its lanes' rows instead, which is the shape the engine
|
|
1940
|
-
itself gives it (horizontal space in the name column, no vertical space). */
|
|
1941
|
-
.ed-wave-lane-rows { position: relative; }
|
|
1942
|
-
/* v3.6.0 Task 14: a row is a drag handle, a name field and (on a {}
|
|
1943
|
-
spacer) a badge. It used to be six buttons plus the field, and the six
|
|
1944
|
-
did not fit: flexed into what was left of 200px the field measured
|
|
1945
|
-
45px on the most crowded row, and a browser given a 24px box and a
|
|
1946
|
-
three-character Chinese caption lays that caption out VERTICALLY — the
|
|
1947
|
-
editor shipped columns of stacked glyphs where labels were meant.
|
|
1948
|
-
Those six are toolbar buttons now (the 訊號 / 群組 sections), acting
|
|
1949
|
-
on the one selection. */
|
|
1950
|
-
.ed-wave-lane-row {
|
|
1951
|
-
position: absolute; left: 0; right: 0; display: flex; align-items: center;
|
|
1952
|
-
gap: 6px; padding-right: 6px; box-sizing: border-box;
|
|
1953
|
-
}
|
|
1954
|
-
.ed-wave-lane-row.is-selected { background: #eaf2fe; }
|
|
1955
|
-
.ed-wave-lane-handle {
|
|
1956
|
-
flex: 0 0 auto; cursor: move; color: #aaa; user-select: none; font-size: 13px;
|
|
1957
|
-
}
|
|
1958
|
-
.ed-wave-lane-handle:active { cursor: move; }
|
|
1959
|
-
/* min-width, not just flex-grow: min-width is what a flex item will not
|
|
1960
|
-
shrink past, and shrinking past it is the vertical-text failure above.
|
|
1961
|
-
MEASURED after this change (real page, 800x600, the journey suite's own
|
|
1962
|
-
wave fixture): rail 201px, handle 9.5px, badge 36px, and the field comes
|
|
1963
|
-
out 178.5px ungrouped, 162.5px one group deep, 136.5px on the {} spacer
|
|
1964
|
-
row that also carries the badge — writing-mode horizontal-tb on all six.
|
|
1965
|
-
So 120 is slack at every depth this fixture reaches, NOT a computed
|
|
1966
|
-
exact fit. Deeper nesting spends 16px a level and would eventually hit
|
|
1967
|
-
the floor; there the field stops shrinking and .ed-wave-lanes scrolls,
|
|
1968
|
-
which is the trade this rule chooses on purpose. */
|
|
1969
|
-
.ed-wave-lane-name {
|
|
1970
|
-
flex: 1 1 auto; min-width: 120px; font-size: 12px; padding: 2px 4px;
|
|
1971
|
-
}
|
|
1972
|
-
.ed-wave-lane-badge { flex: 0 0 auto; font-size: 10px; color: #888; }
|
|
1973
|
-
.ed-wave-group, .ed-wave-group-input {
|
|
1974
|
-
position: absolute; width: 14px; padding: 0; font-size: 11px;
|
|
1975
|
-
writing-mode: vertical-rl; text-orientation: mixed; overflow: hidden;
|
|
1976
|
-
border: 1px solid #d8d8d8; border-radius: 3px; background: #f2f2f2;
|
|
1977
|
-
color: #555; cursor: pointer; box-sizing: border-box;
|
|
1978
|
-
}
|
|
1979
|
-
.ed-wave-group-input { width: 16px; height: 90px; cursor: text; }
|
|
1980
|
-
/* position: relative so the data-label field below (v3.5.0 Task 11) has
|
|
1981
|
-
an offsetParent to measure from: wave-ui.js reads canvas.offsetLeft /
|
|
1982
|
-
offsetTop against THIS element, not a hard-coded copy of the padding
|
|
1983
|
-
number just below, so the two cannot drift apart if that padding ever
|
|
1984
|
-
changes. */
|
|
1985
|
-
.ed-wave-canvas-wrap { flex: 1 1 auto; overflow: auto; padding: 8px; position: relative; }
|
|
1986
|
-
.ed-wave-canvas { display: block; }
|
|
1987
|
-
/* v3.5.0 Task 11: the bus data-label field, positioned by wave-ui.js's
|
|
1988
|
-
openDataEdit() from geometry.cellRect() — left/top/width/height are all
|
|
1989
|
-
set inline per cell, so only the visual skin lives here. Sized to sit
|
|
1990
|
-
exactly over the box it labels, text centred to match .ed-wave-buslabel
|
|
1991
|
-
beside it in the drawing underneath, box-sizing so the border does not
|
|
1992
|
-
push the field past the cell it is meant to cover. */
|
|
1993
|
-
.ed-wave-data-input {
|
|
1994
|
-
position: absolute; z-index: 1; box-sizing: border-box; text-align: center;
|
|
1995
|
-
font-size: 11px; padding: 0 2px; border: 1px solid #1a73e8; border-radius: 2px;
|
|
1996
|
-
background: #fff; color: #222;
|
|
1997
|
-
}
|
|
1998
|
-
.ed-wave-grid { stroke: #ececec; stroke-width: 1; }
|
|
1999
|
-
.ed-wave-line, .ed-wave-edge { stroke: #111; stroke-width: 2; fill: none; }
|
|
2000
|
-
/* v3.6.0 Task 10: the toolbar glyph's plain-level backbone (0/1/h/l/z/u/d
|
|
2001
|
-
only -- bus/clock/x/gap glyphs draw their own shape with their own
|
|
2002
|
-
fixed canvas colour instead, further down). Declared after the rule
|
|
2003
|
-
above on purpose: both are single-class selectors, so source order is
|
|
2004
|
-
what lets currentColor win over the fixed #111 for this one case,
|
|
2005
|
-
turning the icon white together with the rest of the button's text
|
|
2006
|
-
under .ed-wave-brush.is-on. */
|
|
1848
|
+
border: 1px solid transparent; border-radius: 4px; background: transparent;
|
|
1849
|
+
color: #333; padding: 4px 8px; font-size: 13px; min-height: 32px; cursor: pointer;
|
|
1850
|
+
}
|
|
1851
|
+
.ed-wave-toolbar button:hover { background: var(--md-ed-hover); }
|
|
1852
|
+
.ed-wave-toolbar button:disabled { opacity: .45; cursor: default; background: transparent; }
|
|
1853
|
+
.ed-wave-sep { width: 1px; align-self: stretch; margin: 4px 4px; background: #e2e2e2; }
|
|
1854
|
+
.ed-wave-toolbar .ed-wave-done {
|
|
1855
|
+
margin-left: auto; background: #1a73e8; color: #fff; border-color: #1a73e8; padding: 4px 14px;
|
|
1856
|
+
}
|
|
1857
|
+
.ed-wave-toolbar .ed-wave-done:hover { background: #1a73e8; }
|
|
1858
|
+
/* A brush: the level's glyph with its keyboard character in the lower
|
|
1859
|
+
right corner (spec section 4.2). The glyph is wave-draw.js levelIcon,
|
|
1860
|
+
built from the same brick helpers that model the engine's bricks. The
|
|
1861
|
+
selector names the button type so it outranks the generic toolbar
|
|
1862
|
+
button rule above. */
|
|
1863
|
+
button.ed-wave-brush {
|
|
1864
|
+
position: relative; width: 38px; height: 34px; padding: 2px; box-sizing: border-box;
|
|
1865
|
+
border: 1px solid #c4c4c4; border-radius: 4px; background: #fff; font-family: monospace;
|
|
1866
|
+
display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
|
|
1867
|
+
}
|
|
1868
|
+
.ed-wave-brush-char {
|
|
1869
|
+
position: absolute; right: 2px; bottom: 1px; font-size: 9px; line-height: 1; color: #555;
|
|
1870
|
+
}
|
|
1871
|
+
.ed-wave-toolbar button.ed-wave-brush.is-on, .ed-wave-pop button.ed-wave-brush.is-on,
|
|
1872
|
+
.ed-wave-toolbar .ed-wave-brush-more.is-on, .ed-wave-toolbar .ed-wave-edge-arm.is-on {
|
|
1873
|
+
background: #1a73e8; color: #fff; border-color: #1a73e8;
|
|
1874
|
+
}
|
|
1875
|
+
.ed-wave-brush.is-on .ed-wave-brush-char { color: #fff; }
|
|
1876
|
+
.ed-wave-level-icon { display: block; overflow: visible; }
|
|
1877
|
+
/* The glyph's shapes: the plain-level backbone follows the button's text
|
|
1878
|
+
colour (so a picked brush turns white with its caption); bus, x, clock
|
|
1879
|
+
and gap glyphs carry their own canvas colours. */
|
|
1880
|
+
.ed-wave-line { stroke: #111; stroke-width: 2; fill: none; }
|
|
2007
1881
|
.ed-wave-level-icon-path { stroke: currentColor; }
|
|
2008
1882
|
.ed-wave-weak { stroke-dasharray: 4 3; }
|
|
2009
|
-
/* v3.5.0 Task 10: the P/N explicit-edge arrowhead — see wave-ui.js's
|
|
2010
|
-
drawLane isClock branch for the measurement behind its shape and
|
|
2011
|
-
placement. Filled, no stroke, matching the line colour above. */
|
|
2012
1883
|
.ed-wave-clock-arrow { fill: #111; stroke: none; }
|
|
2013
1884
|
.ed-wave-x { fill: #d9d9d9; stroke: #111; stroke-width: 2; }
|
|
2014
1885
|
.ed-wave-bus { fill: #f5e9c8; stroke: #111; stroke-width: 2; }
|
|
2015
1886
|
.ed-wave-bus-3 { fill: #d8ead3; }
|
|
2016
1887
|
.ed-wave-bus-4 { fill: #d6e4f7; }
|
|
2017
1888
|
.ed-wave-bus-5 { fill: #f7d6e4; }
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
.ed-wave-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
.ed-wave-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
}
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
.ed-wave-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
1889
|
+
.ed-wave-gap { stroke: #111; stroke-width: 2; fill: none; }
|
|
1890
|
+
/* The one popover the toolbar has open at a time (wave-ui.js places it
|
|
1891
|
+
under its button): 更多's grouped brush grid, the 訊號 and 匯出 menus. */
|
|
1892
|
+
.ed-wave-pop {
|
|
1893
|
+
position: absolute; z-index: 4; background: #fff; border: 1px solid #e2e2e2;
|
|
1894
|
+
border-radius: 6px; box-shadow: 0 4px 16px var(--md-ed-shadow); padding: 6px;
|
|
1895
|
+
}
|
|
1896
|
+
.ed-wave-menu { display: flex; flex-direction: column; min-width: 140px; }
|
|
1897
|
+
.ed-wave-menu-item {
|
|
1898
|
+
border: 0; background: transparent; color: #333; text-align: left;
|
|
1899
|
+
padding: 6px 10px; font-size: 13px; border-radius: 4px; cursor: pointer;
|
|
1900
|
+
}
|
|
1901
|
+
.ed-wave-menu-item:hover, .ed-wave-menu-item:focus { background: var(--md-ed-hover); }
|
|
1902
|
+
/* Task 8: a menu item says the key its action also has (the grip menu's
|
|
1903
|
+
F2, Ctrl+D, Alt+arrows, Del) on its right; 刪除 is red. */
|
|
1904
|
+
.ed-wave-menu-item { display: flex; align-items: center; gap: 16px; }
|
|
1905
|
+
.ed-wave-menu-label { flex: 1 1 auto; }
|
|
1906
|
+
.ed-wave-menu-key { flex: 0 0 auto; font-size: 11px; color: #8a8a8a; }
|
|
1907
|
+
.ed-wave-menu-item.is-danger { color: var(--md-ed-err); }
|
|
1908
|
+
/* Task 8: the grip menu's 週期與相位 panel, period and phase of one lane. */
|
|
1909
|
+
.ed-wave-period-pop { display: flex; flex-direction: column; gap: 6px; }
|
|
1910
|
+
.ed-wave-period-row { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #333; }
|
|
1911
|
+
.ed-wave-period-cap { flex: 0 0 84px; }
|
|
1912
|
+
.ed-wave-period-input {
|
|
1913
|
+
width: 72px; box-sizing: border-box; padding: 3px 6px; font-size: 12px;
|
|
1914
|
+
border: 1px solid #e2e2e2; border-radius: 4px; background: #fff; color: #222;
|
|
1915
|
+
}
|
|
1916
|
+
/* Task 10 (spec section 4.6): the settings popover, the whole diagram.
|
|
1917
|
+
Title and cycle width on top, the overflow card under them, the rulers
|
|
1918
|
+
and the foot title folded under the advanced toggle. */
|
|
1919
|
+
.ed-wave-settings-pop { display: flex; flex-direction: column; gap: 6px; width: 260px; }
|
|
1920
|
+
.ed-wave-set-cap { font-size: 11px; color: #8a8a8a; margin-top: 2px; }
|
|
1921
|
+
.ed-wave-set-row { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #333; }
|
|
1922
|
+
.ed-wave-set-label { flex: 0 0 44px; }
|
|
1923
|
+
.ed-wave-set-input {
|
|
1924
|
+
flex: 1 1 auto; min-width: 0; box-sizing: border-box; padding: 3px 6px; font-size: 12px;
|
|
1925
|
+
font-family: inherit; border: 1px solid #e2e2e2; border-radius: 4px; background: #fff; color: #222;
|
|
1926
|
+
}
|
|
1927
|
+
textarea.ed-wave-set-input { resize: vertical; }
|
|
1928
|
+
.ed-wave-set-scales { display: flex; gap: 4px; }
|
|
1929
|
+
.ed-wave-set-scales button, .ed-wave-set-advanced, .ed-wave-set-fix {
|
|
1930
|
+
border: 1px solid #e2e2e2; border-radius: 4px; background: #fff; color: #333;
|
|
1931
|
+
padding: 3px 10px; font-size: 12px; cursor: pointer;
|
|
1932
|
+
}
|
|
1933
|
+
.ed-wave-set-scales button:hover, .ed-wave-set-advanced:hover, .ed-wave-set-fix:hover { background: var(--md-ed-hover); }
|
|
1934
|
+
.ed-wave-set-scales button[aria-pressed="true"] { background: #1a73e8; color: #fff; border-color: #1a73e8; }
|
|
1935
|
+
.ed-wave-set-advanced { align-self: flex-start; }
|
|
1936
|
+
.ed-wave-set-advanced::after { content: " ⌄"; }
|
|
1937
|
+
.ed-wave-set-advanced[aria-expanded="true"]::after { content: " ⌃"; }
|
|
1938
|
+
.ed-wave-set-adv { display: flex; flex-direction: column; gap: 6px; }
|
|
1939
|
+
.ed-wave-set-adv[hidden] { display: none; }
|
|
1940
|
+
.ed-wave-set-overflow {
|
|
1941
|
+
display: flex; flex-direction: column; align-items: flex-start; gap: 6px; padding: 6px 8px;
|
|
1942
|
+
border: 1px dashed var(--md-ed-dirty); border-radius: 4px; font-size: 12px; color: #333;
|
|
1943
|
+
}
|
|
1944
|
+
.ed-wave-set-overflow[hidden] { display: none; }
|
|
1945
|
+
.ed-wave-set-fix { color: var(--md-ed-dirty); border-color: var(--md-ed-dirty); }
|
|
1946
|
+
/* The export menu's last line: what every export looks like. */
|
|
1947
|
+
.ed-wave-export-note {
|
|
1948
|
+
border-top: 1px solid #e2e2e2; margin-top: 4px; padding: 6px 10px 2px; font-size: 11px; color: #8a8a8a;
|
|
1949
|
+
}
|
|
1950
|
+
/* The chip beside a label wider than its segment; one press widens the
|
|
1951
|
+
cycles. Above the layer, so a press on it never paints. */
|
|
1952
|
+
.ed-wave-overflow-chip {
|
|
1953
|
+
position: absolute; z-index: 2; white-space: nowrap; padding: 1px 6px; font-size: 11px;
|
|
1954
|
+
line-height: 14px; border: 1px solid var(--md-ed-dirty); border-radius: 8px;
|
|
1955
|
+
background: var(--md-ed-surface); color: var(--md-ed-dirty); cursor: pointer;
|
|
1956
|
+
}
|
|
1957
|
+
.ed-wave-overflow-chip[hidden] { display: none; }
|
|
1958
|
+
.ed-wave-brush-grid { display: flex; flex-direction: column; gap: 6px; }
|
|
1959
|
+
.ed-wave-brush-group { display: flex; align-items: center; gap: 8px; }
|
|
1960
|
+
.ed-wave-brush-group-title { flex: 0 0 32px; font-size: 11px; color: #8a8a8a; }
|
|
1961
|
+
.ed-wave-brushes { display: flex; flex-wrap: wrap; gap: 4px; }
|
|
1962
|
+
/* Spec section 4.9: what the editor cannot place, right under the
|
|
1963
|
+
toolbar, in neutral colours, and it can be folded away. */
|
|
1964
|
+
.ed-wave-notice {
|
|
1965
|
+
display: flex; align-items: flex-start; gap: 8px; padding: 6px 12px;
|
|
1966
|
+
border-bottom: 1px solid #e2e2e2; background: #fafafa; color: #555; font-size: 12px;
|
|
1967
|
+
}
|
|
1968
|
+
.ed-wave-notice[hidden] { display: none; }
|
|
1969
|
+
.ed-wave-notice-text { flex: 1 1 auto; }
|
|
1970
|
+
.ed-wave-notice[data-folded] .ed-wave-notice-text {
|
|
1971
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
1972
|
+
}
|
|
1973
|
+
.ed-wave-notice-fold {
|
|
1974
|
+
flex: 0 0 auto; border: 0; background: transparent; color: #1a73e8;
|
|
1975
|
+
font-size: 12px; cursor: pointer; padding: 0 4px;
|
|
1976
|
+
}
|
|
1977
|
+
/* The stage: the canvas's scroll container, the dialog's whole width.
|
|
1978
|
+
position: relative so the data-label field (wave-ui.js openDataEdit)
|
|
1979
|
+
is placed in its scrolled content box. */
|
|
1980
|
+
.ed-wave-stage {
|
|
1981
|
+
flex: 1 1 auto; min-height: 0; overflow: auto; padding: 8px; position: relative;
|
|
1982
|
+
}
|
|
1983
|
+
/* The engine's svg and the interaction layer share this box. The top
|
|
1984
|
+
padding is the room for the layer's cycle ruler, which sits above the
|
|
1985
|
+
engine's drawing (wave-ui.js RULER_GAP is the baseline's distance). */
|
|
1986
|
+
.ed-wave-canvas-host { position: relative; display: inline-block; padding-top: 20px; }
|
|
1987
|
+
.ed-wave-canvas-error { color: #b00020; padding: 8px; }
|
|
1988
|
+
.ed-wave-canvas { display: block; }
|
|
1989
|
+
/* The drawing is in the Tab cycle, so it has to say when it holds the
|
|
1990
|
+
keyboard. */
|
|
2064
1991
|
.ed-wave-canvas:focus { outline: 2px solid #1a73e8; outline-offset: 2px; }
|
|
2065
|
-
/* The
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
.ed-wave-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
.ed-wave-
|
|
2086
|
-
.ed-wave-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
.ed-wave-edge-hit { stroke: transparent; stroke-width: 10; fill: none; pointer-events: stroke; }
|
|
2101
|
-
/* v3.5.0 Task 11 fix round 4 audit, closed in round 5: this text (drawn
|
|
2102
|
-
by renderEdges, AFTER .ed-wave-edge-hit) is the same shape of hazard as
|
|
2103
|
-
.ed-wave-selection and .ed-wave-gap above — purely decorative, sits on
|
|
2104
|
-
top of the fat invisible click target a press aimed at SELECTING the
|
|
2105
|
-
edge is meant to land on. Round 4 found this but could not apply
|
|
2106
|
-
pointer-events: none to the bare class: ed-wave-edge-label is not this
|
|
2107
|
-
text's own class alone — wave-ui.js's sidebar edgeLabelInput (the real,
|
|
2108
|
-
keyboard-and-mouse text field for editing an edge's label) uses the
|
|
2109
|
-
IDENTICAL class name, and a blanket rule would have also landed on
|
|
2110
|
-
that input field and blocked clicking into it with the mouse.
|
|
2111
|
-
Round 5's fix: scope the SELECTOR by element instead of renaming
|
|
2112
|
-
anything. text.ed-wave-edge-label only ever matches the SVG <text>
|
|
2113
|
-
wave-ui.js draws on the canvas — the sidebar control is an <input>, a
|
|
2114
|
-
different element entirely, and keeps its own default pointer-events
|
|
2115
|
-
untouched no matter what class it shares. */
|
|
2116
|
-
text.ed-wave-edge-label { pointer-events: none; }
|
|
2117
|
-
.ed-wave-edge-label { font-size: 11px; fill: #333; }
|
|
2118
|
-
/* v3.6.0 Task 8: the canvas's own name column — a mirror of the rail's
|
|
2119
|
-
.ed-wave-lane-name / .ed-wave-group (see the comment above
|
|
2120
|
-
.ed-wave-lane-rows). Unlike .ed-wave-edge-label these two class names
|
|
2121
|
-
are not shared with any non-SVG control, so pointer-events: none goes
|
|
2122
|
-
straight on the class rather than needing the element-scoped selector
|
|
2123
|
-
that rule uses. MEASURED (task-8-report.md): the longest lane name in
|
|
2124
|
-
this repo's fixtures is 12 chars ("he said \"hi\""), which at 11px in
|
|
2125
|
-
both the app's default sans-serif and the worst case (monospace)
|
|
2126
|
-
measures well under the 110px a top-level name has to fit in
|
|
2127
|
-
(nameColWidth 120 - NAME_GAP 10), so SIZES.nameColWidth stays 120. */
|
|
2128
|
-
.ed-wave-lane-label, .ed-wave-group-label { pointer-events: none; }
|
|
2129
|
-
.ed-wave-lane-label { font-size: 11px; fill: #333; }
|
|
2130
|
-
.ed-wave-group-label {
|
|
2131
|
-
font-size: 11px; fill: #555; writing-mode: vertical-rl; text-orientation: mixed;
|
|
2132
|
-
}
|
|
2133
|
-
/* v3.6.0 Task 9: the cycle ruler and the head/foot bands — see
|
|
2134
|
-
wave-draw.js's drawRuler()/drawBand() comment for the geometry (tick on
|
|
2135
|
-
boundaries, tock at centres, both filtered by every) and why this is
|
|
2136
|
-
read-only with respect to the document. Purely decorative like the name
|
|
2137
|
-
column above it, so pointer-events: none on the same principle. The
|
|
2138
|
-
ruler's own fill matches the pinned engine's own muted class (fill
|
|
2139
|
-
#aaa, node_modules/wavedrom's waveSkin CSS) so a tick/tock painted here
|
|
2140
|
-
reads the same shade as the identical mark in the WaveDrom preview
|
|
2141
|
-
beside it; the head/foot titles use the darker ed-wave-lane-label shade
|
|
2142
|
-
instead, matching the engine's own plain black fill for head.text /
|
|
2143
|
-
foot.text closely enough at this scale without introducing a third
|
|
2144
|
-
grey. */
|
|
2145
|
-
.ed-wave-ruler-tick, .ed-wave-ruler-tock, .ed-wave-head-text, .ed-wave-foot-text {
|
|
2146
|
-
pointer-events: none;
|
|
1992
|
+
/* The interaction layer's marks (wave-draw.js createLayer). None of them
|
|
1993
|
+
takes a press: the layer svg itself does, and the pointer is resolved
|
|
1994
|
+
to a cell by geometry. The exceptions are a transition dot, which takes
|
|
1995
|
+
it through its wider transparent ring, and the edges' hit twins below. */
|
|
1996
|
+
.ed-wave-ruler-num, .ed-wave-ruler-plus, .ed-wave-row-hover, .ed-wave-hover-col,
|
|
1997
|
+
.ed-wave-selection, .ed-wave-cursor, .ed-wave-dot-mark, .ed-wave-pending,
|
|
1998
|
+
.ed-wave-edge-sel, .ed-wave-edge-end, .ed-wave-overflow, .ed-wave-drop-line { pointer-events: none; }
|
|
1999
|
+
/* Task 8: where a lane dragged by its grip will land. */
|
|
2000
|
+
.ed-wave-drop-line { stroke: #1a73e8; stroke-width: 2; }
|
|
2001
|
+
.ed-wave-ruler-num { font-size: 11px; fill: #aaa; }
|
|
2002
|
+
.ed-wave-ruler-num.is-on { fill: #1a73e8; font-weight: bold; }
|
|
2003
|
+
.ed-wave-ruler-plus-glyph { font-size: 14px; font-weight: bold; fill: #1a73e8; }
|
|
2004
|
+
/* Task 7: the ruler's plus inserts a cycle where it stands, so its ring
|
|
2005
|
+
(wave-draw.js createLayer) is the one ruler mark that takes a press. */
|
|
2006
|
+
.ed-wave-ruler-plus-hit { pointer-events: all; cursor: pointer; }
|
|
2007
|
+
.ed-wave-row-hover, .ed-wave-hover-col { fill: var(--md-ed-sel); }
|
|
2008
|
+
.ed-wave-selection { fill: rgba(26,115,232,.18); stroke: #1a73e8; stroke-width: 1; }
|
|
2009
|
+
/* The keyboard's own cell, deliberately not the selection's mark: the
|
|
2010
|
+
selection is the range an operation acts on, the cursor the one cell
|
|
2011
|
+
the next keystroke paints, and both can be on screen at once. */
|
|
2012
|
+
.ed-wave-cursor { fill: none; stroke: #d93025; stroke-width: 2; }
|
|
2013
|
+
.ed-wave-dot { cursor: crosshair; }
|
|
2014
|
+
.ed-wave-dot-hit { pointer-events: all; }
|
|
2015
|
+
.ed-wave-dot-mark { fill: #fff; stroke: #1a73e8; stroke-width: 1.5; }
|
|
2016
|
+
.ed-wave-dot.is-hot .ed-wave-dot-mark { fill: #1a73e8; stroke: #0041c4; }
|
|
2017
|
+
.ed-wave-pending { stroke: #1a73e8; stroke-width: 2; stroke-dasharray: 5 4; fill: none; }
|
|
2018
|
+
.ed-wave-edge-sel { stroke: #1a73e8; stroke-width: 2; fill: none; }
|
|
2019
|
+
.ed-wave-edge-end { fill: #1a73e8; stroke: #fff; stroke-width: 1.5; }
|
|
2020
|
+
.ed-wave-overflow { fill: none; stroke: var(--md-ed-dirty); stroke-width: 1.5; stroke-dasharray: 3 2; }
|
|
2021
|
+
/* The bus data-label field, placed over its cell by openDataEdit; only
|
|
2022
|
+
the skin lives here. */
|
|
2023
|
+
.ed-wave-data-input {
|
|
2024
|
+
position: absolute; z-index: 1; box-sizing: border-box; text-align: center;
|
|
2025
|
+
font-size: 11px; padding: 0 2px; border: 1px solid #1a73e8; border-radius: 2px;
|
|
2026
|
+
background: #fff; color: #222;
|
|
2147
2027
|
}
|
|
2148
|
-
.
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2028
|
+
/* Task 8 (spec section 4.4): the name column. Each row and each group
|
|
2029
|
+
title has a grip, laid over the column by wave-ui.js (the box sits on
|
|
2030
|
+
the canvas host padding box, above the layer). A grip shows for the row
|
|
2031
|
+
the pointer is over, while it has the keyboard and while its menu is
|
|
2032
|
+
open; otherwise it is invisible and takes no press, so the column under
|
|
2033
|
+
it stays the name the pointer means. */
|
|
2034
|
+
.ed-wave-grips { position: absolute; left: 0; top: 0; width: 0; height: 0; z-index: 2; }
|
|
2035
|
+
.ed-wave-grip {
|
|
2036
|
+
position: absolute; width: 16px; height: 22px; padding: 0; margin: 0;
|
|
2037
|
+
display: flex; align-items: center; justify-content: center;
|
|
2038
|
+
border: 0; border-radius: 4px; background: var(--md-ed-surface); color: var(--md-ed-glyph);
|
|
2039
|
+
font-size: 13px; line-height: 1; cursor: pointer; opacity: 0; pointer-events: none;
|
|
2040
|
+
}
|
|
2041
|
+
.ed-wave-grip.is-on, .ed-wave-grip:focus, .ed-wave-grip[aria-expanded="true"] {
|
|
2042
|
+
opacity: 1; pointer-events: auto;
|
|
2043
|
+
}
|
|
2044
|
+
.ed-wave-grip:hover, .ed-wave-grip[aria-expanded="true"] { background: var(--md-ed-hover); color: #333; }
|
|
2045
|
+
.ed-wave-grip:focus { outline: 2px solid #1a73e8; outline-offset: 0; }
|
|
2046
|
+
/* The in-place name field over a signal name (right-aligned, like the
|
|
2047
|
+
name the engine draws) or a group title. */
|
|
2048
|
+
.ed-wave-rename {
|
|
2049
|
+
position: absolute; z-index: 3; box-sizing: border-box; text-align: right;
|
|
2050
|
+
font-size: 14px; padding: 0 6px; border: 1px solid #1a73e8; border-radius: 3px;
|
|
2051
|
+
background: #fff; color: #222;
|
|
2161
2052
|
}
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
.ed-wave-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
(
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
.ed-wave-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
}
|
|
2208
|
-
.ed-wave-
|
|
2209
|
-
.ed-wave-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
}
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
zero-size, unhittable body the whole time .ed-wave-side itself is
|
|
2220
|
-
collapsed to its <1100px 28px rail (below): 關聯線/文件/Lane all default
|
|
2221
|
-
data-open, and none of that changes just because the rail hides them.
|
|
2222
|
-
Left deliberately this way rather than making data-open track actual
|
|
2223
|
-
visibility — that would conflate "this section is open" with "the rail
|
|
2224
|
-
happens to be showing right now", and toggling ONE section would then
|
|
2225
|
-
have to silently relabel every OTHER already-open section's own state
|
|
2226
|
-
too. What actually closes the gap this could otherwise leave (a control
|
|
2227
|
-
that LOOKS reachable in the DOM but is not) is wave-ui.js's render()
|
|
2228
|
-
expanding the rail on the one transition that matters (selecting an
|
|
2229
|
-
edge) rather than this attribute pretending to mean something it does
|
|
2230
|
-
not. */
|
|
2231
|
-
.ed-wave-section[data-open] .ed-wave-section-body { display: block; }
|
|
2232
|
-
.ed-wave-section-body label { display: flex; align-items: center; gap: 6px; margin: 4px 0; }
|
|
2233
|
-
.ed-wave-section-body label span { flex: 0 0 96px; font-size: 12px; color: #555; }
|
|
2234
|
-
.ed-wave-section-body input {
|
|
2235
|
-
flex: 1 1 auto; min-width: 0; font-size: 12px; padding: 2px 4px;
|
|
2236
|
-
}
|
|
2237
|
-
.ed-wave-source {
|
|
2238
|
-
margin: 0; font-size: 11px; line-height: 1.45; white-space: pre;
|
|
2239
|
-
overflow: auto; max-height: 260px;
|
|
2240
|
-
}
|
|
2241
|
-
/* 視窗窄於這條線時右欄收成一條直立標籤,展開時覆蓋在畫布上 —— 否則
|
|
2242
|
-
800x600 的測試視窗下畫布會被 340px 固定寬的側欄吃掉大半,拖曳手勢會
|
|
2243
|
-
不可用(v3.6.0 Task 13 自己的 .ed-wave-lanes 註解量到的舊值:只剩
|
|
2244
|
-
282px)。收起時仍是普通文件流寬度(28px),展開時疊在畫布上方而不擠
|
|
2245
|
-
壓它。data-expanded 有三個來源:wave-panels.js 的 section() 點擊 title、
|
|
2246
|
-
同檔案的 collapseBtn 反過來移除它、以及 fix round 3 新增的
|
|
2247
|
-
wave-panels.js 的 expandSide()(由 wave-ui.js 的 render() 在
|
|
2248
|
-
selectedEdge 從 null 變成有值的那一拍呼叫——選取一條 edge 之後,
|
|
2249
|
-
整個回饋只長在這個側欄的關聯線面板裡,窄視窗下不展開就等於使用者
|
|
2250
|
-
什麼都看不到、也按不到)。 */
|
|
2251
|
-
/* v3.6.0 Task 13 fix round 1: the collapse-back button. Hidden everywhere
|
|
2252
|
-
except the one state it exists for — the <1100px rail, expanded — so it
|
|
2253
|
-
never shows, and never sits in the tab order, at a wide viewport or in
|
|
2254
|
-
the rail's own default collapsed state. */
|
|
2255
|
-
.ed-wave-side-collapse { display: none; }
|
|
2256
|
-
@media (max-width: 1100px) {
|
|
2257
|
-
.ed-wave-side { flex: 0 0 28px; padding: 4px 2px; }
|
|
2258
|
-
.ed-wave-side:not([data-expanded]) .ed-wave-section-body { display: none; }
|
|
2259
|
-
.ed-wave-side[data-expanded] {
|
|
2260
|
-
position: absolute; right: 0; top: 0; bottom: 0; width: 340px;
|
|
2261
|
-
background: #ffffff; z-index: 3; flex: 0 0 340px; padding: 8px;
|
|
2262
|
-
}
|
|
2263
|
-
.ed-wave-side[data-expanded] .ed-wave-side-collapse {
|
|
2264
|
-
display: block; width: 100%; text-align: right; border: 0;
|
|
2265
|
-
background: transparent; font-size: 12px; color: #555; cursor: pointer;
|
|
2266
|
-
padding: 2px 4px 6px;
|
|
2267
|
-
}
|
|
2053
|
+
.ed-wave-rename.is-group { text-align: left; }
|
|
2054
|
+
/* The add-a-signal row under the last lane. */
|
|
2055
|
+
.ed-wave-add-lane {
|
|
2056
|
+
display: block; margin: 4px 0 0; border: 0; border-radius: 4px; background: transparent;
|
|
2057
|
+
color: #1a73e8; font-size: 12px; padding: 4px 8px; cursor: pointer;
|
|
2058
|
+
}
|
|
2059
|
+
.ed-wave-add-lane:hover, .ed-wave-add-lane:focus { background: var(--md-ed-hover); }
|
|
2060
|
+
/* Spec section 4.3: the floating toolbar over a selected run of cycles,
|
|
2061
|
+
placed against the panel by wave-ui.js (above the run, or below it when
|
|
2062
|
+
there is no room). Under the popovers (z-index 4), so 電位's grid opens
|
|
2063
|
+
over it. */
|
|
2064
|
+
.ed-wave-range {
|
|
2065
|
+
position: absolute; z-index: 3; display: flex; align-items: center; gap: 2px;
|
|
2066
|
+
padding: 3px; background: #fff; border: 1px solid #e2e2e2; border-radius: 6px;
|
|
2067
|
+
box-shadow: 0 4px 16px var(--md-ed-shadow); white-space: nowrap;
|
|
2068
|
+
}
|
|
2069
|
+
.ed-wave-range button {
|
|
2070
|
+
border: 0; border-radius: 4px; background: transparent; color: #333;
|
|
2071
|
+
padding: 4px 8px; font-size: 12px; cursor: pointer;
|
|
2072
|
+
}
|
|
2073
|
+
.ed-wave-range button:hover, .ed-wave-range button:focus { background: var(--md-ed-hover); }
|
|
2074
|
+
/* Task 9 (spec section 4.5): the edges. The engine draws them under the
|
|
2075
|
+
layer, so the layer carries a transparent twin of each for a press to
|
|
2076
|
+
land on: the line (hit on its wide stroke only), the label box and an
|
|
2077
|
+
unlabelled self-loop ring (hit on their area). A selected edge has a
|
|
2078
|
+
ring at each end that takes a press, under its visible mark. The
|
|
2079
|
+
selection is drawn with the engine path and its matrix, so its width
|
|
2080
|
+
must not scale with it. */
|
|
2081
|
+
.ed-wave-edge-hit { pointer-events: stroke; cursor: pointer; }
|
|
2082
|
+
.ed-wave-edge-label-hit, .ed-wave-edge-loop-hit { pointer-events: all; }
|
|
2083
|
+
.ed-wave-edge-end-hit { pointer-events: all; cursor: move; }
|
|
2084
|
+
.ed-wave-edge-sel { vector-effect: non-scaling-stroke; }
|
|
2085
|
+
.ed-wave-note-sel { fill: none; stroke: #1a73e8; stroke-width: 1.5; pointer-events: none; }
|
|
2086
|
+
/* The toolbar beside a selected edge or note (right of its label),
|
|
2087
|
+
placed against the panel like the range toolbar; 更多 lists its shapes
|
|
2088
|
+
on a second row. */
|
|
2089
|
+
.ed-wave-edge-bar {
|
|
2090
|
+
position: absolute; z-index: 3; display: flex; flex-wrap: wrap; align-items: center; gap: 2px;
|
|
2091
|
+
max-width: 360px; padding: 3px; background: #fff; border: 1px solid #e2e2e2; border-radius: 6px;
|
|
2092
|
+
box-shadow: 0 4px 16px var(--md-ed-shadow); white-space: nowrap;
|
|
2093
|
+
}
|
|
2094
|
+
.ed-wave-edge-group { display: flex; gap: 1px; padding-right: 3px; margin-right: 1px; border-right: 1px solid #e2e2e2; }
|
|
2095
|
+
.ed-wave-edge-bar button {
|
|
2096
|
+
border: 0; border-radius: 4px; background: transparent; color: #333;
|
|
2097
|
+
padding: 4px 8px; font-size: 12px; cursor: pointer;
|
|
2098
|
+
}
|
|
2099
|
+
.ed-wave-edge-bar button:hover, .ed-wave-edge-bar button:focus { background: var(--md-ed-hover); }
|
|
2100
|
+
.ed-wave-edge-bar button[aria-pressed="true"] { background: #1a73e8; color: #fff; }
|
|
2101
|
+
.ed-wave-edge-bar button.is-danger { color: var(--md-ed-err); }
|
|
2102
|
+
.ed-wave-edge-more-list { display: flex; flex-wrap: wrap; gap: 1px; flex-basis: 100%; }
|
|
2103
|
+
.ed-wave-edge-more-list[hidden] { display: none; }
|
|
2104
|
+
.ed-wave-edge-more-list button { font-family: monospace; }
|
|
2105
|
+
/* The one-line field over an edge's label or a point note. */
|
|
2106
|
+
.ed-wave-edge-input {
|
|
2107
|
+
position: absolute; z-index: 3; box-sizing: border-box; text-align: center;
|
|
2108
|
+
font-size: 12px; padding: 0 4px; border: 1px solid #1a73e8; border-radius: 3px;
|
|
2109
|
+
background: #fff; color: #222;
|
|
2268
2110
|
}
|
|
2269
|
-
.
|
|
2270
|
-
|
|
2271
|
-
|
|
2111
|
+
/* Spec section 4.8: the bottom line says what can be done next, never
|
|
2112
|
+
what just happened (results go to the toast). Task 11: the sentence
|
|
2113
|
+
follows the state, and 「? 全部快捷鍵」 sits fixed at its right end. */
|
|
2114
|
+
.ed-wave-hint {
|
|
2115
|
+
display: flex; align-items: center; gap: 12px;
|
|
2116
|
+
border-top: 1px solid #e2e2e2; padding: 3px 8px 3px 12px; font-size: 12px; color: #444; min-height: 24px;
|
|
2117
|
+
}
|
|
2118
|
+
.ed-wave-hint-text { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2119
|
+
.ed-wave-hint-help {
|
|
2120
|
+
flex: 0 0 auto; border: 0; border-radius: 4px; background: transparent; color: #1a73e8;
|
|
2121
|
+
font-size: 12px; padding: 3px 8px; cursor: pointer;
|
|
2122
|
+
}
|
|
2123
|
+
.ed-wave-hint-help:hover, .ed-wave-hint-help[aria-expanded="true"] { background: var(--md-ed-hover); }
|
|
2124
|
+
/* Task 11: the shortcut card, in the middle of the editor (wave-ui.js
|
|
2125
|
+
centres it), above every other popover and floating toolbar. */
|
|
2126
|
+
.ed-wave-shortcuts {
|
|
2127
|
+
z-index: 5; box-sizing: border-box; width: min(600px, calc(100% - 16px));
|
|
2128
|
+
max-height: calc(100% - 16px); overflow: auto; padding: 10px 14px 14px;
|
|
2129
|
+
}
|
|
2130
|
+
.ed-wave-shortcuts:focus { outline: none; }
|
|
2131
|
+
.ed-wave-shortcuts-head { display: flex; align-items: center; margin-bottom: 4px; }
|
|
2132
|
+
.ed-wave-shortcuts-title { flex: 1 1 auto; font-weight: bold; font-size: 14px; color: #222; }
|
|
2133
|
+
.ed-wave-shortcuts-close {
|
|
2134
|
+
border: 0; border-radius: 4px; background: transparent; color: #555;
|
|
2135
|
+
width: 26px; height: 26px; font-size: 13px; cursor: pointer;
|
|
2136
|
+
}
|
|
2137
|
+
.ed-wave-shortcuts-close:hover { background: var(--md-ed-hover); }
|
|
2138
|
+
.ed-wave-shortcuts-body { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 24px; }
|
|
2139
|
+
.ed-wave-shortcut-title { font-weight: bold; font-size: 12px; color: #444; margin: 6px 0 4px; }
|
|
2140
|
+
.ed-wave-shortcut-row { display: flex; align-items: baseline; gap: 10px; font-size: 12px; color: #333; padding: 2px 0; }
|
|
2141
|
+
.ed-wave-shortcut-keys { flex: 0 0 92px; display: flex; flex-wrap: wrap; gap: 3px; }
|
|
2142
|
+
.ed-wave-shortcut-keys kbd {
|
|
2143
|
+
font-family: inherit; font-size: 11px; line-height: 16px; padding: 0 4px;
|
|
2144
|
+
border: 1px solid #e2e2e2; border-radius: 3px; background: #fff; color: #222; white-space: nowrap;
|
|
2145
|
+
}
|
|
2146
|
+
/* The cursor narration: for a screen reader only, so visually hidden but
|
|
2147
|
+
never display: none (a live region that is not rendered is not read). */
|
|
2148
|
+
.ed-wave-live {
|
|
2149
|
+
position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
|
|
2150
|
+
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
|
|
2272
2151
|
}
|
|
2273
2152
|
.ed-wave-parse-error { padding: 16px; }
|
|
2274
2153
|
.ed-wave-parse-message { color: #b00020; font-weight: bold; }
|
|
@@ -6377,6 +6256,9 @@ if (require.main === module) {
|
|
|
6377
6256
|
const [,, src, dst] = process.argv;
|
|
6378
6257
|
const BAKE_SVG = process.argv.slice(4).includes('--bake-svg');
|
|
6379
6258
|
const LANG_ARG = (process.argv.slice(4).find((a) => a.startsWith('--lang=')) || '').slice('--lang='.length) || undefined;
|
|
6259
|
+
// bin/md2doc.js renders each format of one input in its own process and
|
|
6260
|
+
// passes this to every one after the first, so --html --pdf warns once.
|
|
6261
|
+
const WAVE_WARN = !process.argv.slice(4).includes('--no-wave-warn');
|
|
6380
6262
|
if (!src || !dst) {
|
|
6381
6263
|
console.error('Usage: node md2doc.js <input.md> <output.html|pdf>');
|
|
6382
6264
|
process.exit(1);
|
|
@@ -6386,7 +6268,7 @@ if (require.main === module) {
|
|
|
6386
6268
|
|
|
6387
6269
|
(async () => {
|
|
6388
6270
|
const mdText = fs.readFileSync(src, 'utf8');
|
|
6389
|
-
const { html } = await renderMarkdown(mdText, path.resolve(src), { lang: LANG_ARG });
|
|
6271
|
+
const { html } = await renderMarkdown(mdText, path.resolve(src), { lang: LANG_ARG, warnWaveOverflow: WAVE_WARN ? src : undefined });
|
|
6390
6272
|
let finalHtml = html;
|
|
6391
6273
|
|
|
6392
6274
|
if (ext === '.html') {
|