@lastbrain/ai-ui-react 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,6 +5,7 @@ Headless React components for LastBrain AI UI Kit.
5
5
  ## Features
6
6
 
7
7
  - Headless components (no UI library dependency)
8
+ - Built-in CSS styles (optional)
8
9
  - React hooks for AI operations
9
10
  - Context provider for configuration
10
11
  - TypeScript support
@@ -18,12 +19,58 @@ npm install @lastbrain/ai-ui-react @lastbrain/ai-ui-core
18
19
  pnpm add @lastbrain/ai-ui-react @lastbrain/ai-ui-core
19
20
  ```
20
21
 
22
+ ## Styles
23
+
24
+ The package includes optional CSS styles. Import them in your app:
25
+
26
+ ```tsx
27
+ import "@lastbrain/ai-ui-react/styles.css";
28
+ ```
29
+
30
+ Or if you prefer to use your own styles, you can skip this import and style the components using the provided CSS classes:
31
+
32
+ - `.ai-input` - Input field
33
+ - `.ai-textarea` - Textarea
34
+ - `.ai-button` - Button
35
+ - `.ai-select` - Select dropdown
36
+ - `.ai-modal` - Modal dialog
37
+ - `.ai-tooltip` - Tooltip
38
+ - `.ai-chip` - Chip/Badge
39
+ - `.ai-status-button` - Status button
40
+ - And more...
41
+
42
+ ### CSS Variables
43
+
44
+ Customize the theme using CSS variables:
45
+
46
+ ```css
47
+ :root {
48
+ --ai-primary: #3b82f6;
49
+ --ai-primary-hover: #2563eb;
50
+ --ai-success: #10b981;
51
+ --ai-warning: #f59e0b;
52
+ --ai-danger: #ef4444;
53
+ --ai-border: #e5e7eb;
54
+ --ai-text: #1f2937;
55
+ --ai-text-secondary: #6b7280;
56
+ --ai-bg: #ffffff;
57
+ --ai-bg-hover: #f9fafb;
58
+ --ai-radius: 8px;
59
+ }
60
+ ```
61
+
21
62
  ## Usage
22
63
 
23
64
  ### With Provider (Recommended)
24
65
 
25
66
  ```tsx
26
- import { AiProvider, AiInput, AiSettingsButton } from "@lastbrain/ai-ui-react";
67
+ import {
68
+ AiProvider,
69
+ AiInput,
70
+ AiSettingsButton,
71
+ AiStatusButton,
72
+ } from "@lastbrain/ai-ui-react";
73
+ import "@lastbrain/ai-ui-react/styles.css";
27
74
 
28
75
  function App() {
29
76
  return (
@@ -35,6 +82,7 @@ function App() {
35
82
  onValue={(text) => console.log(text)}
36
83
  onToast={(toast) => alert(toast.message)}
37
84
  />
85
+ <AiStatusButton />
38
86
  <AiSettingsButton />
39
87
  </div>
40
88
  </AiProvider>
@@ -46,6 +94,7 @@ function App() {
46
94
 
47
95
  ```tsx
48
96
  import { AiInput } from "@lastbrain/ai-ui-react";
97
+ import "@lastbrain/ai-ui-react/styles.css";
49
98
 
50
99
  function MyComponent() {
51
100
  return (
@@ -0,0 +1,8 @@
1
+ import type { AiStatus } from "@lastbrain/ai-ui-core";
2
+ export interface AiStatusButtonProps {
3
+ status: AiStatus | null;
4
+ loading?: boolean;
5
+ className?: string;
6
+ }
7
+ export declare function AiStatusButton({ status, loading, className, }: AiStatusButtonProps): import("react/jsx-runtime").JSX.Element;
8
+ //# sourceMappingURL=AiStatusButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiStatusButton.d.ts","sourceRoot":"","sources":["../../src/components/AiStatusButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,OAAe,EACf,SAAc,GACf,EAAE,mBAAmB,2CAsLrB"}
@@ -0,0 +1,13 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ export function AiStatusButton({ status, loading = false, className = "", }) {
5
+ const [showTooltip, setShowTooltip] = useState(false);
6
+ if (loading) {
7
+ return (_jsx("button", { className: `ai-status-button ai-status-button--loading ${className}`, disabled: true, children: _jsx("svg", { className: "ai-spinner", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" }) }) }));
8
+ }
9
+ if (!status) {
10
+ return (_jsxs("button", { className: `ai-status-button ai-status-button--error ${className}`, onMouseEnter: () => setShowTooltip(true), onMouseLeave: () => setShowTooltip(false), children: [_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("polyline", { points: "22 12 18 12 15 21 9 3 6 12 2 12" }) }), showTooltip && _jsx("div", { className: "ai-tooltip", children: "No status available" })] }));
11
+ }
12
+ return (_jsxs("div", { className: "ai-status-button-wrapper", children: [_jsx("button", { className: `ai-status-button ai-status-button--success ${className}`, onMouseEnter: () => setShowTooltip(true), onMouseLeave: () => setShowTooltip(false), children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("polyline", { points: "22 12 18 12 15 21 9 3 6 12 2 12" }) }) }), showTooltip && (_jsxs("div", { className: "ai-tooltip ai-tooltip--status", children: [_jsx("div", { className: "ai-tooltip__header", children: "API Status" }), _jsxs("div", { className: "ai-tooltip__section", children: [_jsxs("div", { className: "ai-tooltip__row", children: [_jsx("span", { className: "ai-tooltip__label", children: "API Key:" }), _jsx("span", { className: "ai-tooltip__value", children: status.api_key.name })] }), _jsxs("div", { className: "ai-tooltip__row", children: [_jsx("span", { className: "ai-tooltip__label", children: "Env:" }), _jsx("span", { className: "ai-tooltip__value", children: status.api_key.env })] }), _jsxs("div", { className: "ai-tooltip__row", children: [_jsx("span", { className: "ai-tooltip__label", children: "Rate Limit:" }), _jsxs("span", { className: "ai-tooltip__value", children: [status.api_key.rate_limit_rpm, " req/min"] })] })] }), _jsxs("div", { className: "ai-tooltip__section", children: [_jsx("div", { className: "ai-tooltip__subtitle", children: "Balance" }), _jsxs("div", { className: "ai-tooltip__row", children: [_jsx("span", { className: "ai-tooltip__label", children: "Total:" }), _jsx("span", { className: "ai-tooltip__value ai-tooltip__value--bold", children: status.balance.total.toLocaleString() })] }), _jsxs("div", { className: "ai-tooltip__row", children: [_jsx("span", { className: "ai-tooltip__label", children: "Purchased:" }), _jsx("span", { className: "ai-tooltip__value", children: status.balance.purchased.toLocaleString() })] }), _jsxs("div", { className: "ai-tooltip__row", children: [_jsx("span", { className: "ai-tooltip__label", children: "Quota:" }), _jsx("span", { className: "ai-tooltip__value", children: status.balance.quota.toLocaleString() })] })] }), _jsxs("div", { className: "ai-tooltip__section", children: [_jsx("div", { className: "ai-tooltip__subtitle", children: "Plan" }), _jsxs("div", { className: "ai-tooltip__row", children: [_jsx("span", { className: "ai-tooltip__label", children: "Type:" }), _jsx("span", { className: "ai-tooltip__value ai-tooltip__value--capitalize", children: status.quota.plan })] }), _jsxs("div", { className: "ai-tooltip__row", children: [_jsx("span", { className: "ai-tooltip__label", children: "Remaining:" }), _jsxs("span", { className: "ai-tooltip__value", children: [status.quota.remaining_quota.toLocaleString(), " /", " ", status.quota.effective_quota.toLocaleString()] })] }), _jsxs("div", { className: "ai-tooltip__row", children: [_jsx("span", { className: "ai-tooltip__label", children: "Status:" }), _jsx("span", { className: `ai-tooltip__value ${status.quota.is_active ? "ai-tooltip__value--success" : "ai-tooltip__value--warning"}`, children: status.quota.is_active ? "Active" : "Inactive" })] })] }), _jsxs("div", { className: "ai-tooltip__section", children: [_jsx("div", { className: "ai-tooltip__subtitle", children: "Storage" }), _jsxs("div", { className: "ai-tooltip__row", children: [_jsx("span", { className: "ai-tooltip__label", children: "Database:" }), _jsxs("span", { className: "ai-tooltip__value", children: [status.storage.db_mb.toFixed(2), " MB"] })] }), _jsxs("div", { className: "ai-tooltip__row", children: [_jsx("span", { className: "ai-tooltip__label", children: "Files:" }), _jsxs("span", { className: "ai-tooltip__value", children: [status.storage.files_mb.toFixed(2), " MB"] })] }), _jsxs("div", { className: "ai-tooltip__row", children: [_jsx("span", { className: "ai-tooltip__label", children: "Total:" }), _jsxs("span", { className: "ai-tooltip__value ai-tooltip__value--bold", children: [(status.storage.db_mb + status.storage.files_mb).toFixed(2), " MB"] })] })] }), _jsxs("div", { className: "ai-tooltip__section ai-tooltip__actions", children: [_jsx("a", { href: "https://ai.lastbrain.io/fr/auth/dashboard", target: "_blank", rel: "noopener noreferrer", className: "ai-tooltip__link", children: "Dashboard" }), _jsx("a", { href: "https://ai.lastbrain.io/fr/auth/billing", target: "_blank", rel: "noopener noreferrer", className: "ai-tooltip__link", children: "History" }), _jsx("a", { href: "https://ai.lastbrain.io/fr/auth/billing", target: "_blank", rel: "noopener noreferrer", className: "ai-tooltip__link", children: "Settings" })] })] }))] }));
13
+ }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./types";
2
+ import "./styles.css";
2
3
  export * from "./context/AiProvider";
3
4
  export * from "./hooks/useAiClient";
4
5
  export * from "./hooks/useAiModels";
@@ -14,4 +15,5 @@ export * from "./components/AiSelect";
14
15
  export * from "./components/AiChipLabel";
15
16
  export * from "./components/AiImageButton";
16
17
  export * from "./components/AiSettingsButton";
18
+ export * from "./components/AiStatusButton";
17
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AAGxB,cAAc,sBAAsB,CAAC;AAGrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,+BAA+B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AAGxB,OAAO,cAAc,CAAC;AAGtB,cAAc,sBAAsB,CAAC;AAGrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  // Types
2
2
  export * from "./types";
3
+ // Styles
4
+ import "./styles.css";
3
5
  // Context
4
6
  export * from "./context/AiProvider";
5
7
  // Hooks
@@ -18,3 +20,4 @@ export * from "./components/AiSelect";
18
20
  export * from "./components/AiChipLabel";
19
21
  export * from "./components/AiImageButton";
20
22
  export * from "./components/AiSettingsButton";
23
+ export * from "./components/AiStatusButton";
@@ -0,0 +1,539 @@
1
+ /**
2
+ * @lastbrain/ai-ui-react - Styles CSS
3
+ * Framework-agnostic styles for AI UI components
4
+ */
5
+
6
+ /* ========== Variables ========== */
7
+ :root {
8
+ --ai-primary: #3b82f6;
9
+ --ai-primary-hover: #2563eb;
10
+ --ai-success: #10b981;
11
+ --ai-warning: #f59e0b;
12
+ --ai-danger: #ef4444;
13
+ --ai-border: #e5e7eb;
14
+ --ai-text: #1f2937;
15
+ --ai-text-secondary: #6b7280;
16
+ --ai-bg: #ffffff;
17
+ --ai-bg-hover: #f9fafb;
18
+ --ai-radius: 8px;
19
+ --ai-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1);
20
+ --ai-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
21
+ }
22
+
23
+ /* ========== Status Button ========== */
24
+ .ai-status-button-wrapper {
25
+ position: relative;
26
+ display: inline-block;
27
+ }
28
+
29
+ .ai-status-button {
30
+ display: inline-flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ width: 40px;
34
+ height: 40px;
35
+ border: 1px solid var(--ai-border);
36
+ border-radius: var(--ai-radius);
37
+ background: var(--ai-bg);
38
+ color: var(--ai-text);
39
+ cursor: pointer;
40
+ transition: all 0.2s;
41
+ }
42
+
43
+ .ai-status-button:hover {
44
+ background: var(--ai-bg-hover);
45
+ box-shadow: var(--ai-shadow);
46
+ }
47
+
48
+ .ai-status-button:disabled {
49
+ opacity: 0.5;
50
+ cursor: not-allowed;
51
+ }
52
+
53
+ .ai-status-button--loading {
54
+ cursor: wait;
55
+ }
56
+
57
+ .ai-status-button--success {
58
+ color: var(--ai-success);
59
+ }
60
+
61
+ .ai-status-button--error {
62
+ color: var(--ai-danger);
63
+ }
64
+
65
+ .ai-spinner {
66
+ animation: spin 1s linear infinite;
67
+ }
68
+
69
+ @keyframes spin {
70
+ from {
71
+ transform: rotate(0deg);
72
+ }
73
+ to {
74
+ transform: rotate(360deg);
75
+ }
76
+ }
77
+
78
+ /* ========== Tooltip ========== */
79
+ .ai-tooltip {
80
+ position: absolute;
81
+ top: calc(100% + 8px);
82
+ right: 0;
83
+ z-index: 1000;
84
+ min-width: 200px;
85
+ padding: 8px;
86
+ background: var(--ai-bg);
87
+ border: 1px solid var(--ai-border);
88
+ border-radius: var(--ai-radius);
89
+ box-shadow: var(--ai-shadow-lg);
90
+ font-size: 12px;
91
+ animation: fadeIn 0.2s;
92
+ }
93
+
94
+ .ai-tooltip--status {
95
+ min-width: 280px;
96
+ }
97
+
98
+ .ai-tooltip__header {
99
+ font-weight: 600;
100
+ font-size: 14px;
101
+ padding-bottom: 8px;
102
+ margin-bottom: 8px;
103
+ border-bottom: 1px solid var(--ai-border);
104
+ }
105
+
106
+ .ai-tooltip__section {
107
+ padding: 8px 0;
108
+ border-top: 1px solid var(--ai-border);
109
+ }
110
+
111
+ .ai-tooltip__section:first-child {
112
+ border-top: none;
113
+ padding-top: 0;
114
+ }
115
+
116
+ .ai-tooltip__subtitle {
117
+ font-weight: 500;
118
+ margin-bottom: 4px;
119
+ font-size: 11px;
120
+ }
121
+
122
+ .ai-tooltip__row {
123
+ display: flex;
124
+ justify-content: space-between;
125
+ gap: 12px;
126
+ margin-bottom: 4px;
127
+ }
128
+
129
+ .ai-tooltip__label {
130
+ color: var(--ai-text-secondary);
131
+ }
132
+
133
+ .ai-tooltip__value {
134
+ font-family: monospace;
135
+ font-size: 11px;
136
+ }
137
+
138
+ .ai-tooltip__value--bold {
139
+ font-weight: 600;
140
+ }
141
+
142
+ .ai-tooltip__value--capitalize {
143
+ text-transform: capitalize;
144
+ }
145
+
146
+ .ai-tooltip__value--success {
147
+ color: var(--ai-success);
148
+ }
149
+
150
+ .ai-tooltip__value--warning {
151
+ color: var(--ai-warning);
152
+ }
153
+
154
+ .ai-tooltip__actions {
155
+ display: flex;
156
+ gap: 8px;
157
+ justify-content: space-between;
158
+ padding-top: 8px;
159
+ }
160
+
161
+ .ai-tooltip__link {
162
+ flex: 1;
163
+ text-align: center;
164
+ padding: 4px 8px;
165
+ color: var(--ai-primary);
166
+ text-decoration: none;
167
+ border-radius: 4px;
168
+ transition: background 0.2s;
169
+ font-size: 11px;
170
+ }
171
+
172
+ .ai-tooltip__link:hover {
173
+ background: var(--ai-bg-hover);
174
+ }
175
+
176
+ @keyframes fadeIn {
177
+ from {
178
+ opacity: 0;
179
+ transform: translateY(-4px);
180
+ }
181
+ to {
182
+ opacity: 1;
183
+ transform: translateY(0);
184
+ }
185
+ }
186
+
187
+ /* ========== Input ========== */
188
+ .ai-input {
189
+ width: 100%;
190
+ padding: 10px 12px;
191
+ border: 1px solid var(--ai-border);
192
+ border-radius: var(--ai-radius);
193
+ font-size: 14px;
194
+ color: var(--ai-text);
195
+ background: var(--ai-bg);
196
+ transition: all 0.2s;
197
+ }
198
+
199
+ .ai-input:hover {
200
+ border-color: var(--ai-primary);
201
+ }
202
+
203
+ .ai-input:focus {
204
+ outline: none;
205
+ border-color: var(--ai-primary);
206
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
207
+ }
208
+
209
+ .ai-input:disabled {
210
+ opacity: 0.5;
211
+ cursor: not-allowed;
212
+ background: var(--ai-bg-hover);
213
+ }
214
+
215
+ .ai-input--error {
216
+ border-color: var(--ai-danger);
217
+ }
218
+
219
+ .ai-input--error:focus {
220
+ box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
221
+ }
222
+
223
+ /* ========== Textarea ========== */
224
+ .ai-textarea {
225
+ width: 100%;
226
+ min-height: 100px;
227
+ padding: 10px 12px;
228
+ border: 1px solid var(--ai-border);
229
+ border-radius: var(--ai-radius);
230
+ font-size: 14px;
231
+ font-family: inherit;
232
+ color: var(--ai-text);
233
+ background: var(--ai-bg);
234
+ resize: vertical;
235
+ transition: all 0.2s;
236
+ }
237
+
238
+ .ai-textarea:hover {
239
+ border-color: var(--ai-primary);
240
+ }
241
+
242
+ .ai-textarea:focus {
243
+ outline: none;
244
+ border-color: var(--ai-primary);
245
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
246
+ }
247
+
248
+ .ai-textarea:disabled {
249
+ opacity: 0.5;
250
+ cursor: not-allowed;
251
+ background: var(--ai-bg-hover);
252
+ }
253
+
254
+ /* ========== Button ========== */
255
+ .ai-button {
256
+ display: inline-flex;
257
+ align-items: center;
258
+ justify-content: center;
259
+ gap: 8px;
260
+ padding: 10px 16px;
261
+ border: none;
262
+ border-radius: var(--ai-radius);
263
+ font-size: 14px;
264
+ font-weight: 500;
265
+ color: white;
266
+ background: var(--ai-primary);
267
+ cursor: pointer;
268
+ transition: all 0.2s;
269
+ }
270
+
271
+ .ai-button:hover {
272
+ background: var(--ai-primary-hover);
273
+ transform: translateY(-1px);
274
+ box-shadow: var(--ai-shadow);
275
+ }
276
+
277
+ .ai-button:active {
278
+ transform: translateY(0);
279
+ }
280
+
281
+ .ai-button:disabled {
282
+ opacity: 0.5;
283
+ cursor: not-allowed;
284
+ transform: none;
285
+ }
286
+
287
+ .ai-button--secondary {
288
+ background: var(--ai-bg);
289
+ color: var(--ai-text);
290
+ border: 1px solid var(--ai-border);
291
+ }
292
+
293
+ .ai-button--secondary:hover {
294
+ background: var(--ai-bg-hover);
295
+ }
296
+
297
+ .ai-button--danger {
298
+ background: var(--ai-danger);
299
+ }
300
+
301
+ .ai-button--success {
302
+ background: var(--ai-success);
303
+ }
304
+
305
+ .ai-button--sm {
306
+ padding: 6px 12px;
307
+ font-size: 12px;
308
+ }
309
+
310
+ .ai-button--lg {
311
+ padding: 14px 20px;
312
+ font-size: 16px;
313
+ }
314
+
315
+ /* ========== Select ========== */
316
+ .ai-select {
317
+ width: 100%;
318
+ padding: 10px 12px;
319
+ border: 1px solid var(--ai-border);
320
+ border-radius: var(--ai-radius);
321
+ font-size: 14px;
322
+ color: var(--ai-text);
323
+ background: var(--ai-bg);
324
+ cursor: pointer;
325
+ transition: all 0.2s;
326
+ }
327
+
328
+ .ai-select:hover {
329
+ border-color: var(--ai-primary);
330
+ }
331
+
332
+ .ai-select:focus {
333
+ outline: none;
334
+ border-color: var(--ai-primary);
335
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
336
+ }
337
+
338
+ .ai-select:disabled {
339
+ opacity: 0.5;
340
+ cursor: not-allowed;
341
+ background: var(--ai-bg-hover);
342
+ }
343
+
344
+ /* ========== Modal ========== */
345
+ .ai-modal-overlay {
346
+ position: fixed;
347
+ inset: 0;
348
+ z-index: 9998;
349
+ background: rgba(0, 0, 0, 0.5);
350
+ display: flex;
351
+ align-items: center;
352
+ justify-content: center;
353
+ padding: 16px;
354
+ animation: fadeIn 0.2s;
355
+ }
356
+
357
+ .ai-modal {
358
+ position: relative;
359
+ z-index: 9999;
360
+ width: 100%;
361
+ max-width: 500px;
362
+ max-height: 90vh;
363
+ overflow-y: auto;
364
+ background: var(--ai-bg);
365
+ border-radius: var(--ai-radius);
366
+ box-shadow: var(--ai-shadow-lg);
367
+ animation: slideUp 0.3s;
368
+ }
369
+
370
+ .ai-modal__header {
371
+ display: flex;
372
+ align-items: center;
373
+ justify-content: space-between;
374
+ padding: 16px 20px;
375
+ border-bottom: 1px solid var(--ai-border);
376
+ }
377
+
378
+ .ai-modal__title {
379
+ font-size: 18px;
380
+ font-weight: 600;
381
+ color: var(--ai-text);
382
+ }
383
+
384
+ .ai-modal__close {
385
+ display: flex;
386
+ align-items: center;
387
+ justify-content: center;
388
+ width: 32px;
389
+ height: 32px;
390
+ border: none;
391
+ border-radius: 4px;
392
+ background: transparent;
393
+ color: var(--ai-text-secondary);
394
+ cursor: pointer;
395
+ transition: all 0.2s;
396
+ }
397
+
398
+ .ai-modal__close:hover {
399
+ background: var(--ai-bg-hover);
400
+ color: var(--ai-text);
401
+ }
402
+
403
+ .ai-modal__body {
404
+ padding: 20px;
405
+ }
406
+
407
+ .ai-modal__footer {
408
+ display: flex;
409
+ gap: 8px;
410
+ justify-content: flex-end;
411
+ padding: 16px 20px;
412
+ border-top: 1px solid var(--ai-border);
413
+ }
414
+
415
+ @keyframes slideUp {
416
+ from {
417
+ opacity: 0;
418
+ transform: translateY(20px);
419
+ }
420
+ to {
421
+ opacity: 1;
422
+ transform: translateY(0);
423
+ }
424
+ }
425
+
426
+ /* ========== Chip Label ========== */
427
+ .ai-chip {
428
+ display: inline-flex;
429
+ align-items: center;
430
+ gap: 6px;
431
+ padding: 4px 12px;
432
+ border-radius: 9999px;
433
+ font-size: 12px;
434
+ font-weight: 500;
435
+ background: var(--ai-bg-hover);
436
+ color: var(--ai-text);
437
+ }
438
+
439
+ .ai-chip--primary {
440
+ background: rgba(59, 130, 246, 0.1);
441
+ color: var(--ai-primary);
442
+ }
443
+
444
+ .ai-chip--success {
445
+ background: rgba(16, 185, 129, 0.1);
446
+ color: var(--ai-success);
447
+ }
448
+
449
+ .ai-chip--warning {
450
+ background: rgba(245, 158, 11, 0.1);
451
+ color: var(--ai-warning);
452
+ }
453
+
454
+ .ai-chip--danger {
455
+ background: rgba(239, 68, 68, 0.1);
456
+ color: var(--ai-danger);
457
+ }
458
+
459
+ .ai-chip__close {
460
+ display: flex;
461
+ align-items: center;
462
+ justify-content: center;
463
+ width: 16px;
464
+ height: 16px;
465
+ border: none;
466
+ border-radius: 50%;
467
+ background: transparent;
468
+ color: currentColor;
469
+ cursor: pointer;
470
+ transition: background 0.2s;
471
+ }
472
+
473
+ .ai-chip__close:hover {
474
+ background: rgba(0, 0, 0, 0.1);
475
+ }
476
+
477
+ /* ========== Loading States ========== */
478
+ .ai-loading {
479
+ display: inline-flex;
480
+ align-items: center;
481
+ gap: 8px;
482
+ }
483
+
484
+ .ai-loading__spinner {
485
+ width: 16px;
486
+ height: 16px;
487
+ border: 2px solid var(--ai-border);
488
+ border-top-color: var(--ai-primary);
489
+ border-radius: 50%;
490
+ animation: spin 1s linear infinite;
491
+ }
492
+
493
+ .ai-loading__text {
494
+ font-size: 14px;
495
+ color: var(--ai-text-secondary);
496
+ }
497
+
498
+ /* ========== Form Group ========== */
499
+ .ai-form-group {
500
+ margin-bottom: 16px;
501
+ }
502
+
503
+ .ai-form-label {
504
+ display: block;
505
+ margin-bottom: 6px;
506
+ font-size: 14px;
507
+ font-weight: 500;
508
+ color: var(--ai-text);
509
+ }
510
+
511
+ .ai-form-label--required::after {
512
+ content: " *";
513
+ color: var(--ai-danger);
514
+ }
515
+
516
+ .ai-form-hint {
517
+ display: block;
518
+ margin-top: 4px;
519
+ font-size: 12px;
520
+ color: var(--ai-text-secondary);
521
+ }
522
+
523
+ .ai-form-error {
524
+ display: block;
525
+ margin-top: 4px;
526
+ font-size: 12px;
527
+ color: var(--ai-danger);
528
+ }
529
+
530
+ /* ========== Dark Mode Support ========== */
531
+ @media (prefers-color-scheme: dark) {
532
+ :root {
533
+ --ai-border: #374151;
534
+ --ai-text: #f9fafb;
535
+ --ai-text-secondary: #9ca3af;
536
+ --ai-bg: #1f2937;
537
+ --ai-bg-hover: #374151;
538
+ }
539
+ }