bedrock-flows 0.7.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.
Files changed (85) hide show
  1. package/auth-schema.sql +8 -0
  2. package/bin/bedrock-flows.mjs +127 -0
  3. package/lib/setup.mjs +262 -0
  4. package/package.json +11 -0
  5. package/template/.storybook/main.js +46 -0
  6. package/template/.storybook/manager-head.html +963 -0
  7. package/template/.storybook/preview-head.html +35 -0
  8. package/template/.storybook/preview.js +23 -0
  9. package/template/CHANGELOG.md +236 -0
  10. package/template/README.md +26 -0
  11. package/template/apps/dashboard/index.html +15 -0
  12. package/template/apps/dashboard/package.json +22 -0
  13. package/template/apps/dashboard/src/App.module.css +1318 -0
  14. package/template/apps/dashboard/src/App.tsx +2716 -0
  15. package/template/apps/dashboard/src/auth-client.ts +17 -0
  16. package/template/apps/dashboard/src/changelog.tsx +92 -0
  17. package/template/apps/dashboard/src/index.css +86 -0
  18. package/template/apps/dashboard/src/main.tsx +15 -0
  19. package/template/apps/dashboard/src/theme.ts +31 -0
  20. package/template/apps/dashboard/src/vite-env.d.ts +4 -0
  21. package/template/apps/dashboard/vite.config.ts +48 -0
  22. package/template/apps/worker/.dev.vars.example +50 -0
  23. package/template/apps/worker/package.json +19 -0
  24. package/template/apps/worker/src/index.ts +295 -0
  25. package/template/apps/worker/tsconfig.json +11 -0
  26. package/template/apps/worker/wrangler.jsonc +29 -0
  27. package/template/bedrock.config.ts +16 -0
  28. package/template/design-system/README.md +97 -0
  29. package/template/design-system/starter-v1/components/button/component.css +42 -0
  30. package/template/design-system/starter-v1/components/button/danger.html +21 -0
  31. package/template/design-system/starter-v1/components/button/default.html +21 -0
  32. package/template/design-system/starter-v1/components/button/disabled.html +21 -0
  33. package/template/design-system/starter-v1/components/button/ghost.html +21 -0
  34. package/template/design-system/starter-v1/components/button/macro.njk +14 -0
  35. package/template/design-system/starter-v1/components/button/primary.html +21 -0
  36. package/template/design-system/starter-v1/components/button/variants.json +30 -0
  37. package/template/design-system/starter-v1/ds.json +3 -0
  38. package/template/design-system/starter-v1/global.css +52 -0
  39. package/template/design-system/starter-v1/style.css +107 -0
  40. package/template/gitignore +8 -0
  41. package/template/package.json +41 -0
  42. package/template/prototypes/F-001-hello/1-welcome.njk +30 -0
  43. package/template/prototypes/F-001-hello/2-form.njk +46 -0
  44. package/template/prototypes/F-001-hello/3-done.njk +29 -0
  45. package/template/prototypes/F-001-hello/meta.json +6 -0
  46. package/template/prototypes/_shared/_auth-gate.njk +54 -0
  47. package/template/prototypes/_shared/delivery.njk +43 -0
  48. package/template/prototypes/_shared/layout.njk +15 -0
  49. package/template/prototypes/_shared/screen.njk +1818 -0
  50. package/template/prototypes/_shared/wireflow.njk +4731 -0
  51. package/template/public/auth-gate.css +150 -0
  52. package/template/public/bedrock/color-inspector.js +284 -0
  53. package/template/public/bedrock/component-overlay.js +219 -0
  54. package/template/public/bedrock/data/bedrock-config.js +45 -0
  55. package/template/public/bedrock/font-size-overlay.js +590 -0
  56. package/template/public/bedrock/grid-overlay.js +379 -0
  57. package/template/public/bedrock/prototype-navigation.js +974 -0
  58. package/template/public/cmdk.js +146 -0
  59. package/template/public/ds-xray.css +112 -0
  60. package/template/public/ds-xray.js +271 -0
  61. package/template/public/favicon.svg +4 -0
  62. package/template/public/icons/bolt-fill.svg +3 -0
  63. package/template/public/icons/bolt.svg +3 -0
  64. package/template/public/icons/caret-down-fill.svg +3 -0
  65. package/template/public/icons/check-double.svg +4 -0
  66. package/template/public/icons/check.svg +3 -0
  67. package/template/public/icons/chevron-left.svg +3 -0
  68. package/template/public/icons/chevron-right.svg +3 -0
  69. package/template/public/icons/circle-info.svg +6 -0
  70. package/template/public/icons/grid.svg +6 -0
  71. package/template/public/icons/message-square-1.svg +3 -0
  72. package/template/public/icons/message-square.svg +3 -0
  73. package/template/public/icons/messages.svg +4 -0
  74. package/template/public/icons/options-horizontal.svg +5 -0
  75. package/template/public/icons/swatches.svg +6 -0
  76. package/template/public/icons/workflow.svg +6 -0
  77. package/template/public/lightbox.js +87 -0
  78. package/template/public/proto-chrome.css +596 -0
  79. package/template/public/screen-comments.css +723 -0
  80. package/template/public/wireflow-client.js +26 -0
  81. package/template/scripts/build-storybooks.mjs +8 -0
  82. package/template/scripts/dev-setup.mjs +15 -0
  83. package/template/scripts/generate-stories.mjs +12 -0
  84. package/template/scripts/generate-variants.mjs +22 -0
  85. package/template/tsconfig.base.json +19 -0
@@ -0,0 +1,146 @@
1
+ // ⌘K command palette — shared across the prototype harness (screen detail
2
+ // pages + the wireflow). Search the design system and open a component in
3
+ // Storybook. Top window only (disabled inside the wireflow's screen iframes).
4
+ // Targets come from each version's Storybook index.json, so the docs link is
5
+ // correct even for re-homed groups like OS/ (os-*--docs).
6
+ (function () {
7
+ if (window.self !== window.top) return;
8
+ if (window.__cmdkWired) return;
9
+ window.__cmdkWired = true;
10
+ var ov, inp, list, cmds = null, results = [], active = 0;
11
+ function injectStyle() {
12
+ if (document.getElementById('cmdk-style')) return;
13
+ var s = document.createElement('style'); s.id = 'cmdk-style';
14
+ s.textContent =
15
+ '.cmdk-ov{position:fixed;inset:0;z-index:100000;background:rgba(15,23,42,.5);display:none;align-items:flex-start;justify-content:center;padding-top:12vh;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif}.cmdk-ov.is-open{display:flex}.cmdk-box{width:min(560px,92vw);background:#fff;border:1px solid #e5e7eb;border-radius:12px;box-shadow:0 24px 64px rgba(0,0,0,.3);overflow:hidden;color:#0f172a}.cmdk-in{width:100%;box-sizing:border-box;border:0;border-bottom:1px solid #e5e7eb;padding:14px 16px;font-size:15px;outline:0;font-family:inherit;background:#fff;color:#0f172a}.cmdk-list{max-height:52vh;overflow-y:auto;padding:6px 0}.cmdk-row{display:flex;align-items:baseline;gap:10px;width:100%;text-align:left;border:0;background:transparent;cursor:pointer;padding:9px 16px;font-family:inherit;color:#0f172a}.cmdk-row.is-active{background:#f1f5f9}.cmdk-row b{font-weight:600;font-size:14px}.cmdk-row span{font-size:12px;color:#64748b}.cmdk-foot{padding:8px 16px;border-top:1px solid #e5e7eb;font-size:11px;color:#64748b}.cmdk-empty{padding:16px;font-size:13px;color:#64748b}';
16
+ document.head.appendChild(s);
17
+ }
18
+ function build() {
19
+ injectStyle();
20
+ ov = document.createElement('div'); ov.className = 'cmdk-ov';
21
+ ov.innerHTML = '<div class="cmdk-box"><input class="cmdk-in" placeholder="Search components…" aria-label="Search components"><div class="cmdk-list"></div><div class="cmdk-foot">↑↓ navigate · ↵ open in Storybook · esc close</div></div>';
22
+ document.body.appendChild(ov);
23
+ inp = ov.querySelector('.cmdk-in'); list = ov.querySelector('.cmdk-list');
24
+ ov.addEventListener('click', function (e) { if (e.target === ov) close(); });
25
+ inp.addEventListener('input', function () { active = 0; render(); });
26
+ inp.addEventListener('keydown', function (e) {
27
+ if (e.key === 'ArrowDown') { e.preventDefault(); active = Math.min(active + 1, results.length - 1); highlight(); }
28
+ else if (e.key === 'ArrowUp') { e.preventDefault(); active = Math.max(active - 1, 0); highlight(); }
29
+ else if (e.key === 'Enter') { e.preventDefault(); go(results[active]); }
30
+ else if (e.key === 'Escape') { e.preventDefault(); close(); }
31
+ });
32
+ }
33
+ function loadCmds() {
34
+ if (cmds) return Promise.resolve(cmds);
35
+ return fetch('/__ds.json', { credentials: 'include', cache: 'no-store' })
36
+ .then(function (r) { return r.ok ? r.json() : []; })
37
+ .then(function (versions) {
38
+ return Promise.all((versions || []).map(function (v) {
39
+ return fetch('/ds/' + v.version + '/storybook/index.json', { credentials: 'include', cache: 'no-store' })
40
+ .then(function (r) { return r.ok ? r.json() : null; })
41
+ .then(function (idx) {
42
+ var out = [];
43
+ var entries = (idx && (idx.entries || idx.stories)) || {};
44
+ Object.keys(entries).forEach(function (k) {
45
+ var e = entries[k];
46
+ if (e && e.type === 'docs') {
47
+ var parts = String(e.title || '').split('/');
48
+ out.push({ label: parts.slice(1).join(' / ') || (e.title || ''), sub: (parts[0] || '') + ' · ' + v.version, href: '/ds/' + v.version + '/storybook/?path=/docs/' + e.id });
49
+ }
50
+ });
51
+ return out;
52
+ }).catch(function () { return []; });
53
+ })).then(function (lists) { cmds = [].concat.apply([], lists); return cmds; });
54
+ }).catch(function () { cmds = []; return cmds; });
55
+ }
56
+ function render() {
57
+ var q = inp.value.trim().toLowerCase();
58
+ results = q ? cmds.filter(function (c) { return (c.label + ' ' + c.sub).toLowerCase().indexOf(q) >= 0; }) : cmds.slice(0, 80);
59
+ paint();
60
+ }
61
+ // Rebuild the row DOM. Only call when the result set changes — never on
62
+ // hover. Hovering used to call this, which blew away the row mid-click
63
+ // (innerHTML reset between mousedown and mouseup → the click never landed).
64
+ function paint() {
65
+ if (active > results.length - 1) active = Math.max(0, results.length - 1);
66
+ if (!results.length) { list.innerHTML = '<div class="cmdk-empty">No matches.</div>'; return; }
67
+ list.innerHTML = '';
68
+ results.forEach(function (c, i) {
69
+ var b = document.createElement('button'); b.type = 'button'; b.className = 'cmdk-row';
70
+ b.innerHTML = '<b></b><span></span>';
71
+ b.querySelector('b').textContent = c.label; b.querySelector('span').textContent = c.sub;
72
+ b.addEventListener('mouseenter', function () { active = i; highlight(); });
73
+ b.addEventListener('click', function () { go(c); });
74
+ list.appendChild(b);
75
+ });
76
+ highlight();
77
+ }
78
+ // Cheap, DOM-stable: just move the is-active class. Safe to call on hover
79
+ // and arrow-key navigation without disturbing the node under the cursor.
80
+ function highlight() {
81
+ var rows = list.querySelectorAll('.cmdk-row');
82
+ if (active > rows.length - 1) active = Math.max(0, rows.length - 1);
83
+ for (var i = 0; i < rows.length; i++) rows[i].classList.toggle('is-active', i === active);
84
+ var act = rows[active]; if (act && act.scrollIntoView) act.scrollIntoView({ block: 'nearest' });
85
+ }
86
+ function go(c) { if (!c) return; window.open(c.href, '_blank', 'noopener'); close(); }
87
+
88
+ // Programmatic entry points for other harness chrome (the DS X-ray
89
+ // ⌥⌘-click shortcut). cmdkOpenFirst opens the first component whose label
90
+ // matches one of `queries` (a class name or list of them) directly in
91
+ // Storybook. The blank tab is reserved synchronously inside the click
92
+ // gesture, then pointed at the resolved URL once the (usually cached)
93
+ // index loads — this survives the async hop without the popup blocker.
94
+ window.cmdkPreload = function () { return loadCmds(); };
95
+ window.cmdkOpenFirst = function (queries) {
96
+ var arr = (queries && typeof queries !== 'string' && queries.length) ? queries : [queries];
97
+ function find() {
98
+ var hit = null;
99
+ for (var i = 0; i < arr.length && !hit; i++) {
100
+ var raw = String(arr[i] || '').toLowerCase();
101
+ if (!raw) continue;
102
+ // Queries are class names; palette labels are bare component names.
103
+ // Derive candidates: raw → BEM base (strip __elem/--mod) → name
104
+ // (strip the c-/o-/u- namespace prefix), so 'c-mobile-step-bar__trigger'
105
+ // resolves to 'mobile-step-bar'. Prefer an exact label, then substring.
106
+ var base = raw.replace(/(__|--)[\s\S]*$/, '');
107
+ var name = base.replace(/^[cou]-/, '');
108
+ var qs = [raw, base, name];
109
+ for (var j = 0; j < qs.length && !hit; j++) {
110
+ var q = qs[j];
111
+ if (!q) continue;
112
+ hit = cmds.filter(function (c) { return c.label.toLowerCase() === q; })[0]
113
+ || cmds.filter(function (c) { return (c.label + ' ' + c.sub).toLowerCase().indexOf(q) >= 0; })[0]
114
+ || null;
115
+ }
116
+ }
117
+ return hit;
118
+ }
119
+ // Fast path: index already warmed (X-ray preloads it on activate). Resolve
120
+ // and open synchronously inside the click gesture, so a miss — e.g. a
121
+ // layout-only class with no component — opens no tab at all.
122
+ if (cmds) { var h = find(); if (h) window.open(h.href, '_blank', 'noopener'); return Promise.resolve(h); }
123
+ // Cold path: reserve a tab in-gesture, point it once the index loads.
124
+ var w = window.open('', '_blank');
125
+ return loadCmds().then(function () {
126
+ var hit = find();
127
+ if (hit && w) { try { w.opener = null; } catch (e) {} w.location = hit.href; }
128
+ else if (w) w.close();
129
+ return hit;
130
+ }).catch(function () { if (w) { try { w.close(); } catch (e) {} } return null; });
131
+ };
132
+ function open() {
133
+ if (!ov) build();
134
+ ov.classList.add('is-open'); inp.value = ''; active = 0;
135
+ list.innerHTML = '<div class="cmdk-empty">Loading components…</div>';
136
+ inp.focus();
137
+ loadCmds().then(render);
138
+ }
139
+ function close() { if (ov) ov.classList.remove('is-open'); }
140
+ document.addEventListener('keydown', function (e) {
141
+ if ((e.metaKey || e.ctrlKey) && (e.key === 'k' || e.key === 'K')) {
142
+ e.preventDefault();
143
+ if (ov && ov.classList.contains('is-open')) close(); else open();
144
+ }
145
+ });
146
+ })();
@@ -0,0 +1,112 @@
1
+ /* DS X-ray — a review-only overlay that outlines each product element by
2
+ its design-system provenance, derived purely from class membership
3
+ against the loaded DS bundle (/ds/<version>/style.css). Toggled with the
4
+ `x` key or the chrome "X-ray" button. This is harness chrome — it never
5
+ ships as product UI and only ever decorates elements inside
6
+ `.proto-content`.
7
+
8
+ Three buckets:
9
+ ds — every (meaningful) class on the element is defined in the DS
10
+ mixed — element carries DS classes plus page-local ones
11
+ nonds — no DS classes at all (page-local layout / invented markup)
12
+ `outline` is used (not border) so the page layout never shifts. */
13
+
14
+ .ds-xray-ds { outline: 2px solid rgba(21, 128, 61, 0.9) !important; outline-offset: -2px; }
15
+ .ds-xray-mixed { outline: 2px solid rgba(217, 119, 6, 0.95) !important; outline-offset: -2px; }
16
+ .ds-xray-nonds { outline: 2px dashed rgba(220, 38, 38, 0.85) !important; outline-offset: -2px; }
17
+
18
+ /* Legend — fixed pill with live counts; doubles as the off switch. Sits
19
+ above everything, independent of whether the rest of the chrome is shown. */
20
+ .ds-xray-legend {
21
+ position: fixed;
22
+ left: 16px;
23
+ bottom: 16px;
24
+ z-index: 2147483000;
25
+ display: none;
26
+ align-items: center;
27
+ gap: 12px;
28
+ padding: 8px 10px 8px 14px;
29
+ border-radius: 10px;
30
+ background: #0f172a;
31
+ color: #fff;
32
+ font: 500 12px/1.2 'Archivo', system-ui, sans-serif;
33
+ box-shadow: 0 6px 24px rgba(15, 23, 42, 0.4);
34
+ }
35
+ .ds-xray-legend.is-on { display: flex; }
36
+ .ds-xray-legend__title { font-weight: 700; letter-spacing: 0.01em; }
37
+ .ds-xray-key { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
38
+ .ds-xray-key::before {
39
+ content: '';
40
+ width: 12px;
41
+ height: 12px;
42
+ border-radius: 3px;
43
+ box-sizing: border-box;
44
+ }
45
+ .ds-xray-key--ds::before { background: rgba(21, 128, 61, 0.9); }
46
+ .ds-xray-key--mixed::before { background: rgba(217, 119, 6, 0.95); }
47
+ .ds-xray-key--nonds::before { background: transparent; border: 2px dashed rgba(248, 113, 113, 0.95); }
48
+ .ds-xray-key b { font-weight: 700; }
49
+ .ds-xray-legend__close {
50
+ border: none;
51
+ background: rgba(255, 255, 255, 0.12);
52
+ color: #fff;
53
+ width: 22px;
54
+ height: 22px;
55
+ border-radius: 6px;
56
+ cursor: pointer;
57
+ font-size: 14px;
58
+ line-height: 1;
59
+ }
60
+ .ds-xray-legend__close:hover { background: rgba(255, 255, 255, 0.22); }
61
+
62
+ /* Hover tooltip — names the classes under the cursor and which are DS. */
63
+ .ds-xray-tip {
64
+ position: fixed;
65
+ z-index: 2147483001;
66
+ max-width: 320px;
67
+ padding: 6px 9px;
68
+ border-radius: 7px;
69
+ background: #0f172a;
70
+ color: #fff;
71
+ font: 500 11.5px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
72
+ pointer-events: none;
73
+ display: none;
74
+ box-shadow: 0 4px 16px rgba(15, 23, 42, 0.45);
75
+ }
76
+ .ds-xray-tip.is-on { display: block; }
77
+ .ds-xray-tip .is-ds { color: #4ade80; }
78
+ .ds-xray-tip .is-non { color: #fca5a5; }
79
+ .ds-xray-tip__hint {
80
+ display: block;
81
+ margin-top: 5px;
82
+ padding-top: 5px;
83
+ border-top: 1px solid rgba(255, 255, 255, 0.12);
84
+ color: #94a3b8;
85
+ font-size: 10.5px;
86
+ }
87
+
88
+ /* Chrome toggle button — appended into the proto top bar for discovery. */
89
+ .proto-xray-btn {
90
+ border: 1px solid var(--proto-chrome-border, rgba(148, 163, 184, 0.4));
91
+ background: transparent;
92
+ color: var(--proto-chrome-text, #0f172a);
93
+ font: 600 12px/1 'Archivo', system-ui, sans-serif;
94
+ padding: 6px 10px;
95
+ border-radius: 7px;
96
+ cursor: pointer;
97
+ display: inline-flex;
98
+ align-items: center;
99
+ gap: 5px;
100
+ }
101
+ .proto-xray-btn svg { display: block; }
102
+ /* On touch devices chrome competes with a small screen — icon only. */
103
+ @media (pointer: coarse) {
104
+ .proto-xray-btn__label { display: none; }
105
+ }
106
+ /* Active = inverted chrome (dark pill in light mode, light pill in dark
107
+ mode) so the label never blends into the toolbar background. */
108
+ .proto-xray-btn.is-on {
109
+ background: var(--proto-chrome-text, #0f172a);
110
+ color: var(--proto-chrome-bg, #fff);
111
+ border-color: var(--proto-chrome-text, #0f172a);
112
+ }
@@ -0,0 +1,271 @@
1
+ /* DS X-ray — runtime design-system provenance overlay.
2
+ *
3
+ * "On any given screen, know what it's using." Reads the class names the
4
+ * DS actually defines straight from the loaded bundle (the stylesheet whose
5
+ * href matches /ds/<version>/style.css), then classifies every element
6
+ * inside `.proto-content` by class membership:
7
+ *
8
+ * ds — all meaningful classes are DS classes (solid green)
9
+ * mixed — DS classes + page-local classes (solid amber)
10
+ * nonds — no DS classes (page layout / invented) (dashed red)
11
+ *
12
+ * Signal is class-membership only: it answers "is this class from the DS",
13
+ * not "was the DS macro used" — an element that borrows a DS class reads as
14
+ * DS. Toggle with the `x` key or the chrome X-ray button. Top-level only;
15
+ * inside wireflow iframe previews the screen should look like the product.
16
+ */
17
+ (function () {
18
+ if (window.self !== window.top) return;
19
+
20
+ // Vendor/global classes that aren't page-authored and aren't a DS
21
+ // component either — ignore them so icon spans don't read as "page".
22
+ var IGNORE = {
23
+ 'material-symbols-sharp': 1,
24
+ 'material-symbols-outlined': 1,
25
+ 'material-symbols-rounded': 1,
26
+ };
27
+
28
+ var registry = null; // Set<string> of DS class names, built lazily.
29
+ var active = false;
30
+ var legend = null;
31
+ var tip = null;
32
+ var btn = null;
33
+ var listenDoc = null; // document we attached hover/click listeners to.
34
+
35
+ // The document to classify. When a Viewport breakpoint preview is active
36
+ // (a local feature), the real screen renders inside the same-origin preview
37
+ // iframe while the top-level .proto-content is hidden — so target the
38
+ // iframe's document. Otherwise the top-level document.
39
+ function previewDoc() {
40
+ var stage = document.querySelector('.proto-stage');
41
+ if (!stage || !stage.classList.contains('is-previewing')) return null;
42
+ var ifr = document.querySelector('.proto-preview__iframe');
43
+ try {
44
+ var d = ifr && ifr.contentDocument;
45
+ return d && d.querySelector('.proto-content') ? d : null;
46
+ } catch (e) { return null; } // cross-origin (shouldn't happen, same-origin)
47
+ }
48
+ function activeDoc() { return previewDoc() || document; }
49
+
50
+ function buildRegistry(doc) {
51
+ var classes = new Set();
52
+ var sheets = doc.styleSheets;
53
+ for (var i = 0; i < sheets.length; i++) {
54
+ var href = sheets[i].href || '';
55
+ if (!/\/ds\/[^/]+\/style\.css(\?|$)/.test(href)) continue;
56
+ var rules;
57
+ try { rules = sheets[i].cssRules; } catch (e) { continue; } // cross-origin
58
+ if (rules) collect(rules, classes);
59
+ }
60
+ return classes;
61
+ }
62
+
63
+ function collect(rules, classes) {
64
+ for (var i = 0; i < rules.length; i++) {
65
+ var r = rules[i];
66
+ if (r.selectorText) {
67
+ var m = r.selectorText.match(/\.[-_a-zA-Z][-_a-zA-Z0-9]*/g);
68
+ if (m) for (var j = 0; j < m.length; j++) classes.add(m[j].slice(1));
69
+ } else if (r.cssRules) {
70
+ collect(r.cssRules, classes); // @media / @supports
71
+ }
72
+ }
73
+ }
74
+
75
+ function meaningfulClasses(el) {
76
+ var out = [];
77
+ el.classList.forEach(function (c) {
78
+ if (IGNORE[c]) return;
79
+ if (c.indexOf('ds-xray') === 0) return;
80
+ out.push(c);
81
+ });
82
+ return out;
83
+ }
84
+
85
+ function classify(cls) {
86
+ var ds = [], non = [];
87
+ for (var i = 0; i < cls.length; i++) {
88
+ (registry.has(cls[i]) ? ds : non).push(cls[i]);
89
+ }
90
+ if (ds.length && !non.length) return { kind: 'ds', ds: ds, non: non };
91
+ if (!ds.length && non.length) return { kind: 'nonds', ds: ds, non: non };
92
+ if (ds.length && non.length) return { kind: 'mixed', ds: ds, non: non };
93
+ return null;
94
+ }
95
+
96
+ function apply() {
97
+ var doc = activeDoc();
98
+ // Rebuild per call: the active doc can change (top-level ↔ preview iframe)
99
+ // as the Viewport control switches between Full and a breakpoint.
100
+ registry = buildRegistry(doc);
101
+ var counts = { ds: 0, mixed: 0, nonds: 0 };
102
+ var root = doc.querySelector('.proto-content');
103
+ if (!root) return counts;
104
+ var els = root.querySelectorAll('*');
105
+ for (var i = 0; i < els.length; i++) {
106
+ var el = els[i];
107
+ if (el.closest('.wf-edges')) continue; // off-screen wireflow edges
108
+ var cls = meaningfulClasses(el);
109
+ if (!cls.length) continue;
110
+ var res = classify(cls);
111
+ if (!res) continue;
112
+ el.classList.add('ds-xray-' + res.kind);
113
+ el.__dsXray = res;
114
+ counts[res.kind]++;
115
+ }
116
+ return counts;
117
+ }
118
+
119
+ function clear() {
120
+ [document, previewDoc()].forEach(function (doc) {
121
+ if (!doc) return;
122
+ var els = doc.querySelectorAll('.ds-xray-ds, .ds-xray-mixed, .ds-xray-nonds');
123
+ for (var i = 0; i < els.length; i++) {
124
+ els[i].classList.remove('ds-xray-ds', 'ds-xray-mixed', 'ds-xray-nonds');
125
+ els[i].__dsXray = null;
126
+ }
127
+ });
128
+ }
129
+
130
+ function ensureUi() {
131
+ if (legend) return;
132
+ legend = document.createElement('div');
133
+ legend.className = 'ds-xray-legend';
134
+ legend.innerHTML =
135
+ '<span class="ds-xray-legend__title">DS X-ray</span>' +
136
+ '<span class="ds-xray-key ds-xray-key--ds">DS <b data-c="ds">0</b></span>' +
137
+ '<span class="ds-xray-key ds-xray-key--mixed">Mixed <b data-c="mixed">0</b></span>' +
138
+ '<span class="ds-xray-key ds-xray-key--nonds">Page <b data-c="nonds">0</b></span>' +
139
+ '<button type="button" class="ds-xray-legend__close" aria-label="Turn off X-ray">×</button>';
140
+ legend.querySelector('.ds-xray-legend__close').addEventListener('click', function () { setActive(false); });
141
+ document.body.appendChild(legend);
142
+
143
+ tip = document.createElement('div');
144
+ tip.className = 'ds-xray-tip';
145
+ document.body.appendChild(tip);
146
+ }
147
+
148
+ function setCounts(counts) {
149
+ legend.querySelector('[data-c="ds"]').textContent = counts.ds;
150
+ legend.querySelector('[data-c="mixed"]').textContent = counts.mixed;
151
+ legend.querySelector('[data-c="nonds"]').textContent = counts.nonds;
152
+ }
153
+
154
+ // When listening inside the preview iframe, event coords are iframe-relative
155
+ // but the tip lives in the top-level body — shift by the iframe's position.
156
+ function frameOffset() {
157
+ if (listenDoc && listenDoc !== document) {
158
+ var ifr = document.querySelector('.proto-preview__iframe');
159
+ if (ifr) { var r = ifr.getBoundingClientRect(); return { x: r.left, y: r.top }; }
160
+ }
161
+ return { x: 0, y: 0 };
162
+ }
163
+
164
+ function onMove(e) {
165
+ var el = e.target.closest && e.target.closest('.ds-xray-ds, .ds-xray-mixed, .ds-xray-nonds');
166
+ if (!el || !el.__dsXray) { tip.classList.remove('is-on'); return; }
167
+ var r = el.__dsXray;
168
+ var html = '';
169
+ if (r.ds.length) html += '<span class="is-ds">DS: ' + r.ds.join(' ') + '</span>';
170
+ if (r.ds.length && r.non.length) html += '<br>';
171
+ if (r.non.length) html += '<span class="is-non">page: ' + r.non.join(' ') + '</span>';
172
+ if (r.ds.length) html += '<span class="ds-xray-tip__hint">⌥⌘ click → open in Storybook</span>';
173
+ tip.innerHTML = html;
174
+ tip.classList.add('is-on');
175
+ var off = frameOffset();
176
+ var x = Math.min(e.clientX + off.x + 14, window.innerWidth - 330);
177
+ var y = Math.min(e.clientY + off.y + 16, window.innerHeight - 60);
178
+ tip.style.left = Math.max(8, x) + 'px';
179
+ tip.style.top = y + 'px';
180
+ }
181
+
182
+ // ⌥⌘-click a DS (or mixed) element to jump straight to its Storybook docs
183
+ // — a shortcut for "what is this and how do I use it". We hand the element's
184
+ // DS class names to the ⌘K palette, which opens the first matching
185
+ // component. Capture phase + preventDefault so the underlying screen never
186
+ // reacts to the modified click.
187
+ function onClick(e) {
188
+ if (!active || !e.altKey || !(e.metaKey || e.ctrlKey)) return;
189
+ var el = e.target.closest && e.target.closest('.ds-xray-ds, .ds-xray-mixed');
190
+ if (!el || !el.__dsXray || !el.__dsXray.ds.length) return;
191
+ e.preventDefault();
192
+ e.stopPropagation();
193
+ if (typeof window.cmdkOpenFirst === 'function') window.cmdkOpenFirst(el.__dsXray.ds);
194
+ }
195
+
196
+ function addListeners() {
197
+ listenDoc = activeDoc();
198
+ listenDoc.addEventListener('mousemove', onMove, true);
199
+ listenDoc.addEventListener('click', onClick, true);
200
+ }
201
+ function removeListeners() {
202
+ if (!listenDoc) return;
203
+ listenDoc.removeEventListener('mousemove', onMove, true);
204
+ listenDoc.removeEventListener('click', onClick, true);
205
+ listenDoc = null;
206
+ }
207
+
208
+ function setActive(on) {
209
+ active = on;
210
+ ensureUi();
211
+ if (on) {
212
+ var counts = apply();
213
+ setCounts(counts);
214
+ legend.classList.add('is-on');
215
+ addListeners();
216
+ // Warm the palette's component index so the ⌥⌘-click open is instant.
217
+ if (typeof window.cmdkPreload === 'function') window.cmdkPreload();
218
+ } else {
219
+ clear();
220
+ legend.classList.remove('is-on');
221
+ tip.classList.remove('is-on');
222
+ removeListeners();
223
+ }
224
+ if (btn) btn.classList.toggle('is-on', on);
225
+ }
226
+
227
+ function toggle() { setActive(!active); }
228
+ window.dsXrayToggle = toggle;
229
+ window.dsXrayActive = function () { return active; };
230
+
231
+ // Re-run while active — used by the Viewport control after it switches
232
+ // breakpoint (the active doc flips between top-level and the preview iframe).
233
+ window.dsXrayReapply = function () {
234
+ if (!active) return;
235
+ clear();
236
+ removeListeners();
237
+ setCounts(apply());
238
+ addListeners();
239
+ };
240
+
241
+ function isEditable(el) {
242
+ if (!el) return false;
243
+ var t = el.tagName;
244
+ return t === 'INPUT' || t === 'TEXTAREA' || t === 'SELECT' || el.isContentEditable;
245
+ }
246
+
247
+ document.addEventListener('keydown', function (e) {
248
+ if (e.defaultPrevented || e.metaKey || e.ctrlKey || e.altKey || e.shiftKey) return;
249
+ if (isEditable(e.target)) return;
250
+ if (e.key === 'x' || e.key === 'X') { e.preventDefault(); toggle(); }
251
+ });
252
+
253
+ // Discoverability: a toggle button in the proto top bar (when present).
254
+ document.addEventListener('DOMContentLoaded', function () {
255
+ var bar = document.querySelector('.proto-topbar');
256
+ if (!bar) return;
257
+ btn = document.createElement('button');
258
+ btn.type = 'button';
259
+ btn.className = 'proto-xray-btn';
260
+ // Icon + label; the label collapses away on touch devices (CSS) where
261
+ // chrome competes with a small screen.
262
+ btn.innerHTML =
263
+ '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' +
264
+ '<path d="M4 8V6a2 2 0 0 1 2-2h2M16 4h2a2 2 0 0 1 2 2v2M20 16v2a2 2 0 0 1-2 2h-2M8 20H6a2 2 0 0 1-2-2v-2M7 12h10"/></svg>' +
265
+ '<span class="proto-xray-btn__label">X-ray</span>';
266
+ btn.title = 'Toggle DS X-ray (x)';
267
+ btn.setAttribute('aria-label', 'Toggle DS X-ray');
268
+ btn.addEventListener('click', toggle);
269
+ bar.appendChild(btn);
270
+ });
271
+ })();
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FF4B26">
2
+ <!-- Bolt fill — Bedrock Flows brand mark. Source: public/icons/bolt-fill.svg recolored. -->
3
+ <path d="M13.9098 3.5H8.37203C8.15122 3.5 7.95656 3.64483 7.89311 3.85633L5.19311 12.8563C5.09687 13.1771 5.33709 13.5 5.67203 13.5H9.35962C9.68491 13.5 9.92359 13.8057 9.84469 14.1213L8.47795 19.5882C8.35217 20.0914 8.98115 20.4297 9.3316 20.0474L18.232 10.3379C18.5259 10.0172 18.2984 9.5 17.8634 9.5H14.0902C13.7813 9.5 13.5462 9.22257 13.597 8.9178L14.403 4.0822C14.4538 3.77743 14.2187 3.5 13.9098 3.5Z"/>
4
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M13.9098 3.5H8.37203C8.15122 3.5 7.95656 3.64483 7.89311 3.85633L5.19311 12.8563C5.09687 13.1771 5.33709 13.5 5.67203 13.5H9.35962C9.68491 13.5 9.92359 13.8057 9.84469 14.1213L8.47795 19.5882C8.35217 20.0914 8.98115 20.4297 9.3316 20.0474L18.232 10.3379C18.5259 10.0172 18.2984 9.5 17.8634 9.5H14.0902C13.7813 9.5 13.5462 9.22257 13.597 8.9178L14.403 4.0822C14.4538 3.77743 14.2187 3.5 13.9098 3.5Z" fill="black"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M13.9098 3.5H8.37203C8.15122 3.5 7.95656 3.64483 7.89311 3.85633L5.19311 12.8563C5.09687 13.1771 5.33709 13.5 5.67203 13.5H9.35962C9.68491 13.5 9.92359 13.8057 9.84469 14.1213L8.47795 19.5882C8.35217 20.0914 8.98115 20.4297 9.3316 20.0474L18.232 10.3379C18.5259 10.0172 18.2984 9.5 17.8634 9.5H14.0902C13.7813 9.5 13.5462 9.22257 13.597 8.9178L14.403 4.0822C14.4538 3.77743 14.2187 3.5 13.9098 3.5Z" stroke="black" stroke-width="1.5" stroke-miterlimit="2.16568" stroke-linecap="round" stroke-linejoin="round"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M15.9325 10C16.3564 10 16.588 10.4944 16.3166 10.8201L12.3841 15.5391C12.1842 15.7789 11.8158 15.7789 11.6159 15.5391L7.68341 10.8201C7.41202 10.4944 7.6436 10 8.06752 10H15.9325Z" fill="black"/>
3
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M20 8L12 18L11.0773 16.9733" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M4 11L8 16L16 6" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M4 12L9.33333 18L20 6" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M14 18L8 12L14 6" stroke="black" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M10 6L16 12L10 18" stroke="black" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
3
+ </svg>
@@ -0,0 +1,6 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21Z" stroke="black" stroke-width="1.5"/>
3
+ <path d="M11.9999 16.7122V10.6733" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
4
+ <path d="M12.0029 7.32996H12.0149" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
5
+ <path d="M12.0029 16.7541V10.7153" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
6
+ </svg>
@@ -0,0 +1,6 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M19 4H15C14.4477 4 14 4.44772 14 5V9C14 9.55228 14.4477 10 15 10H19C19.5523 10 20 9.55228 20 9V5C20 4.44772 19.5523 4 19 4Z" stroke="black" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M19 14H15C14.4477 14 14 14.4477 14 15V19C14 19.5523 14.4477 20 15 20H19C19.5523 20 20 19.5523 20 19V15C20 14.4477 19.5523 14 19 14Z" stroke="black" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
4
+ <path d="M9 4H5C4.44772 4 4 4.44772 4 5V9C4 9.55228 4.44772 10 5 10H9C9.55228 10 10 9.55228 10 9V5C10 4.44772 9.55228 4 9 4Z" stroke="black" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
5
+ <path d="M9 14H5C4.44772 14 4 14.4477 4 15V19C4 19.5523 4.44772 20 5 20H9C9.55228 20 10 19.5523 10 19V15C10 14.4477 9.55228 14 9 14Z" stroke="black" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
6
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M21 15C21 15.5304 20.7893 16.0391 20.4142 16.4142C20.0391 16.7893 19.5304 17 19 17H7L3 21V5C3 4.46957 3.21071 3.96086 3.58579 3.58579C3.96086 3.21071 4.46957 3 5 3H19C19.5304 3 20.0391 3.21071 20.4142 3.58579C20.7893 3.96086 21 4.46957 21 5V15Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M21 15C21 15.5304 20.7893 16.0391 20.4142 16.4142C20.0391 16.7893 19.5304 17 19 17H7L3 21V5C3 4.46957 3.21071 3.96086 3.58579 3.58579C3.96086 3.21071 4.46957 3 5 3H19C19.5304 3 20.0391 3.21071 20.4142 3.58579C20.7893 3.96086 21 4.46957 21 5V15Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M14 5H4C3.44772 5 3 5.44772 3 6V12.9375C3 13.4898 3.44772 13.9375 4 13.9375H7V16L11 13.9375H14C14.5523 13.9375 15 13.4898 15 12.9375V6C15 5.44772 14.5523 5 14 5Z" stroke="black" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M18 8H20C20.5523 8 21 8.44772 21 9V15.9375C21 16.4898 20.5523 16.9375 20 16.9375H17V19L13 16.9375H11" stroke="black" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M18.006 11.994V12.006" stroke="black" stroke-width="3" stroke-linecap="round"/>
3
+ <path d="M12.006 11.994V12.006" stroke="black" stroke-width="3" stroke-linecap="round"/>
4
+ <path d="M6.00598 11.994V12.006" stroke="black" stroke-width="3" stroke-linecap="round"/>
5
+ </svg>
@@ -0,0 +1,6 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M3 7C3 5.11438 3 4.17157 3.58579 3.58579C4.17157 3 5.11438 3 7 3C8.88562 3 9.82843 3 10.4142 3.58579C11 4.17157 11 5.11438 11 7V12V17C11 18.8856 11 19.8284 10.4142 20.4142C9.82843 21 8.88562 21 7 21C5.11438 21 4.17157 21 3.58579 20.4142C3 19.8284 3 18.8856 3 17V12V7Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M11 7.49994L12.6716 5.82837C14.0049 4.49503 14.6716 3.82837 15.5 3.82837C16.3284 3.82837 16.9951 4.49503 18.3284 5.82837L19.1716 6.67151C20.5049 8.00485 21.1716 8.67151 21.1716 9.49994C21.1716 10.3283 20.5049 10.995 19.1716 12.3283L11 20.4999" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
4
+ <path d="M7 21H17C18.8856 21 19.8284 21 20.4142 20.4142C21 19.8284 21 18.8856 21 17V15.5C21 15.0353 21 14.803 20.9616 14.6098C20.8038 13.8164 20.1836 13.1962 19.3902 13.0384C19.197 13 18.9647 13 18.5 13" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
5
+ <path d="M7 16.9915L7.00849 17" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
6
+ </svg>
@@ -0,0 +1,6 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M6.25 10C8.59721 10 10.5 8.76878 10.5 7.25C10.5 5.73122 8.59721 4.5 6.25 4.5C3.90279 4.5 2 5.73122 2 7.25C2 8.76878 3.90279 10 6.25 10Z" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
3
+ <path d="M12 19H8C7 19 5 18.4 5 16C5 13.6 7 13 8 13H17C18 13 20 12.4 20 10C20 7.6 18 7 17 7H13" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
4
+ <path d="M11 21.4142L13.7071 18.7071L11 16" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
5
+ <path d="M21 16H17C16.4477 16 16 16.4477 16 17V21C16 21.5523 16.4477 22 17 22H21C21.5523 22 22 21.5523 22 21V17C22 16.4477 21.5523 16 21 16Z" fill="black"/>
6
+ </svg>