@sero-ai/ui 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/.turbo/turbo-build.log +386 -378
  2. package/.turbo/turbo-test.log +12 -11
  3. package/CHANGELOG.md +2 -2
  4. package/dist/components/ai-elements/conversation-virtual.cjs +115 -0
  5. package/dist/components/ai-elements/conversation-virtual.d.cts +30 -0
  6. package/dist/components/ai-elements/conversation-virtual.d.ts +30 -0
  7. package/dist/components/ai-elements/conversation-virtual.js +115 -0
  8. package/dist/components/model-selection/available-model-picker.cjs +21 -77
  9. package/dist/components/model-selection/available-model-picker.js +22 -78
  10. package/dist/components/model-selection/model-picker-body.cjs +106 -0
  11. package/dist/components/model-selection/model-picker-body.d.cts +41 -0
  12. package/dist/components/model-selection/model-picker-body.d.ts +41 -0
  13. package/dist/components/model-selection/model-picker-body.js +106 -0
  14. package/dist/components/model-selection/thinking-level-picker.cjs +7 -3
  15. package/dist/components/model-selection/thinking-level-picker.js +7 -3
  16. package/dist/components/model-selection/thinking-picker.cjs +1 -1
  17. package/dist/components/model-selection/thinking-picker.js +1 -1
  18. package/dist/components/ui/calendar.cjs +1 -1
  19. package/dist/components/ui/calendar.js +1 -1
  20. package/dist/components/ui/combobox.cjs +1 -1
  21. package/dist/components/ui/combobox.js +1 -1
  22. package/dist/components/ui/form.d.cts +1 -1
  23. package/dist/components/ui/form.d.ts +1 -1
  24. package/dist/components/ui/input-group.cjs +1 -1
  25. package/dist/components/ui/input-group.js +1 -1
  26. package/dist/components/ui/input-otp.cjs +1 -1
  27. package/dist/components/ui/input-otp.js +1 -1
  28. package/dist/components/ui/input.cjs +3 -3
  29. package/dist/components/ui/input.js +3 -3
  30. package/dist/components/ui/native-select.cjs +2 -2
  31. package/dist/components/ui/native-select.js +2 -2
  32. package/dist/components/ui/select.cjs +1 -1
  33. package/dist/components/ui/select.js +1 -1
  34. package/dist/components/ui/slider.cjs +6 -0
  35. package/dist/components/ui/slider.d.cts +1 -1
  36. package/dist/components/ui/slider.d.ts +1 -1
  37. package/dist/components/ui/slider.js +6 -0
  38. package/dist/components/ui/spinner.d.cts +2 -1
  39. package/dist/components/ui/spinner.d.ts +2 -1
  40. package/dist/components/ui/textarea.cjs +1 -1
  41. package/dist/components/ui/textarea.js +1 -1
  42. package/dist/index.d.cts +2 -1
  43. package/dist/index.d.ts +2 -1
  44. package/dist/styles/globals.css +32 -0
  45. package/dist/theme/apply-theme.cjs +65 -12
  46. package/dist/theme/apply-theme.js +55 -2
  47. package/dist/theme/index.d.cts +1 -1
  48. package/dist/theme/index.d.ts +1 -1
  49. package/dist/theme/types.cjs +15 -2
  50. package/dist/theme/types.d.cts +21 -1
  51. package/dist/theme/types.d.ts +21 -1
  52. package/dist/theme/types.js +14 -1
  53. package/package.json +21 -20
  54. package/scripts/smoke-dist.mjs +2 -2
  55. package/src/components/ai-elements/conversation-virtual.test.tsx +126 -0
  56. package/src/components/ai-elements/conversation-virtual.tsx +162 -0
  57. package/src/components/model-selection/available-model-picker.tsx +26 -98
  58. package/src/components/model-selection/model-picker-body.test.tsx +152 -0
  59. package/src/components/model-selection/model-picker-body.tsx +188 -0
  60. package/src/components/model-selection/thinking-level-picker.tsx +9 -3
  61. package/src/components/model-selection/thinking-picker.test.tsx +41 -0
  62. package/src/components/model-selection/thinking-picker.tsx +1 -1
  63. package/src/components/ui/calendar.test.tsx +43 -0
  64. package/src/components/ui/calendar.tsx +1 -1
  65. package/src/components/ui/combobox.tsx +1 -1
  66. package/src/components/ui/input-group.tsx +1 -1
  67. package/src/components/ui/input-otp.tsx +1 -1
  68. package/src/components/ui/input.tsx +3 -3
  69. package/src/components/ui/native-select.tsx +2 -2
  70. package/src/components/ui/select.tsx +1 -1
  71. package/src/components/ui/slider.tsx +6 -0
  72. package/src/components/ui/spinner.tsx +1 -1
  73. package/src/components/ui/textarea.tsx +1 -1
  74. package/src/styles/globals.css +32 -0
  75. package/src/theme/apply-theme.test.ts +126 -0
  76. package/src/theme/apply-theme.ts +66 -3
  77. package/src/theme/types.ts +33 -0
@@ -1,6 +1,9 @@
1
- import type {
2
- ColorTokens,
3
- ThemePreset,
1
+ import {
2
+ DEFAULT_GLASS_EFFECT,
3
+ WINDOWS_GLASS_MATERIALS,
4
+ type ColorTokens,
5
+ type ThemeGlassEffect,
6
+ type ThemePreset,
4
7
  } from './types';
5
8
 
6
9
  // ── Token → CSS Variable Mapping ─────────────────────────────
@@ -86,6 +89,38 @@ function tint(color: string, percentage: number): string {
86
89
  return `color-mix(in srgb, ${color} ${percentage}%, transparent)`;
87
90
  }
88
91
 
92
+ function applyGlassEffect(
93
+ root: HTMLElement,
94
+ glass: ThemeGlassEffect | undefined,
95
+ colors: ColorTokens,
96
+ ): void {
97
+ const enabled = glass?.enabled === true;
98
+ root.classList.toggle('theme-glass', enabled);
99
+ root.style.removeProperty('--window-glass-opaque-base');
100
+ root.style.removeProperty('--window-glass-sidebar');
101
+ root.style.removeProperty('--window-glass-opaque-surface');
102
+ root.style.removeProperty('--window-glass-opaque-elevated');
103
+ if (!enabled) return;
104
+
105
+ const opacity = Math.min(1, Math.max(0, glass.opacity));
106
+ const percentage = Math.round(opacity * 100);
107
+ root.style.setProperty('--bg-base', tint(colors.bgBase, percentage));
108
+ root.style.setProperty(
109
+ '--window-glass-sidebar',
110
+ tint(colors.bgSurface, (glass.sidebarOpacity ?? DEFAULT_GLASS_EFFECT.sidebarOpacity) * 100),
111
+ );
112
+ root.style.setProperty('--window-glass-opaque-base', colors.bgBase);
113
+ root.style.setProperty('--window-glass-opaque-surface', colors.bgSurface);
114
+ root.style.setProperty('--window-glass-opaque-elevated', colors.bgElevated);
115
+ // Thin local layers preserve the wallpaper while separating nested content.
116
+ root.style.setProperty('--bg-surface', tint(colors.bgSurface, (glass.surfaceOpacity ?? DEFAULT_GLASS_EFFECT.surfaceOpacity) * 100));
117
+ root.style.setProperty('--bg-elevated', tint(colors.textPrimary, (glass.selectionOpacity ?? DEFAULT_GLASS_EFFECT.selectionOpacity) * 100));
118
+ root.style.setProperty('--text-muted', colors.textSecondary);
119
+ const border = (glass.borderOpacity ?? DEFAULT_GLASS_EFFECT.borderOpacity) * 100;
120
+ root.style.setProperty('--border-subtle', tint(colors.textPrimary, border * 2 / 3));
121
+ root.style.setProperty('--border-default', tint(colors.textPrimary, border));
122
+ }
123
+
89
124
  // ── Apply / Reset ────────────────────────────────────────────
90
125
 
91
126
  /** Apply a theme preset for the given mode. */
@@ -130,6 +165,8 @@ export function applyThemePreset(
130
165
  root.style.setProperty('--radius', preset.radius.md);
131
166
  }
132
167
 
168
+ applyGlassEffect(root, preset.glass, colors);
169
+
133
170
  root.classList.toggle('dark', mode === 'dark');
134
171
  }
135
172
 
@@ -142,6 +179,10 @@ const ALL_MANAGED_VARS = [
142
179
  '--font-size-base',
143
180
  '--spacing',
144
181
  '--radius',
182
+ '--window-glass-opaque-base',
183
+ '--window-glass-sidebar',
184
+ '--window-glass-opaque-surface',
185
+ '--window-glass-opaque-elevated',
145
186
  ];
146
187
 
147
188
  /** Remove all inline theme overrides, reverting to CSS defaults. */
@@ -150,6 +191,7 @@ export function resetTheme(): void {
150
191
  for (const cssVar of ALL_MANAGED_VARS) {
151
192
  root.style.removeProperty(cssVar);
152
193
  }
194
+ root.classList.remove('theme-glass');
153
195
  }
154
196
 
155
197
  // ── Validation ───────────────────────────────────────────────
@@ -221,6 +263,26 @@ function sanitiseRadius(raw: unknown): ThemePreset['radius'] | undefined {
221
263
  return Object.keys(result).length > 0 ? result : undefined;
222
264
  }
223
265
 
266
+ function sanitiseGlass(raw: unknown): ThemeGlassEffect | undefined {
267
+ if (!raw || typeof raw !== 'object') return undefined;
268
+ const value = raw as Record<string, unknown>;
269
+ if (typeof value.enabled !== 'boolean') return undefined;
270
+
271
+ const result = { ...DEFAULT_GLASS_EFFECT, enabled: value.enabled };
272
+ for (const key of ['opacity', 'sidebarOpacity', 'surfaceOpacity', 'selectionOpacity', 'borderOpacity'] as const) {
273
+ const amount = value[key];
274
+ if (typeof amount === 'number' && Number.isFinite(amount)) {
275
+ result[key] = Math.min(1, Math.max(0, amount));
276
+ }
277
+ }
278
+ if (typeof value.blurRadius === 'number' && Number.isFinite(value.blurRadius)) {
279
+ result.blurRadius = Math.round(Math.min(64, Math.max(0, value.blurRadius)));
280
+ }
281
+ result.windowsMaterial = WINDOWS_GLASS_MATERIALS.find((material) => material === value.windowsMaterial)
282
+ ?? DEFAULT_GLASS_EFFECT.windowsMaterial;
283
+ return result;
284
+ }
285
+
224
286
  /** Validate and normalise an unknown value into a ThemePreset. */
225
287
  export function validateThemePreset(data: unknown): ThemePreset | null {
226
288
  if (!data || typeof data !== 'object') return null;
@@ -246,5 +308,6 @@ export function validateThemePreset(data: unknown): ThemePreset | null {
246
308
  typography: sanitiseTypography(d.typography),
247
309
  spacing: sanitiseSpacing(d.spacing),
248
310
  radius: sanitiseRadius(d.radius),
311
+ glass: sanitiseGlass(d.glass),
249
312
  };
250
313
  }
@@ -105,6 +105,25 @@ export interface RadiusTokens {
105
105
  lg?: string;
106
106
  }
107
107
 
108
+ export const WINDOWS_GLASS_MATERIALS = ['acrylic', 'mica', 'tabbed'] as const;
109
+ export type WindowsGlassMaterial = typeof WINDOWS_GLASS_MATERIALS[number];
110
+
111
+ export interface ThemeGlassEffect {
112
+ /** Show the desktop through Sero's surfaces. */
113
+ enabled: boolean;
114
+ /** Window tint opacity from 0 to 1. */
115
+ opacity: number;
116
+ /** Direct macOS desktop blur radius, from 0 to 64 pixels. */
117
+ blurRadius?: number;
118
+ /** Windows 11 system backdrop. */
119
+ windowsMaterial?: WindowsGlassMaterial;
120
+ /** Independent local layer opacities from 0 to 1. */
121
+ sidebarOpacity?: number;
122
+ surfaceOpacity?: number;
123
+ selectionOpacity?: number;
124
+ borderOpacity?: number;
125
+ }
126
+
108
127
  // ── Theme Preset ─────────────────────────────────────────────
109
128
 
110
129
  export interface ThemePreset {
@@ -141,6 +160,9 @@ export interface ThemePreset {
141
160
 
142
161
  /** Border radius overrides. */
143
162
  radius?: RadiusTokens;
163
+
164
+ /** Optional translucent desktop window treatment. */
165
+ glass?: ThemeGlassEffect;
144
166
  }
145
167
 
146
168
  /** Lightweight metadata for listing presets without loading full data. */
@@ -218,6 +240,17 @@ export const DEFAULT_RADIUS: Required<RadiusTokens> = {
218
240
  lg: '12px',
219
241
  };
220
242
 
243
+ export const DEFAULT_GLASS_EFFECT: Required<ThemeGlassEffect> = {
244
+ enabled: false,
245
+ opacity: 0.78,
246
+ blurRadius: 24,
247
+ windowsMaterial: 'acrylic',
248
+ sidebarOpacity: 0.08,
249
+ surfaceOpacity: 0.18,
250
+ selectionOpacity: 0.08,
251
+ borderOpacity: 0.18,
252
+ };
253
+
221
254
  /** Default colour tokens matching globals.css .dark values. */
222
255
  export const DEFAULT_DARK_COLORS: ColorTokens = {
223
256
  bgBase: '#0a0a0b',