@rasensio/aidlc 1.77.0 → 1.79.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.
Files changed (58) hide show
  1. package/README.md +7 -3
  2. package/dist/cli.js +2 -2
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/menu.d.ts +5 -8
  5. package/dist/commands/menu.d.ts.map +1 -1
  6. package/dist/commands/menu.js +12 -13
  7. package/dist/commands/menu.js.map +1 -1
  8. package/dist/commands/status.d.ts +9 -0
  9. package/dist/commands/status.d.ts.map +1 -1
  10. package/dist/commands/status.js +24 -0
  11. package/dist/commands/status.js.map +1 -1
  12. package/dist/console/autopilot-view.d.ts +50 -0
  13. package/dist/console/autopilot-view.d.ts.map +1 -0
  14. package/dist/console/autopilot-view.js +99 -0
  15. package/dist/console/autopilot-view.js.map +1 -0
  16. package/dist/console/harness.d.ts +84 -0
  17. package/dist/console/harness.d.ts.map +1 -0
  18. package/dist/console/harness.js +191 -0
  19. package/dist/console/harness.js.map +1 -0
  20. package/dist/console/index.d.ts +28 -0
  21. package/dist/console/index.d.ts.map +1 -0
  22. package/dist/console/index.js +154 -0
  23. package/dist/console/index.js.map +1 -0
  24. package/dist/console/route.d.ts +45 -0
  25. package/dist/console/route.d.ts.map +1 -0
  26. package/dist/console/route.js +118 -0
  27. package/dist/console/route.js.map +1 -0
  28. package/dist/console/session.d.ts +92 -0
  29. package/dist/console/session.d.ts.map +1 -0
  30. package/dist/console/session.js +337 -0
  31. package/dist/console/session.js.map +1 -0
  32. package/dist/console/standing.d.ts +44 -0
  33. package/dist/console/standing.d.ts.map +1 -0
  34. package/dist/console/standing.js +96 -0
  35. package/dist/console/standing.js.map +1 -0
  36. package/dist/console/terminal.d.ts +31 -0
  37. package/dist/console/terminal.d.ts.map +1 -0
  38. package/dist/console/terminal.js +216 -0
  39. package/dist/console/terminal.js.map +1 -0
  40. package/dist/menu/index.d.ts +7 -24
  41. package/dist/menu/index.d.ts.map +1 -1
  42. package/dist/menu/index.js +6 -86
  43. package/dist/menu/index.js.map +1 -1
  44. package/dist/roadmap/graph.d.ts +53 -3
  45. package/dist/roadmap/graph.d.ts.map +1 -1
  46. package/dist/roadmap/graph.js +126 -54
  47. package/dist/roadmap/graph.js.map +1 -1
  48. package/dist/roadmap/view-assets.d.ts +5 -0
  49. package/dist/roadmap/view-assets.d.ts.map +1 -1
  50. package/dist/roadmap/view-assets.js +302 -91
  51. package/dist/roadmap/view-assets.js.map +1 -1
  52. package/dist/roadmap/view-html.d.ts +11 -1
  53. package/dist/roadmap/view-html.d.ts.map +1 -1
  54. package/dist/roadmap/view-html.js +166 -35
  55. package/dist/roadmap/view-html.js.map +1 -1
  56. package/dist/roadmap/view-model.js +1 -1
  57. package/dist/roadmap/view-model.js.map +1 -1
  58. package/package.json +2 -2
@@ -11,84 +11,136 @@
11
11
  * plain ES2020 without template literals so `String.raw` can hold it unescaped; a test
12
12
  * fails it if it grows past 16 KB or ever contains `</script`.
13
13
  *
14
+ * roadmap-view-revamp added the tabs, per-tab filters kept in the URL hash, the theme,
15
+ * shortcuts and copy buttons. Visibility is still CSS: the script sets classes and
16
+ * attributes, the stylesheet hides. Every colour is a token on `:root` and
17
+ * `:root[data-theme="dark"]`, which the contrast test reads (roadmap-view-revamp/AC-24).
18
+ *
14
19
  * @module
15
20
  */
16
21
  export const VIEW_STYLE = String.raw `
17
- :root { color-scheme: light; --ink: #111827; --muted: #374151; --line: #d1d5db; --focus: #1d4ed8; --link: #1e40af; }
22
+ :root { color-scheme: light; --bg: #ffffff; --bg-2: #f6f7f9; --card: #ffffff; --ink: #1b1d22; --muted: #5b616e; --line: #e1e4e8; --line-2: #c4c9d1;
23
+ --link: #3e4ab8; --accent-ink: #3e4ab8; --chip-on: #e8eafb; --focus: #5e6ad2; --unmet: #c9252d; --shade: rgba(15, 17, 20, .12);
24
+ --st-inbox: #6b7280; --st-backlog: #6b7280; --st-in-progress: #b06d0b; --st-done: #5e6ad2; --st-hold: #6b7280;
25
+ --ready-bg: #e2f4e7; --ready-ink: #17643a; --blocked-bg: #fde7e7; --blocked-ink: #ae1f28; }
26
+ :root[data-theme="dark"] { color-scheme: dark; --bg: #101113; --bg-2: #16171a; --card: #1c1d21; --ink: #e8e9ec; --muted: #a0a5ae; --line: #2c2e34; --line-2: #464a53;
27
+ --link: #a9b0f6; --accent-ink: #a9b0f6; --chip-on: #2b2e4c; --focus: #8f98f4; --unmet: #ff6b6b; --shade: rgba(0, 0, 0, .5);
28
+ --st-inbox: #9aa0aa; --st-backlog: #9aa0aa; --st-in-progress: #e3a33b; --st-done: #8f98f4; --st-hold: #9aa0aa;
29
+ --ready-bg: #12301f; --ready-ink: #74dc9e; --blocked-bg: #3b1719; --blocked-ink: #ff9d9d; }
18
30
  * { box-sizing: border-box; }
19
- body { margin: 0; color: var(--ink); background: #fff; font: 1rem/1.45 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; }
31
+ body { margin: 0; color: var(--ink); background: var(--bg); font: .875rem/1.5 Inter, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; }
20
32
  [hidden] { display: none !important; }
21
33
  a { color: var(--link); }
22
- code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .9em; }
23
- :focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
24
- button { font: inherit; color: var(--ink); background: #fff; border: 1px solid #6b7280; border-radius: .35rem; padding: .35rem .8rem; min-height: 2.75rem; cursor: pointer; }
25
- header.top { display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem 2rem; padding: 1rem 1.5rem; border-bottom: 1px solid var(--line); }
26
- h1 { margin: 0; font-size: 1.5rem; }
27
- h2 { font-size: 1.2rem; margin: 1.25rem 1.5rem .75rem; }
28
- .generated, .search, .search-status { margin: 0; color: var(--muted); }
29
- .search input { font: inherit; padding: .35rem .6rem; min-height: 2.75rem; min-width: 18rem; border: 1px solid #6b7280; border-radius: .35rem; }
30
- .board { display: grid; grid-template-columns: repeat(5, minmax(15rem, 1fr)); gap: 1rem; padding: 0 1.5rem; overflow-x: auto; align-items: start; }
31
- .col { background: #f9fafb; border: 1px solid var(--line); border-radius: .5rem; padding: .5rem; }
32
- .col h3 { display: flex; justify-content: space-between; margin: .25rem .25rem .5rem; font-size: 1rem; }
33
- .count { font-weight: 400; color: var(--muted); }
34
- .cards { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .5rem; }
35
- .card { border: 1px solid; border-left-width: .35rem; border-radius: .4rem; padding: .5rem .6rem; cursor: pointer; }
36
- .card p { margin: .25rem 0 0; }
37
- .card .open { border: 0; background: none; padding: 0; min-height: 0; text-align: left; font-weight: 600; line-height: 1.35; }
38
- .rank { font-weight: 700; }
39
- .badge { display: inline-block; margin-right: .35rem; padding: 0 .45rem; border-radius: 999px; font-size: .75rem; font-weight: 700; }
40
- .badge.ready { background: #065f46; color: #fff; }
41
- .badge.blocked { background: #991b1b; color: #fff; }
42
- .age, .links, .hold { font-size: .85rem; }
43
- .age { color: var(--muted); }
34
+ code, kbd { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .8125rem; }
35
+ kbd { padding: 0 .3rem; border: 1px solid var(--line-2); border-radius: .25rem; background: var(--bg-2); }
36
+ :focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
37
+ button { font: inherit; color: var(--ink); background: var(--bg); border: 1px solid var(--line); border-radius: .375rem; padding: .2rem .6rem; min-height: 1.75rem; min-width: 1.75rem; cursor: pointer; }
38
+ button:hover { border-color: var(--line-2); }
39
+ button[aria-pressed="true"] { background: var(--chip-on); border-color: var(--focus); }
40
+ @media (pointer: coarse) { button, .search input { min-height: 2.75rem; min-width: 2.75rem; } }
41
+ .sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
42
+ header.top { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1.5rem; padding: .75rem 1.5rem; border-bottom: 1px solid var(--line); }
43
+ h1 { margin: 0; font-size: 1rem; font-weight: 600; }
44
+ .generated, .search, .search-status, .keys { margin: 0; color: var(--muted); }
45
+ .search input { font: inherit; color: var(--ink); background: var(--bg); padding: .2rem .6rem; min-height: 1.75rem; min-width: 16rem; border: 1px solid var(--line-2); border-radius: .375rem; }
46
+ #theme { margin-left: auto; }
47
+ .tabs { display: flex; gap: .25rem; padding: 0 1.5rem; border-bottom: 1px solid var(--line); }
48
+ .tabs [role="tab"] { border: 0; border-bottom: 2px solid transparent; border-radius: 0; background: none; padding: .5rem .75rem; color: var(--muted); font-weight: 500; }
49
+ .tabs [role="tab"][aria-selected="true"] { color: var(--ink); border-bottom-color: var(--focus); }
50
+ .n { color: var(--muted); font-weight: 400; margin-left: .25rem; }
51
+ .view { padding: .75rem 1.5rem 0; }
52
+ .filters { display: flex; flex-wrap: wrap; gap: .5rem 1.25rem; margin-bottom: .75rem; }
53
+ .group { display: flex; flex-wrap: wrap; gap: .25rem; }
54
+ .chip, .preset, .only, #open-chains { border-radius: 999px; font-size: .8125rem; }
55
+ .ring { display: inline-block; position: relative; width: .75rem; height: .75rem; margin-right: .4rem; vertical-align: -.1rem; border: 1.5px solid var(--rc); border-radius: 50%; flex: none; }
56
+ .r-inbox { --rc: var(--st-inbox); border-style: dashed; }
57
+ .r-backlog { --rc: var(--st-backlog); }
58
+ .r-in-progress { --rc: var(--st-in-progress); background: linear-gradient(90deg, var(--rc) 50%, transparent 50%); }
59
+ .r-done { --rc: var(--st-done); background: var(--rc); }
60
+ .r-done::after { content: ""; position: absolute; left: .19rem; top: .06rem; width: .15rem; height: .3rem; border: solid var(--bg); border-width: 0 1.5px 1.5px 0; transform: rotate(45deg); }
61
+ .r-hold { --rc: var(--st-hold); }
62
+ .r-hold::after { content: ""; position: absolute; left: .15rem; right: .15rem; top: calc(50% - .75px); height: 1.5px; background: var(--rc); }
63
+ .board { display: flex; gap: .75rem; overflow-x: auto; align-items: flex-start; padding-bottom: 1rem; }
64
+ .col { flex: 1 1 16rem; min-width: 16rem; }
65
+ .col > .rail, .col.off > .body { display: none; }
66
+ .col.off { flex: 0 0 2.75rem; min-width: 2.75rem; }
67
+ .col.off > .rail { display: flex; align-items: center; gap: .4rem; width: 2.75rem; min-height: 14rem; padding: .6rem 0; writing-mode: vertical-rl; background: var(--bg-2); color: var(--muted); }
68
+ .col h3 { display: flex; align-items: center; margin: 0 0 .5rem; padding: .25rem .25rem; font-size: .8125rem; font-weight: 600; }
69
+ .count { font-weight: 400; color: var(--muted); margin-left: .4rem; }
70
+ .cards { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .375rem; }
71
+ .card { background: var(--card); border: 1px solid var(--line); border-radius: .375rem; padding: .5rem .625rem; cursor: pointer; }
72
+ .card:hover { border-color: var(--line-2); box-shadow: 0 1px 2px var(--shade); }
73
+ .card p { margin: .2rem 0 0; }
74
+ .card .title { display: flex; align-items: baseline; margin: 0; }
75
+ .card .open { border: 0; background: none; padding: 0; min-height: 0; text-align: left; font-weight: 500; line-height: 1.4; }
76
+ .rank { color: var(--muted); font-weight: 600; }
77
+ .badge { display: inline-block; margin-right: .35rem; padding: 0 .45rem; border-radius: 999px; font-size: .75rem; font-weight: 600; }
78
+ .badge.ready { background: var(--ready-bg); color: var(--ready-ink); }
79
+ .badge.blocked { background: var(--blocked-bg); color: var(--blocked-ink); }
80
+ .age, .links, .hold { font-size: .75rem; }
81
+ .age, .hold { color: var(--muted); }
44
82
  .board:not(.searching) .col:not(.expanded) .capped { display: none; }
45
- .show-all { margin: .5rem .25rem .25rem; }
46
- .s-inbox { background: #eef2ff; border-color: #6366f1; }
47
- .s-backlog { background: #fef9c3; border-color: #ca8a04; }
48
- .s-in-progress { background: #dbeafe; border-color: #2563eb; }
49
- .s-done { background: #dcfce7; border-color: #16a34a; }
50
- .s-hold { background: #f3f4f6; border-color: #6b7280; }
51
- .map-controls, .legend, .empty { margin: 0 1.5rem .75rem; }
52
- .map-controls button { margin-right: .5rem; }
53
- .sw { display: inline-block; width: .9rem; height: .9rem; margin: 0 .3rem 0 .9rem; border: 1.5px solid; border-radius: .2rem; vertical-align: -.1rem; }
54
- .sw.missing { background: #fee2e2; border-color: #dc2626; border-style: dashed; }
55
- .map-wrap { padding: 0 1.5rem; }
56
- #map { display: block; width: 100%; height: 70vh; border: 1px solid var(--line); border-radius: .5rem; background: #fff; touch-action: none; cursor: grab; }
57
- #map text { fill: var(--ink); font-size: 13px; }
58
- #map .sub { fill: var(--muted); font-size: 11px; }
83
+ .show-all { margin: .5rem 0; }
84
+ .map-controls, .legend, .empty { margin: 0 0 .5rem; }
85
+ .legend { color: var(--muted); font-size: .8125rem; }
86
+ #map { display: block; width: 100%; height: calc(100vh - 12.5rem); min-height: 24rem; border: 1px solid var(--line); border-radius: .5rem; background: var(--bg-2); touch-action: none; cursor: grab; }
87
+ #map text { fill: var(--ink); font-size: 14px; }
88
+ #map .sub, #map .clabel { fill: var(--muted); font-size: 12px; }
59
89
  #map .node { cursor: pointer; }
60
- #map .node rect { stroke-width: 1.5; }
90
+ #map .node rect { fill: var(--card); stroke: var(--line-2); stroke-width: 1.5; }
61
91
  #map .node:focus { outline: none; }
62
92
  #map .node:focus-visible rect, #map .toggle:focus-visible circle { stroke: var(--focus); stroke-width: 3; }
63
- #map .s-inbox rect { fill: #eef2ff; stroke: #6366f1; }
64
- #map .s-backlog rect { fill: #fef9c3; stroke: #ca8a04; }
65
- #map .s-in-progress rect { fill: #dbeafe; stroke: #2563eb; }
66
- #map .s-done rect { fill: #dcfce7; stroke: #16a34a; }
67
- #map .s-hold rect { fill: #f3f4f6; stroke: #6b7280; }
68
- #map .missing rect { fill: #fee2e2; stroke: #dc2626; stroke-dasharray: 6 3; }
69
- #map .edge { fill: none; stroke: #6b7280; stroke-width: 1.5; }
70
- #map .edge.dashed { stroke-dasharray: 6 4; }
93
+ #map .s-inbox rect { stroke: var(--st-inbox); stroke-dasharray: 4 3; }
94
+ #map .s-in-progress rect { stroke: var(--st-in-progress); }
95
+ #map .s-done rect { stroke: var(--st-done); }
96
+ #map .missing rect { fill: var(--blocked-bg); stroke: var(--unmet); stroke-dasharray: 6 3; }
97
+ #map .edge { fill: none; stroke: var(--st-hold); stroke-width: 1.5; }
98
+ #map .edge.further { stroke-dasharray: 2 4; }
99
+ #map .edge.unmet { stroke: var(--unmet); stroke-dasharray: 6 4; }
71
100
  #map .toggle { cursor: pointer; }
72
- #map .toggle circle { fill: #fff; stroke: #374151; stroke-width: 1.5; }
101
+ #map .toggle circle { fill: var(--card); stroke: var(--muted); stroke-width: 1.5; }
73
102
  #map .toggle text { text-anchor: middle; font-weight: 700; font-size: 15px; }
74
103
  #map .off { display: none; }
75
- #map.focus .node:not(.hl) { opacity: .3; }
76
- #map.focus .edge:not(.hl) { opacity: .15; }
104
+ #map .dim { opacity: .25; }
105
+ #map.focus .node:not(.hl) { opacity: .25; }
106
+ #map.focus .edge:not(.hl) { opacity: .12; }
77
107
  #map .node.hl rect { stroke-width: 3; }
78
- #map .edge.hl { stroke: var(--focus); stroke-width: 2.5; }
79
- .panel { position: fixed; top: 0; right: 0; bottom: 0; width: min(34rem, 100%); overflow-y: auto; padding: 1rem 1.25rem; background: #fff; border-left: 1px solid var(--line); box-shadow: -4px 0 16px rgba(0, 0, 0, .15); }
80
- .panel h2 { margin: .25rem 3rem .5rem 0; font-size: 1.15rem; }
81
- .panel h3 { margin: 1rem 0 .35rem; font-size: 1rem; }
108
+ #map .edge.hl { stroke-width: 2.5; opacity: 1; }
109
+ .panel { position: fixed; top: 0; right: 0; bottom: 0; width: min(34rem, 100%); overflow-y: auto; padding: 1rem 1.25rem; color: var(--ink); background: var(--bg); border-left: 1px solid var(--line); box-shadow: -4px 0 16px var(--shade); }
110
+ .panel h2 { margin: .25rem 3rem .5rem 0; font-size: 1rem; font-weight: 600; }
111
+ .panel h3 { margin: 1rem 0 .35rem; font-size: .8125rem; font-weight: 600; color: var(--muted); }
112
+ .panel .meta { display: flex; align-items: center; flex-wrap: wrap; gap: .25rem; color: var(--muted); }
82
113
  .panel .close { float: right; }
83
- footer { margin-top: 2rem; padding: 1rem 1.5rem; border-top: 1px solid var(--line); color: var(--muted); }
114
+ .cmds { list-style: none; margin: 0; padding: 0; display: grid; gap: .4rem; }
115
+ .cmds li { display: flex; flex-wrap: wrap; align-items: center; gap: .25rem .5rem; }
116
+ .cmd-label { flex-basis: 100%; color: var(--muted); font-size: .75rem; }
117
+ .cmds code { flex: 1; padding: .2rem .45rem; border-radius: .25rem; background: var(--bg-2); overflow-wrap: anywhere; }
118
+ .keys { padding: 1rem 1.5rem 0; font-size: .75rem; }
119
+ footer { margin-top: 1rem; padding: .75rem 1.5rem; border-top: 1px solid var(--line); color: var(--muted); font-size: .75rem; }
84
120
  `;
85
121
  export const VIEW_SCRIPT = String.raw `
86
122
  (function () {
87
123
  'use strict';
88
124
  var doc = document;
89
- function one(sel, root) { return (root || doc).querySelector(sel); }
90
- function all(sel, root) { return Array.prototype.slice.call((root || doc).querySelectorAll(sel)); }
125
+ var root = doc.documentElement;
126
+ function one(sel, r) { return (r || doc).querySelector(sel); }
127
+ function all(sel, r) { return Array.prototype.slice.call((r || doc).querySelectorAll(sel)); }
91
128
  function refs(el, name) { var v = el ? el.getAttribute(name) : ''; return v ? v.split(' ') : []; }
129
+ function attr(el, name, v) { el.setAttribute(name, String(v)); }
130
+
131
+ // The theme lives in the browser, so regenerating the page keeps it; storage may throw.
132
+ var KEY = 'aidlc-roadmap-theme';
133
+ var themeBtn = one('#theme');
134
+ function setTheme(dark) {
135
+ if (dark) root.setAttribute('data-theme', 'dark'); else root.removeAttribute('data-theme');
136
+ attr(themeBtn, 'aria-pressed', dark);
137
+ }
138
+ try { setTheme(localStorage.getItem(KEY) === 'dark'); } catch (e) { setTheme(false); }
139
+ themeBtn.addEventListener('click', function () {
140
+ var dark = root.getAttribute('data-theme') !== 'dark';
141
+ setTheme(dark);
142
+ try { localStorage.setItem(KEY, dark ? 'dark' : 'light'); } catch (e) { /* this visit only */ }
143
+ });
92
144
 
93
145
  // Ages are worked out here, not by the generator, so the file's bytes do not change from
94
146
  // one day to the next.
@@ -102,21 +154,135 @@ export const VIEW_SCRIPT = String.raw `
102
154
  t.setAttribute('title', 'created ' + t.getAttribute('datetime'));
103
155
  });
104
156
 
105
- var board = one('.board');
157
+ // View state. Each tab keeps its own filters; all of it round-trips through the hash.
158
+ var ALL = ['inbox', 'backlog', 'in-progress', 'done', 'hold'];
159
+ var ONLY = ['ready', 'blocked'];
160
+ var S = { tab: 'board', open: true, board: { s: ALL.slice(0, 3), o: [] }, deps: { s: ALL.slice(), o: [] } };
161
+ function listOf(v, known) {
162
+ if (v === '') return [];
163
+ var out = v.split('.');
164
+ for (var i = 0; i < out.length; i++) if (known.indexOf(out[i]) < 0) return null;
165
+ return out;
166
+ }
167
+ // The hash is untrusted: it is only ever compared against the constants above.
168
+ function readHash() {
169
+ location.hash.replace(/^#/, '').split('&').forEach(function (part) {
170
+ var at = part.indexOf('=');
171
+ if (at < 0) return;
172
+ var k = part.slice(0, at), v = part.slice(at + 1), list;
173
+ if (k === 'tab' && (v === 'board' || v === 'deps')) S.tab = v;
174
+ else if (k === 'open' && (v === '0' || v === '1')) S.open = v === '1';
175
+ else if (k === 'board' || k === 'deps') { list = listOf(v, ALL); if (list) S[k].s = list; }
176
+ else if (k === 'board-only' || k === 'deps-only') { list = listOf(v, ONLY); if (list) S[k.slice(0, -5)].o = list; }
177
+ });
178
+ }
179
+ var live = false;
180
+ function writeHash() {
181
+ if (!live) return;
182
+ var h = 'tab=' + S.tab + '&board=' + S.board.s.join('.') + '&board-only=' + S.board.o.join('.') +
183
+ '&deps=' + S.deps.s.join('.') + '&deps-only=' + S.deps.o.join('.') + '&open=' + (S.open ? 1 : 0);
184
+ try { history.replaceState(null, '', '#' + h); } catch (e) { /* the view still works */ }
185
+ }
186
+ function toggled(list, v, order) {
187
+ var on = list.indexOf(v) < 0;
188
+ return order.filter(function (x) { return x === v ? on : list.indexOf(x) >= 0; });
189
+ }
190
+ function same(a, b) { return a.length === b.length && a.every(function (x) { return b.indexOf(x) >= 0; }); }
191
+
106
192
  var search = one('#q');
107
- var searchStatus = one('#search-status');
108
- function applySearch() {
109
- var q = search.value.trim().toLowerCase();
193
+ var q = '';
194
+ function match(el, f) {
195
+ var st = el.getAttribute('data-status');
196
+ if (st !== 'missing' && f.s.indexOf(st) < 0) return false;
197
+ if (f.o.length && !f.o.some(function (o) { return el.classList.contains(o); })) return false;
198
+ return q === '' || el.getAttribute('data-search').indexOf(q) !== -1;
199
+ }
200
+
201
+ var tabs = all('[role="tab"]');
202
+ var board = one('.board');
203
+ var svg = one('#map');
204
+ var viewport = one('#viewport', svg);
205
+ var unlinked = one('.unlinked', svg);
206
+ var openBtn = one('#open-chains');
207
+ function place(g) {
208
+ if (!g) return;
209
+ var p = g.getAttribute(S.open ? 'data-open' : 'data-all') || g.getAttribute('data-all');
210
+ g.setAttribute('transform', 'translate(' + p + ')');
211
+ }
212
+ function render() {
213
+ tabs.forEach(function (t) {
214
+ var on = t.getAttribute('data-tab') === S.tab;
215
+ attr(t, 'aria-selected', on);
216
+ attr(t, 'tabindex', on ? 0 : -1);
217
+ doc.getElementById(t.getAttribute('aria-controls')).hidden = !on;
218
+ });
219
+ all('.filters').forEach(function (bar) {
220
+ var f = S[bar.getAttribute('data-for')];
221
+ all('.chip', bar).forEach(function (c) { attr(c, 'aria-pressed', f.s.indexOf(c.getAttribute('data-status')) >= 0); });
222
+ all('.only', bar).forEach(function (c) { attr(c, 'aria-pressed', f.o.indexOf(c.getAttribute('data-only')) >= 0); });
223
+ all('.preset', bar).forEach(function (c) { attr(c, 'aria-pressed', !f.o.length && same(refs(c, 'data-set'), f.s)); });
224
+ });
225
+ all('.col').forEach(function (c) { c.classList.toggle('off', S.board.s.indexOf(c.getAttribute('data-status')) < 0); });
226
+ var shown = 0, lit = 0, inDeps = 0, dim = {};
227
+ all('.card').forEach(function (c) { var m = match(c, S.board); c.hidden = !m; if (m) shown++; });
110
228
  board.classList.toggle('searching', q !== '');
111
- var shown = 0;
112
- all('.card').forEach(function (card) {
113
- var match = q === '' || card.getAttribute('data-search').indexOf(q) !== -1;
114
- card.hidden = !match;
115
- if (match) shown++;
229
+ if (openBtn) attr(openBtn, 'aria-pressed', S.open);
230
+ all('.cluster', svg).forEach(function (c) {
231
+ var off = S.open && c.classList.contains('closed');
232
+ c.classList.toggle('off', off);
233
+ place(c);
234
+ if (!off) inDeps += +c.getAttribute('data-n');
235
+ });
236
+ place(unlinked);
237
+ all('.node', svg).forEach(function (n) {
238
+ var d = !match(n, S.deps);
239
+ n.classList.toggle('dim', d);
240
+ if (d) dim[n.getAttribute('data-ref')] = 1; else if (!n.closest('.off')) lit++;
116
241
  });
117
- searchStatus.textContent = q === '' ? '' : shown === 1 ? '1 card matches' : shown + ' cards match';
242
+ all('.edge', svg).forEach(function (e) { e.classList.toggle('dim', !!(dim[e.getAttribute('data-from')] || dim[e.getAttribute('data-to')])); });
243
+ one('#n-board').textContent = shown;
244
+ one('#n-deps').textContent = inDeps;
245
+ var n = S.tab === 'board' ? shown : lit;
246
+ one('#search-status').textContent = q === '' ? '' : n === 1 ? '1 item matches' : n + ' items match';
247
+ writeHash();
118
248
  }
119
- search.addEventListener('input', applySearch);
249
+
250
+ var fitted = false;
251
+ function selectTab(name, focus) {
252
+ S.tab = name;
253
+ render();
254
+ if (name === 'deps' && !fitted) { fitted = true; fit(); }
255
+ if (focus) doc.getElementById('tab-' + name).focus();
256
+ }
257
+ tabs.forEach(function (t) {
258
+ t.addEventListener('click', function () { selectTab(t.getAttribute('data-tab')); });
259
+ t.addEventListener('keydown', function (e) {
260
+ if (e.key !== 'ArrowRight' && e.key !== 'ArrowLeft') return;
261
+ e.preventDefault();
262
+ selectTab(S.tab === 'board' ? 'deps' : 'board', true);
263
+ });
264
+ });
265
+ all('.filters').forEach(function (bar) {
266
+ var f = S[bar.getAttribute('data-for')];
267
+ bar.addEventListener('click', function (e) {
268
+ var b = e.target.closest('button');
269
+ if (!b) return;
270
+ if (b.hasAttribute('data-status')) f.s = toggled(f.s, b.getAttribute('data-status'), ALL);
271
+ else if (b.hasAttribute('data-only')) f.o = toggled(f.o, b.getAttribute('data-only'), ONLY);
272
+ else if (b.hasAttribute('data-set')) { f.s = refs(b, 'data-set'); f.o = []; }
273
+ render();
274
+ });
275
+ });
276
+ // A rail brings its column back, so a hidden status is one click away.
277
+ all('.rail').forEach(function (r) {
278
+ r.addEventListener('click', function () {
279
+ var st = r.getAttribute('data-show');
280
+ if (S.board.s.indexOf(st) < 0) S.board.s = toggled(S.board.s, st, ALL);
281
+ render();
282
+ one('.filters[data-for="board"] .chip[data-status="' + st + '"]').focus();
283
+ });
284
+ });
285
+ search.addEventListener('input', function () { q = search.value.trim().toLowerCase(); render(); });
120
286
 
121
287
  all('.show-all').forEach(function (btn) {
122
288
  btn.addEventListener('click', function () {
@@ -131,11 +297,13 @@ export const VIEW_SCRIPT = String.raw `
131
297
  var panel = one('#panel');
132
298
  var panelBody = one('#panel-body');
133
299
  var closeBtn = one('#panel-close');
300
+ var say = one('#live');
134
301
  var opener = null;
135
302
  function openPanel(ref, from) {
136
303
  var tpl = doc.getElementById('detail-' + ref);
137
304
  if (!tpl) return;
138
305
  panelBody.replaceChildren(tpl.content.cloneNode(true));
306
+ say.textContent = '';
139
307
  panel.hidden = false;
140
308
  opener = from || null;
141
309
  closeBtn.focus();
@@ -151,13 +319,31 @@ export const VIEW_SCRIPT = String.raw `
151
319
  function gotoCard(ref) {
152
320
  var card = doc.getElementById('card-' + ref);
153
321
  if (!card) { openPanel(ref, null); return; }
154
- if (card.hidden) { search.value = ''; applySearch(); }
322
+ var st = card.getAttribute('data-status');
323
+ if (S.board.s.indexOf(st) < 0) S.board.s = toggled(S.board.s, st, ALL);
324
+ if (!match(card, S.board)) { S.board.o = []; search.value = ''; q = ''; }
325
+ S.tab = 'board';
326
+ render();
155
327
  var col = card.closest('.col');
156
328
  if (card.classList.contains('capped') && !col.classList.contains('expanded')) one('.show-all', col).click();
157
329
  if (card.scrollIntoView) card.scrollIntoView({ block: 'center' });
158
330
  openPanel(ref, one('.open', card));
159
331
  }
332
+ // The page runs nothing: it puts the command on the clipboard, or selects it to copy by hand.
333
+ function copy(btn) {
334
+ var code = doc.getElementById(btn.getAttribute('data-code'));
335
+ function byHand() {
336
+ var sel = window.getSelection ? window.getSelection() : null;
337
+ if (sel) sel.selectAllChildren(code);
338
+ say.textContent = 'Selected — press Ctrl+C or ⌘C to copy';
339
+ }
340
+ var cb = navigator.clipboard;
341
+ if (!cb || !cb.writeText) { byHand(); return; }
342
+ cb.writeText(btn.getAttribute('data-cmd')).then(function () { say.textContent = 'Copied'; }, byHand);
343
+ }
160
344
  panelBody.addEventListener('click', function (e) {
345
+ var c = e.target.closest('.copy');
346
+ if (c) { copy(c); return; }
161
347
  var a = e.target.closest('a[data-goto]');
162
348
  if (!a) return;
163
349
  e.preventDefault();
@@ -170,26 +356,49 @@ export const VIEW_SCRIPT = String.raw `
170
356
  });
171
357
  });
172
358
 
173
- var svg = one('#map');
174
- function clearFocus() {
175
- if (!svg) return;
359
+ var pinned = null;
360
+ function unlight() {
176
361
  svg.classList.remove('focus');
177
362
  all('.hl', svg).forEach(function (el) { el.classList.remove('hl'); });
178
363
  }
364
+ function light(node) {
365
+ var lit = {};
366
+ lit[node.getAttribute('data-ref')] = true;
367
+ refs(node, 'data-chain').forEach(function (r) { lit[r] = true; });
368
+ svg.classList.add('focus');
369
+ all('.node', svg).forEach(function (n) { n.classList.toggle('hl', !!lit[n.getAttribute('data-ref')]); });
370
+ all('.edge', svg).forEach(function (ed) { ed.classList.toggle('hl', !!lit[ed.getAttribute('data-from')] && !!lit[ed.getAttribute('data-to')]); });
371
+ }
372
+ function clearFocus() { pinned = null; unlight(); }
373
+ function nodeOf(e) { return e.target.closest ? e.target.closest('.node') : null; }
374
+ function enter(e) { var n = nodeOf(e); if (n && !pinned) light(n); }
375
+ function leave(e) { var n = nodeOf(e); if (n && !pinned && !(e.relatedTarget && n.contains(e.relatedTarget))) unlight(); }
376
+ svg.addEventListener('mouseover', enter);
377
+ svg.addEventListener('focusin', enter);
378
+ svg.addEventListener('mouseout', leave);
379
+ svg.addEventListener('focusout', leave);
380
+
179
381
  doc.addEventListener('keydown', function (e) {
180
- if (e.key === 'Escape') { closePanel(); clearFocus(); }
382
+ if (e.ctrlKey || e.metaKey || e.altKey) return;
383
+ if (e.key === 'Escape') {
384
+ if (!panel.hidden) closePanel();
385
+ else if (search.value !== '') { search.value = ''; q = ''; render(); }
386
+ clearFocus();
387
+ return;
388
+ }
389
+ if (/^(INPUT|TEXTAREA|SELECT)$/.test(e.target.tagName || '')) return;
390
+ if (e.key === '/') { e.preventDefault(); search.focus(); }
391
+ else if (e.key === '1' || e.key === '2') selectTab(e.key === '1' ? 'board' : 'deps', true);
181
392
  });
182
- if (!svg) return;
183
393
 
184
- var viewport = one('#viewport', svg);
185
- var unlinked = one('.unlinked', svg);
186
394
  var k = 1, tx = 0, ty = 0;
187
395
  function r3(n) { return Math.round(n * 1000) / 1000; }
188
396
  function apply() { viewport.setAttribute('transform', 'translate(' + r3(tx) + ' ' + r3(ty) + ') scale(' + r3(k) + ')'); }
189
397
  function frame() { return { w: svg.clientWidth || 1000, h: svg.clientHeight || 600 }; }
190
398
  function extent() {
191
399
  var full = unlinked && !unlinked.classList.contains('off');
192
- return { w: +svg.getAttribute(full ? 'data-full-w' : 'data-linked-w'), h: +svg.getAttribute(full ? 'data-full-h' : 'data-linked-h') };
400
+ var pre = 'data-' + (S.open ? 'open' : 'all') + '-' + (full ? 'full' : 'linked') + '-';
401
+ return { w: +svg.getAttribute(pre + 'w'), h: +svg.getAttribute(pre + 'h') };
193
402
  }
194
403
  function fit() {
195
404
  var f = frame(), s = extent();
@@ -242,19 +451,16 @@ export const VIEW_SCRIPT = String.raw `
242
451
  svg.addEventListener('pointercancel', release);
243
452
  svg.addEventListener('click', function (e) { if (!onControl(e) && !dragged) clearFocus(); });
244
453
 
454
+ // Node offsets are relative to their cluster, so centring adds the cluster's current place.
245
455
  function activate(node) {
246
- var ref = node.getAttribute('data-ref');
247
- var lit = {};
248
- lit[ref] = true;
249
- refs(node, 'data-chain').forEach(function (r) { lit[r] = true; });
250
- svg.classList.add('focus');
251
- all('.node', svg).forEach(function (n) { n.classList.toggle('hl', !!lit[n.getAttribute('data-ref')]); });
252
- all('.edge', svg).forEach(function (ed) { ed.classList.toggle('hl', !!lit[ed.getAttribute('data-from')] && !!lit[ed.getAttribute('data-to')]); });
456
+ pinned = node;
457
+ light(node);
458
+ var m = /translate\(([-\d.]+) ([-\d.]+)\)/.exec(node.parentNode.getAttribute('transform') || '');
253
459
  var f = frame();
254
- tx = f.w / 2 - (+node.getAttribute('data-x') + +node.getAttribute('data-cx')) * k;
255
- ty = f.h / 2 - (+node.getAttribute('data-y') + +node.getAttribute('data-cy')) * k;
460
+ tx = f.w / 2 - ((m ? +m[1] : 0) + +node.getAttribute('data-x') + +node.getAttribute('data-cx')) * k;
461
+ ty = f.h / 2 - ((m ? +m[2] : 0) + +node.getAttribute('data-y') + +node.getAttribute('data-cy')) * k;
256
462
  apply();
257
- openPanel(ref, node);
463
+ openPanel(node.getAttribute('data-ref'), node);
258
464
  }
259
465
 
260
466
  var collapsed = {};
@@ -263,7 +469,7 @@ export const VIEW_SCRIPT = String.raw `
263
469
  Object.keys(collapsed).forEach(function (r) {
264
470
  refs(doc.getElementById('node-' + r), 'data-sub').forEach(function (s) { hidden[s] = true; });
265
471
  });
266
- all('.forest .node', svg).forEach(function (n) { n.classList.toggle('off', !!hidden[n.getAttribute('data-ref')]); });
472
+ all('.cluster .node', svg).forEach(function (n) { n.classList.toggle('off', !!hidden[n.getAttribute('data-ref')]); });
267
473
  all('.toggle', svg).forEach(function (t) { t.classList.toggle('off', !!hidden[t.getAttribute('data-toggle')]); });
268
474
  all('.edge', svg).forEach(function (ed) { ed.classList.toggle('off', !!hidden[ed.getAttribute('data-from')] || !!hidden[ed.getAttribute('data-to')]); });
269
475
  }
@@ -285,7 +491,7 @@ export const VIEW_SCRIPT = String.raw `
285
491
  all('.toggle', svg).forEach(function (t) { onActivate(t, toggle); });
286
492
 
287
493
  var unlinkedBtn = one('#unlinked-toggle');
288
- if (unlinkedBtn && unlinked) {
494
+ if (unlinkedBtn) {
289
495
  unlinkedBtn.addEventListener('click', function () {
290
496
  var show = unlinked.classList.contains('off');
291
497
  unlinked.classList.toggle('off', !show);
@@ -294,8 +500,13 @@ export const VIEW_SCRIPT = String.raw `
294
500
  fit();
295
501
  });
296
502
  }
503
+ if (openBtn) openBtn.addEventListener('click', function () { S.open = !S.open; render(); fit(); });
297
504
  one('#fit').addEventListener('click', fit);
298
- fit();
505
+
506
+ readHash();
507
+ render();
508
+ live = true;
509
+ if (S.tab === 'deps') { fitted = true; fit(); }
299
510
  })();
300
- `;
511
+ `.replace(/^ +/gm, ''); // Indentation is for reading here; the page ships without it (AC-34).
301
512
  //# sourceMappingURL=view-assets.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"view-assets.js","sourceRoot":"","sources":["../../src/roadmap/view-assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoEnC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuNpC,CAAC"}
1
+ {"version":3,"file":"view-assets.js","sourceRoot":"","sources":["../../src/roadmap/view-assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmGnC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsYpC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,sEAAsE"}
@@ -15,7 +15,8 @@
15
15
  *
16
16
  * @module
17
17
  */
18
- import type { RoadmapView } from './view-model.js';
18
+ import { type RoadmapStatus } from './paths.js';
19
+ import type { RoadmapView, ViewItem } from './view-model.js';
19
20
  /** Done cards shown before "show all" (AC-18). */
20
21
  export declare const DONE_VISIBLE = 20;
21
22
  /** Escape text for HTML text and double-quoted attribute values alike. */
@@ -32,6 +33,15 @@ export declare function contentSecurityPolicy(): string;
32
33
  * library would be the first dependency the page grows.
33
34
  */
34
35
  export declare function renderSummary(markdown: string): string;
36
+ /** Statuses the Board shows when the URL names none (AC-1). */
37
+ export declare const BOARD_DEFAULT: readonly RoadmapStatus[];
38
+ /** The five presets every tab offers, in order (AC-6). */
39
+ export declare const PRESETS: readonly {
40
+ name: string;
41
+ statuses: readonly RoadmapStatus[];
42
+ }[];
43
+ /** The commands that apply to an item, as `[label, command]`; the page runs none (AC-29). */
44
+ export declare function commandsFor(v: ViewItem): [string, string][];
35
45
  /** Render the page. Pure. */
36
46
  export declare function renderView(view: RoadmapView): string;
37
47
  //# sourceMappingURL=view-html.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"view-html.d.ts","sourceRoot":"","sources":["../../src/roadmap/view-html.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,OAAO,KAAK,EAAE,WAAW,EAAY,MAAM,iBAAiB,CAAC;AAE7D,kDAAkD;AAClD,eAAO,MAAM,YAAY,KAAK,CAAC;AA8B/B,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,0CAA0C;AAC1C,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,iGAAiG;AACjG,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAcD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgCtD;AA4LD,6BAA6B;AAC7B,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAwCpD"}
1
+ {"version":3,"file":"view-html.d.ts","sourceRoot":"","sources":["../../src/roadmap/view-html.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAElE,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE7D,kDAAkD;AAClD,eAAO,MAAM,YAAY,KAAK,CAAC;AA8B/B,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,0CAA0C;AAC1C,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,iGAAiG;AACjG,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAcD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgCtD;AA8DD,+DAA+D;AAC/D,eAAO,MAAM,aAAa,EAAE,SAAS,aAAa,EAAwC,CAAC;AAuB3F,0DAA0D;AAC1D,eAAO,MAAM,OAAO,EAAE,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAA;CAAE,EAMlF,CAAC;AAwKF,6FAA6F;AAC7F,wBAAgB,WAAW,CAAC,CAAC,EAAE,QAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAmB3D;AAgDD,6BAA6B;AAC7B,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAiDpD"}