@graineai/inapp-react-native 0.36.0 → 0.37.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/dist/theme.js ADDED
@@ -0,0 +1,1999 @@
1
+ export const DESIGN_SCHEMES = ["auto", "light", "dark"];
2
+ export const DESIGN_DENSITIES = ["compact", "comfortable", "spacious"];
3
+ export const DESIGN_SHADOWS = ["preset", "none", "soft", "medium", "strong", "hard"];
4
+ export const DESIGN_MOTIONS = ["preset", "none", "subtle", "lively"];
5
+ export const DESIGN_NEUTRALS = ["preset", "cool", "neutral", "warm"];
6
+ export const DESIGN_BUBBLES = ["filled", "outline", "minimal"];
7
+ export const LAUNCHER_SHAPES = ["circle", "squircle", "rounded-square", "square", "pill"];
8
+ export const ICON_STYLES = ["outline", "filled", "duotone", "sharp"];
9
+ const HEX6 = /^#?([0-9a-f]{6})$/i;
10
+ const HEX3 = /^#?([0-9a-f]{3})$/i;
11
+ export function normaliseHex(value) {
12
+ const s = String(value ?? "").trim();
13
+ const six = HEX6.exec(s);
14
+ if (six)
15
+ return `#${six[1].toUpperCase()}`;
16
+ const three = HEX3.exec(s);
17
+ if (three)
18
+ return `#${three[1].split("").map((c) => c + c).join("").toUpperCase()}`;
19
+ return null;
20
+ }
21
+ function rgb(hex) {
22
+ const n = parseInt((normaliseHex(hex) ?? "#000000").slice(1), 16);
23
+ return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
24
+ }
25
+ function toHex(r, g, b) {
26
+ const c = (v) => Math.max(0, Math.min(255, Math.round(v))).toString(16).padStart(2, "0");
27
+ return `#${c(r)}${c(g)}${c(b)}`.toUpperCase();
28
+ }
29
+ export function mixHex(a, b, t) {
30
+ const [r1, g1, b1] = rgb(a);
31
+ const [r2, g2, b2] = rgb(b);
32
+ return toHex(r1 + (r2 - r1) * t, g1 + (g2 - g1) * t, b1 + (b2 - b1) * t);
33
+ }
34
+ export function luminanceOf(hex) {
35
+ const ch = (c) => {
36
+ const s = c / 255;
37
+ return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
38
+ };
39
+ const [r, g, b] = rgb(hex);
40
+ return 0.2126 * ch(r) + 0.7152 * ch(g) + 0.0722 * ch(b);
41
+ }
42
+ export function contrastRatio(a, b) {
43
+ const la = luminanceOf(a);
44
+ const lb = luminanceOf(b);
45
+ return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
46
+ }
47
+ export function readableOn(bg, candidates = ["#FFFFFF", "#101318"]) {
48
+ let best = candidates[0];
49
+ for (const c of candidates)
50
+ if (contrastRatio(c, bg) > contrastRatio(best, bg))
51
+ best = c;
52
+ return best;
53
+ }
54
+ export function ensureContrast(fg, bg, min = 4.5) {
55
+ if (contrastRatio(fg, bg) >= min)
56
+ return fg;
57
+ const toward = luminanceOf(bg) > 0.5 ? "#000000" : "#FFFFFF";
58
+ for (let t = 0.1; t <= 1.0001; t += 0.1) {
59
+ const c = mixHex(fg, toward, t);
60
+ if (contrastRatio(c, bg) >= min)
61
+ return c;
62
+ }
63
+ return toward;
64
+ }
65
+ export const LAUNCHER_ICONS = [
66
+ { id: "waveform", label: "Waveform", stroke: true, path: "M2 13a2 2 0 0 0 2 -2V7a2 2 0 0 1 4 0v13a2 2 0 0 0 4 0V4a2 2 0 0 1 4 0v13a2 2 0 0 0 4 0v-4a2 2 0 0 1 2 -2" },
67
+ { id: "audio-lines", label: "Audio lines", stroke: true, path: "M2 10v3M6 6v11M10 3v18M14 8v7M18 5v13M22 10v3" },
68
+ { id: "sparkles", label: "Sparkles", stroke: true, path: "M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0 -1.594 1.594l-1.051 5.558a1 1 0 0 1 -1.966 0l-1.051 -5.558a2 2 0 0 0 -1.594 -1.594l-5.558 -1.051a1 1 0 0 1 0 -1.966l5.558 -1.051a2 2 0 0 0 1.594 -1.594zM20 2v4M22 4h-4M2 20a2 2 0 1 0 4 0a2 2 0 1 0 -4 0z" },
69
+ { id: "microphone", label: "Microphone", stroke: true, path: "M12 19v3M19 10v2a7 7 0 0 1 -14 0v-2M12 2h0a3 3 0 0 1 3 3v7a3 3 0 0 1 -3 3h0a3 3 0 0 1 -3 -3v-7a3 3 0 0 1 3 -3z" },
70
+ { id: "message-circle", label: "Chat bubble", stroke: true, path: "M2.992 16.342a2 2 0 0 1 0.094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413 -0.998a2 2 0 0 1 1.099 0.092a10 10 0 1 0 -4.777 -4.719" },
71
+ { id: "headset", label: "Headset", stroke: true, path: "M3 11h3a2 2 0 0 1 2 2v3a2 2 0 0 1 -2 2H5a2 2 0 0 1 -2 -2v-5Zm0 0a9 9 0 1 1 18 0m0 0v5a2 2 0 0 1 -2 2h-1a2 2 0 0 1 -2 -2v-3a2 2 0 0 1 2 -2h3ZM21 16v2a4 4 0 0 1 -4 4h-5" },
72
+ { id: "bot", label: "Bot", stroke: true, path: "M12 8V4H8M6 8h12a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-8a2 2 0 0 1 2 -2zM2 14h2M20 14h2M15 13v2M9 13v2" },
73
+ { id: "phone", label: "Phone", stroke: true, path: "M13.832 16.568a1 1 0 0 0 1.213 -0.303l0.355 -0.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1 -2 2A18 18 0 0 1 2 4a2 2 0 0 1 2 -2h3a2 2 0 0 1 2 2v3a2 2 0 0 1 -0.8 1.6l-0.468 0.351a1 1 0 0 0 -0.292 1.233a14 14 0 0 0 6.392 6.384" },
74
+ { id: "zap", label: "Zap", stroke: true, path: "M15.914 4a1.5 1.5 0 0 0 -2.474 -1.561l-9 9A1.5 1.5 0 0 0 5.5 14h4.002a0.5 0.5 0 0 1 0.471 0.666L8.086 20a1.5 1.5 0 0 0 2.475 1.56l9 -9A1.5 1.5 0 0 0 18.5 10h-3.997a0.5 0.5 0 0 1 -0.472 -0.667z" },
75
+ { id: "star", label: "Star", stroke: true, path: "M11.525 2.295a0.53 0.53 0 0 1 0.95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166 0.756a0.53 0.53 0 0 1 0.294 0.904l-3.736 3.638a2.123 2.123 0 0 0 -0.611 1.878l0.882 5.14a0.53 0.53 0 0 1 -0.771 0.56l-4.618 -2.428a2.122 2.122 0 0 0 -1.973 0L6.396 21.01a0.53 0.53 0 0 1 -0.77 -0.56l0.881 -5.139a2.122 2.122 0 0 0 -0.611 -1.879L2.16 9.795a0.53 0.53 0 0 1 0.294 -0.906l5.165 -0.755a2.122 2.122 0 0 0 1.597 -1.16z" },
76
+ { id: "orbit", label: "Orbit", stroke: true, path: "M20.341 6.484A10 10 0 0 1 10.266 21.85M3.659 17.516A10 10 0 0 1 13.74 2.152M9 12a3 3 0 1 0 6 0a3 3 0 1 0 -6 0zM17 5a2 2 0 1 0 4 0a2 2 0 1 0 -4 0zM3 19a2 2 0 1 0 4 0a2 2 0 1 0 -4 0z" },
77
+ { id: "sparkle-filled", label: "Sparkle (filled)", stroke: false, path: "M19.5 13.5a1.479 1.479 0 0 1 -0.977 1.401L13.688 16.688l-1.781 4.839a1.492 1.492 0 0 1 -2.801 0L7.312 16.688l-4.839 -1.781a1.492 1.492 0 0 1 0 -2.801L7.312 10.312l1.781 -4.839a1.492 1.492 0 0 1 2.801 0L13.688 10.312l4.839 1.781A1.479 1.479 0 0 1 19.5 13.5ZM14.25 4.5h1.5V6a0.75 0.75 0 0 0 1.5 0V4.5h1.5a0.75 0.75 0 0 0 0 -1.5H17.25V1.5a0.75 0.75 0 0 0 -1.5 0V3H14.25a0.75 0.75 0 0 0 0 1.5Zm8.25 3h-0.75V6.75a0.75 0.75 0 0 0 -1.5 0v0.75h-0.75a0.75 0.75 0 0 0 0 1.5h0.75v0.75a0.75 0.75 0 0 0 1.5 0V9h0.75a0.75 0.75 0 0 0 0 -1.5Z" },
78
+ { id: "chat-dots-filled", label: "Chat dots (filled)", stroke: false, path: "M12 2.25A9.75 9.75 0 0 0 3.392 16.582L2.328 19.775a1.5 1.5 0 0 0 1.897 1.897l3.192 -1.064A9.75 9.75 0 1 0 12 2.25ZM7.875 13.125a1.125 1.125 0 1 1 1.125 -1.125A1.125 1.125 0 0 1 7.875 13.125Zm4.125 0a1.125 1.125 0 1 1 1.125 -1.125A1.125 1.125 0 0 1 12 13.125Zm4.125 0a1.125 1.125 0 1 1 1.125 -1.125A1.125 1.125 0 0 1 16.125 13.125Z" },
79
+ { id: "graphic-eq", label: "Voice equalizer (filled)", stroke: false, path: "M7 17v-10q0 -0.425 0.288 -0.713T8 6q0.425 0 0.713 0.288T9 7v10q0 0.425 -0.288 0.713T8 18q-0.425 0 -0.713 -0.288T7 17Zm4 4v-18q0 -0.425 0.288 -0.713T12 2q0.425 0 0.713 0.288T13 3v18q0 0.425 -0.288 0.713T12 22q-0.425 0 -0.713 -0.288T11 21ZM3 13v-2q0 -0.425 0.288 -0.713T4 10q0.425 0 0.713 0.288T5 11v2q0 0.425 -0.288 0.713T4 14q-0.425 0 -0.713 -0.288T3 13Zm12 4v-10q0 -0.425 0.288 -0.713T16 6q0.425 0 0.713 0.288T17 7v10q0 0.425 -0.288 0.713T16 18q-0.425 0 -0.713 -0.288T15 17Zm4 -4v-2q0 -0.425 0.288 -0.713T20 10q0.425 0 0.713 0.288T21 11v2q0 0.425 -0.288 0.713T20 14q-0.425 0 -0.713 -0.288T19 13Z" },
80
+ ];
81
+ export const DESIGN_FONTS = [
82
+ { family: "system", category: "sans", scripts: ["latin", "devanagari", "bengali", "tamil", "telugu", "kannada", "malayalam", "gujarati", "gurmukhi", "oriya"] },
83
+ { family: "Inter", category: "sans", scripts: ["latin", "latin-ext", "cyrillic", "cyrillic-ext", "greek", "greek-ext", "vietnamese"] },
84
+ { family: "Roboto", category: "sans", scripts: ["latin", "latin-ext", "cyrillic", "cyrillic-ext", "greek", "greek-ext", "vietnamese"] },
85
+ { family: "Geist", category: "sans", scripts: ["latin", "latin-ext", "cyrillic", "cyrillic-ext", "vietnamese"] },
86
+ { family: "IBM Plex Sans", category: "sans", scripts: ["latin", "latin-ext", "cyrillic", "cyrillic-ext", "greek", "vietnamese"] },
87
+ { family: "IBM Plex Sans Devanagari", category: "sans", scripts: ["devanagari", "latin", "latin-ext", "cyrillic-ext"] },
88
+ { family: "DM Sans", category: "sans", scripts: ["latin", "latin-ext"] },
89
+ { family: "Noto Sans", category: "sans", scripts: ["latin", "latin-ext", "devanagari", "cyrillic", "cyrillic-ext", "greek", "greek-ext", "vietnamese"] },
90
+ { family: "Noto Sans Devanagari", category: "sans", scripts: ["devanagari", "latin", "latin-ext"] },
91
+ { family: "Noto Sans Tamil", category: "sans", scripts: ["tamil", "latin", "latin-ext"] },
92
+ { family: "Mukta", category: "sans", scripts: ["devanagari", "latin", "latin-ext"] },
93
+ { family: "Hind", category: "sans", scripts: ["devanagari", "latin", "latin-ext"] },
94
+ { family: "Anek Devanagari", category: "sans", scripts: ["devanagari", "latin", "latin-ext"] },
95
+ { family: "Poppins", category: "sans", scripts: ["devanagari", "latin", "latin-ext"] },
96
+ { family: "Baloo 2", category: "display", scripts: ["devanagari", "latin", "latin-ext", "vietnamese"] },
97
+ { family: "Nunito", category: "sans", scripts: ["latin", "latin-ext", "cyrillic", "cyrillic-ext", "vietnamese"] },
98
+ { family: "Atkinson Hyperlegible Next", category: "sans", scripts: ["latin", "latin-ext"] },
99
+ { family: "Source Serif 4", category: "serif", scripts: ["latin", "latin-ext", "cyrillic", "cyrillic-ext", "greek", "vietnamese"] },
100
+ { family: "Newsreader", category: "serif", scripts: ["latin", "latin-ext", "vietnamese"] },
101
+ { family: "Fraunces", category: "serif", scripts: ["latin", "latin-ext", "vietnamese"] },
102
+ { family: "Playfair Display", category: "serif", scripts: ["latin", "latin-ext", "cyrillic", "vietnamese"] },
103
+ { family: "Tiro Devanagari Hindi", category: "serif", scripts: ["devanagari", "latin", "latin-ext"] },
104
+ { family: "Noto Serif Devanagari", category: "serif", scripts: ["devanagari", "latin", "latin-ext"] },
105
+ { family: "Martel", category: "serif", scripts: ["devanagari", "latin", "latin-ext"] },
106
+ { family: "JetBrains Mono", category: "mono", scripts: ["latin", "latin-ext", "cyrillic", "cyrillic-ext", "greek", "vietnamese"] },
107
+ ];
108
+ const GRAINE_CLASSIC = {
109
+ id: "graine-classic",
110
+ name: "Graine Classic",
111
+ tagline: "The look your widget has today",
112
+ family: "graine",
113
+ inspiredBy: ["Graine"],
114
+ font: {
115
+ body: "system", heading: "system",
116
+ scale: { caption: 12, body: 15, title: 17, display: 22 },
117
+ weights: { body: 400, strong: 600, title: 600 },
118
+ letterSpacing: { body: 0, title: -0.01 },
119
+ lineHeight: { body: 1.45, title: 1.25 },
120
+ },
121
+ shape: { radius: { sm: 8, md: 12, lg: 18, xl: 24 }, borderWidth: 1, launcher: "circle" },
122
+ spacing: { unit: 4, cardPadding: 14, gap: 10 },
123
+ elevation: {
124
+ card: { y: 2, blur: 8, spread: 0, opacity: 0.08, color: "#000000" },
125
+ floating: { y: 8, blur: 24, spread: 0, opacity: 0.16, color: "#000000" },
126
+ hardOffset: null,
127
+ },
128
+ motion: { durationMs: 200, easing: "standard", bounce: 0 },
129
+ icons: { style: "outline", stroke: 2, launcher: "waveform" },
130
+ glass: null,
131
+ bubbles: "filled",
132
+ colors: {
133
+ light: {
134
+ background: "#F7F7F5", surface: "#FFFFFF", surfaceRaised: "#FFFFFF", text: "#101318", textMuted: "#5F6672",
135
+ border: "#E5E4E0", accent: "#318CE7", onAccent: "#FFFFFF", success: "#16A34A", warning: "#B45309", danger: "#DC2626",
136
+ userBubble: "#318CE7", userBubbleText: "#FFFFFF", agentBubble: "#F1F1EF", agentBubbleText: "#101318", scrim: "#000000",
137
+ },
138
+ dark: {
139
+ background: "#0E0F11", surface: "#17181B", surfaceRaised: "#1F2024", text: "#F5F5F4", textMuted: "#A0A5AE",
140
+ border: "#2C2E33", accent: "#318CE7", onAccent: "#FFFFFF", success: "#22C55E", warning: "#F59E0B", danger: "#F87171",
141
+ userBubble: "#318CE7", userBubbleText: "#FFFFFF", agentBubble: "#23252A", agentBubbleText: "#F5F5F4", scrim: "#000000",
142
+ },
143
+ },
144
+ };
145
+ const P_CHAT_NEUTRAL = {
146
+ "id": "chat-neutral",
147
+ "name": "Chat Neutral",
148
+ "tagline": "Quiet greys and system type; your brand shows up only in the accent.",
149
+ "family": "native",
150
+ "inspiredBy": [
151
+ "OpenAI Apps SDK UI (openai/apps-sdk-ui)",
152
+ "OpenAI Apps SDK design guidelines"
153
+ ],
154
+ "font": {
155
+ "body": "system",
156
+ "heading": "system",
157
+ "scale": {
158
+ "caption": 12,
159
+ "body": 16,
160
+ "title": 18,
161
+ "display": 24
162
+ },
163
+ "weights": {
164
+ "body": 400,
165
+ "strong": 600,
166
+ "title": 600
167
+ },
168
+ "letterSpacing": {
169
+ "body": 0,
170
+ "title": 0
171
+ },
172
+ "lineHeight": {
173
+ "body": 1.5,
174
+ "title": 1.44
175
+ }
176
+ },
177
+ "shape": {
178
+ "radius": {
179
+ "sm": 6,
180
+ "md": 12,
181
+ "lg": 16,
182
+ "xl": 24
183
+ },
184
+ "borderWidth": 1,
185
+ "launcher": "circle"
186
+ },
187
+ "spacing": {
188
+ "unit": 4,
189
+ "cardPadding": 16,
190
+ "gap": 12
191
+ },
192
+ "elevation": {
193
+ "card": {
194
+ "y": 1,
195
+ "blur": 2,
196
+ "spread": -1,
197
+ "opacity": 0.08,
198
+ "color": "#000000"
199
+ },
200
+ "floating": {
201
+ "y": 8,
202
+ "blur": 16,
203
+ "spread": -4,
204
+ "opacity": 0.12,
205
+ "color": "#000000"
206
+ },
207
+ "hardOffset": null
208
+ },
209
+ "motion": {
210
+ "durationMs": 150,
211
+ "easing": "standard",
212
+ "bounce": 0
213
+ },
214
+ "icons": {
215
+ "style": "outline",
216
+ "stroke": 2,
217
+ "launcher": "sparkles"
218
+ },
219
+ "glass": null,
220
+ "bubbles": "minimal",
221
+ "colors": {
222
+ "light": {
223
+ "background": "#FFFFFF",
224
+ "surface": "#F9F9F9",
225
+ "surfaceRaised": "#FFFFFF",
226
+ "text": "#0D0D0D",
227
+ "textMuted": "#5D5D5D",
228
+ "border": "#E7E7E7",
229
+ "accent": "#181818",
230
+ "onAccent": "#FFFFFF",
231
+ "success": "#00692A",
232
+ "warning": "#923B0F",
233
+ "danger": "#911E1B",
234
+ "userBubble": "#EDEDED",
235
+ "userBubbleText": "#0D0D0D",
236
+ "agentBubble": "#FFFFFF",
237
+ "agentBubbleText": "#0D0D0D",
238
+ "scrim": "#000000"
239
+ },
240
+ "dark": {
241
+ "background": "#212121",
242
+ "surface": "#181818",
243
+ "surfaceRaised": "#303030",
244
+ "text": "#FFFFFF",
245
+ "textMuted": "#AFAFAF",
246
+ "border": "#3C3C3C",
247
+ "accent": "#F3F3F3",
248
+ "onAccent": "#0D0D0D",
249
+ "success": "#04B84C",
250
+ "warning": "#E25507",
251
+ "danger": "#E02E2A",
252
+ "userBubble": "#303030",
253
+ "userBubbleText": "#FFFFFF",
254
+ "agentBubble": "#212121",
255
+ "agentBubbleText": "#FFFFFF",
256
+ "scrim": "#000000"
257
+ }
258
+ }
259
+ };
260
+ const P_MATERIAL_3 = {
261
+ "id": "material-3",
262
+ "name": "Material You",
263
+ "tagline": "Google's Material 3: tonal surfaces, soft corners, springy motion.",
264
+ "family": "native",
265
+ "inspiredBy": [
266
+ "Material Design 3 (material-components/material-web tokens)"
267
+ ],
268
+ "font": {
269
+ "body": "Roboto",
270
+ "heading": "Roboto",
271
+ "scale": {
272
+ "caption": 12,
273
+ "body": 16,
274
+ "title": 22,
275
+ "display": 28
276
+ },
277
+ "weights": {
278
+ "body": 400,
279
+ "strong": 500,
280
+ "title": 400
281
+ },
282
+ "letterSpacing": {
283
+ "body": 0.0313,
284
+ "title": 0
285
+ },
286
+ "lineHeight": {
287
+ "body": 1.5,
288
+ "title": 1.27
289
+ }
290
+ },
291
+ "shape": {
292
+ "radius": {
293
+ "sm": 8,
294
+ "md": 12,
295
+ "lg": 16,
296
+ "xl": 28
297
+ },
298
+ "borderWidth": 1,
299
+ "launcher": "rounded-square"
300
+ },
301
+ "spacing": {
302
+ "unit": 4,
303
+ "cardPadding": 16,
304
+ "gap": 8
305
+ },
306
+ "elevation": {
307
+ "card": {
308
+ "y": 1,
309
+ "blur": 3,
310
+ "spread": 1,
311
+ "opacity": 0.15,
312
+ "color": "#000000"
313
+ },
314
+ "floating": {
315
+ "y": 4,
316
+ "blur": 8,
317
+ "spread": 3,
318
+ "opacity": 0.15,
319
+ "color": "#000000"
320
+ },
321
+ "hardOffset": null
322
+ },
323
+ "motion": {
324
+ "durationMs": 240,
325
+ "easing": "spring",
326
+ "bounce": 0.1
327
+ },
328
+ "icons": {
329
+ "style": "outline",
330
+ "stroke": 2,
331
+ "launcher": "graphic-eq"
332
+ },
333
+ "glass": null,
334
+ "bubbles": "filled",
335
+ "colors": {
336
+ "light": {
337
+ "background": "#FEF7FF",
338
+ "surface": "#F7F2FA",
339
+ "surfaceRaised": "#ECE6F0",
340
+ "text": "#1D1B20",
341
+ "textMuted": "#49454F",
342
+ "border": "#CAC4D0",
343
+ "accent": "#6750A4",
344
+ "onAccent": "#FFFFFF",
345
+ "success": "#287630",
346
+ "warning": "#8A5700",
347
+ "danger": "#B3261E",
348
+ "userBubble": "#EADDFF",
349
+ "userBubbleText": "#4F378B",
350
+ "agentBubble": "#E6E0E9",
351
+ "agentBubbleText": "#1D1B20",
352
+ "scrim": "#000000"
353
+ },
354
+ "dark": {
355
+ "background": "#141218",
356
+ "surface": "#1D1B20",
357
+ "surfaceRaised": "#2B2930",
358
+ "text": "#E6E0E9",
359
+ "textMuted": "#CAC4D0",
360
+ "border": "#49454F",
361
+ "accent": "#D0BCFF",
362
+ "onAccent": "#381E72",
363
+ "success": "#9BDC9C",
364
+ "warning": "#F3BE74",
365
+ "danger": "#F2B8B5",
366
+ "userBubble": "#4F378B",
367
+ "userBubbleText": "#EADDFF",
368
+ "agentBubble": "#36343B",
369
+ "agentBubbleText": "#E6E0E9",
370
+ "scrim": "#000000"
371
+ }
372
+ }
373
+ };
374
+ const P_IOS_NATIVE = {
375
+ "id": "ios-native",
376
+ "name": "iOS Native",
377
+ "tagline": "Feels like it shipped with the iPhone: SF type, system blue, grey bubbles.",
378
+ "family": "native",
379
+ "inspiredBy": [
380
+ "Apple Human Interface Guidelines (iOS system colors, Dynamic Type, SF Pro)"
381
+ ],
382
+ "font": {
383
+ "body": "system",
384
+ "heading": "system",
385
+ "scale": {
386
+ "caption": 12,
387
+ "body": 17,
388
+ "title": 20,
389
+ "display": 28
390
+ },
391
+ "weights": {
392
+ "body": 400,
393
+ "strong": 600,
394
+ "title": 600
395
+ },
396
+ "letterSpacing": {
397
+ "body": -0.026,
398
+ "title": -0.023
399
+ },
400
+ "lineHeight": {
401
+ "body": 1.29,
402
+ "title": 1.25
403
+ }
404
+ },
405
+ "shape": {
406
+ "radius": {
407
+ "sm": 8,
408
+ "md": 12,
409
+ "lg": 18,
410
+ "xl": 26
411
+ },
412
+ "borderWidth": 0.5,
413
+ "launcher": "circle"
414
+ },
415
+ "spacing": {
416
+ "unit": 4,
417
+ "cardPadding": 16,
418
+ "gap": 8
419
+ },
420
+ "elevation": {
421
+ "card": {
422
+ "y": 1,
423
+ "blur": 3,
424
+ "spread": 0,
425
+ "opacity": 0.06,
426
+ "color": "#000000"
427
+ },
428
+ "floating": {
429
+ "y": 8,
430
+ "blur": 24,
431
+ "spread": 0,
432
+ "opacity": 0.12,
433
+ "color": "#000000"
434
+ },
435
+ "hardOffset": null
436
+ },
437
+ "motion": {
438
+ "durationMs": 500,
439
+ "easing": "spring",
440
+ "bounce": 0
441
+ },
442
+ "icons": {
443
+ "style": "outline",
444
+ "stroke": 1.5,
445
+ "launcher": "waveform"
446
+ },
447
+ "glass": null,
448
+ "bubbles": "filled",
449
+ "colors": {
450
+ "light": {
451
+ "background": "#FFFFFF",
452
+ "surface": "#F2F2F7",
453
+ "surfaceRaised": "#FFFFFF",
454
+ "text": "#000000",
455
+ "textMuted": "#6C6C70",
456
+ "border": "#C6C6C8",
457
+ "accent": "#1E6EF4",
458
+ "onAccent": "#FFFFFF",
459
+ "success": "#008932",
460
+ "warning": "#C55300",
461
+ "danger": "#FF383C",
462
+ "userBubble": "#1E6EF4",
463
+ "userBubbleText": "#FFFFFF",
464
+ "agentBubble": "#E5E5EA",
465
+ "agentBubbleText": "#000000",
466
+ "scrim": "#000000"
467
+ },
468
+ "dark": {
469
+ "background": "#000000",
470
+ "surface": "#1C1C1E",
471
+ "surfaceRaised": "#2C2C2E",
472
+ "text": "#FFFFFF",
473
+ "textMuted": "#8D8D93",
474
+ "border": "#323235",
475
+ "accent": "#1E6EF4",
476
+ "onAccent": "#FFFFFF",
477
+ "success": "#30D158",
478
+ "warning": "#FF9230",
479
+ "danger": "#FF4245",
480
+ "userBubble": "#1E6EF4",
481
+ "userBubbleText": "#FFFFFF",
482
+ "agentBubble": "#2C2C2E",
483
+ "agentBubbleText": "#FFFFFF",
484
+ "scrim": "#000000"
485
+ }
486
+ }
487
+ };
488
+ const P_MINIMAL_MONO = {
489
+ "id": "minimal-mono",
490
+ "name": "Minimal Mono",
491
+ "tagline": "Black, white and one hairline border. Lets the words do the work.",
492
+ "family": "modern",
493
+ "inspiredBy": [
494
+ "shadcn/ui default theme (shadcn-ui/ui)",
495
+ "Vercel Geist typeface (vercel/geist-font)",
496
+ "Tailwind CSS palette"
497
+ ],
498
+ "font": {
499
+ "body": "Geist",
500
+ "heading": "Geist",
501
+ "scale": {
502
+ "caption": 12,
503
+ "body": 14,
504
+ "title": 16,
505
+ "display": 24
506
+ },
507
+ "weights": {
508
+ "body": 400,
509
+ "strong": 500,
510
+ "title": 600
511
+ },
512
+ "letterSpacing": {
513
+ "body": 0,
514
+ "title": -0.025
515
+ },
516
+ "lineHeight": {
517
+ "body": 1.43,
518
+ "title": 1.5
519
+ }
520
+ },
521
+ "shape": {
522
+ "radius": {
523
+ "sm": 6,
524
+ "md": 8,
525
+ "lg": 10,
526
+ "xl": 14
527
+ },
528
+ "borderWidth": 1,
529
+ "launcher": "rounded-square"
530
+ },
531
+ "spacing": {
532
+ "unit": 4,
533
+ "cardPadding": 24,
534
+ "gap": 24
535
+ },
536
+ "elevation": {
537
+ "card": {
538
+ "y": 1,
539
+ "blur": 3,
540
+ "spread": 0,
541
+ "opacity": 0.1,
542
+ "color": "#000000"
543
+ },
544
+ "floating": {
545
+ "y": 10,
546
+ "blur": 15,
547
+ "spread": -3,
548
+ "opacity": 0.1,
549
+ "color": "#000000"
550
+ },
551
+ "hardOffset": null
552
+ },
553
+ "motion": {
554
+ "durationMs": 150,
555
+ "easing": "standard",
556
+ "bounce": 0
557
+ },
558
+ "icons": {
559
+ "style": "outline",
560
+ "stroke": 2,
561
+ "launcher": "audio-lines"
562
+ },
563
+ "glass": null,
564
+ "bubbles": "filled",
565
+ "colors": {
566
+ "light": {
567
+ "background": "#FFFFFF",
568
+ "surface": "#FFFFFF",
569
+ "surfaceRaised": "#FFFFFF",
570
+ "text": "#0A0A0A",
571
+ "textMuted": "#737373",
572
+ "border": "#E5E5E5",
573
+ "accent": "#171717",
574
+ "onAccent": "#FAFAFA",
575
+ "success": "#00813A",
576
+ "warning": "#B55200",
577
+ "danger": "#E40016",
578
+ "userBubble": "#171717",
579
+ "userBubbleText": "#FAFAFA",
580
+ "agentBubble": "#F5F5F5",
581
+ "agentBubbleText": "#171717",
582
+ "scrim": "#000000"
583
+ },
584
+ "dark": {
585
+ "background": "#0A0A0A",
586
+ "surface": "#171717",
587
+ "surfaceRaised": "#171717",
588
+ "text": "#FAFAFA",
589
+ "textMuted": "#A1A1A1",
590
+ "border": "#232323",
591
+ "accent": "#E5E5E5",
592
+ "onAccent": "#171717",
593
+ "success": "#05DF72",
594
+ "warning": "#FABC00",
595
+ "danger": "#FF6568",
596
+ "userBubble": "#E5E5E5",
597
+ "userBubbleText": "#171717",
598
+ "agentBubble": "#262626",
599
+ "agentBubbleText": "#FAFAFA",
600
+ "scrim": "#000000"
601
+ }
602
+ }
603
+ };
604
+ const P_FLUENT_2 = {
605
+ "id": "fluent-2",
606
+ "name": "Fluent",
607
+ "tagline": "Microsoft's calm, businesslike Fluent 2: familiar from Teams and Office.",
608
+ "family": "enterprise",
609
+ "inspiredBy": [
610
+ "Microsoft Fluent 2 (microsoft/fluentui @fluentui/tokens)"
611
+ ],
612
+ "font": {
613
+ "body": "system",
614
+ "heading": "system",
615
+ "scale": {
616
+ "caption": 12,
617
+ "body": 14,
618
+ "title": 20,
619
+ "display": 28
620
+ },
621
+ "weights": {
622
+ "body": 400,
623
+ "strong": 600,
624
+ "title": 600
625
+ },
626
+ "letterSpacing": {
627
+ "body": 0,
628
+ "title": 0
629
+ },
630
+ "lineHeight": {
631
+ "body": 1.43,
632
+ "title": 1.4
633
+ }
634
+ },
635
+ "shape": {
636
+ "radius": {
637
+ "sm": 2,
638
+ "md": 4,
639
+ "lg": 6,
640
+ "xl": 8
641
+ },
642
+ "borderWidth": 1,
643
+ "launcher": "circle"
644
+ },
645
+ "spacing": {
646
+ "unit": 4,
647
+ "cardPadding": 12,
648
+ "gap": 12
649
+ },
650
+ "elevation": {
651
+ "card": {
652
+ "y": 2,
653
+ "blur": 4,
654
+ "spread": 0,
655
+ "opacity": 0.14,
656
+ "color": "#000000"
657
+ },
658
+ "floating": {
659
+ "y": 8,
660
+ "blur": 16,
661
+ "spread": 0,
662
+ "opacity": 0.14,
663
+ "color": "#000000"
664
+ },
665
+ "hardOffset": null
666
+ },
667
+ "motion": {
668
+ "durationMs": 200,
669
+ "easing": "standard",
670
+ "bounce": 0
671
+ },
672
+ "icons": {
673
+ "style": "outline",
674
+ "stroke": 1.5,
675
+ "launcher": "message-circle"
676
+ },
677
+ "glass": null,
678
+ "bubbles": "filled",
679
+ "colors": {
680
+ "light": {
681
+ "background": "#FAFAFA",
682
+ "surface": "#FFFFFF",
683
+ "surfaceRaised": "#FFFFFF",
684
+ "text": "#242424",
685
+ "textMuted": "#616161",
686
+ "border": "#E0E0E0",
687
+ "accent": "#0F6CBD",
688
+ "onAccent": "#FFFFFF",
689
+ "success": "#0E700E",
690
+ "warning": "#BC4B09",
691
+ "danger": "#B10E1C",
692
+ "userBubble": "#EBF3FC",
693
+ "userBubbleText": "#242424",
694
+ "agentBubble": "#FFFFFF",
695
+ "agentBubbleText": "#242424",
696
+ "scrim": "#000000"
697
+ },
698
+ "dark": {
699
+ "background": "#1F1F1F",
700
+ "surface": "#292929",
701
+ "surfaceRaised": "#292929",
702
+ "text": "#FFFFFF",
703
+ "textMuted": "#ADADAD",
704
+ "border": "#525252",
705
+ "accent": "#115EA3",
706
+ "onAccent": "#FFFFFF",
707
+ "success": "#54B054",
708
+ "warning": "#FAA06B",
709
+ "danger": "#DC626D",
710
+ "userBubble": "#082338",
711
+ "userBubbleText": "#FFFFFF",
712
+ "agentBubble": "#292929",
713
+ "agentBubbleText": "#FFFFFF",
714
+ "scrim": "#000000"
715
+ }
716
+ }
717
+ };
718
+ const P_CARBON = {
719
+ "id": "carbon",
720
+ "name": "Enterprise Grid",
721
+ "tagline": "IBM Carbon: square, precise and serious, built for trust.",
722
+ "family": "enterprise",
723
+ "inspiredBy": [
724
+ "IBM Carbon Design System v11 (carbon-design-system/carbon)"
725
+ ],
726
+ "font": {
727
+ "body": "IBM Plex Sans",
728
+ "heading": "IBM Plex Sans",
729
+ "scale": {
730
+ "caption": 12,
731
+ "body": 14,
732
+ "title": 20,
733
+ "display": 28
734
+ },
735
+ "weights": {
736
+ "body": 400,
737
+ "strong": 600,
738
+ "title": 400
739
+ },
740
+ "letterSpacing": {
741
+ "body": 0.0114,
742
+ "title": 0
743
+ },
744
+ "lineHeight": {
745
+ "body": 1.43,
746
+ "title": 1.4
747
+ }
748
+ },
749
+ "shape": {
750
+ "radius": {
751
+ "sm": 0,
752
+ "md": 2,
753
+ "lg": 4,
754
+ "xl": 8
755
+ },
756
+ "borderWidth": 1,
757
+ "launcher": "square"
758
+ },
759
+ "spacing": {
760
+ "unit": 8,
761
+ "cardPadding": 16,
762
+ "gap": 8
763
+ },
764
+ "elevation": {
765
+ "card": {
766
+ "y": 0,
767
+ "blur": 0,
768
+ "spread": 0,
769
+ "opacity": 0,
770
+ "color": "#000000"
771
+ },
772
+ "floating": {
773
+ "y": 2,
774
+ "blur": 6,
775
+ "spread": 0,
776
+ "opacity": 0.3,
777
+ "color": "#000000"
778
+ },
779
+ "hardOffset": null
780
+ },
781
+ "motion": {
782
+ "durationMs": 150,
783
+ "easing": "standard",
784
+ "bounce": 0
785
+ },
786
+ "icons": {
787
+ "style": "sharp",
788
+ "stroke": 1.5,
789
+ "launcher": "headset"
790
+ },
791
+ "glass": null,
792
+ "bubbles": "filled",
793
+ "colors": {
794
+ "light": {
795
+ "background": "#FFFFFF",
796
+ "surface": "#F4F4F4",
797
+ "surfaceRaised": "#FFFFFF",
798
+ "text": "#161616",
799
+ "textMuted": "#525252",
800
+ "border": "#C6C6C6",
801
+ "accent": "#0F62FE",
802
+ "onAccent": "#FFFFFF",
803
+ "success": "#24A148",
804
+ "warning": "#AB8800",
805
+ "danger": "#DA1E28",
806
+ "userBubble": "#E0E0E0",
807
+ "userBubbleText": "#161616",
808
+ "agentBubble": "#FFFFFF",
809
+ "agentBubbleText": "#161616",
810
+ "scrim": "#000000"
811
+ },
812
+ "dark": {
813
+ "background": "#161616",
814
+ "surface": "#262626",
815
+ "surfaceRaised": "#393939",
816
+ "text": "#F4F4F4",
817
+ "textMuted": "#C6C6C6",
818
+ "border": "#525252",
819
+ "accent": "#0F62FE",
820
+ "onAccent": "#FFFFFF",
821
+ "success": "#42BE65",
822
+ "warning": "#F1C21B",
823
+ "danger": "#FA4D56",
824
+ "userBubble": "#393939",
825
+ "userBubbleText": "#F4F4F4",
826
+ "agentBubble": "#262626",
827
+ "agentBubbleText": "#F4F4F4",
828
+ "scrim": "#000000"
829
+ }
830
+ }
831
+ };
832
+ const P_LIQUID_GLASS = {
833
+ "id": "liquid-glass",
834
+ "name": "Liquid Glass",
835
+ "tagline": "Frosted, floating panels that let the page glow through.",
836
+ "family": "modern",
837
+ "inspiredBy": [
838
+ "Apple Liquid Glass & standard materials (HIG Materials)",
839
+ "css.glass glassmorphism generator"
840
+ ],
841
+ "font": {
842
+ "body": "system",
843
+ "heading": "system",
844
+ "scale": {
845
+ "caption": 12,
846
+ "body": 17,
847
+ "title": 20,
848
+ "display": 34
849
+ },
850
+ "weights": {
851
+ "body": 400,
852
+ "strong": 600,
853
+ "title": 600
854
+ },
855
+ "letterSpacing": {
856
+ "body": -0.026,
857
+ "title": -0.023
858
+ },
859
+ "lineHeight": {
860
+ "body": 1.29,
861
+ "title": 1.25
862
+ }
863
+ },
864
+ "shape": {
865
+ "radius": {
866
+ "sm": 12,
867
+ "md": 20,
868
+ "lg": 28,
869
+ "xl": 36
870
+ },
871
+ "borderWidth": 1,
872
+ "launcher": "pill"
873
+ },
874
+ "spacing": {
875
+ "unit": 4,
876
+ "cardPadding": 16,
877
+ "gap": 12
878
+ },
879
+ "elevation": {
880
+ "card": {
881
+ "y": 2,
882
+ "blur": 12,
883
+ "spread": 0,
884
+ "opacity": 0.08,
885
+ "color": "#000000"
886
+ },
887
+ "floating": {
888
+ "y": 4,
889
+ "blur": 30,
890
+ "spread": 0,
891
+ "opacity": 0.1,
892
+ "color": "#000000"
893
+ },
894
+ "hardOffset": null
895
+ },
896
+ "motion": {
897
+ "durationMs": 500,
898
+ "easing": "spring",
899
+ "bounce": 0.15
900
+ },
901
+ "icons": {
902
+ "style": "outline",
903
+ "stroke": 1.5,
904
+ "launcher": "sparkle-filled"
905
+ },
906
+ "glass": {
907
+ "blur": 20,
908
+ "surfaceOpacity": 0.72
909
+ },
910
+ "bubbles": "filled",
911
+ "colors": {
912
+ "light": {
913
+ "background": "#F2F2F7",
914
+ "surface": "#FFFFFF",
915
+ "surfaceRaised": "#FFFFFF",
916
+ "text": "#000000",
917
+ "textMuted": "#3C3C43",
918
+ "border": "#C6C6C8",
919
+ "accent": "#6155F5",
920
+ "onAccent": "#FFFFFF",
921
+ "success": "#008932",
922
+ "warning": "#C55300",
923
+ "danger": "#FF383C",
924
+ "userBubble": "#6155F5",
925
+ "userBubbleText": "#FFFFFF",
926
+ "agentBubble": "#FFFFFF",
927
+ "agentBubbleText": "#000000",
928
+ "scrim": "#000000"
929
+ },
930
+ "dark": {
931
+ "background": "#000000",
932
+ "surface": "#1C1C1E",
933
+ "surfaceRaised": "#2C2C2E",
934
+ "text": "#FFFFFF",
935
+ "textMuted": "#EBEBF5",
936
+ "border": "#38383A",
937
+ "accent": "#6155F5",
938
+ "onAccent": "#FFFFFF",
939
+ "success": "#30D158",
940
+ "warning": "#FF9230",
941
+ "danger": "#FF4245",
942
+ "userBubble": "#6155F5",
943
+ "userBubbleText": "#FFFFFF",
944
+ "agentBubble": "#2C2C2E",
945
+ "agentBubbleText": "#FFFFFF",
946
+ "scrim": "#000000"
947
+ }
948
+ }
949
+ };
950
+ const P_SOFT_PLAYFUL = {
951
+ "id": "soft-playful",
952
+ "name": "Soft & Playful",
953
+ "tagline": "Rounded, pastel and friendly, like a cupcake shop that answers the phone.",
954
+ "family": "modern",
955
+ "inspiredBy": [
956
+ "daisyUI \"cupcake\" theme (saadeghi/daisyui)"
957
+ ],
958
+ "font": {
959
+ "body": "Nunito",
960
+ "heading": "Baloo 2",
961
+ "scale": {
962
+ "caption": 12,
963
+ "body": 16,
964
+ "title": 20,
965
+ "display": 28
966
+ },
967
+ "weights": {
968
+ "body": 400,
969
+ "strong": 700,
970
+ "title": 700
971
+ },
972
+ "letterSpacing": {
973
+ "body": 0,
974
+ "title": 0
975
+ },
976
+ "lineHeight": {
977
+ "body": 1.5,
978
+ "title": 1.25
979
+ }
980
+ },
981
+ "shape": {
982
+ "radius": {
983
+ "sm": 16,
984
+ "md": 32,
985
+ "lg": 16,
986
+ "xl": 24
987
+ },
988
+ "borderWidth": 2,
989
+ "launcher": "circle"
990
+ },
991
+ "spacing": {
992
+ "unit": 4,
993
+ "cardPadding": 24,
994
+ "gap": 8
995
+ },
996
+ "elevation": {
997
+ "card": {
998
+ "y": 1,
999
+ "blur": 3,
1000
+ "spread": 0,
1001
+ "opacity": 0.1,
1002
+ "color": "#291334"
1003
+ },
1004
+ "floating": {
1005
+ "y": 10,
1006
+ "blur": 15,
1007
+ "spread": -3,
1008
+ "opacity": 0.12,
1009
+ "color": "#291334"
1010
+ },
1011
+ "hardOffset": null
1012
+ },
1013
+ "motion": {
1014
+ "durationMs": 400,
1015
+ "easing": "spring",
1016
+ "bounce": 0.3
1017
+ },
1018
+ "icons": {
1019
+ "style": "filled",
1020
+ "stroke": 1.5,
1021
+ "launcher": "chat-dots-filled"
1022
+ },
1023
+ "glass": null,
1024
+ "bubbles": "filled",
1025
+ "colors": {
1026
+ "light": {
1027
+ "background": "#FAF7F5",
1028
+ "surface": "#EFEAE6",
1029
+ "surfaceRaised": "#FAF7F5",
1030
+ "text": "#291334",
1031
+ "textMuted": "#64536A",
1032
+ "border": "#E7E2DF",
1033
+ "accent": "#44EBD3",
1034
+ "onAccent": "#005D58",
1035
+ "success": "#009768",
1036
+ "warning": "#A87F00",
1037
+ "danger": "#FE1C55",
1038
+ "userBubble": "#44EBD3",
1039
+ "userBubbleText": "#005D58",
1040
+ "agentBubble": "#E7E2DF",
1041
+ "agentBubbleText": "#291334",
1042
+ "scrim": "#262629"
1043
+ },
1044
+ "dark": {
1045
+ "background": "#262629",
1046
+ "surface": "#303033",
1047
+ "surfaceRaised": "#3A3A3E",
1048
+ "text": "#E4E4E7",
1049
+ "textMuted": "#ABABAE",
1050
+ "border": "#47474B",
1051
+ "accent": "#44EBD3",
1052
+ "onAccent": "#005D58",
1053
+ "success": "#00B77F",
1054
+ "warning": "#E8B100",
1055
+ "danger": "#FE1C55",
1056
+ "userBubble": "#44EBD3",
1057
+ "userBubbleText": "#005D58",
1058
+ "agentBubble": "#3A3A3E",
1059
+ "agentBubbleText": "#E4E4E7",
1060
+ "scrim": "#000000"
1061
+ }
1062
+ }
1063
+ };
1064
+ const P_NEO_BRUTALISM = {
1065
+ "id": "neo-brutalism",
1066
+ "name": "Neo-Brutalist",
1067
+ "tagline": "Thick black outlines, hard shadows, loud colour. Impossible to ignore.",
1068
+ "family": "expressive",
1069
+ "inspiredBy": [
1070
+ "neobrutalism.dev (ekmas/neobrutalism-components)"
1071
+ ],
1072
+ "font": {
1073
+ "body": "DM Sans",
1074
+ "heading": "DM Sans",
1075
+ "scale": {
1076
+ "caption": 12,
1077
+ "body": 16,
1078
+ "title": 20,
1079
+ "display": 32
1080
+ },
1081
+ "weights": {
1082
+ "body": 500,
1083
+ "strong": 700,
1084
+ "title": 700
1085
+ },
1086
+ "letterSpacing": {
1087
+ "body": 0,
1088
+ "title": 0
1089
+ },
1090
+ "lineHeight": {
1091
+ "body": 1.5,
1092
+ "title": 1.2
1093
+ }
1094
+ },
1095
+ "shape": {
1096
+ "radius": {
1097
+ "sm": 5,
1098
+ "md": 5,
1099
+ "lg": 5,
1100
+ "xl": 5
1101
+ },
1102
+ "borderWidth": 2,
1103
+ "launcher": "rounded-square"
1104
+ },
1105
+ "spacing": {
1106
+ "unit": 4,
1107
+ "cardPadding": 24,
1108
+ "gap": 24
1109
+ },
1110
+ "elevation": {
1111
+ "card": {
1112
+ "y": 0,
1113
+ "blur": 0,
1114
+ "spread": 0,
1115
+ "opacity": 0,
1116
+ "color": "#000000"
1117
+ },
1118
+ "floating": {
1119
+ "y": 0,
1120
+ "blur": 0,
1121
+ "spread": 0,
1122
+ "opacity": 0,
1123
+ "color": "#000000"
1124
+ },
1125
+ "hardOffset": {
1126
+ "x": 4,
1127
+ "y": 4
1128
+ }
1129
+ },
1130
+ "motion": {
1131
+ "durationMs": 150,
1132
+ "easing": "standard",
1133
+ "bounce": 0
1134
+ },
1135
+ "icons": {
1136
+ "style": "outline",
1137
+ "stroke": 2,
1138
+ "launcher": "zap"
1139
+ },
1140
+ "glass": null,
1141
+ "bubbles": "filled",
1142
+ "colors": {
1143
+ "light": {
1144
+ "background": "#DCEBFE",
1145
+ "surface": "#FFFFFF",
1146
+ "surfaceRaised": "#FFFFFF",
1147
+ "text": "#000000",
1148
+ "textMuted": "#383838",
1149
+ "border": "#000000",
1150
+ "accent": "#5294FF",
1151
+ "onAccent": "#000000",
1152
+ "success": "#009951",
1153
+ "warning": "#A08100",
1154
+ "danger": "#F24045",
1155
+ "userBubble": "#5294FF",
1156
+ "userBubbleText": "#000000",
1157
+ "agentBubble": "#FFFFFF",
1158
+ "agentBubbleText": "#000000",
1159
+ "scrim": "#000000"
1160
+ },
1161
+ "dark": {
1162
+ "background": "#20294B",
1163
+ "surface": "#1F1F1F",
1164
+ "surfaceRaised": "#1F1F1F",
1165
+ "text": "#E6E6E6",
1166
+ "textMuted": "#BABCC4",
1167
+ "border": "#000000",
1168
+ "accent": "#5294FF",
1169
+ "onAccent": "#000000",
1170
+ "success": "#05E17A",
1171
+ "warning": "#FACC00",
1172
+ "danger": "#FF4D50",
1173
+ "userBubble": "#5294FF",
1174
+ "userBubbleText": "#000000",
1175
+ "agentBubble": "#1F1F1F",
1176
+ "agentBubbleText": "#E6E6E6",
1177
+ "scrim": "#000000"
1178
+ }
1179
+ }
1180
+ };
1181
+ const P_EDITORIAL = {
1182
+ "id": "editorial",
1183
+ "name": "Editorial",
1184
+ "tagline": "Reads like a well-set magazine: serif type, warm paper, ink-red accents.",
1185
+ "family": "expressive",
1186
+ "inspiredBy": [
1187
+ "Radix Themes (radix-ui/themes)",
1188
+ "Radix Colors sand + ruby (radix-ui/colors)"
1189
+ ],
1190
+ "font": {
1191
+ "body": "Source Serif 4",
1192
+ "heading": "Newsreader",
1193
+ "scale": {
1194
+ "caption": 12,
1195
+ "body": 16,
1196
+ "title": 24,
1197
+ "display": 35
1198
+ },
1199
+ "weights": {
1200
+ "body": 400,
1201
+ "strong": 700,
1202
+ "title": 700
1203
+ },
1204
+ "letterSpacing": {
1205
+ "body": 0,
1206
+ "title": -0.00625
1207
+ },
1208
+ "lineHeight": {
1209
+ "body": 1.5,
1210
+ "title": 1.25
1211
+ }
1212
+ },
1213
+ "shape": {
1214
+ "radius": {
1215
+ "sm": 4,
1216
+ "md": 6,
1217
+ "lg": 8,
1218
+ "xl": 12
1219
+ },
1220
+ "borderWidth": 1,
1221
+ "launcher": "circle"
1222
+ },
1223
+ "spacing": {
1224
+ "unit": 4,
1225
+ "cardPadding": 24,
1226
+ "gap": 16
1227
+ },
1228
+ "elevation": {
1229
+ "card": {
1230
+ "y": 1,
1231
+ "blur": 3,
1232
+ "spread": 0,
1233
+ "opacity": 0.05,
1234
+ "color": "#000000"
1235
+ },
1236
+ "floating": {
1237
+ "y": 12,
1238
+ "blur": 60,
1239
+ "spread": 0,
1240
+ "opacity": 0.15,
1241
+ "color": "#000000"
1242
+ },
1243
+ "hardOffset": null
1244
+ },
1245
+ "motion": {
1246
+ "durationMs": 120,
1247
+ "easing": "standard",
1248
+ "bounce": 0
1249
+ },
1250
+ "icons": {
1251
+ "style": "outline",
1252
+ "stroke": 1.5,
1253
+ "launcher": "message-circle"
1254
+ },
1255
+ "glass": null,
1256
+ "bubbles": "minimal",
1257
+ "colors": {
1258
+ "light": {
1259
+ "background": "#FDFDFC",
1260
+ "surface": "#F9F9F8",
1261
+ "surfaceRaised": "#FFFFFF",
1262
+ "text": "#21201C",
1263
+ "textMuted": "#63635E",
1264
+ "border": "#DAD9D6",
1265
+ "accent": "#CA244D",
1266
+ "onAccent": "#FFFFFF",
1267
+ "success": "#2A7E3B",
1268
+ "warning": "#AB6400",
1269
+ "danger": "#CE2C31",
1270
+ "userBubble": "#FEEAED",
1271
+ "userBubbleText": "#64172B",
1272
+ "agentBubble": "#F1F0EF",
1273
+ "agentBubbleText": "#21201C",
1274
+ "scrim": "#000000"
1275
+ },
1276
+ "dark": {
1277
+ "background": "#111110",
1278
+ "surface": "#191918",
1279
+ "surfaceRaised": "#222221",
1280
+ "text": "#EEEEEC",
1281
+ "textMuted": "#B5B3AD",
1282
+ "border": "#3B3A37",
1283
+ "accent": "#CA244D",
1284
+ "onAccent": "#FFFFFF",
1285
+ "success": "#71D083",
1286
+ "warning": "#FFCA16",
1287
+ "danger": "#FF9592",
1288
+ "userBubble": "#3A141E",
1289
+ "userBubbleText": "#FED2E1",
1290
+ "agentBubble": "#222221",
1291
+ "agentBubbleText": "#EEEEEC",
1292
+ "scrim": "#000000"
1293
+ }
1294
+ }
1295
+ };
1296
+ const P_LUXURY = {
1297
+ "id": "luxury",
1298
+ "name": "Black & Gold",
1299
+ "tagline": "Gold on near-black with high-contrast serif titles. Quietly expensive.",
1300
+ "family": "expressive",
1301
+ "inspiredBy": [
1302
+ "daisyUI \"luxury\" theme (saadeghi/daisyui)"
1303
+ ],
1304
+ "font": {
1305
+ "body": "Poppins",
1306
+ "heading": "Playfair Display",
1307
+ "scale": {
1308
+ "caption": 12,
1309
+ "body": 15,
1310
+ "title": 22,
1311
+ "display": 32
1312
+ },
1313
+ "weights": {
1314
+ "body": 400,
1315
+ "strong": 500,
1316
+ "title": 600
1317
+ },
1318
+ "letterSpacing": {
1319
+ "body": 0.01,
1320
+ "title": 0
1321
+ },
1322
+ "lineHeight": {
1323
+ "body": 1.6,
1324
+ "title": 1.2
1325
+ }
1326
+ },
1327
+ "shape": {
1328
+ "radius": {
1329
+ "sm": 16,
1330
+ "md": 8,
1331
+ "lg": 16,
1332
+ "xl": 24
1333
+ },
1334
+ "borderWidth": 1,
1335
+ "launcher": "circle"
1336
+ },
1337
+ "spacing": {
1338
+ "unit": 4,
1339
+ "cardPadding": 24,
1340
+ "gap": 8
1341
+ },
1342
+ "elevation": {
1343
+ "card": {
1344
+ "y": 1,
1345
+ "blur": 3,
1346
+ "spread": 0,
1347
+ "opacity": 0.3,
1348
+ "color": "#000000"
1349
+ },
1350
+ "floating": {
1351
+ "y": 12,
1352
+ "blur": 32,
1353
+ "spread": -4,
1354
+ "opacity": 0.45,
1355
+ "color": "#000000"
1356
+ },
1357
+ "hardOffset": null
1358
+ },
1359
+ "motion": {
1360
+ "durationMs": 300,
1361
+ "easing": "emphasized",
1362
+ "bounce": 0
1363
+ },
1364
+ "icons": {
1365
+ "style": "outline",
1366
+ "stroke": 1.25,
1367
+ "launcher": "star"
1368
+ },
1369
+ "glass": null,
1370
+ "bubbles": "filled",
1371
+ "colors": {
1372
+ "light": {
1373
+ "background": "#FAF7EE",
1374
+ "surface": "#F3EEE0",
1375
+ "surfaceRaised": "#FFFFFF",
1376
+ "text": "#09090B",
1377
+ "textMuted": "#565554",
1378
+ "border": "#E2CEB1",
1379
+ "accent": "#DCA54D",
1380
+ "onAccent": "#09090B",
1381
+ "success": "#5A9700",
1382
+ "warning": "#968900",
1383
+ "danger": "#E6585A",
1384
+ "userBubble": "#DCA54D",
1385
+ "userBubbleText": "#09090B",
1386
+ "agentBubble": "#F3EEE0",
1387
+ "agentBubbleText": "#09090B",
1388
+ "scrim": "#000000"
1389
+ },
1390
+ "dark": {
1391
+ "background": "#09090B",
1392
+ "surface": "#171618",
1393
+ "surfaceRaised": "#1E1D1F",
1394
+ "text": "#FFE7A4",
1395
+ "textMuted": "#A2936A",
1396
+ "border": "#331800",
1397
+ "accent": "#DCA54D",
1398
+ "onAccent": "#09090B",
1399
+ "success": "#87D03A",
1400
+ "warning": "#E2D563",
1401
+ "danger": "#FF6F6F",
1402
+ "userBubble": "#DCA54D",
1403
+ "userBubbleText": "#09090B",
1404
+ "agentBubble": "#1E1D1F",
1405
+ "agentBubbleText": "#DCA54D",
1406
+ "scrim": "#000000"
1407
+ }
1408
+ }
1409
+ };
1410
+ const P_RETRO_TERMINAL = {
1411
+ "id": "retro-terminal",
1412
+ "name": "Retro Terminal",
1413
+ "tagline": "Green phosphor on black, monospace everything. For builders and tinkerers.",
1414
+ "family": "expressive",
1415
+ "inspiredBy": [
1416
+ "terminal.css (Gioni06/terminal.css)",
1417
+ "cool-retro-term profiles (Swordfish90/cool-retro-term)"
1418
+ ],
1419
+ "font": {
1420
+ "body": "JetBrains Mono",
1421
+ "heading": "JetBrains Mono",
1422
+ "scale": {
1423
+ "caption": 12,
1424
+ "body": 15,
1425
+ "title": 15,
1426
+ "display": 20
1427
+ },
1428
+ "weights": {
1429
+ "body": 400,
1430
+ "strong": 700,
1431
+ "title": 600
1432
+ },
1433
+ "letterSpacing": {
1434
+ "body": 0,
1435
+ "title": 0
1436
+ },
1437
+ "lineHeight": {
1438
+ "body": 1.4,
1439
+ "title": 1.4
1440
+ }
1441
+ },
1442
+ "shape": {
1443
+ "radius": {
1444
+ "sm": 0,
1445
+ "md": 0,
1446
+ "lg": 0,
1447
+ "xl": 0
1448
+ },
1449
+ "borderWidth": 1,
1450
+ "launcher": "square"
1451
+ },
1452
+ "spacing": {
1453
+ "unit": 10,
1454
+ "cardPadding": 10,
1455
+ "gap": 10
1456
+ },
1457
+ "elevation": {
1458
+ "card": {
1459
+ "y": 0,
1460
+ "blur": 0,
1461
+ "spread": 0,
1462
+ "opacity": 0,
1463
+ "color": "#000000"
1464
+ },
1465
+ "floating": {
1466
+ "y": 0,
1467
+ "blur": 0,
1468
+ "spread": 0,
1469
+ "opacity": 0,
1470
+ "color": "#000000"
1471
+ },
1472
+ "hardOffset": null
1473
+ },
1474
+ "motion": {
1475
+ "durationMs": 0,
1476
+ "easing": "none",
1477
+ "bounce": 0
1478
+ },
1479
+ "icons": {
1480
+ "style": "sharp",
1481
+ "stroke": 2,
1482
+ "launcher": "bot"
1483
+ },
1484
+ "glass": null,
1485
+ "bubbles": "outline",
1486
+ "colors": {
1487
+ "light": {
1488
+ "background": "#F2F2EC",
1489
+ "surface": "#E7E7E1",
1490
+ "surfaceRaised": "#F2F2EC",
1491
+ "text": "#101010",
1492
+ "textMuted": "#65686B",
1493
+ "border": "#727578",
1494
+ "accent": "#007E3D",
1495
+ "onAccent": "#F2F2EC",
1496
+ "success": "#00964A",
1497
+ "warning": "#CB6600",
1498
+ "danger": "#D20962",
1499
+ "userBubble": "#007E3D",
1500
+ "userBubbleText": "#F2F2EC",
1501
+ "agentBubble": "#F2F2EC",
1502
+ "agentBubbleText": "#101010",
1503
+ "scrim": "#000000"
1504
+ },
1505
+ "dark": {
1506
+ "background": "#000000",
1507
+ "surface": "#010C06",
1508
+ "surfaceRaised": "#01140A",
1509
+ "text": "#0CCC68",
1510
+ "textMuted": "#088B47",
1511
+ "border": "#055C2F",
1512
+ "accent": "#0CCC68",
1513
+ "onAccent": "#000000",
1514
+ "success": "#0CCC68",
1515
+ "warning": "#FF8100",
1516
+ "danger": "#FF3C74",
1517
+ "userBubble": "#0CCC68",
1518
+ "userBubbleText": "#000000",
1519
+ "agentBubble": "#000000",
1520
+ "agentBubbleText": "#0CCC68",
1521
+ "scrim": "#000000"
1522
+ }
1523
+ }
1524
+ };
1525
+ const P_WARM_EARTHY = {
1526
+ "id": "warm-earthy",
1527
+ "name": "Warm & Earthy",
1528
+ "tagline": "Caramel, cream and coffee tones with soft serif titles. Human and grounded.",
1529
+ "family": "expressive",
1530
+ "inspiredBy": [
1531
+ "daisyUI \"caramellatte\" (light) and \"coffee\" (dark) themes (saadeghi/daisyui)"
1532
+ ],
1533
+ "font": {
1534
+ "body": "Mukta",
1535
+ "heading": "Fraunces",
1536
+ "scale": {
1537
+ "caption": 12,
1538
+ "body": 16,
1539
+ "title": 22,
1540
+ "display": 30
1541
+ },
1542
+ "weights": {
1543
+ "body": 400,
1544
+ "strong": 600,
1545
+ "title": 600
1546
+ },
1547
+ "letterSpacing": {
1548
+ "body": 0,
1549
+ "title": -0.01
1550
+ },
1551
+ "lineHeight": {
1552
+ "body": 1.5,
1553
+ "title": 1.2
1554
+ }
1555
+ },
1556
+ "shape": {
1557
+ "radius": {
1558
+ "sm": 32,
1559
+ "md": 8,
1560
+ "lg": 16,
1561
+ "xl": 24
1562
+ },
1563
+ "borderWidth": 2,
1564
+ "launcher": "squircle"
1565
+ },
1566
+ "spacing": {
1567
+ "unit": 4,
1568
+ "cardPadding": 24,
1569
+ "gap": 8
1570
+ },
1571
+ "elevation": {
1572
+ "card": {
1573
+ "y": 1,
1574
+ "blur": 3,
1575
+ "spread": 0,
1576
+ "opacity": 0.12,
1577
+ "color": "#7C2808"
1578
+ },
1579
+ "floating": {
1580
+ "y": 12,
1581
+ "blur": 32,
1582
+ "spread": -8,
1583
+ "opacity": 0.2,
1584
+ "color": "#7C2808"
1585
+ },
1586
+ "hardOffset": null
1587
+ },
1588
+ "motion": {
1589
+ "durationMs": 200,
1590
+ "easing": "standard",
1591
+ "bounce": 0
1592
+ },
1593
+ "icons": {
1594
+ "style": "duotone",
1595
+ "stroke": 1.5,
1596
+ "launcher": "orbit"
1597
+ },
1598
+ "glass": null,
1599
+ "bubbles": "filled",
1600
+ "colors": {
1601
+ "light": {
1602
+ "background": "#FFF7ED",
1603
+ "surface": "#FEECD3",
1604
+ "surfaceRaised": "#FFF7ED",
1605
+ "text": "#7C2808",
1606
+ "textMuted": "#99563A",
1607
+ "border": "#FFD6A7",
1608
+ "accent": "#8C3F27",
1609
+ "onAccent": "#FFD6A7",
1610
+ "success": "#005F45",
1611
+ "warning": "#AD8100",
1612
+ "danger": "#ED5157",
1613
+ "userBubble": "#8C3F27",
1614
+ "userBubbleText": "#FFD6A7",
1615
+ "agentBubble": "#FFD6A7",
1616
+ "agentBubbleText": "#7C2808",
1617
+ "scrim": "#000000"
1618
+ },
1619
+ "dark": {
1620
+ "background": "#261B25",
1621
+ "surface": "#1E151D",
1622
+ "surfaceRaised": "#30252E",
1623
+ "text": "#C59F61",
1624
+ "textMuted": "#A58555",
1625
+ "border": "#433330",
1626
+ "accent": "#DB924C",
1627
+ "onAccent": "#110802",
1628
+ "success": "#9DB787",
1629
+ "warning": "#FFD260",
1630
+ "danger": "#FC9581",
1631
+ "userBubble": "#DB924C",
1632
+ "userBubbleText": "#110802",
1633
+ "agentBubble": "#120A11",
1634
+ "agentBubbleText": "#C59F61",
1635
+ "scrim": "#000000"
1636
+ }
1637
+ }
1638
+ };
1639
+ const P_HIGH_CONTRAST = {
1640
+ "id": "high-contrast",
1641
+ "name": "High Contrast",
1642
+ "tagline": "Big type, strong edges, no fades. Easy to read for everyone, everywhere.",
1643
+ "family": "enterprise",
1644
+ "inspiredBy": [
1645
+ "GitHub Primer light/dark high-contrast themes (primer/primitives)",
1646
+ "GOV.UK Design System (alphagov/govuk-frontend)"
1647
+ ],
1648
+ "font": {
1649
+ "body": "Atkinson Hyperlegible Next",
1650
+ "heading": "Atkinson Hyperlegible Next",
1651
+ "scale": {
1652
+ "caption": 16,
1653
+ "body": 19,
1654
+ "title": 24,
1655
+ "display": 36
1656
+ },
1657
+ "weights": {
1658
+ "body": 400,
1659
+ "strong": 700,
1660
+ "title": 700
1661
+ },
1662
+ "letterSpacing": {
1663
+ "body": 0,
1664
+ "title": 0
1665
+ },
1666
+ "lineHeight": {
1667
+ "body": 1.32,
1668
+ "title": 1.25
1669
+ }
1670
+ },
1671
+ "shape": {
1672
+ "radius": {
1673
+ "sm": 0,
1674
+ "md": 0,
1675
+ "lg": 0,
1676
+ "xl": 0
1677
+ },
1678
+ "borderWidth": 2,
1679
+ "launcher": "square"
1680
+ },
1681
+ "spacing": {
1682
+ "unit": 5,
1683
+ "cardPadding": 20,
1684
+ "gap": 15
1685
+ },
1686
+ "elevation": {
1687
+ "card": {
1688
+ "y": 1,
1689
+ "blur": 2,
1690
+ "spread": 0,
1691
+ "opacity": 0.03,
1692
+ "color": "#010409"
1693
+ },
1694
+ "floating": {
1695
+ "y": 6,
1696
+ "blur": 18,
1697
+ "spread": 0,
1698
+ "opacity": 0.12,
1699
+ "color": "#25292E"
1700
+ },
1701
+ "hardOffset": null
1702
+ },
1703
+ "motion": {
1704
+ "durationMs": 0,
1705
+ "easing": "none",
1706
+ "bounce": 0
1707
+ },
1708
+ "icons": {
1709
+ "style": "filled",
1710
+ "stroke": 2,
1711
+ "launcher": "microphone"
1712
+ },
1713
+ "glass": null,
1714
+ "bubbles": "filled",
1715
+ "colors": {
1716
+ "light": {
1717
+ "background": "#FFFFFF",
1718
+ "surface": "#E6EAEF",
1719
+ "surfaceRaised": "#FFFFFF",
1720
+ "text": "#010409",
1721
+ "textMuted": "#454C54",
1722
+ "border": "#454C54",
1723
+ "accent": "#0349B4",
1724
+ "onAccent": "#FFFFFF",
1725
+ "success": "#04591F",
1726
+ "warning": "#603700",
1727
+ "danger": "#960D1E",
1728
+ "userBubble": "#0349B4",
1729
+ "userBubbleText": "#FFFFFF",
1730
+ "agentBubble": "#E6EAEF",
1731
+ "agentBubbleText": "#010409",
1732
+ "scrim": "#393F46"
1733
+ },
1734
+ "dark": {
1735
+ "background": "#010409",
1736
+ "surface": "#151B23",
1737
+ "surfaceRaised": "#212830",
1738
+ "text": "#FFFFFF",
1739
+ "textMuted": "#B7BDC8",
1740
+ "border": "#B7BDC8",
1741
+ "accent": "#194FB1",
1742
+ "onAccent": "#FFFFFF",
1743
+ "success": "#2BD853",
1744
+ "warning": "#F0B72F",
1745
+ "danger": "#FF9492",
1746
+ "userBubble": "#194FB1",
1747
+ "userBubbleText": "#FFFFFF",
1748
+ "agentBubble": "#151B23",
1749
+ "agentBubbleText": "#FFFFFF",
1750
+ "scrim": "#D1D7E0"
1751
+ }
1752
+ }
1753
+ };
1754
+ export const DESIGN_PRESETS = [GRAINE_CLASSIC, P_CHAT_NEUTRAL, P_MATERIAL_3, P_IOS_NATIVE, P_MINIMAL_MONO, P_FLUENT_2, P_CARBON, P_LIQUID_GLASS, P_SOFT_PLAYFUL, P_NEO_BRUTALISM, P_EDITORIAL, P_LUXURY, P_RETRO_TERMINAL, P_WARM_EARTHY, P_HIGH_CONTRAST];
1755
+ export const DEFAULT_DESIGN_PRESET = "graine-classic";
1756
+ export function designPreset(id) {
1757
+ const want = String(id ?? "").trim().toLowerCase();
1758
+ return DESIGN_PRESETS.find((p) => p.id === want) ?? DESIGN_PRESETS[0];
1759
+ }
1760
+ function pick(v, allowed) {
1761
+ const s = String(v ?? "").trim().toLowerCase();
1762
+ return allowed.includes(s) ? s : undefined;
1763
+ }
1764
+ function fontFamily(v) {
1765
+ const s = String(v ?? "").trim();
1766
+ if (!s)
1767
+ return undefined;
1768
+ const hit = DESIGN_FONTS.find((f) => f.family.toLowerCase() === s.toLowerCase());
1769
+ return hit ? hit.family : undefined;
1770
+ }
1771
+ export function normaliseDesignConfig(raw) {
1772
+ const r = (raw && typeof raw === "object" ? raw : {});
1773
+ const out = { preset: designPreset(r.preset).id };
1774
+ const accent = normaliseHex(r.accent);
1775
+ if (accent)
1776
+ out.accent = accent;
1777
+ const scheme = pick(r.scheme, DESIGN_SCHEMES);
1778
+ if (scheme)
1779
+ out.scheme = scheme;
1780
+ if (typeof r.roundness === "number" && Number.isFinite(r.roundness)) {
1781
+ out.roundness = Math.round(Math.max(0, Math.min(2, r.roundness)) * 100) / 100;
1782
+ }
1783
+ const font = fontFamily(r.font);
1784
+ if (font)
1785
+ out.font = font;
1786
+ const heading = fontFamily(r.headingFont);
1787
+ if (heading)
1788
+ out.headingFont = heading;
1789
+ const density = pick(r.density, DESIGN_DENSITIES);
1790
+ if (density)
1791
+ out.density = density;
1792
+ const shadow = pick(r.shadow, DESIGN_SHADOWS);
1793
+ if (shadow)
1794
+ out.shadow = shadow;
1795
+ const motion = pick(r.motion, DESIGN_MOTIONS);
1796
+ if (motion)
1797
+ out.motion = motion;
1798
+ const neutral = pick(r.neutral, DESIGN_NEUTRALS);
1799
+ if (neutral)
1800
+ out.neutral = neutral;
1801
+ if (typeof r.glass === "boolean")
1802
+ out.glass = r.glass;
1803
+ const bubbles = pick(r.bubbles, DESIGN_BUBBLES);
1804
+ if (bubbles)
1805
+ out.bubbles = bubbles;
1806
+ const icon = String(r.launcherIcon ?? "").trim().toLowerCase();
1807
+ if (icon === "logo" || LAUNCHER_ICONS.some((i) => i.id === icon))
1808
+ out.launcherIcon = icon;
1809
+ const shape = pick(r.launcherShape, LAUNCHER_SHAPES);
1810
+ if (shape)
1811
+ out.launcherShape = shape;
1812
+ const iconStyle = pick(r.iconStyle, ICON_STYLES);
1813
+ if (iconStyle)
1814
+ out.iconStyle = iconStyle;
1815
+ return out;
1816
+ }
1817
+ export function designFromAppearance(appearance) {
1818
+ const a = (appearance && typeof appearance === "object" ? appearance : {});
1819
+ if (a.design && typeof a.design === "object")
1820
+ return normaliseDesignConfig(a.design);
1821
+ const legacy = { preset: DEFAULT_DESIGN_PRESET };
1822
+ if (normaliseHex(a.accent))
1823
+ legacy.accent = a.accent;
1824
+ const scheme = pick(a.theme, DESIGN_SCHEMES);
1825
+ if (scheme)
1826
+ legacy.scheme = scheme;
1827
+ const radius = typeof a.radius === "number" ? a.radius : Number(a.radius);
1828
+ if (Number.isFinite(radius) && radius >= 0)
1829
+ legacy.roundness = radius / GRAINE_CLASSIC.shape.radius.lg;
1830
+ return normaliseDesignConfig(legacy);
1831
+ }
1832
+ export function legacyAppearanceFor(config) {
1833
+ const tokens = resolveTokens(config, "light");
1834
+ return { accent: tokens.colors.accent, radius: tokens.shape.radius.lg, theme: config.scheme ?? "auto" };
1835
+ }
1836
+ const DENSITY_SCALE = { compact: 0.8, comfortable: 1, spacious: 1.25 };
1837
+ export const ON_ACCENT_MIN = 3;
1838
+ const SHADOWS = {
1839
+ none: { card: null, floating: null },
1840
+ soft: {
1841
+ card: { y: 1, blur: 4, spread: 0, opacity: 0.06, color: "#000000" },
1842
+ floating: { y: 6, blur: 18, spread: 0, opacity: 0.12, color: "#000000" },
1843
+ },
1844
+ medium: {
1845
+ card: { y: 2, blur: 10, spread: 0, opacity: 0.1, color: "#000000" },
1846
+ floating: { y: 10, blur: 28, spread: 0, opacity: 0.18, color: "#000000" },
1847
+ },
1848
+ strong: {
1849
+ card: { y: 4, blur: 16, spread: 0, opacity: 0.16, color: "#000000" },
1850
+ floating: { y: 16, blur: 40, spread: -4, opacity: 0.28, color: "#000000" },
1851
+ },
1852
+ };
1853
+ const MOTIONS = {
1854
+ none: { durationMs: 0, easing: "none", bounce: 0 },
1855
+ subtle: { durationMs: 150, easing: "standard", bounce: 0 },
1856
+ lively: { durationMs: 320, easing: "spring", bounce: 0.3 },
1857
+ };
1858
+ const NEUTRAL_TINT = {
1859
+ cool: "#3B6FD9",
1860
+ neutral: "#808080",
1861
+ warm: "#D9822B",
1862
+ };
1863
+ function tintNeutrals(c, neutral, scheme) {
1864
+ if (neutral === "preset")
1865
+ return c;
1866
+ const tint = NEUTRAL_TINT[neutral];
1867
+ const t = neutral === "neutral" ? 0.0 : scheme === "light" ? 0.05 : 0.07;
1868
+ const grey = (hex) => {
1869
+ if (neutral !== "neutral")
1870
+ return mixHex(hex, tint, t);
1871
+ const [r, g, b] = rgb(hex);
1872
+ const y = Math.round(0.2126 * r + 0.7152 * g + 0.0722 * b);
1873
+ return toHex(y, y, y);
1874
+ };
1875
+ return {
1876
+ ...c,
1877
+ background: grey(c.background),
1878
+ surface: grey(c.surface),
1879
+ surfaceRaised: grey(c.surfaceRaised),
1880
+ border: grey(c.border),
1881
+ agentBubble: grey(c.agentBubble),
1882
+ };
1883
+ }
1884
+ export function resolveTokens(configInput, scheme) {
1885
+ const config = normaliseDesignConfig(configInput);
1886
+ const p = designPreset(config.preset);
1887
+ const base = p.colors[scheme];
1888
+ let c = tintNeutrals({ ...base }, config.neutral ?? "preset", scheme);
1889
+ if (config.accent) {
1890
+ const followsAccent = base.userBubble.toUpperCase() === base.accent.toUpperCase();
1891
+ c = { ...c, accent: config.accent };
1892
+ if (followsAccent)
1893
+ c.userBubble = config.accent;
1894
+ }
1895
+ const bubbles = config.bubbles ?? p.bubbles;
1896
+ const colors = {
1897
+ ...c,
1898
+ text: ensureContrast(c.text, c.background),
1899
+ textMuted: ensureContrast(c.textMuted, c.surface, 4.5),
1900
+ onAccent: contrastRatio(c.onAccent, c.accent) >= ON_ACCENT_MIN ? c.onAccent : readableOn(c.accent, [c.onAccent, "#FFFFFF", "#101318"]),
1901
+ userBubbleText: contrastRatio(c.userBubbleText, c.userBubble) >= ON_ACCENT_MIN ? c.userBubbleText : readableOn(c.userBubble, [c.userBubbleText, "#FFFFFF", "#101318"]),
1902
+ agentBubbleText: ensureContrast(c.agentBubbleText, c.agentBubble),
1903
+ accentSoft: mixHex(c.surface, c.accent, scheme === "light" ? 0.12 : 0.22),
1904
+ };
1905
+ const k = config.roundness ?? 1;
1906
+ const r = (v) => Math.round(Math.min(48, v * k));
1907
+ const radius = { sm: r(p.shape.radius.sm), md: r(p.shape.radius.md), lg: r(p.shape.radius.lg), xl: r(p.shape.radius.xl) };
1908
+ const d = DENSITY_SCALE[config.density ?? "comfortable"];
1909
+ const spacing = {
1910
+ unit: p.spacing.unit,
1911
+ cardPadding: Math.round(p.spacing.cardPadding * d),
1912
+ gap: Math.round(p.spacing.gap * d),
1913
+ };
1914
+ const shadow = config.shadow ?? "preset";
1915
+ let elevation;
1916
+ let borderWidth = p.shape.borderWidth;
1917
+ if (shadow === "preset")
1918
+ elevation = p.elevation;
1919
+ else if (shadow === "hard") {
1920
+ elevation = { card: null, floating: null, hardOffset: { x: 4, y: 4 } };
1921
+ borderWidth = Math.max(2, borderWidth);
1922
+ }
1923
+ else
1924
+ elevation = { ...SHADOWS[shadow], hardOffset: null };
1925
+ if (scheme === "dark" && elevation.card)
1926
+ elevation = { ...elevation, card: { ...elevation.card, opacity: Math.min(0.5, elevation.card.opacity * 2) } };
1927
+ const motion = !config.motion || config.motion === "preset" ? p.motion : MOTIONS[config.motion];
1928
+ const glass = config.glass === false ? null : config.glass === true ? (p.glass ?? { blur: 20, surfaceOpacity: 0.72 }) : p.glass;
1929
+ const iconId = config.launcherIcon ?? p.icons.launcher;
1930
+ const glyph = iconId === "logo" ? null : LAUNCHER_ICONS.find((i) => i.id === iconId) ?? LAUNCHER_ICONS[0];
1931
+ return {
1932
+ v: 1,
1933
+ preset: p.id,
1934
+ scheme,
1935
+ colors,
1936
+ font: {
1937
+ ...p.font,
1938
+ body: config.font ?? p.font.body,
1939
+ heading: config.headingFont ?? config.font ?? p.font.heading,
1940
+ },
1941
+ shape: { radius, borderWidth, launcher: config.launcherShape ?? p.shape.launcher },
1942
+ spacing,
1943
+ elevation,
1944
+ motion,
1945
+ icons: {
1946
+ style: config.iconStyle ?? p.icons.style,
1947
+ stroke: p.icons.stroke,
1948
+ launcher: glyph ? { id: glyph.id, path: glyph.path, stroke: glyph.stroke } : null,
1949
+ },
1950
+ glass,
1951
+ bubbles,
1952
+ };
1953
+ }
1954
+ export function resolveDesign(configInput) {
1955
+ const config = normaliseDesignConfig(configInput);
1956
+ return { config, light: resolveTokens(config, "light"), dark: resolveTokens(config, "dark") };
1957
+ }
1958
+ export function pickTokens(design, deviceScheme) {
1959
+ const s = design.config.scheme ?? "auto";
1960
+ if (s === "light")
1961
+ return design.light;
1962
+ if (s === "dark")
1963
+ return design.dark;
1964
+ return deviceScheme === "dark" ? design.dark : design.light;
1965
+ }
1966
+ export const GLYPH_ASSET_VERSION = 2;
1967
+ export function glyphParams(raw) {
1968
+ const id = String(raw.id ?? "").trim().toLowerCase();
1969
+ const color = normaliseHex(raw.color) ?? "#FFFFFF";
1970
+ const size = Math.round(Math.max(16, Math.min(512, Number(raw.size) || 96)));
1971
+ const stroke = Math.max(0.5, Math.min(4, Number(raw.stroke) || 2));
1972
+ return { id, color, size, stroke: Math.round(stroke * 4) / 4 };
1973
+ }
1974
+ export function glyphUrl(origin, id, color, sizePx, stroke = 2) {
1975
+ const p = glyphParams({ id, color, size: sizePx, stroke });
1976
+ const base = String(origin || "https://www.graine.ai").replace(/\/+$/, "");
1977
+ return `${base}/api/embed/glyph?id=${encodeURIComponent(p.id)}&c=${p.color.slice(1)}&s=${p.size}&w=${p.stroke}&v=${GLYPH_ASSET_VERSION}`;
1978
+ }
1979
+ export function encodeDesignParam(config) {
1980
+ const json = JSON.stringify(normaliseDesignConfig(config));
1981
+ const b64 = typeof btoa === "function"
1982
+ ? btoa(unescape(encodeURIComponent(json)))
1983
+ : globalThis.Buffer.from(json, "utf8").toString("base64");
1984
+ return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
1985
+ }
1986
+ export function decodeDesignParam(param) {
1987
+ if (!param)
1988
+ return null;
1989
+ try {
1990
+ const b64 = param.replace(/-/g, "+").replace(/_/g, "/");
1991
+ const json = typeof atob === "function"
1992
+ ? decodeURIComponent(escape(atob(b64)))
1993
+ : globalThis.Buffer.from(b64, "base64").toString("utf8");
1994
+ return normaliseDesignConfig(JSON.parse(json));
1995
+ }
1996
+ catch {
1997
+ return null;
1998
+ }
1999
+ }