@lastbrain/ai-ui-react 1.0.68 → 1.0.70

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 (76) hide show
  1. package/dist/components/AiChipLabel.d.ts +8 -3
  2. package/dist/components/AiChipLabel.d.ts.map +1 -1
  3. package/dist/components/AiChipLabel.js +23 -70
  4. package/dist/components/AiContextButton.d.ts +10 -2
  5. package/dist/components/AiContextButton.d.ts.map +1 -1
  6. package/dist/components/AiContextButton.js +73 -291
  7. package/dist/components/AiImageButton.d.ts +5 -1
  8. package/dist/components/AiImageButton.d.ts.map +1 -1
  9. package/dist/components/AiImageButton.js +6 -142
  10. package/dist/components/AiInput.d.ts +5 -3
  11. package/dist/components/AiInput.d.ts.map +1 -1
  12. package/dist/components/AiInput.js +13 -25
  13. package/dist/components/AiPromptPanel.d.ts.map +1 -1
  14. package/dist/components/AiPromptPanel.js +64 -212
  15. package/dist/components/AiSelect.d.ts +5 -3
  16. package/dist/components/AiSelect.d.ts.map +1 -1
  17. package/dist/components/AiSelect.js +21 -30
  18. package/dist/components/AiStatusButton.d.ts +4 -1
  19. package/dist/components/AiStatusButton.d.ts.map +1 -1
  20. package/dist/components/AiStatusButton.js +211 -676
  21. package/dist/components/AiTextarea.d.ts +4 -2
  22. package/dist/components/AiTextarea.d.ts.map +1 -1
  23. package/dist/components/AiTextarea.js +14 -26
  24. package/dist/components/LBApiKeySelector.d.ts.map +1 -1
  25. package/dist/components/LBApiKeySelector.js +5 -166
  26. package/dist/components/LBConnectButton.d.ts +4 -7
  27. package/dist/components/LBConnectButton.d.ts.map +1 -1
  28. package/dist/components/LBConnectButton.js +17 -86
  29. package/dist/components/LBSigninModal.d.ts +1 -1
  30. package/dist/components/LBSigninModal.d.ts.map +1 -1
  31. package/dist/components/LBSigninModal.js +42 -320
  32. package/dist/context/LBAuthProvider.d.ts +35 -3
  33. package/dist/context/LBAuthProvider.d.ts.map +1 -1
  34. package/dist/context/LBAuthProvider.js +2 -0
  35. package/dist/examples/AiUiPremiumShowcase.d.ts +2 -0
  36. package/dist/examples/AiUiPremiumShowcase.d.ts.map +1 -0
  37. package/dist/examples/AiUiPremiumShowcase.js +15 -0
  38. package/dist/hooks/useAiModels.d.ts.map +1 -1
  39. package/dist/hooks/useModelManagement.d.ts.map +1 -1
  40. package/dist/index.d.ts +2 -0
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +2 -0
  43. package/dist/styles/inline.d.ts +1 -0
  44. package/dist/styles/inline.d.ts.map +1 -1
  45. package/dist/styles/inline.js +25 -129
  46. package/dist/styles.css +1268 -369
  47. package/dist/types.d.ts +3 -0
  48. package/dist/types.d.ts.map +1 -1
  49. package/dist/utils/errorHandler.d.ts +2 -2
  50. package/dist/utils/errorHandler.d.ts.map +1 -1
  51. package/dist/utils/errorHandler.js +8 -1
  52. package/dist/utils/modelManagement.d.ts +13 -10
  53. package/dist/utils/modelManagement.d.ts.map +1 -1
  54. package/dist/utils/modelManagement.js +19 -2
  55. package/package.json +2 -2
  56. package/src/components/AiChipLabel.tsx +68 -101
  57. package/src/components/AiContextButton.tsx +142 -413
  58. package/src/components/AiImageButton.tsx +29 -190
  59. package/src/components/AiInput.tsx +49 -74
  60. package/src/components/AiPromptPanel.tsx +81 -260
  61. package/src/components/AiSelect.tsx +61 -69
  62. package/src/components/AiStatusButton.tsx +496 -1327
  63. package/src/components/AiTextarea.tsx +50 -63
  64. package/src/components/LBApiKeySelector.tsx +93 -271
  65. package/src/components/LBConnectButton.tsx +39 -336
  66. package/src/components/LBSigninModal.tsx +141 -472
  67. package/src/context/LBAuthProvider.tsx +45 -6
  68. package/src/examples/AiUiPremiumShowcase.tsx +94 -0
  69. package/src/hooks/useAiModels.ts +2 -1
  70. package/src/hooks/useModelManagement.ts +2 -1
  71. package/src/index.ts +3 -0
  72. package/src/styles/inline.ts +27 -148
  73. package/src/styles.css +1268 -369
  74. package/src/types.ts +3 -0
  75. package/src/utils/errorHandler.ts +16 -3
  76. package/src/utils/modelManagement.ts +53 -15
@@ -24,6 +24,39 @@ import type {
24
24
  } from "@lastbrain/ai-ui-core";
25
25
  import { createLBClient } from "@lastbrain/ai-ui-core";
26
26
 
27
+ export interface ApiKeyUser {
28
+ id?: string;
29
+ name?: string;
30
+ prefix?: string;
31
+ env?: string;
32
+ rate_limit_rpm?: number;
33
+ scopes?: string[];
34
+ }
35
+
36
+ export interface BasicStatus {
37
+ authType?: string;
38
+ user?: {
39
+ id?: string;
40
+ email?: string;
41
+ } | null;
42
+ apiKey?: ApiKeyUser | null;
43
+ api_key?: ApiKeyUser | null;
44
+ balance?: {
45
+ used?: number;
46
+ total?: number;
47
+ percentage?: number;
48
+ };
49
+ storage?: StorageStatus["storage"];
50
+ }
51
+
52
+ export interface StorageStatus {
53
+ storage?: {
54
+ used_mb?: number;
55
+ allocated_mb?: number;
56
+ percentage?: number;
57
+ } | null;
58
+ }
59
+
27
60
  interface LBProviderProps {
28
61
  children: ReactNode;
29
62
  /** URL de l'API LastBrain (ex: https://api.lastbrain.io) */
@@ -66,9 +99,9 @@ interface LBContextValue extends LBAuthState {
66
99
  /** Status API (balance, storage, API key info) */
67
100
  apiStatus: AiStatus | null;
68
101
  /** Status basique (rapide) - balance, API key info sans storage */
69
- basicStatus: any;
102
+ basicStatus: BasicStatus | null;
70
103
  /** Status storage (lent) avec cache */
71
- storageStatus: any;
104
+ storageStatus: StorageStatus | null;
72
105
  /** Fonction pour rafraîchir le status rapide */
73
106
  refreshBasicStatus: () => Promise<void>;
74
107
  /** Fonction pour rafraîchir le storage (avec cache optionnel) */
@@ -83,6 +116,9 @@ interface LBContextValue extends LBAuthState {
83
116
 
84
117
  const LBContext = createContext<LBContextValue | undefined>(undefined);
85
118
 
119
+ // Export pour usage dans d'autres composants
120
+ export { LBContext };
121
+
86
122
  export function LBProvider({
87
123
  children,
88
124
  baseUrl: _baseUrl = "/api/lastbrain",
@@ -96,8 +132,8 @@ export function LBProvider({
96
132
  const [apiKeys, setApiKeys] = useState<LBApiKey[]>([]);
97
133
  const [accessToken, setAccessToken] = useState<string>();
98
134
  const [apiStatus, setApiStatus] = useState<AiStatus | null>(null);
99
- const [basicStatus, setBasicStatus] = useState<any>(null);
100
- const [storageStatus, setStorageStatus] = useState<any>(null);
135
+ const [basicStatus, setBasicStatus] = useState<BasicStatus | null>(null);
136
+ const [storageStatus, setStorageStatus] = useState<StorageStatus | null>(null);
101
137
  const [isLoadingStatus, setIsLoadingStatus] = useState(false);
102
138
  const [isLoadingStorage, setIsLoadingStorage] = useState(false);
103
139
  const [storageLastFetch, setStorageLastFetch] = useState<number>(0);
@@ -373,7 +409,7 @@ export function LBProvider({
373
409
 
374
410
  setIsLoadingStatus(true);
375
411
  try {
376
- let data: any;
412
+ let data: BasicStatus;
377
413
  try {
378
414
  data = await lbClient.getStatus();
379
415
  } catch {
@@ -452,7 +488,7 @@ export function LBProvider({
452
488
  ...basicStatus,
453
489
  storage: storageData?.storage,
454
490
  };
455
- setApiStatus(combinedStatus);
491
+ setApiStatus(combinedStatus as AiStatus);
456
492
  } catch (error) {
457
493
  console.error("[LBProvider] Failed to fetch storage status:", error);
458
494
  // Arrêter les tentatives répétées si erreur persistante
@@ -619,3 +655,6 @@ export function useLB(): LBContextValue {
619
655
  }
620
656
  return context;
621
657
  }
658
+
659
+ // Re-export des types pour usage externe
660
+ export type { LBApiKey };
@@ -0,0 +1,94 @@
1
+ "use client";
2
+
3
+ import { Sparkles } from "lucide-react";
4
+ import { AiProvider } from "../context/AiProvider";
5
+ import { AiInput } from "../components/AiInput";
6
+ import { AiTextarea } from "../components/AiTextarea";
7
+ import { AiChipLabel } from "../components/AiChipLabel";
8
+
9
+ function ShowcasePanel({
10
+ title,
11
+ theme,
12
+ }: {
13
+ title: string;
14
+ theme: "light" | "dark";
15
+ }) {
16
+ return (
17
+ <section
18
+ data-theme={theme}
19
+ className={`rounded-2xl border p-6 ${
20
+ theme === "dark"
21
+ ? "bg-slate-950 border-slate-800 text-slate-100"
22
+ : "bg-white border-slate-200 text-slate-900"
23
+ }`}
24
+ >
25
+ <div className="mb-4 flex items-center justify-between">
26
+ <h3 className="text-base font-semibold">{title}</h3>
27
+ <span className="text-xs opacity-70">{theme}</span>
28
+ </div>
29
+
30
+ <AiProvider baseUrl="/api/ai" apiKeyId="demo-key">
31
+ <div className="space-y-4">
32
+ <AiInput
33
+ value=""
34
+ onChange={() => {}}
35
+ placeholder="Ask anything..."
36
+ data-testid={`showcase-input-${theme}`}
37
+ />
38
+
39
+ <AiTextarea
40
+ value=""
41
+ onChange={() => {}}
42
+ placeholder="Generate a concise product description..."
43
+ data-testid={`showcase-textarea-${theme}`}
44
+ />
45
+
46
+ <div className="flex flex-wrap gap-2">
47
+ <AiChipLabel>Default</AiChipLabel>
48
+ <AiChipLabel variant="selected">Selected</AiChipLabel>
49
+ <AiChipLabel variant="success">Success</AiChipLabel>
50
+ <AiChipLabel variant="danger">Danger</AiChipLabel>
51
+ </div>
52
+
53
+ <div className="ai-surface p-3">
54
+ <div className="flex items-center justify-between">
55
+ <span className="text-sm font-medium">Static control states</span>
56
+ <Sparkles size={14} className="text-[var(--ai-primary)]" />
57
+ </div>
58
+ <div className="mt-3 space-y-2">
59
+ <input
60
+ className="ai-control ai-control-input"
61
+ placeholder="Default state"
62
+ />
63
+ <input
64
+ className="ai-control ai-control-input ai-control--focused"
65
+ placeholder="Focused style"
66
+ readOnly
67
+ />
68
+ <input
69
+ className="ai-control ai-control-input ai-control--error"
70
+ aria-invalid="true"
71
+ placeholder="Error style"
72
+ readOnly
73
+ />
74
+ <input
75
+ className="ai-control ai-control-input ai-control--disabled"
76
+ placeholder="Disabled style"
77
+ disabled
78
+ />
79
+ </div>
80
+ </div>
81
+ </div>
82
+ </AiProvider>
83
+ </section>
84
+ );
85
+ }
86
+
87
+ export function AiUiPremiumShowcase() {
88
+ return (
89
+ <div className="grid gap-6 md:grid-cols-2">
90
+ <ShowcasePanel title="Premium UI Showcase" theme="dark" />
91
+ <ShowcasePanel title="Premium UI Showcase" theme="light" />
92
+ </div>
93
+ );
94
+ }
@@ -26,7 +26,8 @@ export function useAiModels(options?: UseAiModelsOptions): UseAiModelsResult {
26
26
 
27
27
  // Utiliser le contexte dès que la base URL correspond.
28
28
  // L'apiKeyId peut varier (session/api-key sélectionnée) sans invalider le contexte.
29
- const useContextData = !options?.baseUrl || options.baseUrl === context.baseUrl;
29
+ const useContextData =
30
+ !options?.baseUrl || options.baseUrl === context.baseUrl;
30
31
 
31
32
  // Filtrer les modèles selon le type demandé
32
33
  const filteredModels = useMemo(() => {
@@ -44,7 +44,8 @@ export function useModelManagement(
44
44
  // Utiliser les données du contexte si la base URL correspond.
45
45
  // L'apiKey peut changer selon le mode d'auth (lb_session/supabase/api_key) sans
46
46
  // devoir casser le cache/provider de modèles déjà présent.
47
- const useContextData = !options.baseUrl || options.baseUrl === context.baseUrl;
47
+ const useContextData =
48
+ !options.baseUrl || options.baseUrl === context.baseUrl;
48
49
 
49
50
  // Filtrer par catégorie si nécessaire
50
51
  const filteredModels = useMemo(() => {
package/src/index.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import "./styles.css";
2
+
1
3
  // Types
2
4
  export * from "./types";
3
5
 
@@ -45,3 +47,4 @@ export * from "./examples/AiPromptPanelAdvanced";
45
47
  export * from "./examples/AiChipInputExample";
46
48
  export * from "./examples/AiImageButtonExample";
47
49
  export * from "./examples/AiContextButtonExample";
50
+ export * from "./examples/AiUiPremiumShowcase";
@@ -3,6 +3,8 @@
3
3
  * Theme: Purple/Violet primary color with light/dark mode support
4
4
  */
5
5
 
6
+ import type React from "react";
7
+
6
8
  // Theme colors
7
9
  const colors = {
8
10
  // Purple/Violet theme
@@ -18,114 +20,23 @@ const colors = {
18
20
  warningLight: "#fbbf24",
19
21
  danger: "#ef4444",
20
22
  dangerLight: "#f87171",
21
-
22
- // Neutral colors - Light mode
23
- light: {
24
- bg: "#ffffff",
25
- bgSecondary: "#f9fafb",
26
- bgTertiary: "#f3f4f6",
27
- border: "#e2e8f0",
28
- borderLight: "#f1f5f9",
29
- text: "#111827",
30
- textSecondary: "#6b7280",
31
- textTertiary: "#9ca3af",
32
- shadow: "rgba(0, 0, 0, 0.1)",
33
- shadowMd:
34
- "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)",
35
- shadowLg:
36
- "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1)",
37
- shadowXl:
38
- "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)",
39
- },
40
-
41
- // Neutral colors - Dark mode
42
- dark: {
43
- bg: "#1f2937",
44
- bgSecondary: "#111827",
45
- bgTertiary: "#0f172a",
46
- border: "#374151",
47
- borderLight: "#4b5563",
48
- text: "#f9fafb",
49
- textSecondary: "#d1d5db",
50
- textTertiary: "#9ca3af",
51
- shadow: "rgba(0, 0, 0, 0.3)",
52
- shadowMd:
53
- "0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -2px rgba(0, 0, 0, 0.3)",
54
- shadowLg:
55
- "0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -4px rgba(0, 0, 0, 0.3)",
56
- shadowXl:
57
- "0 20px 25px -5px rgba(0, 0, 0, 0.3), 0 8px 10px -6px rgba(0, 0, 0, 0.3)",
58
- },
59
- };
60
-
61
- // Use light mode by default (can be made dynamic later)
62
- const getIsDark = () => {
63
- if (typeof document !== "undefined") {
64
- const root = document.documentElement;
65
- if (root.classList.contains("dark")) {
66
- return true;
67
- }
68
- if (root.classList.contains("light")) {
69
- return false;
70
- }
71
- }
72
-
73
- return (
74
- typeof window !== "undefined" &&
75
- window.matchMedia?.("(prefers-color-scheme: dark)").matches
76
- );
77
23
  };
78
24
 
79
25
  const themeVars = {
80
26
  bg: "var(--ai-bg)",
81
- bgSecondary: "var(--ai-bg-secondary)",
82
- bgTertiary: "var(--ai-bg-tertiary)",
27
+ bgSecondary: "var(--ai-bg2)",
28
+ bgTertiary: "var(--ai-bg3)",
83
29
  border: "var(--ai-border)",
84
- borderLight: "var(--ai-border-light)",
85
- text: "var(--ai-text)",
86
- textSecondary: "var(--ai-text-secondary)",
87
- textTertiary: "var(--ai-text-tertiary)",
88
- shadow: "var(--ai-shadow)",
89
- shadowMd: "var(--ai-shadow-md)",
30
+ borderLight: "color-mix(in srgb, var(--ai-border) 70%, transparent)",
31
+ text: "var(--ai-foreground)",
32
+ textSecondary: "var(--ai-secondary)",
33
+ textTertiary: "var(--ai-tertiary)",
34
+ shadow: "var(--ai-shadow-sm)",
35
+ shadowMd: "var(--ai-shadow-sm)",
90
36
  shadowLg: "var(--ai-shadow-lg)",
91
37
  shadowXl: "var(--ai-shadow-xl)",
92
38
  } as const;
93
39
 
94
- const applyThemeVariables = () => {
95
- if (typeof document === "undefined") {
96
- return;
97
- }
98
-
99
- const nextTheme = getIsDark() ? colors.dark : colors.light;
100
- const root = document.documentElement;
101
-
102
- root.style.setProperty("--ai-bg", nextTheme.bg);
103
- root.style.setProperty("--ai-bg-secondary", nextTheme.bgSecondary);
104
- root.style.setProperty("--ai-bg-tertiary", nextTheme.bgTertiary);
105
- root.style.setProperty("--ai-border", nextTheme.border);
106
- root.style.setProperty("--ai-border-light", nextTheme.borderLight);
107
- root.style.setProperty("--ai-text", nextTheme.text);
108
- root.style.setProperty("--ai-text-secondary", nextTheme.textSecondary);
109
- root.style.setProperty("--ai-text-tertiary", nextTheme.textTertiary);
110
- root.style.setProperty("--ai-shadow", nextTheme.shadow);
111
- root.style.setProperty("--ai-shadow-md", nextTheme.shadowMd);
112
- root.style.setProperty("--ai-shadow-lg", nextTheme.shadowLg);
113
- root.style.setProperty("--ai-shadow-xl", nextTheme.shadowXl);
114
- };
115
-
116
- if (typeof window !== "undefined") {
117
- applyThemeVariables();
118
-
119
- const media = window.matchMedia?.("(prefers-color-scheme: dark)");
120
- media?.addEventListener?.("change", applyThemeVariables);
121
-
122
- const observer = new MutationObserver(() => applyThemeVariables());
123
- observer.observe(document.documentElement, {
124
- attributes: true,
125
- attributeFilter: ["class"],
126
- });
127
- }
128
-
129
40
  export const aiStyles = {
130
41
  // Input field
131
42
  input: {
@@ -134,7 +45,7 @@ export const aiStyles = {
134
45
  fontSize: "14px",
135
46
  lineHeight: "1.5",
136
47
  color: themeVars.text,
137
- background: themeVars.bg,
48
+ background: "var(--ai-glass-strong)",
138
49
  border: `1px solid ${themeVars.border}`,
139
50
  borderColor: themeVars.border,
140
51
  borderRadius: "8px",
@@ -192,7 +103,7 @@ export const aiStyles = {
192
103
  fontSize: "14px",
193
104
  lineHeight: "1.5",
194
105
  color: themeVars.text,
195
- background: themeVars.bg,
106
+ background: "var(--ai-glass-strong)",
196
107
  border: `1px solid ${themeVars.border}`,
197
108
  borderColor: themeVars.border,
198
109
  borderRadius: "8px",
@@ -277,7 +188,7 @@ export const aiStyles = {
277
188
  fontSize: "14px",
278
189
  lineHeight: "1.5",
279
190
  color: themeVars.text,
280
- background: themeVars.bg,
191
+ background: "var(--ai-glass-strong)",
281
192
  borderWidth: "1px",
282
193
  borderStyle: "solid",
283
194
  borderColor: themeVars.border,
@@ -311,7 +222,7 @@ export const aiStyles = {
311
222
  height: "40px",
312
223
  border: `1px solid ${themeVars.border}`,
313
224
  borderRadius: "8px",
314
- background: themeVars.bg,
225
+ background: "var(--ai-glass, rgba(255,255,255,0.7))",
315
226
  color: themeVars.text,
316
227
  cursor: "pointer" as const,
317
228
  transition: "all 0.2s",
@@ -319,7 +230,7 @@ export const aiStyles = {
319
230
  } as React.CSSProperties,
320
231
 
321
232
  statusButtonHover: {
322
- background: themeVars.bgSecondary,
233
+ background: "var(--ai-glass-strong, rgba(255,255,255,0.82))",
323
234
  boxShadow: themeVars.shadowLg,
324
235
  transform: "scale(1.05)",
325
236
  } as React.CSSProperties,
@@ -336,7 +247,7 @@ export const aiStyles = {
336
247
  minWidth: "320px",
337
248
  maxWidth: "400px",
338
249
  padding: "16px",
339
- background: themeVars.bg,
250
+ background: "var(--ai-bg2)",
340
251
  border: `1px solid ${themeVars.border}`,
341
252
  borderRadius: "12px",
342
253
  boxShadow: themeVars.shadowXl,
@@ -428,13 +339,13 @@ export const aiStyles = {
428
339
  transition: "all 0.2s",
429
340
  fontSize: "11px",
430
341
  fontWeight: 500,
431
- background: `${colors.primary}10`,
432
- border: `1px solid ${colors.primary}30`,
342
+ background: "color-mix(in srgb, var(--ai-primary) 10%, transparent)",
343
+ border: "1px solid color-mix(in srgb, var(--ai-primary) 30%, transparent)",
433
344
  } as React.CSSProperties,
434
345
 
435
346
  tooltipLinkHover: {
436
- background: `${colors.primary}20`,
437
- borderColor: colors.primary,
347
+ background: "color-mix(in srgb, var(--ai-primary) 20%, transparent)",
348
+ borderColor: "var(--ai-primary)",
438
349
  } as React.CSSProperties,
439
350
 
440
351
  // Chip/Label
@@ -446,9 +357,9 @@ export const aiStyles = {
446
357
  fontSize: "12px",
447
358
  fontWeight: 500,
448
359
  borderRadius: "6px",
449
- background: `${colors.primary}10`,
450
- color: colors.primary,
451
- border: `1px solid ${colors.primary}30`,
360
+ background: "color-mix(in srgb, var(--ai-primary) 10%, transparent)",
361
+ color: "var(--ai-primary)",
362
+ border: "1px solid color-mix(in srgb, var(--ai-primary) 30%, transparent)",
452
363
  } as React.CSSProperties,
453
364
 
454
365
  // Modal (basic styles)
@@ -458,7 +369,7 @@ export const aiStyles = {
458
369
  left: 0,
459
370
  right: 0,
460
371
  bottom: 0,
461
- zIndex: 9999,
372
+ zIndex: 2147483645,
462
373
  display: "flex" as const,
463
374
  alignItems: "center" as const,
464
375
  justifyContent: "center" as const,
@@ -472,7 +383,7 @@ export const aiStyles = {
472
383
  left: 0,
473
384
  right: 0,
474
385
  bottom: 0,
475
- background: "rgba(0, 0, 0, 0.5)",
386
+ background: "var(--ai-overlay, rgba(0, 0, 0, 0.5))",
476
387
  backdropFilter: "blur(4px)",
477
388
  WebkitBackdropFilter: "blur(4px)",
478
389
  } as React.CSSProperties,
@@ -483,7 +394,8 @@ export const aiStyles = {
483
394
  maxWidth: "600px",
484
395
  maxHeight: "90vh",
485
396
  overflow: "auto" as const,
486
- background: themeVars.bg,
397
+ background: "var(--ai-bg2)",
398
+ border: "1px solid var(--ai-border)",
487
399
  borderRadius: "12px",
488
400
  boxShadow: themeVars.shadowXl,
489
401
  padding: "0",
@@ -578,39 +490,6 @@ export const aiStyles = {
578
490
  } as React.CSSProperties,
579
491
  };
580
492
 
581
- // Inject keyframes animation for spinner
582
- if (typeof document !== "undefined") {
583
- const styleSheet = document.createElement("style");
584
- styleSheet.textContent = `
585
- @keyframes ai-spin {
586
- from { transform: rotate(0deg); }
587
- to { transform: rotate(360deg); }
588
- }
589
-
590
- @keyframes ai-fadeIn {
591
- from { opacity: 0; }
592
- to { opacity: 1; }
593
- }
594
-
595
- @keyframes ai-slideUp {
596
- from {
597
- opacity: 0;
598
- transform: translateY(20px);
599
- }
600
- to {
601
- opacity: 1;
602
- transform: translateY(0);
603
- }
604
- }
605
-
606
- @keyframes pulse {
607
- 0%, 100% { opacity: 1; }
608
- 50% { opacity: 0.6; }
609
- }
610
- `;
611
- document.head.appendChild(styleSheet);
612
- }
613
-
614
493
  // Helper function to calculate tooltip position
615
494
  export function calculateTooltipPosition(
616
495
  buttonRect: DOMRect,