@medalsocial/meda 0.3.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -0
- package/dist/brand/index.d.ts +2 -0
- package/dist/brand/index.js +1 -0
- package/dist/brand/medal-social-mark.d.ts +7 -0
- package/dist/brand/medal-social-mark.js +6 -0
- package/dist/brand/public.d.ts +2 -0
- package/dist/brand/public.js +1 -0
- package/dist/chat/latency-badge.js +7 -3
- package/dist/chat/latency-breakdown.js +1 -1
- package/dist/chat/tool-call-block.js +1 -1
- package/dist/chat/turn-card.js +4 -2
- package/dist/components/ui/command.d.ts +19 -0
- package/dist/components/ui/command.js +37 -0
- package/dist/components/ui/dialog.d.ts +26 -0
- package/dist/components/ui/dialog.js +39 -0
- package/dist/components/ui/drawer.d.ts +16 -0
- package/dist/components/ui/drawer.js +35 -0
- package/dist/components/ui/dropdown-menu.d.ts +32 -0
- package/dist/components/ui/dropdown-menu.js +68 -0
- package/dist/components/ui/tooltip.d.ts +6 -0
- package/dist/components/ui/tooltip.js +17 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +5 -0
- package/dist/marketing/index.d.ts +5 -0
- package/dist/marketing/index.js +4 -0
- package/dist/marketing/marketing-callout.d.ts +2 -0
- package/dist/marketing/marketing-callout.js +8 -0
- package/dist/marketing/marketing-contact.d.ts +2 -0
- package/dist/marketing/marketing-contact.js +19 -0
- package/dist/marketing/marketing-cta-list.d.ts +2 -0
- package/dist/marketing/marketing-cta-list.js +21 -0
- package/dist/marketing/marketing-lead-magnet.d.ts +2 -0
- package/dist/marketing/marketing-lead-magnet.js +9 -0
- package/dist/marketing/public.d.ts +1 -0
- package/dist/marketing/public.js +1 -0
- package/dist/marketing/types.d.ts +67 -0
- package/dist/marketing/types.js +1 -0
- package/dist/marketing/utils.d.ts +2 -0
- package/dist/marketing/utils.js +6 -0
- package/dist/panel/inspector-json.js +5 -3
- package/dist/shell/app-shell-auth.d.ts +5 -0
- package/dist/shell/app-shell-auth.js +50 -0
- package/dist/shell/app-shell-chat.d.ts +6 -0
- package/dist/shell/app-shell-chat.js +9 -0
- package/dist/shell/app-shell-workspace.d.ts +10 -0
- package/dist/shell/app-shell-workspace.js +55 -0
- package/dist/shell/app-shell.d.ts +25 -0
- package/dist/shell/app-shell.js +26 -0
- package/dist/shell/command-palette.d.ts +23 -0
- package/dist/shell/command-palette.js +138 -0
- package/dist/shell/context-rail.d.ts +31 -0
- package/dist/shell/context-rail.js +81 -0
- package/dist/shell/extras/index.d.ts +4 -0
- package/dist/shell/extras/index.js +4 -0
- package/dist/shell/{shell-desktop-panel-dock.js → extras/shell-desktop-panel-dock.js} +1 -1
- package/dist/shell/extras/types.d.ts +64 -0
- package/dist/shell/extras/types.js +2 -0
- package/dist/shell/icon-rail.d.ts +29 -0
- package/dist/shell/icon-rail.js +38 -0
- package/dist/shell/index.d.ts +19 -15
- package/dist/shell/index.js +30 -15
- package/dist/shell/internal/mobile-bottom-nav.d.ts +23 -0
- package/dist/shell/internal/mobile-bottom-nav.js +79 -0
- package/dist/shell/internal/mobile-drawers.d.ts +26 -0
- package/dist/shell/internal/mobile-drawers.js +76 -0
- package/dist/shell/internal/mobile-header.d.ts +23 -0
- package/dist/shell/internal/mobile-header.js +22 -0
- package/dist/shell/layout-state.d.ts +32 -0
- package/dist/shell/layout-state.js +98 -0
- package/dist/shell/motion.d.ts +22 -0
- package/dist/shell/motion.js +19 -0
- package/dist/shell/resizable-shell.d.ts +45 -0
- package/dist/shell/resizable-shell.js +15 -0
- package/dist/shell/right-panel.d.ts +15 -0
- package/dist/shell/right-panel.js +126 -0
- package/dist/shell/shell-header.d.ts +10 -9
- package/dist/shell/shell-header.js +57 -14
- package/dist/shell/shell-layout-utils.d.ts +2 -2
- package/dist/shell/shell-main.d.ts +8 -0
- package/dist/shell/shell-main.js +11 -0
- package/dist/shell/shell-provider.d.ts +65 -0
- package/dist/shell/shell-provider.js +152 -0
- package/dist/shell/shell-route-utils.d.ts +1 -1
- package/dist/shell/{navigation-area.d.ts → theme-next-themes.d.ts} +1 -1
- package/dist/shell/theme-next-themes.js +24 -0
- package/dist/shell/theme.d.ts +9 -0
- package/dist/shell/theme.js +58 -0
- package/dist/shell/types.d.ts +73 -38
- package/dist/shell/types.js +1 -0
- package/dist/shell/use-shell-viewport.d.ts +2 -0
- package/dist/shell/use-shell-viewport.js +40 -0
- package/dist/shell/utils.d.ts +1 -1
- package/dist/{styles.css → styles/globals.css} +2 -2
- package/dist/styles/theme.css +147 -0
- package/dist/styles/tokens.css +338 -0
- package/dist/timeline/date-switcher.js +3 -1
- package/dist/timeline/event-card.js +5 -13
- package/dist/timeline/live-indicator.js +3 -1
- package/dist/timeline/scrub-bar.js +8 -9
- package/dist/timeline/timeline-rail.js +6 -4
- package/dist/timeline/timeline-tape.js +7 -8
- package/dist/voice/voice-level.js +3 -1
- package/dist/voice/voice-orb-scene.js +1 -0
- package/dist/voice/voice-orb.js +9 -11
- package/dist/voice/voice-status-pill.js +4 -9
- package/package.json +57 -6
- package/dist/shell/navigation-area.js +0 -4
- package/dist/shell/public.d.ts +0 -2
- package/dist/shell/public.js +0 -1
- package/dist/shell/shell-app-rail.d.ts +0 -18
- package/dist/shell/shell-app-rail.js +0 -27
- package/dist/shell/shell-frame.d.ts +0 -13
- package/dist/shell/shell-frame.js +0 -4
- package/dist/shell/shell-module-nav.d.ts +0 -29
- package/dist/shell/shell-module-nav.js +0 -16
- package/dist/shell/shell-panel-rail.d.ts +0 -9
- package/dist/shell/shell-panel-rail.js +0 -17
- package/dist/shell/shell-state.d.ts +0 -39
- package/dist/shell/shell-state.js +0 -135
- /package/dist/shell/{shell-desktop-panel-dock.d.ts → extras/shell-desktop-panel-dock.d.ts} +0 -0
- /package/dist/shell/{shell-scrollable-content.d.ts → extras/shell-scrollable-content.d.ts} +0 -0
- /package/dist/shell/{shell-scrollable-content.js → extras/shell-scrollable-content.js} +0 -0
- /package/dist/shell/{shell-tab-bar.d.ts → extras/shell-tab-bar.d.ts} +0 -0
- /package/dist/shell/{shell-tab-bar.js → extras/shell-tab-bar.js} +0 -0
- /package/dist/shell/{workbench-layout.d.ts → extras/workbench-layout.d.ts} +0 -0
- /package/dist/shell/{workbench-layout.js → extras/workbench-layout.js} +0 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
Meda design tokens — canonical from 00-design-system.lib.pen
|
|
3
|
+
Source: /Users/ali/Library/CloudStorage/GoogleDrive-ali@medalsocial.com/
|
|
4
|
+
Delte disker/03 Product/Pencil Design/00-design-system.lib.pen
|
|
5
|
+
Retrieved via mcp__pencil__get_variables 2026-04-27.
|
|
6
|
+
|
|
7
|
+
NOTE: 00-design-system.pen and 050-meda-ui.pen are illustrative
|
|
8
|
+
companion boards — not normative. This file is derived from the
|
|
9
|
+
.lib.pen (library file) which is the sole canonical contract.
|
|
10
|
+
============================================================ */
|
|
11
|
+
|
|
12
|
+
/* ============================================================
|
|
13
|
+
Layer 0 — Color primitives (theme: brand, constant)
|
|
14
|
+
============================================================ */
|
|
15
|
+
:root {
|
|
16
|
+
/* Brand (purple) — 11 stops */
|
|
17
|
+
--color-brand-50: #fafafd;
|
|
18
|
+
--color-brand-100: #eeeaf5;
|
|
19
|
+
--color-brand-200: #dcd4e8;
|
|
20
|
+
--color-brand-300: #b8a3d2;
|
|
21
|
+
--color-brand-400: #9a6ac2;
|
|
22
|
+
--color-brand-500: #7e3fac;
|
|
23
|
+
--color-brand-600: #6a2e96;
|
|
24
|
+
--color-brand-700: #482070;
|
|
25
|
+
--color-brand-800: #2f1552;
|
|
26
|
+
--color-brand-900: #1c0e38;
|
|
27
|
+
--color-brand-950: #120a24;
|
|
28
|
+
|
|
29
|
+
/* Neutral (zinc-aligned) — 11 stops */
|
|
30
|
+
--color-neutral-50: #fafafa;
|
|
31
|
+
--color-neutral-100: #f4f4f5;
|
|
32
|
+
--color-neutral-200: #e4e4e7;
|
|
33
|
+
--color-neutral-300: #a1a1aa;
|
|
34
|
+
--color-neutral-400: #71717a;
|
|
35
|
+
--color-neutral-500: #52525b;
|
|
36
|
+
--color-neutral-600: #3f3f46;
|
|
37
|
+
--color-neutral-700: #27272a;
|
|
38
|
+
--color-neutral-800: #18181b;
|
|
39
|
+
--color-neutral-900: #111113;
|
|
40
|
+
--color-neutral-950: #09090b;
|
|
41
|
+
|
|
42
|
+
/* Error (rose) — 10 stops */
|
|
43
|
+
--color-error-50: #fff1f2;
|
|
44
|
+
--color-error-100: #ffe4e6;
|
|
45
|
+
--color-error-200: #fecdd3;
|
|
46
|
+
--color-error-300: #fda4af;
|
|
47
|
+
--color-error-400: #fb7185;
|
|
48
|
+
--color-error-500: #f43f5e;
|
|
49
|
+
--color-error-600: #e11d48;
|
|
50
|
+
--color-error-700: #be123c;
|
|
51
|
+
--color-error-800: #9f1239;
|
|
52
|
+
--color-error-900: #881337;
|
|
53
|
+
|
|
54
|
+
/* Info (blue) — 10 stops */
|
|
55
|
+
--color-info-50: #eff6ff;
|
|
56
|
+
--color-info-100: #dbeafe;
|
|
57
|
+
--color-info-200: #bfdbfe;
|
|
58
|
+
--color-info-300: #93c5fd;
|
|
59
|
+
--color-info-400: #60a5fa;
|
|
60
|
+
--color-info-500: #3b82f6;
|
|
61
|
+
--color-info-600: #2563eb;
|
|
62
|
+
--color-info-700: #1d4ed8;
|
|
63
|
+
--color-info-800: #1e3a8a;
|
|
64
|
+
--color-info-900: #172554;
|
|
65
|
+
|
|
66
|
+
/* Success (teal) — 10 stops */
|
|
67
|
+
--color-success-50: #f0fdfa;
|
|
68
|
+
--color-success-100: #ccfbf1;
|
|
69
|
+
--color-success-200: #99f6e4;
|
|
70
|
+
--color-success-300: #5eead4;
|
|
71
|
+
--color-success-400: #2dd4bf;
|
|
72
|
+
--color-success-500: #14b8a6;
|
|
73
|
+
--color-success-600: #0d9488;
|
|
74
|
+
--color-success-700: #0f766e;
|
|
75
|
+
--color-success-800: #115e59;
|
|
76
|
+
--color-success-900: #134e4a;
|
|
77
|
+
|
|
78
|
+
/* Warning (amber) — 10 stops */
|
|
79
|
+
--color-warning-50: #fffbeb;
|
|
80
|
+
--color-warning-100: #fef3c7;
|
|
81
|
+
--color-warning-200: #fde68a;
|
|
82
|
+
--color-warning-300: #fcd34d;
|
|
83
|
+
--color-warning-400: #fbbf24;
|
|
84
|
+
--color-warning-500: #f59e0b;
|
|
85
|
+
--color-warning-600: #d97706;
|
|
86
|
+
--color-warning-700: #b45309;
|
|
87
|
+
--color-warning-800: #92400e;
|
|
88
|
+
--color-warning-900: #78350f;
|
|
89
|
+
|
|
90
|
+
/* Surface primitives (dark-mode only) */
|
|
91
|
+
--color-surface-bg: #09090b;
|
|
92
|
+
--color-surface-card: #18181b;
|
|
93
|
+
--color-surface-muted: #27272a;
|
|
94
|
+
--color-surface-border: #2e2e33;
|
|
95
|
+
--color-surface-sidebar: #0f0f12;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* ============================================================
|
|
99
|
+
Layer 1 — shadcn semantic + status + sidebar + chart + text
|
|
100
|
+
Light mode (default on :root)
|
|
101
|
+
============================================================ */
|
|
102
|
+
:root {
|
|
103
|
+
/* shadcn shape */
|
|
104
|
+
--background: var(--color-neutral-50);
|
|
105
|
+
--foreground: var(--color-neutral-950);
|
|
106
|
+
--card: var(--color-neutral-50);
|
|
107
|
+
--card-foreground: var(--color-neutral-950);
|
|
108
|
+
--popover: var(--color-neutral-50);
|
|
109
|
+
--popover-foreground: var(--color-neutral-950);
|
|
110
|
+
--primary: var(--color-brand-800);
|
|
111
|
+
--primary-foreground: var(--color-brand-50);
|
|
112
|
+
--secondary: var(--color-neutral-200);
|
|
113
|
+
--secondary-foreground: var(--color-neutral-950);
|
|
114
|
+
--muted: var(--color-neutral-100);
|
|
115
|
+
--muted-foreground: var(--color-neutral-500);
|
|
116
|
+
--muted-foreground-strong: var(--color-neutral-600);
|
|
117
|
+
--accent: var(--color-neutral-200);
|
|
118
|
+
--accent-foreground: var(--color-neutral-950);
|
|
119
|
+
--destructive: var(--color-error-600);
|
|
120
|
+
--destructive-foreground: var(--color-neutral-50);
|
|
121
|
+
--destructive-bg: var(--color-error-50);
|
|
122
|
+
--danger: var(--color-error-600);
|
|
123
|
+
--border: var(--color-neutral-200);
|
|
124
|
+
--input: var(--color-neutral-200);
|
|
125
|
+
--ring: var(--color-brand-600);
|
|
126
|
+
|
|
127
|
+
/* Status */
|
|
128
|
+
--success: var(--color-success-600);
|
|
129
|
+
--success-foreground: var(--color-neutral-950);
|
|
130
|
+
--success-text: var(--color-success-700);
|
|
131
|
+
--warning: var(--color-warning-600);
|
|
132
|
+
--warning-foreground: var(--color-neutral-950);
|
|
133
|
+
--warning-text: var(--color-warning-800);
|
|
134
|
+
--info: var(--color-info-600);
|
|
135
|
+
--info-foreground: var(--color-neutral-50);
|
|
136
|
+
|
|
137
|
+
/* Sidebar */
|
|
138
|
+
--sidebar: var(--color-neutral-100);
|
|
139
|
+
--sidebar-foreground: var(--color-neutral-950);
|
|
140
|
+
--sidebar-primary: var(--color-brand-700);
|
|
141
|
+
--sidebar-primary-foreground: var(--color-neutral-50);
|
|
142
|
+
--sidebar-accent: var(--color-neutral-50);
|
|
143
|
+
--sidebar-accent-foreground: var(--color-neutral-950);
|
|
144
|
+
--sidebar-border: var(--color-neutral-200);
|
|
145
|
+
--sidebar-ring: var(--color-brand-500);
|
|
146
|
+
|
|
147
|
+
/* Chart */
|
|
148
|
+
--chart-1: var(--color-brand-600);
|
|
149
|
+
--chart-2: var(--color-info-600);
|
|
150
|
+
--chart-3: var(--color-success-600);
|
|
151
|
+
--chart-4: var(--color-warning-600);
|
|
152
|
+
--chart-5: var(--color-error-600);
|
|
153
|
+
|
|
154
|
+
/* Text */
|
|
155
|
+
--text: var(--color-neutral-950);
|
|
156
|
+
--text-foreground: var(--color-neutral-950);
|
|
157
|
+
--text-muted: var(--color-neutral-500);
|
|
158
|
+
--text-link: var(--color-brand-600);
|
|
159
|
+
--text-primary: var(--color-neutral-950);
|
|
160
|
+
--text-secondary: var(--color-neutral-600);
|
|
161
|
+
--highlight-foreground: var(--color-brand-500);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/* Dark mode overrides — class AND data-theme to support both runtime + Storybook */
|
|
165
|
+
.dark,
|
|
166
|
+
[data-theme="dark"] {
|
|
167
|
+
/* shadcn shape */
|
|
168
|
+
--background: var(--color-surface-bg);
|
|
169
|
+
--foreground: var(--color-neutral-50);
|
|
170
|
+
--card: var(--color-surface-card);
|
|
171
|
+
--card-foreground: var(--color-neutral-50);
|
|
172
|
+
--popover: var(--color-surface-card);
|
|
173
|
+
--popover-foreground: var(--color-neutral-50);
|
|
174
|
+
--primary: var(--color-brand-100);
|
|
175
|
+
--primary-foreground: var(--color-brand-900);
|
|
176
|
+
--secondary: var(--color-surface-muted);
|
|
177
|
+
--secondary-foreground: var(--color-neutral-50);
|
|
178
|
+
--muted: var(--color-surface-muted);
|
|
179
|
+
--muted-foreground: var(--color-neutral-300);
|
|
180
|
+
--muted-foreground-strong: var(--color-neutral-300);
|
|
181
|
+
--accent: var(--color-surface-muted);
|
|
182
|
+
--accent-foreground: var(--color-neutral-50);
|
|
183
|
+
--destructive: var(--color-error-400);
|
|
184
|
+
--destructive-foreground: var(--color-neutral-950);
|
|
185
|
+
--destructive-bg: var(--color-error-900);
|
|
186
|
+
--danger: var(--color-error-400);
|
|
187
|
+
--border: var(--color-surface-border);
|
|
188
|
+
--input: var(--color-surface-muted);
|
|
189
|
+
--ring: var(--color-brand-400);
|
|
190
|
+
|
|
191
|
+
/* Status */
|
|
192
|
+
--success: var(--color-success-400);
|
|
193
|
+
--success-foreground: var(--color-neutral-950);
|
|
194
|
+
--success-text: var(--color-success-400);
|
|
195
|
+
--warning: var(--color-warning-400);
|
|
196
|
+
--warning-foreground: var(--color-neutral-950);
|
|
197
|
+
--warning-text: var(--color-warning-400);
|
|
198
|
+
--info: var(--color-info-400);
|
|
199
|
+
--info-foreground: var(--color-neutral-950);
|
|
200
|
+
|
|
201
|
+
/* Sidebar */
|
|
202
|
+
--sidebar: var(--color-surface-sidebar);
|
|
203
|
+
--sidebar-foreground: var(--color-neutral-50);
|
|
204
|
+
--sidebar-primary: var(--color-brand-400);
|
|
205
|
+
--sidebar-primary-foreground: var(--color-neutral-950);
|
|
206
|
+
--sidebar-accent: var(--color-surface-card);
|
|
207
|
+
--sidebar-accent-foreground: var(--color-neutral-50);
|
|
208
|
+
--sidebar-border: var(--color-surface-border);
|
|
209
|
+
--sidebar-ring: var(--color-brand-400);
|
|
210
|
+
|
|
211
|
+
/* Chart */
|
|
212
|
+
--chart-1: var(--color-brand-500);
|
|
213
|
+
--chart-2: var(--color-info-500);
|
|
214
|
+
--chart-3: var(--color-success-400);
|
|
215
|
+
--chart-4: var(--color-warning-500);
|
|
216
|
+
--chart-5: var(--color-error-400);
|
|
217
|
+
|
|
218
|
+
/* Text */
|
|
219
|
+
--text: var(--color-neutral-50);
|
|
220
|
+
--text-foreground: var(--color-neutral-50);
|
|
221
|
+
--text-muted: var(--color-neutral-300);
|
|
222
|
+
--text-link: var(--color-brand-400);
|
|
223
|
+
--text-primary: var(--color-neutral-100);
|
|
224
|
+
--text-secondary: var(--color-neutral-300);
|
|
225
|
+
--highlight-foreground: var(--color-brand-200);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* ============================================================
|
|
229
|
+
Layer 2 — Shell semantic + sizing
|
|
230
|
+
============================================================ */
|
|
231
|
+
:root {
|
|
232
|
+
--shell-header: var(--color-neutral-50);
|
|
233
|
+
--shell-rail: var(--color-neutral-50);
|
|
234
|
+
--shell-context: var(--color-neutral-50);
|
|
235
|
+
--shell-main: var(--color-neutral-100);
|
|
236
|
+
--shell-panel: var(--color-neutral-50);
|
|
237
|
+
--shell-border: var(--color-neutral-200);
|
|
238
|
+
--shell-shadow: 0 4px 12px hsl(0 0% 0% / 0.04);
|
|
239
|
+
--shell-header-height: 56px;
|
|
240
|
+
--shell-rail-width: 60px;
|
|
241
|
+
--shell-context-default: 300px;
|
|
242
|
+
--shell-context-min: 240px;
|
|
243
|
+
--shell-context-max: 420px;
|
|
244
|
+
--shell-panel-default: 340px;
|
|
245
|
+
--shell-panel-min: 300px;
|
|
246
|
+
--shell-panel-max: 520px;
|
|
247
|
+
--shell-bottom-nav-height: 56px;
|
|
248
|
+
--z-shell-header: 40;
|
|
249
|
+
--z-shell-panel: 45;
|
|
250
|
+
--z-shell-fullscreen: 60;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.dark,
|
|
254
|
+
[data-theme="dark"] {
|
|
255
|
+
--shell-header: var(--color-surface-card);
|
|
256
|
+
--shell-rail: var(--color-surface-sidebar);
|
|
257
|
+
--shell-context: var(--color-surface-sidebar);
|
|
258
|
+
--shell-main: var(--color-surface-bg);
|
|
259
|
+
--shell-panel: var(--color-surface-card);
|
|
260
|
+
--shell-border: var(--color-surface-border);
|
|
261
|
+
--shell-shadow: 0 4px 12px hsl(0 0% 0% / 0.45);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/* ============================================================
|
|
265
|
+
Motion (theme: base, constant)
|
|
266
|
+
Mirror of motion.ts (Phase 5)
|
|
267
|
+
============================================================ */
|
|
268
|
+
:root {
|
|
269
|
+
--motion-fast: 140ms;
|
|
270
|
+
--motion-default: 200ms;
|
|
271
|
+
--motion-panel: 240ms;
|
|
272
|
+
--motion-fullscreen: 280ms;
|
|
273
|
+
--motion-ease: cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* ============================================================
|
|
277
|
+
Typography (theme: base, constant)
|
|
278
|
+
Font family: Geist + Geist Mono only. No Inter.
|
|
279
|
+
============================================================ */
|
|
280
|
+
:root {
|
|
281
|
+
--font-family-sans: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
282
|
+
--font-family-mono: "Geist Mono", "SF Mono", "Fira Code", monospace;
|
|
283
|
+
--font-size-display: 36px;
|
|
284
|
+
--font-size-h1: 30px;
|
|
285
|
+
--font-size-h2: 24px;
|
|
286
|
+
--font-size-h3: 20px;
|
|
287
|
+
--font-size-h4: 18px;
|
|
288
|
+
--font-size-body-lg: 16px;
|
|
289
|
+
--font-size-body: 14px;
|
|
290
|
+
--font-size-body-sm: 13px;
|
|
291
|
+
--font-size-caption: 11px;
|
|
292
|
+
--font-size-overline: 10px;
|
|
293
|
+
--font-size-mono-data: 12px;
|
|
294
|
+
--font-size-mono-caption: 10px;
|
|
295
|
+
--font-weight-regular: 400;
|
|
296
|
+
--font-weight-medium: 500;
|
|
297
|
+
--font-weight-semibold: 600;
|
|
298
|
+
--font-weight-bold: 700;
|
|
299
|
+
--line-height-display: 1.1;
|
|
300
|
+
--line-height-heading: 1.15;
|
|
301
|
+
--line-height-tight: 1.2;
|
|
302
|
+
--line-height-snug: 1.25;
|
|
303
|
+
--line-height-normal: 1.5;
|
|
304
|
+
--line-height-relaxed: 1.6;
|
|
305
|
+
--letter-spacing-tighter: -0.5px;
|
|
306
|
+
--letter-spacing-tight: -0.2px;
|
|
307
|
+
--letter-spacing-normal: 0;
|
|
308
|
+
--letter-spacing-wide: 1.2px;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/* ============================================================
|
|
312
|
+
Radii (theme: base, constant)
|
|
313
|
+
============================================================ */
|
|
314
|
+
:root {
|
|
315
|
+
--radius: 8px;
|
|
316
|
+
--radius-sm: 4px;
|
|
317
|
+
--radius-md: 6px;
|
|
318
|
+
--radius-lg: 8px;
|
|
319
|
+
--radius-xl: 12px;
|
|
320
|
+
--radius-2xl: 16px;
|
|
321
|
+
--radius-3xl: 20px;
|
|
322
|
+
--radius-4xl: 24px;
|
|
323
|
+
--radius-full: 9999px;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/* ============================================================
|
|
327
|
+
Spacing scale (theme: base, constant)
|
|
328
|
+
============================================================ */
|
|
329
|
+
:root {
|
|
330
|
+
--spacing-xs: 4px;
|
|
331
|
+
--spacing-sm: 8px;
|
|
332
|
+
--spacing-md: 12px;
|
|
333
|
+
--spacing-lg: 16px;
|
|
334
|
+
--spacing-xl: 24px;
|
|
335
|
+
--spacing-2xl: 32px;
|
|
336
|
+
--spacing-3xl: 48px;
|
|
337
|
+
--spacing-4xl: 64px;
|
|
338
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { Calendar, ChevronLeft, ChevronRight } from 'lucide-react';
|
|
4
|
+
import { cn } from '../lib/utils.js';
|
|
3
5
|
// "YYYY-MM-DD" in the given tz (or local if undefined). Comparing these
|
|
4
6
|
// strings is a robust same-day check that doesn't depend on local-time math.
|
|
5
7
|
function ymdInTz(d, tz) {
|
|
@@ -58,5 +60,5 @@ export function DateSwitcher({ value, now, tz, onChange, className }) {
|
|
|
58
60
|
const isToday = isSameDay(value, now, tz);
|
|
59
61
|
const goPrev = () => onChange(addDays(value, -1, tz));
|
|
60
62
|
const goNext = () => onChange(addDays(value, 1, tz));
|
|
61
|
-
return (_jsxs("div", { className:
|
|
63
|
+
return (_jsxs("div", { className: cn('flex items-center gap-2', className), children: [_jsx("button", { type: "button", "aria-label": "Previous day", onClick: goPrev, className: "inline-flex size-7 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground", children: _jsx(ChevronLeft, { className: "size-4" }) }), _jsxs("div", { className: "flex flex-1 items-center justify-center gap-1.5 rounded-md border border-border bg-card px-3 py-1.5 text-sm", children: [_jsx(Calendar, { className: "size-3.5 text-muted-foreground" }), _jsx("span", { className: "font-medium", children: fmtDate(value, tz) }), isToday && (_jsx("span", { className: "ml-1 rounded-full bg-success/15 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-success-800", children: "today" }))] }), _jsx("button", { type: "button", "aria-label": "Next day", disabled: isToday, onClick: goNext, className: "inline-flex size-7 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground disabled:cursor-not-allowed disabled:opacity-30 disabled:hover:bg-transparent", children: _jsx(ChevronRight, { className: "size-4" }) })] }));
|
|
62
64
|
}
|
|
@@ -1,26 +1,18 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from '../lib/utils.js';
|
|
2
4
|
export function EventCard({ event, size = 'default', onSelect, className }) {
|
|
3
5
|
const isLive = event.isLive ?? false;
|
|
4
6
|
const isSelected = event.selected ?? false;
|
|
5
7
|
const isError = event.kind === 'error';
|
|
6
|
-
const liveClasses = isLive
|
|
7
|
-
? 'border-emerald-500/40 bg-gradient-to-b from-emerald-500/10 to-card'
|
|
8
|
-
: '';
|
|
8
|
+
const liveClasses = isLive ? 'border-success/40 bg-gradient-to-b from-success/10 to-card' : '';
|
|
9
9
|
const errorClasses = isError ? 'border-destructive/40' : '';
|
|
10
10
|
const selectedClasses = isSelected
|
|
11
11
|
? 'border-primary/60 bg-primary/[0.10] shadow-[0_4px_12px_rgba(0,0,0,0.15)]'
|
|
12
12
|
: '';
|
|
13
13
|
const sizeClasses = size === 'tiny' ? 'px-2 py-1 text-[11px]' : 'px-2.5 py-2 text-xs';
|
|
14
|
-
return (_jsxs("button", { type: "button", "data-live": String(isLive), "data-selected": String(isSelected), "data-kind": event.kind, onClick: () => onSelect?.(event), className: [
|
|
15
|
-
'relative w-full cursor-pointer rounded-md border border-border bg-card text-left transition-[border-color,transform] duration-100',
|
|
16
|
-
'hover:translate-x-px hover:border-primary/40',
|
|
17
|
-
sizeClasses,
|
|
18
|
-
liveClasses,
|
|
19
|
-
errorClasses,
|
|
20
|
-
selectedClasses,
|
|
21
|
-
className ?? '',
|
|
22
|
-
].join(' '), children: [_jsx("span", { "aria-hidden": "true", className: "absolute -left-2.5 top-1/2 h-px w-2.5 bg-border" }), _jsxs("span", { className: "flex items-center gap-1.5 font-medium", children: [isLive && _jsx(LiveDot, {}), _jsx("span", { className: "min-w-0 flex-1 truncate", children: event.primary })] }), size === 'default' && event.secondary != null && (_jsx("span", { className: "mt-0.5 block text-[10px] text-muted-foreground tabular-nums", children: event.secondary }))] }));
|
|
14
|
+
return (_jsxs("button", { type: "button", "data-live": String(isLive), "data-selected": String(isSelected), "data-kind": event.kind, onClick: () => onSelect?.(event), className: cn('relative w-full cursor-pointer rounded-md border border-border bg-card text-left transition-[border-color,transform] duration-100', 'hover:translate-x-px hover:border-primary/40', sizeClasses, liveClasses, errorClasses, selectedClasses, className), children: [_jsx("span", { "aria-hidden": "true", className: "absolute -left-2.5 top-1/2 h-px w-2.5 bg-border" }), _jsxs("span", { className: "flex items-center gap-1.5 font-medium", children: [isLive && _jsx(LiveDot, {}), _jsx("span", { className: "min-w-0 flex-1 truncate", children: event.primary })] }), size === 'default' && event.secondary != null && (_jsx("span", { className: "mt-0.5 block text-[10px] text-muted-foreground tabular-nums", children: event.secondary }))] }));
|
|
23
15
|
}
|
|
24
16
|
function LiveDot() {
|
|
25
|
-
return (_jsx("span", { "aria-hidden": "true", className: "inline-block size-1.5 shrink-0 animate-pulse rounded-full bg-
|
|
17
|
+
return (_jsx("span", { "aria-hidden": "true", "data-chromatic": "ignore", className: "inline-block size-1.5 shrink-0 animate-pulse rounded-full bg-success shadow-[0_0_0_3px_rgba(16,185,129,0.20)]" }));
|
|
26
18
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { formatClock } from '../lib/format-time.js';
|
|
4
|
+
import { cn } from '../lib/utils.js';
|
|
3
5
|
export function LiveIndicator({ now, tz, className }) {
|
|
4
|
-
return (_jsxs("div", { role: "status", "aria-live": "polite", className:
|
|
6
|
+
return (_jsxs("div", { role: "status", "aria-live": "polite", className: cn('relative h-0 pointer-events-none', className), children: [_jsx("span", { className: "pointer-events-auto absolute left-0 top-0 inline-flex items-center rounded-md bg-success-700 px-2.5 py-1 text-[11px] font-bold uppercase tracking-wider text-white shadow-[0_6px_18px_rgba(16,185,129,0.35)]", children: "LIVE" }), _jsx("span", { "aria-hidden": "true", className: "absolute right-0 top-1/2 left-[60px] h-px bg-gradient-to-r from-success via-success/30 to-transparent" }), _jsx("span", { "data-chromatic": "ignore", className: "absolute right-1.5 top-[-2px] text-[10px] font-semibold tabular-nums text-success-700", children: formatClock(now, { tz }) })] }));
|
|
5
7
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { Pause, Play, SkipBack, SkipForward } from 'lucide-react';
|
|
3
4
|
import { useRef } from 'react';
|
|
4
5
|
import { formatDuration } from '../lib/format-time.js';
|
|
6
|
+
import { cn } from '../lib/utils.js';
|
|
5
7
|
const KIND_COLOR = {
|
|
6
8
|
turn: 'bg-primary',
|
|
7
|
-
tool: 'bg-
|
|
8
|
-
barge: 'bg-
|
|
9
|
+
tool: 'bg-warning',
|
|
10
|
+
barge: 'bg-success',
|
|
9
11
|
error: 'bg-destructive',
|
|
10
12
|
};
|
|
11
13
|
export function ScrubBar({ durationMs, positionMs, isLive = false, marks, onSeek, isPlaying, onPlayPause, onSkipBack, onSkipForward, className, }) {
|
|
@@ -32,13 +34,10 @@ export function ScrubBar({ durationMs, positionMs, isLive = false, marks, onSeek
|
|
|
32
34
|
onSeek(Math.min(durationMs, positionMs + step));
|
|
33
35
|
}
|
|
34
36
|
};
|
|
35
|
-
return (_jsxs("div", { className:
|
|
37
|
+
return (_jsxs("div", { className: cn('rounded-2xl border border-border bg-card p-3.5', className), children: [_jsxs("div", { className: "mb-2.5 flex items-center justify-between", children: [_jsx("span", { className: "text-[11px] font-semibold uppercase tracking-wider text-muted-foreground", children: "Timeline \u00B7 this call" }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsxs("span", { className: "px-1.5 text-[11px] tabular-nums text-muted-foreground", children: [formatDuration(positionMs), isLive ? ' / live' : ` / ${formatDuration(durationMs)}`] }), onSkipBack && (_jsx(TransportBtn, { label: "Skip back", onClick: onSkipBack, children: _jsx(SkipBack, { className: "size-3.5" }) })), showPlayPause && (_jsx(TransportBtn, { label: isPlayingValue ? 'Pause' : 'Play', variant: "primary", onClick: onPlayPause ?? (() => { }), children: isPlayingValue ? _jsx(Pause, { className: "size-3.5" }) : _jsx(Play, { className: "size-3.5" }) })), onSkipForward && (_jsx(TransportBtn, { label: "Skip forward", onClick: onSkipForward, children: _jsx(SkipForward, { className: "size-3.5" }) }))] })] }), _jsxs("div", { ref: trackRef, role: "slider", "aria-label": "Conversation position", "aria-valuemin": 0, "aria-valuemax": durationMs, "aria-valuenow": positionMs, onClick: handleSeek, onKeyDown: handleKeyDown, tabIndex: 0, className: "relative h-8 cursor-pointer overflow-hidden rounded-md bg-muted focus:outline-none focus:ring-2 focus:ring-primary/50", children: [_jsx("span", { "aria-hidden": "true", className: "absolute inset-y-0 left-0 bg-primary/10", style: { width: `${fillPct}%` } }), marks.map((m) => (_jsx("span", { "data-mark-kind": m.kind, title: m.label ?? m.kind, className: cn('absolute inset-y-1 w-0.5 rounded-sm', KIND_COLOR[m.kind]), style: { left: `${m.positionPct}%` } }, m.id))), _jsx("span", { "aria-hidden": "true", "data-chromatic": "ignore", className: "absolute -inset-y-0.5 w-0.5 bg-foreground", style: { left: `${fillPct}%` } })] }), _jsxs("div", { className: "flex justify-between pt-2 text-[10px] tabular-nums text-muted-foreground", children: [_jsx("span", { children: "0:00" }), _jsx("span", { children: isLive ? 'now' : formatDuration(durationMs) })] })] }));
|
|
36
38
|
}
|
|
37
39
|
function TransportBtn({ label, onClick, variant, children, }) {
|
|
38
|
-
return (_jsx("button", { type: "button", "aria-label": label, onClick: onClick, className:
|
|
39
|
-
'
|
|
40
|
-
|
|
41
|
-
? 'bg-primary text-primary-foreground'
|
|
42
|
-
: 'text-muted-foreground hover:bg-muted hover:text-foreground',
|
|
43
|
-
].join(' '), children: children }));
|
|
40
|
+
return (_jsx("button", { type: "button", "aria-label": label, onClick: onClick, className: cn('inline-flex size-6 items-center justify-center rounded', variant === 'primary'
|
|
41
|
+
? 'bg-primary text-primary-foreground'
|
|
42
|
+
: 'text-muted-foreground hover:bg-muted hover:text-foreground'), children: children }));
|
|
44
43
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { Archive } from 'lucide-react';
|
|
3
4
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
5
|
+
import { cn } from '../lib/utils.js';
|
|
4
6
|
import { DateSwitcher } from './date-switcher.js';
|
|
5
7
|
import { LiveIndicator } from './live-indicator.js';
|
|
6
8
|
import { TimelineTape } from './timeline-tape.js';
|
|
@@ -50,10 +52,10 @@ export function TimelineRail({ date, now, events, zoom = 1.2, followLive = true,
|
|
|
50
52
|
setIsFollowing(true);
|
|
51
53
|
onFollowChange?.(true);
|
|
52
54
|
};
|
|
53
|
-
return (_jsxs("aside", { className: [
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
return (_jsxs("aside", { className: cn('relative flex h-full flex-col border-r border-border bg-card text-sm', className), "data-meda-component": "timeline-rail", children: [_jsx("div", { className: "px-3.5 pb-2.5 pt-3.5", children: _jsx(DateSwitcher, { value: date, now: now, tz: tz, onChange: (d) => onDateChange?.(d) }) }), _jsxs("div", { className: "flex items-center gap-2.5 px-3.5 pb-3", children: [_jsx(ZoomBtn, { label: "\u2212", onClick: () => onZoomChange?.(Math.max(0.2, zoom - 0.4)) }), _jsx(ZoomTrack, { value: zoom, min: 0.2, max: 6 }), _jsx(ZoomBtn, { label: "+", onClick: () => onZoomChange?.(Math.min(6, zoom + 0.4)) })] }), _jsxs("div", { className: "flex items-center gap-2 border-b border-border px-3.5 pb-2.5 text-[11px] tabular-nums text-muted-foreground", children: [liveCount > 0 && (_jsxs("span", { className: "inline-flex items-center gap-1.5 font-semibold text-success-700", children: [_jsx("span", { className: "size-1.5 animate-pulse rounded-full bg-success shadow-[0_0_0_3px_rgba(16,185,129,0.18)]" }), liveCount, " live"] })), _jsxs("span", { className: "ml-auto", children: [_jsx("strong", { className: "font-semibold text-foreground", children: totalCalls }), " sessions"] })] }), _jsxs("div", { ref: scrollRef, className: "relative flex-1 overflow-y-auto overflow-x-hidden pb-16", children: [_jsx("div", { className: "sticky top-3 z-10 mx-3.5", children: _jsx(LiveIndicator, { now: now, tz: tz }) }), _jsx(TimelineTape, { now: now, events: events, pxPerSec: zoom, futurePadSec: FUTURE_PAD_SEC, pastSpanSec: PAST_SPAN_SEC, tz: tz, onSelect: onSelect })] }), !isFollowing && (_jsx("button", { type: "button", onClick: jumpToLive,
|
|
56
|
+
// bg-success-700/-800 (vs. -600/-700) so 11px white text clears
|
|
57
|
+
// WCAG AA on hover and at rest.
|
|
58
|
+
className: "absolute right-3 top-32 z-20 inline-flex items-center gap-1.5 rounded-full bg-success-700 px-3 py-1 text-[11px] font-semibold text-white shadow-md hover:bg-success-800", children: "Jump to live" })), _jsxs("button", { type: "button", onClick: () => onManageRetention?.(), className: "absolute inset-x-0 bottom-0 z-20 flex items-center justify-center gap-1.5 bg-gradient-to-t from-card via-card/85 to-transparent px-3.5 py-2.5 text-[12px] text-primary hover:text-primary/80", children: [_jsx(Archive, { className: "size-3.5" }), " Manage retention"] })] }));
|
|
57
59
|
}
|
|
58
60
|
function ZoomBtn({ label, onClick }) {
|
|
59
61
|
return (_jsx("button", { type: "button", onClick: onClick, className: "inline-flex size-5 items-center justify-center rounded bg-muted text-sm leading-none text-muted-foreground hover:text-foreground", children: label }));
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { formatClock } from '../lib/format-time.js';
|
|
4
|
+
import { cn } from '../lib/utils.js';
|
|
3
5
|
import { EventCard } from './event-card.js';
|
|
4
6
|
const DEFAULT_PX_PER_SEC = 1.2;
|
|
5
7
|
const DEFAULT_FUTURE_PAD_SEC = 300;
|
|
@@ -51,14 +53,11 @@ export function TimelineTape({ now, events, pxPerSec = DEFAULT_PX_PER_SEC, futur
|
|
|
51
53
|
const height = Math.max(2, Math.abs(endY - startY));
|
|
52
54
|
return { event: e, top, height };
|
|
53
55
|
});
|
|
54
|
-
return (_jsxs("div", { className:
|
|
55
|
-
'
|
|
56
|
-
event.
|
|
57
|
-
? 'bg-
|
|
58
|
-
: event.
|
|
59
|
-
? 'bg-destructive'
|
|
60
|
-
: 'bg-sky-400 shadow-[0_0_8px_rgba(56,189,248,0.35)]',
|
|
61
|
-
].join(' '), style: { left: 44, top: `${top}px`, height: `${height}px` } }, `seg-${event.id}`))), events.map((event) => {
|
|
56
|
+
return (_jsxs("div", { className: cn('relative ml-3.5 mr-3', className), style: { height: `${canvasHeight}px` }, "data-canvas-height": canvasHeight, children: [ticks.map((t) => t.major ? (_jsxs("div", { children: [_jsx("span", { className: "absolute left-0 w-[30px] -translate-y-[7px] text-right text-[11px] font-medium tabular-nums text-muted-foreground", style: { top: `${t.y}px` }, children: t.label }), _jsx("span", { className: "absolute h-px w-2 bg-border", style: { left: 32, top: `${t.y}px` } })] }, `maj-${t.y}`)) : (_jsx("span", { className: "absolute h-px w-1.5 bg-border/40", style: { left: 35, top: `${t.y}px` } }, `min-${t.y}`))), segments.map(({ event, top, height }) => (_jsx("span", { "aria-hidden": "true", "data-tape-segment-id": event.id, "data-tape-segment-live": String(event.isLive ?? false), className: cn('absolute w-1 rounded-sm', event.isLive
|
|
57
|
+
? 'bg-success-600 shadow-[0_0_12px_rgba(16,185,129,0.5)]'
|
|
58
|
+
: event.kind === 'error'
|
|
59
|
+
? 'bg-destructive'
|
|
60
|
+
: 'bg-info-500 shadow-[0_0_8px_rgba(56,189,248,0.35)]'), style: { left: 44, top: `${top}px`, height: `${height}px` } }, `seg-${event.id}`))), events.map((event) => {
|
|
62
61
|
const startY = instantToY(event.startedAt, nowMs, pxPerSec, futurePadSec);
|
|
63
62
|
const isSubEvent = event.kind === 'sub-event';
|
|
64
63
|
return (_jsx("div", { "data-event-id": event.id, className: "absolute", style: { top: `${startY}px`, left: 56, right: 0 }, children: _jsx(EventCard, { event: event, size: isSubEvent ? 'tiny' : 'default', onSelect: onSelect }) }, event.id));
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import * as React from 'react';
|
|
4
|
+
import { cn } from '../lib/utils.js';
|
|
3
5
|
const BAR_COUNT = 9;
|
|
4
6
|
export function VoiceLevel({ level, variant = 'bars', width = 140, height = 48, className, }) {
|
|
5
7
|
const historyRef = React.useRef(Array(BAR_COUNT).fill(0));
|
|
@@ -14,7 +16,7 @@ export function VoiceLevel({ level, variant = 'bars', width = 140, height = 48,
|
|
|
14
16
|
force((v) => v + 1);
|
|
15
17
|
}, [level, variant]);
|
|
16
18
|
if (variant === 'bars') {
|
|
17
|
-
return (_jsx("div", { role: "presentation", className:
|
|
19
|
+
return (_jsx("div", { role: "presentation", className: cn('flex items-end gap-1', className), style: { width, height }, children: historyRef.current.map((v, i) => (_jsx("span", { className: "flex-1 rounded-sm bg-primary", style: { height: `${15 + v * 85}%`, opacity: 0.4 + v * 0.6 } }, i))) }));
|
|
18
20
|
}
|
|
19
21
|
if (variant === 'ring') {
|
|
20
22
|
const r = Math.min(width, height) / 2 - 4;
|
package/dist/voice/voice-orb.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
4
|
import { Canvas } from '@react-three/fiber';
|
|
4
5
|
import * as React from 'react';
|
|
6
|
+
import { cn } from '../lib/utils.js';
|
|
5
7
|
import { Scene } from './voice-orb-scene.js';
|
|
6
8
|
// Note: CSS for `.meda-voice-orb` is shipped via @medalsocial/meda/styles.css
|
|
7
9
|
// (re-exported from globals.css). We avoid a JS-side CSS import here so
|
|
@@ -9,7 +11,11 @@ import { Scene } from './voice-orb-scene.js';
|
|
|
9
11
|
// ---------------------------------------------------------------------------
|
|
10
12
|
// Theming helpers
|
|
11
13
|
// ---------------------------------------------------------------------------
|
|
12
|
-
|
|
14
|
+
// Fallback colors used only if CSS custom properties fail to resolve.
|
|
15
|
+
// Both values are from the canonical brand ramp (.lib.pen):
|
|
16
|
+
// brand-400 (#9A6AC2) and brand-500 (#7E3FAC).
|
|
17
|
+
// Runtime path reads --primary and --accent first.
|
|
18
|
+
const FALLBACK_COLOR = '#9A6AC2'; // brand-400
|
|
13
19
|
function hslToHex(h, s, l) {
|
|
14
20
|
const sl = s / 100;
|
|
15
21
|
const ll = l / 100;
|
|
@@ -70,7 +76,7 @@ function readMedaColors(el) {
|
|
|
70
76
|
const colorB = accent ? parseColor(accent) : colorA;
|
|
71
77
|
return [colorA, colorB];
|
|
72
78
|
}
|
|
73
|
-
const DEFAULT_COLORS = ['#9A6AC2', '#
|
|
79
|
+
const DEFAULT_COLORS = ['#9A6AC2', '#7E3FAC']; // brand-400 → brand-500
|
|
74
80
|
// ---------------------------------------------------------------------------
|
|
75
81
|
// Component
|
|
76
82
|
// ---------------------------------------------------------------------------
|
|
@@ -111,13 +117,5 @@ export const VoiceOrb = React.forwardRef(function VoiceOrb({ pressed, level = 0,
|
|
|
111
117
|
height: size,
|
|
112
118
|
...style,
|
|
113
119
|
};
|
|
114
|
-
return (_jsx("button", { ref: buttonRef, type: "button", "aria-pressed": pressed, "aria-label": label, disabled: disabled, "data-phase": phase, "data-pressed": pressed, className: [
|
|
115
|
-
'meda-voice-orb',
|
|
116
|
-
'relative inline-flex items-center justify-center rounded-full',
|
|
117
|
-
'select-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
|
118
|
-
'transition-transform duration-200 ease-out',
|
|
119
|
-
disabled ? 'cursor-not-allowed opacity-60' : 'cursor-pointer',
|
|
120
|
-
pressed ? 'scale-[0.97]' : 'scale-100',
|
|
121
|
-
className ?? '',
|
|
122
|
-
].join(' '), style: btnStyle, ...rest, children: _jsx(Canvas, { gl: { alpha: true, antialias: true, premultipliedAlpha: true }, style: { pointerEvents: 'none' }, children: _jsx(Scene, { level: level, outputLevel: outputLevel, phase: phase, colors: resolvedColors, reducedMotion: reducedMotion, pressed: pressed, variant: variant }) }) }));
|
|
120
|
+
return (_jsx("button", { ref: buttonRef, type: "button", "aria-pressed": pressed, "aria-label": label, disabled: disabled, "data-phase": phase, "data-pressed": pressed, className: cn('meda-voice-orb', 'relative inline-flex items-center justify-center rounded-full', 'select-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', 'transition-transform duration-200 ease-out', disabled ? 'cursor-not-allowed opacity-60' : 'cursor-pointer', pressed ? 'scale-[0.97]' : 'scale-100', className), style: btnStyle, ...rest, children: _jsx(Canvas, { gl: { alpha: true, antialias: true, premultipliedAlpha: true }, style: { pointerEvents: 'none' }, children: _jsx(Scene, { level: level, outputLevel: outputLevel, phase: phase, colors: resolvedColors, reducedMotion: reducedMotion, pressed: pressed, variant: variant }) }) }));
|
|
123
121
|
});
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from '../lib/utils.js';
|
|
2
4
|
const PHASE_LABEL = {
|
|
3
5
|
idle: 'Idle',
|
|
4
6
|
listening: 'Listening',
|
|
@@ -10,17 +12,10 @@ export function VoiceStatusPill({ phase, thinkingForMs, className }) {
|
|
|
10
12
|
const tone = phase === 'listening'
|
|
11
13
|
? 'bg-primary text-primary-foreground'
|
|
12
14
|
: phase === 'speaking'
|
|
13
|
-
? 'bg-
|
|
15
|
+
? 'bg-success text-success-foreground'
|
|
14
16
|
: phase === 'error'
|
|
15
17
|
? 'bg-destructive text-destructive-foreground'
|
|
16
18
|
: 'bg-muted text-muted-foreground';
|
|
17
19
|
const seconds = thinkingForMs ? (thinkingForMs / 1000).toFixed(1) : null;
|
|
18
|
-
return (_jsxs("span", { role: "status", "data-phase": phase, className: [
|
|
19
|
-
'inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-medium',
|
|
20
|
-
tone,
|
|
21
|
-
className ?? '',
|
|
22
|
-
].join(' '), children: [_jsx("span", { className: [
|
|
23
|
-
'size-1.5 rounded-full bg-current',
|
|
24
|
-
phase === 'thinking' ? 'animate-pulse' : '',
|
|
25
|
-
].join(' ') }), PHASE_LABEL[phase], phase === 'thinking' && seconds && _jsxs("span", { className: "opacity-70", children: ["\u00B7 ", seconds, "s"] })] }));
|
|
20
|
+
return (_jsxs("span", { role: "status", "data-phase": phase, className: cn('inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-medium', tone, className), children: [_jsx("span", { className: cn('size-1.5 rounded-full bg-current', phase === 'thinking' && 'animate-pulse') }), PHASE_LABEL[phase], phase === 'thinking' && seconds && _jsxs("span", { children: ["\u00B7 ", seconds, "s"] })] }));
|
|
26
21
|
}
|