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.
- package/auth-schema.sql +8 -0
- package/bin/bedrock-flows.mjs +127 -0
- package/lib/setup.mjs +262 -0
- package/package.json +11 -0
- package/template/.storybook/main.js +46 -0
- package/template/.storybook/manager-head.html +963 -0
- package/template/.storybook/preview-head.html +35 -0
- package/template/.storybook/preview.js +23 -0
- package/template/CHANGELOG.md +236 -0
- package/template/README.md +26 -0
- package/template/apps/dashboard/index.html +15 -0
- package/template/apps/dashboard/package.json +22 -0
- package/template/apps/dashboard/src/App.module.css +1318 -0
- package/template/apps/dashboard/src/App.tsx +2716 -0
- package/template/apps/dashboard/src/auth-client.ts +17 -0
- package/template/apps/dashboard/src/changelog.tsx +92 -0
- package/template/apps/dashboard/src/index.css +86 -0
- package/template/apps/dashboard/src/main.tsx +15 -0
- package/template/apps/dashboard/src/theme.ts +31 -0
- package/template/apps/dashboard/src/vite-env.d.ts +4 -0
- package/template/apps/dashboard/vite.config.ts +48 -0
- package/template/apps/worker/.dev.vars.example +50 -0
- package/template/apps/worker/package.json +19 -0
- package/template/apps/worker/src/index.ts +295 -0
- package/template/apps/worker/tsconfig.json +11 -0
- package/template/apps/worker/wrangler.jsonc +29 -0
- package/template/bedrock.config.ts +16 -0
- package/template/design-system/README.md +97 -0
- package/template/design-system/starter-v1/components/button/component.css +42 -0
- package/template/design-system/starter-v1/components/button/danger.html +21 -0
- package/template/design-system/starter-v1/components/button/default.html +21 -0
- package/template/design-system/starter-v1/components/button/disabled.html +21 -0
- package/template/design-system/starter-v1/components/button/ghost.html +21 -0
- package/template/design-system/starter-v1/components/button/macro.njk +14 -0
- package/template/design-system/starter-v1/components/button/primary.html +21 -0
- package/template/design-system/starter-v1/components/button/variants.json +30 -0
- package/template/design-system/starter-v1/ds.json +3 -0
- package/template/design-system/starter-v1/global.css +52 -0
- package/template/design-system/starter-v1/style.css +107 -0
- package/template/gitignore +8 -0
- package/template/package.json +41 -0
- package/template/prototypes/F-001-hello/1-welcome.njk +30 -0
- package/template/prototypes/F-001-hello/2-form.njk +46 -0
- package/template/prototypes/F-001-hello/3-done.njk +29 -0
- package/template/prototypes/F-001-hello/meta.json +6 -0
- package/template/prototypes/_shared/_auth-gate.njk +54 -0
- package/template/prototypes/_shared/delivery.njk +43 -0
- package/template/prototypes/_shared/layout.njk +15 -0
- package/template/prototypes/_shared/screen.njk +1818 -0
- package/template/prototypes/_shared/wireflow.njk +4731 -0
- package/template/public/auth-gate.css +150 -0
- package/template/public/bedrock/color-inspector.js +284 -0
- package/template/public/bedrock/component-overlay.js +219 -0
- package/template/public/bedrock/data/bedrock-config.js +45 -0
- package/template/public/bedrock/font-size-overlay.js +590 -0
- package/template/public/bedrock/grid-overlay.js +379 -0
- package/template/public/bedrock/prototype-navigation.js +974 -0
- package/template/public/cmdk.js +146 -0
- package/template/public/ds-xray.css +112 -0
- package/template/public/ds-xray.js +271 -0
- package/template/public/favicon.svg +4 -0
- package/template/public/icons/bolt-fill.svg +3 -0
- package/template/public/icons/bolt.svg +3 -0
- package/template/public/icons/caret-down-fill.svg +3 -0
- package/template/public/icons/check-double.svg +4 -0
- package/template/public/icons/check.svg +3 -0
- package/template/public/icons/chevron-left.svg +3 -0
- package/template/public/icons/chevron-right.svg +3 -0
- package/template/public/icons/circle-info.svg +6 -0
- package/template/public/icons/grid.svg +6 -0
- package/template/public/icons/message-square-1.svg +3 -0
- package/template/public/icons/message-square.svg +3 -0
- package/template/public/icons/messages.svg +4 -0
- package/template/public/icons/options-horizontal.svg +5 -0
- package/template/public/icons/swatches.svg +6 -0
- package/template/public/icons/workflow.svg +6 -0
- package/template/public/lightbox.js +87 -0
- package/template/public/proto-chrome.css +596 -0
- package/template/public/screen-comments.css +723 -0
- package/template/public/wireflow-client.js +26 -0
- package/template/scripts/build-storybooks.mjs +8 -0
- package/template/scripts/dev-setup.mjs +15 -0
- package/template/scripts/generate-stories.mjs +12 -0
- package/template/scripts/generate-variants.mjs +22 -0
- package/template/tsconfig.base.json +19 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/* Shared auth-gate UI — THE single source for the sign-in/sign-up card.
|
|
2
|
+
Used by all three surfaces:
|
|
3
|
+
- dashboard SPA (apps/dashboard/src/App.tsx → <AuthGate/>)
|
|
4
|
+
- wireflow pages (prototypes/_shared/_auth-gate.njk via wireflow.njk)
|
|
5
|
+
- screen pages (prototypes/_shared/_auth-gate.njk via screen.njk)
|
|
6
|
+
Behaviour JS stays per-surface (each host has its own session plumbing);
|
|
7
|
+
markup + styling live here and in _auth-gate.njk. Change the gate's look
|
|
8
|
+
ONCE, here.
|
|
9
|
+
|
|
10
|
+
Self-contained --bfa-* tokens (light defaults, dark via the shared
|
|
11
|
+
data-theme override on <html> falling back to the OS preference) so the
|
|
12
|
+
gate renders identically over the dashboard, a wireflow canvas, or any
|
|
13
|
+
prototype's design system. The font is pinned for the same reason a DS
|
|
14
|
+
that styles bare headings would otherwise repaint the card. */
|
|
15
|
+
.bf-auth-gate {
|
|
16
|
+
--bfa-surface: #ffffff;
|
|
17
|
+
--bfa-surface-alt: #fafbfc;
|
|
18
|
+
--bfa-border: rgba(15, 23, 42, 0.15);
|
|
19
|
+
--bfa-text: #0f172a;
|
|
20
|
+
--bfa-text-muted: #64748b;
|
|
21
|
+
--bfa-accent: #0f172a;
|
|
22
|
+
--bfa-accent-fg: #ffffff;
|
|
23
|
+
|
|
24
|
+
position: fixed; inset: 0; z-index: 1000;
|
|
25
|
+
display: flex; align-items: center; justify-content: center;
|
|
26
|
+
padding: 24px;
|
|
27
|
+
background: rgba(15, 23, 42, 0.78);
|
|
28
|
+
backdrop-filter: blur(4px);
|
|
29
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
30
|
+
}
|
|
31
|
+
@media (prefers-color-scheme: dark) {
|
|
32
|
+
:root:not([data-theme='light']) .bf-auth-gate {
|
|
33
|
+
--bfa-surface: #0f172a;
|
|
34
|
+
--bfa-surface-alt: #111c33;
|
|
35
|
+
--bfa-border: rgba(255, 255, 255, 0.12);
|
|
36
|
+
--bfa-text: #f1f5f9;
|
|
37
|
+
--bfa-text-muted: #94a3b8;
|
|
38
|
+
--bfa-accent: #f1f5f9;
|
|
39
|
+
--bfa-accent-fg: #0f172a;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
:root[data-theme='dark'] .bf-auth-gate {
|
|
43
|
+
--bfa-surface: #0f172a;
|
|
44
|
+
--bfa-surface-alt: #111c33;
|
|
45
|
+
--bfa-border: rgba(255, 255, 255, 0.12);
|
|
46
|
+
--bfa-text: #f1f5f9;
|
|
47
|
+
--bfa-text-muted: #94a3b8;
|
|
48
|
+
--bfa-accent: #f1f5f9;
|
|
49
|
+
--bfa-accent-fg: #0f172a;
|
|
50
|
+
}
|
|
51
|
+
.bf-auth-gate[hidden] { display: none !important; }
|
|
52
|
+
/* A DS that styles bare headings (display font, heading color) hits the
|
|
53
|
+
gate's heading directly — element-level rules beat inheritance. */
|
|
54
|
+
.bf-auth-gate h1, .bf-auth-gate h2, .bf-auth-gate h3,
|
|
55
|
+
.bf-auth-gate h4, .bf-auth-gate h5, .bf-auth-gate h6 {
|
|
56
|
+
font-family: inherit;
|
|
57
|
+
color: inherit;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.bf-auth-gate__card {
|
|
61
|
+
width: 100%; max-width: 360px;
|
|
62
|
+
background: var(--bfa-surface);
|
|
63
|
+
color: var(--bfa-text);
|
|
64
|
+
border: 1px solid var(--bfa-border);
|
|
65
|
+
border-radius: 12px;
|
|
66
|
+
box-shadow: 0 20px 60px rgba(15, 23, 42, 0.4);
|
|
67
|
+
padding: 24px;
|
|
68
|
+
}
|
|
69
|
+
.bf-auth-gate__title { margin: 0 0 4px; font-size: 16px; font-weight: 600; }
|
|
70
|
+
.bf-auth-gate__lede { margin: 0 0 16px; font-size: 13px; color: var(--bfa-text-muted); }
|
|
71
|
+
|
|
72
|
+
/* Errors are an alert banner at the top of the card (between the lede and
|
|
73
|
+
the form), not a small note under the submit button. */
|
|
74
|
+
.bf-auth-error {
|
|
75
|
+
margin: 0 0 12px;
|
|
76
|
+
padding: 10px 12px;
|
|
77
|
+
font-size: 13px;
|
|
78
|
+
line-height: 1.45;
|
|
79
|
+
color: #b91c1c;
|
|
80
|
+
background: #fef2f2;
|
|
81
|
+
border: 1px solid #fca5a5;
|
|
82
|
+
border-radius: 8px;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.bf-auth-form { display: flex; flex-direction: column; gap: 8px; }
|
|
86
|
+
.bf-auth-form[hidden],
|
|
87
|
+
.bf-auth-form__field[hidden] { display: none !important; }
|
|
88
|
+
|
|
89
|
+
.bf-auth-form__tabs { display: flex; gap: 4px; margin-bottom: 4px; }
|
|
90
|
+
.bf-auth-form__tab {
|
|
91
|
+
flex: 1; padding: 6px 10px;
|
|
92
|
+
background: transparent;
|
|
93
|
+
border: 1px solid var(--bfa-border);
|
|
94
|
+
color: var(--bfa-text-muted);
|
|
95
|
+
font: inherit; font-size: 12px; font-weight: 500;
|
|
96
|
+
border-radius: 6px; cursor: pointer;
|
|
97
|
+
}
|
|
98
|
+
.bf-auth-form__tab.is-active {
|
|
99
|
+
background: var(--bfa-accent);
|
|
100
|
+
color: var(--bfa-accent-fg);
|
|
101
|
+
border-color: var(--bfa-accent);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.bf-auth-form__google {
|
|
105
|
+
display: flex; align-items: center; justify-content: center; gap: 8px;
|
|
106
|
+
padding: 8px 12px;
|
|
107
|
+
background: var(--bfa-surface);
|
|
108
|
+
color: var(--bfa-text);
|
|
109
|
+
border: 1px solid var(--bfa-border);
|
|
110
|
+
border-radius: 6px;
|
|
111
|
+
font: inherit; font-size: 13px; font-weight: 500;
|
|
112
|
+
cursor: pointer;
|
|
113
|
+
}
|
|
114
|
+
.bf-auth-form__google:hover { background: var(--bfa-surface-alt); }
|
|
115
|
+
.bf-auth-form__google svg { display: block; }
|
|
116
|
+
|
|
117
|
+
.bf-auth-form__divider {
|
|
118
|
+
display: flex; align-items: center; gap: 8px;
|
|
119
|
+
color: var(--bfa-text-muted); font-size: 11px;
|
|
120
|
+
}
|
|
121
|
+
.bf-auth-form__divider::before,
|
|
122
|
+
.bf-auth-form__divider::after {
|
|
123
|
+
content: ''; flex: 1; height: 1px; background: var(--bfa-border);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* Visible labels above the inputs — placeholder-only labels disappear the
|
|
127
|
+
moment you type. */
|
|
128
|
+
.bf-auth-form__field { display: flex; flex-direction: column; gap: 4px; }
|
|
129
|
+
.bf-auth-form__label { font-size: 12px; font-weight: 500; color: var(--bfa-text-muted); }
|
|
130
|
+
|
|
131
|
+
.bf-auth-form input {
|
|
132
|
+
padding: 7px 10px;
|
|
133
|
+
border: 1px solid var(--bfa-border);
|
|
134
|
+
border-radius: 6px;
|
|
135
|
+
font: inherit; font-size: 13px;
|
|
136
|
+
background: var(--bfa-surface-alt);
|
|
137
|
+
color: var(--bfa-text);
|
|
138
|
+
width: 100%;
|
|
139
|
+
box-sizing: border-box;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.bf-auth-form__submit {
|
|
143
|
+
padding: 8px 12px;
|
|
144
|
+
border: 1px solid var(--bfa-accent);
|
|
145
|
+
background: var(--bfa-accent);
|
|
146
|
+
color: var(--bfa-accent-fg);
|
|
147
|
+
font: inherit; font-size: 13px; font-weight: 500;
|
|
148
|
+
border-radius: 6px; cursor: pointer;
|
|
149
|
+
}
|
|
150
|
+
.bf-auth-form__submit:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color Inspector Overlay
|
|
3
|
+
*
|
|
4
|
+
* Hover over any element to reveal its computed text color, background color,
|
|
5
|
+
* and any matching CSS custom property tokens (e.g. --color-neutral-800).
|
|
6
|
+
*
|
|
7
|
+
* Keyboard shortcut: Ctrl+Shift+C
|
|
8
|
+
* State persisted in localStorage.
|
|
9
|
+
*/
|
|
10
|
+
(function () {
|
|
11
|
+
const STORAGE_KEY = 'colorOverlayEnabled';
|
|
12
|
+
const TOOLTIP_ID = 'color-overlay-tooltip';
|
|
13
|
+
const TOAST_ID = 'color-overlay-toast';
|
|
14
|
+
const HIGHLIGHT_CLASS = 'color-overlay-highlight';
|
|
15
|
+
|
|
16
|
+
let enabled = false;
|
|
17
|
+
let tooltip = null;
|
|
18
|
+
let tokenIndex = null;
|
|
19
|
+
|
|
20
|
+
function injectStyles() {
|
|
21
|
+
if (document.getElementById('color-overlay-styles')) return;
|
|
22
|
+
const style = document.createElement('style');
|
|
23
|
+
style.id = 'color-overlay-styles';
|
|
24
|
+
style.textContent = `
|
|
25
|
+
#${TOOLTIP_ID} {
|
|
26
|
+
position: fixed;
|
|
27
|
+
background: #1A1A2E;
|
|
28
|
+
color: white;
|
|
29
|
+
font-size: 11px;
|
|
30
|
+
font-family: monospace;
|
|
31
|
+
padding: 6px 8px;
|
|
32
|
+
border-radius: 4px;
|
|
33
|
+
z-index: 99999;
|
|
34
|
+
pointer-events: none;
|
|
35
|
+
white-space: nowrap;
|
|
36
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
|
37
|
+
opacity: 0;
|
|
38
|
+
transition: opacity 0.15s ease;
|
|
39
|
+
line-height: 1.5;
|
|
40
|
+
}
|
|
41
|
+
#${TOOLTIP_ID}.visible { opacity: 1; }
|
|
42
|
+
#${TOOLTIP_ID} .swatch {
|
|
43
|
+
display: inline-block;
|
|
44
|
+
width: 10px;
|
|
45
|
+
height: 10px;
|
|
46
|
+
border-radius: 2px;
|
|
47
|
+
vertical-align: -1px;
|
|
48
|
+
margin-right: 4px;
|
|
49
|
+
border: 1px solid rgba(255,255,255,0.2);
|
|
50
|
+
}
|
|
51
|
+
#${TOOLTIP_ID} .token {
|
|
52
|
+
color: #8ED6FB;
|
|
53
|
+
}
|
|
54
|
+
.${HIGHLIGHT_CLASS} {
|
|
55
|
+
outline: 1px dashed #8ED6FB !important;
|
|
56
|
+
outline-offset: 1px;
|
|
57
|
+
}
|
|
58
|
+
#${TOAST_ID} {
|
|
59
|
+
position: fixed;
|
|
60
|
+
bottom: 20px;
|
|
61
|
+
left: 50%;
|
|
62
|
+
transform: translateX(-50%) translateY(100px);
|
|
63
|
+
background: #1A1A2E;
|
|
64
|
+
color: white;
|
|
65
|
+
font-size: 13px;
|
|
66
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
67
|
+
padding: 10px 20px;
|
|
68
|
+
border-radius: 6px;
|
|
69
|
+
z-index: 100000;
|
|
70
|
+
pointer-events: none;
|
|
71
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
|
72
|
+
opacity: 0;
|
|
73
|
+
transition: transform 0.3s ease, opacity 0.3s ease;
|
|
74
|
+
}
|
|
75
|
+
#${TOAST_ID}.visible {
|
|
76
|
+
opacity: 1;
|
|
77
|
+
transform: translateX(-50%) translateY(0);
|
|
78
|
+
}
|
|
79
|
+
#${TOAST_ID} .toast-status--on { color: #4CAF50; font-weight: 600; }
|
|
80
|
+
#${TOAST_ID} .toast-status--off { color: #F44336; font-weight: 600; }
|
|
81
|
+
`;
|
|
82
|
+
document.head.appendChild(style);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function createTooltip() {
|
|
86
|
+
if (tooltip) return;
|
|
87
|
+
tooltip = document.createElement('div');
|
|
88
|
+
tooltip.id = TOOLTIP_ID;
|
|
89
|
+
document.body.appendChild(tooltip);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
let toastTimeout = null;
|
|
93
|
+
function showToast(message, isOn) {
|
|
94
|
+
injectStyles();
|
|
95
|
+
let toast = document.getElementById(TOAST_ID);
|
|
96
|
+
if (!toast) {
|
|
97
|
+
toast = document.createElement('div');
|
|
98
|
+
toast.id = TOAST_ID;
|
|
99
|
+
document.body.appendChild(toast);
|
|
100
|
+
}
|
|
101
|
+
const cls = isOn ? 'toast-status--on' : 'toast-status--off';
|
|
102
|
+
const status = isOn ? 'ON' : 'OFF';
|
|
103
|
+
toast.innerHTML = `${message}: <span class="${cls}">${status}</span>`;
|
|
104
|
+
if (toastTimeout) clearTimeout(toastTimeout);
|
|
105
|
+
requestAnimationFrame(() => toast.classList.add('visible'));
|
|
106
|
+
toastTimeout = setTimeout(() => toast.classList.remove('visible'), 2000);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Normalize any CSS color string to canonical rgb()/rgba() via the browser
|
|
110
|
+
function normalizeColor(str) {
|
|
111
|
+
if (!str) return null;
|
|
112
|
+
const tmp = document.createElement('div');
|
|
113
|
+
tmp.style.color = '';
|
|
114
|
+
tmp.style.color = str;
|
|
115
|
+
if (!tmp.style.color) return null;
|
|
116
|
+
document.body.appendChild(tmp);
|
|
117
|
+
const resolved = getComputedStyle(tmp).color;
|
|
118
|
+
document.body.removeChild(tmp);
|
|
119
|
+
return resolved;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Build a reverse lookup map: normalized rgb string -> [token names]
|
|
123
|
+
function buildTokenIndex() {
|
|
124
|
+
const index = {};
|
|
125
|
+
const rootStyle = getComputedStyle(document.documentElement);
|
|
126
|
+
const seen = new Set();
|
|
127
|
+
|
|
128
|
+
for (const sheet of document.styleSheets) {
|
|
129
|
+
let rules;
|
|
130
|
+
try { rules = sheet.cssRules; } catch (e) { continue; }
|
|
131
|
+
if (!rules) continue;
|
|
132
|
+
for (const rule of rules) {
|
|
133
|
+
if (!rule.style || !rule.selectorText) continue;
|
|
134
|
+
if (rule.selectorText !== ':root' && rule.selectorText !== 'html') continue;
|
|
135
|
+
for (let i = 0; i < rule.style.length; i++) {
|
|
136
|
+
const name = rule.style[i];
|
|
137
|
+
if (!name.startsWith('--color') || seen.has(name)) continue;
|
|
138
|
+
seen.add(name);
|
|
139
|
+
const raw = rootStyle.getPropertyValue(name).trim();
|
|
140
|
+
if (!raw) continue;
|
|
141
|
+
const rgb = normalizeColor(raw);
|
|
142
|
+
if (!rgb) continue;
|
|
143
|
+
if (!index[rgb]) index[rgb] = [];
|
|
144
|
+
index[rgb].push(name);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return index;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function rgbToHex(rgb) {
|
|
152
|
+
const m = rgb.match(/\d+(\.\d+)?/g);
|
|
153
|
+
if (!m || m.length < 3) return null;
|
|
154
|
+
const [r, g, b, a] = m.map(Number);
|
|
155
|
+
const hex = '#' + [r, g, b].map(n => n.toString(16).padStart(2, '0')).join('').toUpperCase();
|
|
156
|
+
if (a !== undefined && a < 1) {
|
|
157
|
+
return hex + Math.round(a * 255).toString(16).padStart(2, '0').toUpperCase();
|
|
158
|
+
}
|
|
159
|
+
return hex;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function lookupTokens(rgb) {
|
|
163
|
+
if (!tokenIndex || !rgb) return [];
|
|
164
|
+
return tokenIndex[rgb] || [];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function formatEntry(label, rgb) {
|
|
168
|
+
if (!rgb || rgb === 'rgba(0, 0, 0, 0)' || rgb === 'transparent') return '';
|
|
169
|
+
const hex = rgbToHex(rgb);
|
|
170
|
+
const tokens = lookupTokens(rgb);
|
|
171
|
+
const swatch = `<span class="swatch" style="background:${rgb}"></span>`;
|
|
172
|
+
const tokenStr = tokens.length ? ` <span class="token">${tokens.join(', ')}</span>` : '';
|
|
173
|
+
return `${swatch}<strong>${label}</strong> ${hex || rgb}${tokenStr}`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function handleMouseMove(e) {
|
|
177
|
+
if (!enabled || !tooltip) return;
|
|
178
|
+
const element = document.elementFromPoint(e.clientX, e.clientY);
|
|
179
|
+
if (!element || element === tooltip || element.closest('#protoNav')) {
|
|
180
|
+
tooltip.classList.remove('visible');
|
|
181
|
+
clearHighlight();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
clearHighlight();
|
|
186
|
+
element.classList.add(HIGHLIGHT_CLASS);
|
|
187
|
+
|
|
188
|
+
const cs = getComputedStyle(element);
|
|
189
|
+
const tag = element.tagName.toLowerCase();
|
|
190
|
+
const lines = [`<strong><${tag}></strong>`];
|
|
191
|
+
const colorLine = formatEntry('color:', cs.color);
|
|
192
|
+
if (colorLine) lines.push(colorLine);
|
|
193
|
+
const bgLine = formatEntry('bg:', cs.backgroundColor);
|
|
194
|
+
if (bgLine) lines.push(bgLine);
|
|
195
|
+
|
|
196
|
+
tooltip.innerHTML = lines.join('<br>');
|
|
197
|
+
tooltip.classList.add('visible');
|
|
198
|
+
positionTooltip(e.clientX, e.clientY);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function clearHighlight() {
|
|
202
|
+
document.querySelectorAll('.' + HIGHLIGHT_CLASS).forEach(el => {
|
|
203
|
+
el.classList.remove(HIGHLIGHT_CLASS);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function positionTooltip(x, y) {
|
|
208
|
+
if (!tooltip) return;
|
|
209
|
+
const rect = tooltip.getBoundingClientRect();
|
|
210
|
+
let posX = x + 15;
|
|
211
|
+
let posY = y + 15;
|
|
212
|
+
if (posX + rect.width > window.innerWidth) posX = x - rect.width - 10;
|
|
213
|
+
if (posY + rect.height > window.innerHeight) posY = y - rect.height - 10;
|
|
214
|
+
tooltip.style.left = posX + 'px';
|
|
215
|
+
tooltip.style.top = posY + 'px';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function enable() {
|
|
219
|
+
enabled = true;
|
|
220
|
+
injectStyles();
|
|
221
|
+
createTooltip();
|
|
222
|
+
if (!tokenIndex) tokenIndex = buildTokenIndex();
|
|
223
|
+
// Disable typography modes if active
|
|
224
|
+
if (window.TypographyOverlay) {
|
|
225
|
+
if (window.TypographyOverlay.isFontModeEnabled()) window.TypographyOverlay.disableFontMode(true);
|
|
226
|
+
if (window.TypographyOverlay.isDistanceModeEnabled()) window.TypographyOverlay.disableDistanceMode(true);
|
|
227
|
+
}
|
|
228
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
229
|
+
localStorage.setItem(STORAGE_KEY, 'true');
|
|
230
|
+
window.dispatchEvent(new CustomEvent('colorOverlayToggled', { detail: { enabled: true } }));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function disable(skipSave) {
|
|
234
|
+
enabled = false;
|
|
235
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
236
|
+
if (tooltip) tooltip.classList.remove('visible');
|
|
237
|
+
clearHighlight();
|
|
238
|
+
if (!skipSave) localStorage.setItem(STORAGE_KEY, 'false');
|
|
239
|
+
window.dispatchEvent(new CustomEvent('colorOverlayToggled', { detail: { enabled: false } }));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function toggle() {
|
|
243
|
+
if (enabled) {
|
|
244
|
+
disable();
|
|
245
|
+
showToast('Color Inspector', false);
|
|
246
|
+
} else {
|
|
247
|
+
enable();
|
|
248
|
+
showToast('Color Inspector', true);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Auto-disable if a typography mode turns on
|
|
253
|
+
window.addEventListener('typographyOverlayToggled', (e) => {
|
|
254
|
+
if (e.detail && e.detail.enabled && enabled) disable(true);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
window.ColorOverlay = {
|
|
258
|
+
toggle: toggle,
|
|
259
|
+
enable: enable,
|
|
260
|
+
disable: disable,
|
|
261
|
+
isEnabled: function () { return enabled; }
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
document.addEventListener('keydown', function (e) {
|
|
265
|
+
if (e.ctrlKey && e.shiftKey && (e.key === 'C' || e.key === 'c')) {
|
|
266
|
+
// Avoid breaking copy
|
|
267
|
+
if (window.getSelection && window.getSelection().toString()) return;
|
|
268
|
+
e.preventDefault();
|
|
269
|
+
toggle();
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
function restore() {
|
|
274
|
+
if (localStorage.getItem(STORAGE_KEY) === 'true') enable();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (document.readyState !== 'loading') {
|
|
278
|
+
restore();
|
|
279
|
+
} else {
|
|
280
|
+
document.addEventListener('DOMContentLoaded', restore);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
console.log('[Color Inspector] Ready. Ctrl+Shift+C to toggle.');
|
|
284
|
+
})();
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BEM Component Name Overlay Utility
|
|
3
|
+
*
|
|
4
|
+
* Displays the component name above elements with c-* classes.
|
|
5
|
+
* Supports two modes:
|
|
6
|
+
* - Top-level components (larger structural components)
|
|
7
|
+
* - Small components (atoms and molecules like icons, buttons, filter-chips)
|
|
8
|
+
* Toggle with Ctrl+Shift+B (top-level) or Ctrl+Shift+A (small components)
|
|
9
|
+
* State is persisted in localStorage
|
|
10
|
+
*/
|
|
11
|
+
(function() {
|
|
12
|
+
const STORAGE_KEY_TOPLEVEL = 'bemOverlayTopLevel';
|
|
13
|
+
const STORAGE_KEY_ATOMS = 'bemOverlayAtoms';
|
|
14
|
+
const OVERLAY_CLASS = 'component-overlay-label';
|
|
15
|
+
const OVERLAY_CLASS_TOPLEVEL = 'component-overlay-label--toplevel';
|
|
16
|
+
const OVERLAY_CLASS_ATOM = 'component-overlay-label--atom';
|
|
17
|
+
const POSITIONED_CLASS = 'component-overlay-positioned';
|
|
18
|
+
|
|
19
|
+
// Small components - atoms and molecules (reusable UI elements)
|
|
20
|
+
const SMALL_COMPONENTS = [
|
|
21
|
+
'icon', 'icon-list', 'progress-icon', 'progress-track', 'button', 'link', 'tag', 'tag-more', 'badge', 'chip', 'filter-chip',
|
|
22
|
+
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
|
23
|
+
'input', 'checkbox', 'radio', 'radio-option', 'select', 'textarea',
|
|
24
|
+
'label', 'required-marker', 'avatar', 'spinner', 'loader', 'divider',
|
|
25
|
+
'tooltip', 'arrow', 'arrow-list', 'academy-reference', 'completion-summary'
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
let topLevelVisible = false;
|
|
29
|
+
let atomsVisible = false;
|
|
30
|
+
|
|
31
|
+
// Inject styles once
|
|
32
|
+
function injectStyles() {
|
|
33
|
+
if (document.getElementById('component-overlay-styles')) return;
|
|
34
|
+
|
|
35
|
+
const style = document.createElement('style');
|
|
36
|
+
style.id = 'component-overlay-styles';
|
|
37
|
+
style.textContent = `
|
|
38
|
+
.${OVERLAY_CLASS} {
|
|
39
|
+
position: absolute;
|
|
40
|
+
top: 0;
|
|
41
|
+
left: 0;
|
|
42
|
+
color: white;
|
|
43
|
+
font-size: 10px;
|
|
44
|
+
font-family: monospace;
|
|
45
|
+
padding: 2px 6px;
|
|
46
|
+
z-index: 9999;
|
|
47
|
+
pointer-events: none;
|
|
48
|
+
line-height: 1.2;
|
|
49
|
+
white-space: nowrap;
|
|
50
|
+
border-radius: 0 0 3px 0;
|
|
51
|
+
}
|
|
52
|
+
.${OVERLAY_CLASS_TOPLEVEL} {
|
|
53
|
+
background: #E91E63;
|
|
54
|
+
}
|
|
55
|
+
.${OVERLAY_CLASS_ATOM} {
|
|
56
|
+
background: #9C27B0;
|
|
57
|
+
}
|
|
58
|
+
.${POSITIONED_CLASS} {
|
|
59
|
+
position: relative;
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
document.head.appendChild(style);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Check if a component is a small component (atom or molecule)
|
|
66
|
+
function isSmallComponent(name) {
|
|
67
|
+
return SMALL_COMPONENTS.includes(name.toLowerCase());
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Extract the first c-* class name from an element
|
|
71
|
+
function getComponentName(element) {
|
|
72
|
+
const classes = Array.from(element.classList);
|
|
73
|
+
for (const cls of classes) {
|
|
74
|
+
if (cls.startsWith('c-') && !cls.includes('__') && !cls.includes('--')) {
|
|
75
|
+
return cls.substring(2); // Remove 'c-' prefix
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Create an overlay label for a component
|
|
82
|
+
function createOverlay(element, name, type) {
|
|
83
|
+
const label = document.createElement('span');
|
|
84
|
+
const typeClass = type === 'atom' ? OVERLAY_CLASS_ATOM : OVERLAY_CLASS_TOPLEVEL;
|
|
85
|
+
label.className = `${OVERLAY_CLASS} ${typeClass}`;
|
|
86
|
+
label.textContent = name;
|
|
87
|
+
label.dataset.overlayType = type;
|
|
88
|
+
|
|
89
|
+
// Ensure parent is positioned for absolute positioning to work
|
|
90
|
+
const computedStyle = window.getComputedStyle(element);
|
|
91
|
+
if (computedStyle.position === 'static') {
|
|
92
|
+
element.classList.add(POSITIONED_CLASS);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
element.appendChild(label);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Refresh overlays based on current visibility states
|
|
99
|
+
function refreshOverlays() {
|
|
100
|
+
injectStyles();
|
|
101
|
+
|
|
102
|
+
// First, remove all existing overlays
|
|
103
|
+
const existingOverlays = document.querySelectorAll('.' + OVERLAY_CLASS);
|
|
104
|
+
existingOverlays.forEach(function(overlay) {
|
|
105
|
+
overlay.remove();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Remove positioning class from all elements
|
|
109
|
+
const positioned = document.querySelectorAll('.' + POSITIONED_CLASS);
|
|
110
|
+
positioned.forEach(function(element) {
|
|
111
|
+
element.classList.remove(POSITIONED_CLASS);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// If nothing is visible, we're done
|
|
115
|
+
if (!topLevelVisible && !atomsVisible) return;
|
|
116
|
+
|
|
117
|
+
// Find all elements with c-* classes (component blocks, not elements or modifiers)
|
|
118
|
+
const allElements = document.querySelectorAll('[class*="c-"]');
|
|
119
|
+
|
|
120
|
+
allElements.forEach(function(element) {
|
|
121
|
+
const name = getComponentName(element);
|
|
122
|
+
if (name) {
|
|
123
|
+
const isSmall = isSmallComponent(name);
|
|
124
|
+
if (isSmall && atomsVisible) {
|
|
125
|
+
createOverlay(element, name, 'atom');
|
|
126
|
+
} else if (!isSmall && topLevelVisible) {
|
|
127
|
+
createOverlay(element, name, 'toplevel');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Set top-level overlay state
|
|
134
|
+
function setTopLevelState(visible) {
|
|
135
|
+
topLevelVisible = visible;
|
|
136
|
+
localStorage.setItem(STORAGE_KEY_TOPLEVEL, visible ? 'true' : 'false');
|
|
137
|
+
refreshOverlays();
|
|
138
|
+
|
|
139
|
+
// Dispatch custom event for sync with prototype nav
|
|
140
|
+
window.dispatchEvent(new CustomEvent('bemOverlayToggled', {
|
|
141
|
+
detail: { topLevel: topLevelVisible, atoms: atomsVisible }
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Set atoms overlay state
|
|
146
|
+
function setAtomsState(visible) {
|
|
147
|
+
atomsVisible = visible;
|
|
148
|
+
localStorage.setItem(STORAGE_KEY_ATOMS, visible ? 'true' : 'false');
|
|
149
|
+
refreshOverlays();
|
|
150
|
+
|
|
151
|
+
// Dispatch custom event for sync with prototype nav
|
|
152
|
+
window.dispatchEvent(new CustomEvent('bemOverlayToggled', {
|
|
153
|
+
detail: { topLevel: topLevelVisible, atoms: atomsVisible }
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Toggle top-level overlays
|
|
158
|
+
function toggleTopLevel() {
|
|
159
|
+
setTopLevelState(!topLevelVisible);
|
|
160
|
+
console.log('[Component Overlay] Top-level: ' + (topLevelVisible ? 'ON' : 'OFF'));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Toggle small component overlays
|
|
164
|
+
function toggleAtoms() {
|
|
165
|
+
setAtomsState(!atomsVisible);
|
|
166
|
+
console.log('[Component Overlay] Small components: ' + (atomsVisible ? 'ON' : 'OFF'));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Expose global API for prototype navigation
|
|
170
|
+
window.BEMOverlay = {
|
|
171
|
+
toggleTopLevel: toggleTopLevel,
|
|
172
|
+
toggleAtoms: toggleAtoms,
|
|
173
|
+
showTopLevel: function() { setTopLevelState(true); },
|
|
174
|
+
hideTopLevel: function() { setTopLevelState(false); },
|
|
175
|
+
showAtoms: function() { setAtomsState(true); },
|
|
176
|
+
hideAtoms: function() { setAtomsState(false); },
|
|
177
|
+
isTopLevelVisible: function() { return topLevelVisible; },
|
|
178
|
+
isAtomsVisible: function() { return atomsVisible; },
|
|
179
|
+
// Legacy API for backwards compatibility
|
|
180
|
+
toggle: toggleTopLevel,
|
|
181
|
+
show: function() { setTopLevelState(true); },
|
|
182
|
+
hide: function() { setTopLevelState(false); },
|
|
183
|
+
isVisible: function() { return topLevelVisible || atomsVisible; }
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// Listen for keyboard shortcuts
|
|
187
|
+
document.addEventListener('keydown', function(e) {
|
|
188
|
+
// Ctrl+Shift+B for top-level components
|
|
189
|
+
if (e.ctrlKey && e.shiftKey && (e.key === 'B' || e.key === 'b')) {
|
|
190
|
+
e.preventDefault();
|
|
191
|
+
toggleTopLevel();
|
|
192
|
+
}
|
|
193
|
+
// Ctrl+Shift+A for atoms
|
|
194
|
+
if (e.ctrlKey && e.shiftKey && (e.key === 'A' || e.key === 'a')) {
|
|
195
|
+
e.preventDefault();
|
|
196
|
+
toggleAtoms();
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Restore state from localStorage on page load
|
|
201
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
202
|
+
const savedTopLevel = localStorage.getItem(STORAGE_KEY_TOPLEVEL);
|
|
203
|
+
const savedAtoms = localStorage.getItem(STORAGE_KEY_ATOMS);
|
|
204
|
+
|
|
205
|
+
if (savedTopLevel === 'true') {
|
|
206
|
+
topLevelVisible = true;
|
|
207
|
+
}
|
|
208
|
+
if (savedAtoms === 'true') {
|
|
209
|
+
atomsVisible = true;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (topLevelVisible || atomsVisible) {
|
|
213
|
+
refreshOverlays();
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Log availability on load
|
|
218
|
+
console.log('[Component Overlay] Ready. Ctrl+Shift+B for top-level, Ctrl+Shift+A for small components.');
|
|
219
|
+
})();
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Auto-generated by bedrock/generate-pages.js — do not edit manually
|
|
2
|
+
// Run: npm run generate
|
|
3
|
+
window.BEDROCK_CONFIG = {
|
|
4
|
+
"plugins": {
|
|
5
|
+
"viewport": true,
|
|
6
|
+
"componentOverlay": true,
|
|
7
|
+
"gridOverlay": true,
|
|
8
|
+
"typographyInspector": true,
|
|
9
|
+
"colorInspector": true,
|
|
10
|
+
"storybook": true
|
|
11
|
+
},
|
|
12
|
+
"breakpoints": [
|
|
13
|
+
{
|
|
14
|
+
"id": "full",
|
|
15
|
+
"label": "Full width",
|
|
16
|
+
"width": null
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "desktop",
|
|
20
|
+
"label": "Desktop",
|
|
21
|
+
"width": 1280
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "tablet-lg",
|
|
25
|
+
"label": "Large Tablet",
|
|
26
|
+
"width": 960
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "tablet",
|
|
30
|
+
"label": "Tablet",
|
|
31
|
+
"width": 720
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "mobile",
|
|
35
|
+
"label": "Mobile",
|
|
36
|
+
"width": 375,
|
|
37
|
+
"height": 812
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"storybookPorts": [
|
|
41
|
+
6006,
|
|
42
|
+
6007,
|
|
43
|
+
6008
|
|
44
|
+
]
|
|
45
|
+
};
|