@timbal-ai/timbal-react 0.8.1 → 1.0.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/CHANGELOG.md +40 -0
- package/README.md +69 -6
- package/dist/app.cjs +704 -56
- package/dist/app.d.cts +4 -3
- package/dist/app.d.ts +4 -3
- package/dist/app.esm.js +26 -4
- package/dist/{button-ClSgD6OF.d.cts → button-BoyX5pM_.d.cts} +1 -1
- package/dist/{button-ClSgD6OF.d.ts → button-BoyX5pM_.d.ts} +1 -1
- package/dist/{chart-artifact-DwfRtQWL.d.ts → chart-artifact-CBo9x8Ch.d.ts} +237 -13
- package/dist/{chart-artifact-DWkqIAK5.d.cts → chart-artifact-DOkwSTjQ.d.cts} +237 -13
- package/dist/chat.cjs +23 -1
- package/dist/chat.esm.js +3 -3
- package/dist/{chunk-OISVICYF.esm.js → chunk-AYHOVAMI.esm.js} +1 -1
- package/dist/{chunk-VWHHKAHN.esm.js → chunk-C6IXFM4T.esm.js} +4 -4
- package/dist/{chunk-QVAUCVQA.esm.js → chunk-FOD67Z6G.esm.js} +42 -0
- package/dist/{chunk-GBBLAM3G.esm.js → chunk-GLPOVYEA.esm.js} +776 -172
- package/dist/{chunk-CFU3YDTV.esm.js → chunk-RZ6QC6RG.esm.js} +5 -5
- package/dist/{chunk-5ZKLPWVN.esm.js → chunk-SNLXVG7H.esm.js} +1 -3
- package/dist/chunk-YEFBANNF.esm.js +3485 -0
- package/dist/index.cjs +3965 -84
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.esm.js +409 -9
- package/dist/studio.cjs +23 -1
- package/dist/studio.esm.js +5 -5
- package/dist/styles.css +194 -0
- package/dist/ui.cjs +3354 -89
- package/dist/ui.d.cts +402 -6
- package/dist/ui.d.ts +402 -6
- package/dist/ui.esm.js +384 -6
- package/package.json +3 -1
- package/dist/chunk-P4SN7M67.esm.js +0 -435
|
@@ -13,14 +13,12 @@ import {
|
|
|
13
13
|
monotoneAreaPath,
|
|
14
14
|
monotoneLinePath,
|
|
15
15
|
studioIntegrationCardClass,
|
|
16
|
-
studioSearchChromeClass,
|
|
17
|
-
studioSecondaryChromeClass,
|
|
18
16
|
studioTopbarPillHeightClass,
|
|
19
17
|
toNum
|
|
20
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-SNLXVG7H.esm.js";
|
|
21
19
|
import {
|
|
22
20
|
PillSegmentedTabs
|
|
23
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-AYHOVAMI.esm.js";
|
|
24
22
|
import {
|
|
25
23
|
Button,
|
|
26
24
|
Dialog,
|
|
@@ -31,10 +29,195 @@ import {
|
|
|
31
29
|
TIMBAL_V2_SWITCH_THUMB,
|
|
32
30
|
TIMBAL_V2_SWITCH_TRACK_OFF,
|
|
33
31
|
TimbalV2Button,
|
|
34
|
-
cn
|
|
35
|
-
|
|
32
|
+
cn,
|
|
33
|
+
controlClass
|
|
34
|
+
} from "./chunk-FOD67Z6G.esm.js";
|
|
35
|
+
|
|
36
|
+
// src/design/ui-vocabulary.ts
|
|
37
|
+
var SEMANTIC_COLOR_TOKENS = [
|
|
38
|
+
// shadcn-style base tokens
|
|
39
|
+
"background",
|
|
40
|
+
"foreground",
|
|
41
|
+
"card",
|
|
42
|
+
"card-foreground",
|
|
43
|
+
"popover",
|
|
44
|
+
"popover-foreground",
|
|
45
|
+
"primary",
|
|
46
|
+
"primary-foreground",
|
|
47
|
+
"secondary",
|
|
48
|
+
"secondary-foreground",
|
|
49
|
+
"muted",
|
|
50
|
+
"muted-foreground",
|
|
51
|
+
"accent",
|
|
52
|
+
"accent-foreground",
|
|
53
|
+
"destructive",
|
|
54
|
+
"destructive-foreground",
|
|
55
|
+
"border",
|
|
56
|
+
"input",
|
|
57
|
+
"ring",
|
|
58
|
+
// sidebar scope
|
|
59
|
+
"sidebar",
|
|
60
|
+
"sidebar-foreground",
|
|
61
|
+
"sidebar-primary",
|
|
62
|
+
"sidebar-primary-foreground",
|
|
63
|
+
"sidebar-accent",
|
|
64
|
+
"sidebar-accent-foreground",
|
|
65
|
+
"sidebar-border",
|
|
66
|
+
"sidebar-ring",
|
|
67
|
+
// timbal chrome extensions
|
|
68
|
+
"elevated-from",
|
|
69
|
+
"elevated-to",
|
|
70
|
+
"modal-from",
|
|
71
|
+
"modal-to",
|
|
72
|
+
"playground-from",
|
|
73
|
+
"playground-via",
|
|
74
|
+
"playground-to",
|
|
75
|
+
"composer-bg",
|
|
76
|
+
"composer-border",
|
|
77
|
+
"composer-border-focus",
|
|
78
|
+
"bubble-user",
|
|
79
|
+
"bubble-user-foreground",
|
|
80
|
+
"code-block-bg",
|
|
81
|
+
"code-header-bg"
|
|
82
|
+
];
|
|
83
|
+
var RESERVED_GRADIENT_TOKENS = [
|
|
84
|
+
"primary-fill-from",
|
|
85
|
+
"primary-fill-to",
|
|
86
|
+
"primary-fill-hover-from",
|
|
87
|
+
"primary-fill-hover-to",
|
|
88
|
+
"primary-fill-active-from",
|
|
89
|
+
"primary-fill-active-to",
|
|
90
|
+
"secondary-fill-hover-from",
|
|
91
|
+
"secondary-fill-hover-to",
|
|
92
|
+
"secondary-fill-active-from",
|
|
93
|
+
"secondary-fill-active-to",
|
|
94
|
+
"destructive-fill-hover-from",
|
|
95
|
+
"destructive-fill-hover-to",
|
|
96
|
+
"destructive-fill-active-from",
|
|
97
|
+
"destructive-fill-active-to",
|
|
98
|
+
"ghost-fill-hover",
|
|
99
|
+
"ghost-fill-active",
|
|
100
|
+
"elevated-from",
|
|
101
|
+
"elevated-to",
|
|
102
|
+
"modal-from",
|
|
103
|
+
"modal-to",
|
|
104
|
+
"playground-from",
|
|
105
|
+
"playground-via",
|
|
106
|
+
"playground-to"
|
|
107
|
+
];
|
|
108
|
+
var TAILWIND_PALETTE_COLORS = [
|
|
109
|
+
"slate",
|
|
110
|
+
"gray",
|
|
111
|
+
"zinc",
|
|
112
|
+
"neutral",
|
|
113
|
+
"stone",
|
|
114
|
+
"red",
|
|
115
|
+
"orange",
|
|
116
|
+
"amber",
|
|
117
|
+
"yellow",
|
|
118
|
+
"lime",
|
|
119
|
+
"green",
|
|
120
|
+
"emerald",
|
|
121
|
+
"teal",
|
|
122
|
+
"cyan",
|
|
123
|
+
"sky",
|
|
124
|
+
"blue",
|
|
125
|
+
"indigo",
|
|
126
|
+
"violet",
|
|
127
|
+
"purple",
|
|
128
|
+
"fuchsia",
|
|
129
|
+
"pink",
|
|
130
|
+
"rose"
|
|
131
|
+
];
|
|
132
|
+
var COLOR_UTILITY_PREFIXES = [
|
|
133
|
+
"bg",
|
|
134
|
+
"text",
|
|
135
|
+
"border",
|
|
136
|
+
"ring",
|
|
137
|
+
"from",
|
|
138
|
+
"via",
|
|
139
|
+
"to",
|
|
140
|
+
"fill",
|
|
141
|
+
"stroke",
|
|
142
|
+
"decoration",
|
|
143
|
+
"outline",
|
|
144
|
+
"shadow",
|
|
145
|
+
"divide",
|
|
146
|
+
"accent",
|
|
147
|
+
"caret"
|
|
148
|
+
];
|
|
149
|
+
var SLOP_BUDGETS = {
|
|
150
|
+
/** Max decorative/standalone icons rendered in a single generated file. */
|
|
151
|
+
maxIconsPerView: 6,
|
|
152
|
+
/** Max consecutive list rows separated by an explicit border/divider before
|
|
153
|
+
* it reads as a "ruled table" — prefer spacing or zebra instead. */
|
|
154
|
+
maxRowDividers: 2
|
|
155
|
+
};
|
|
156
|
+
var HOUSE_RULES = [
|
|
157
|
+
{
|
|
158
|
+
id: "semantic-color",
|
|
159
|
+
rule: "Color only through semantic tokens \u2014 never a raw palette color, hex, or oklch literal.",
|
|
160
|
+
why: "The theme generator owns every color; hardcoding breaks dark mode and rebranding.",
|
|
161
|
+
slop: `<span className="text-blue-600 bg-green-50">`,
|
|
162
|
+
good: `<span className="text-primary bg-muted">`
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
id: "no-decorative-icons",
|
|
166
|
+
rule: "Icons must earn their place (action, nav, or status). Never add an icon beside a label that already says the thing.",
|
|
167
|
+
why: "An icon on every tile/card is the #1 tell of generated slop.",
|
|
168
|
+
slop: `<StatTile label={<><BarChart2 /> Revenue</>} value="$95k" />`,
|
|
169
|
+
good: `<StatTile label="Revenue" value="$95k" />`
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
id: "neutral-trend",
|
|
173
|
+
rule: "Don't put a colored trend pill on every metric. Use a trend only when the delta is the point, and keep it muted.",
|
|
174
|
+
why: "Loud green/red pills everywhere are noise, not signal.",
|
|
175
|
+
slop: `<MetricTile trend="+8%" className="text-green-500" />`,
|
|
176
|
+
good: `<MetricTile label="Win rate" value="50%" />`
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
id: "values-normal-weight",
|
|
180
|
+
rule: "Metric values use normal font weight, not bold.",
|
|
181
|
+
why: "Giant bold numbers read as a template; normal weight reads as a product.",
|
|
182
|
+
slop: `<span className="text-3xl font-bold tabular-nums">$322k</span>`,
|
|
183
|
+
good: `<span className="text-2xl font-normal tabular-nums">$322k</span>`
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
id: "no-card-in-card",
|
|
187
|
+
rule: "Don't nest a bordered card inside another bordered card. Group with spacing or a Section instead.",
|
|
188
|
+
why: "Card-in-card doubles borders and shadows for no information gain."
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
id: "no-row-dividers",
|
|
192
|
+
rule: "Don't put a divider between every list row. Use spacing or zebra striping.",
|
|
193
|
+
why: "A rule under every row turns a clean list into a dense ledger."
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
id: "no-data-gradient",
|
|
197
|
+
rule: "Gradients are reserved for chrome (composer, elevated surface, playground). Never on a data card, tile, or table.",
|
|
198
|
+
why: "Gradient stat cards are the canonical 'AI dashboard' look."
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: "compose-from-blocks",
|
|
202
|
+
rule: "Build from premade blocks (MetricRow, MetricChartCard, DataTable, IntegrationCard). Drop to raw primitives only when no block fits.",
|
|
203
|
+
why: "Slop appears the moment generation falls below the curated block layer."
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
id: "use-kit-controls",
|
|
207
|
+
rule: "Use the kit's controls (SearchInput, Select, DropdownMenu, FieldInput, FieldSelect) \u2014 never hand-roll an input/trigger surface (`border-input rounded-* bg-\u2026`).",
|
|
208
|
+
why: "Hand-rolled controls drift from the shared control-surface skin and look foreign next to kit controls.",
|
|
209
|
+
slop: `<button className="rounded-lg border border-input bg-transparent px-3 h-9">`,
|
|
210
|
+
good: `<SelectTrigger><SelectValue /></SelectTrigger>`
|
|
211
|
+
}
|
|
212
|
+
];
|
|
36
213
|
|
|
37
214
|
// src/app/agent-instructions.ts
|
|
215
|
+
var ANTI_SLOP_CHECKLIST = HOUSE_RULES.map((r) => {
|
|
216
|
+
const pair = r.slop && r.good ? `
|
|
217
|
+
- slop: \`${r.slop}\`
|
|
218
|
+
- good: \`${r.good}\`` : "";
|
|
219
|
+
return `- **${r.id}** \u2014 ${r.rule} (${r.why})${pair}`;
|
|
220
|
+
}).join("\n");
|
|
38
221
|
var APP_KIT_AGENT_INSTRUCTIONS = `
|
|
39
222
|
## App kit (@timbal-ai/timbal-react/app)
|
|
40
223
|
|
|
@@ -82,7 +265,15 @@ Theming helpers (import from the package root or \`/app\`): \`createTimbalTheme\
|
|
|
82
265
|
| **Modals** | Use \`AppConfirmDialog\` for destructive/export confirmations. |
|
|
83
266
|
| **Metrics** | Overview KPIs \u2192 \`MetricRow\` or \`MetricChartCard\` (not four separate heavy cards). Values use **normal** font weight, not bold. |
|
|
84
267
|
| **Integrations** | Catalog \u2192 \`IntegrationCard\` grid; connected list \u2192 \`ConnectionRow\` inside \`ConnectionRowList\`. Footer CTAs: \`Button variant="secondary"\`. |
|
|
85
|
-
| **Anti-slop** | No loud green/red trend pills on every tile; no \`bg-card\` flat grids when platform chrome exists; avoid recycling demo names ("Operations", mock workforce lists). |
|
|
268
|
+
| **Anti-slop** | Follow the **anti-slop checklist** below. No loud green/red trend pills on every tile; no \`bg-card\` flat grids when platform chrome exists; avoid recycling demo names ("Operations", mock workforce lists). |
|
|
269
|
+
|
|
270
|
+
### Anti-slop checklist (required \u2014 output is linted against this)
|
|
271
|
+
|
|
272
|
+
Generated UIs are checked by \`lintGeneratedUi\` and rejected on any error. Self-review against these before returning code (icon budget: ${SLOP_BUDGETS.maxIconsPerView} per view; at most ${SLOP_BUDGETS.maxRowDividers} ruled rows before it reads as a ledger):
|
|
273
|
+
|
|
274
|
+
${ANTI_SLOP_CHECKLIST}
|
|
275
|
+
|
|
276
|
+
The cause of slop is dropping **below** the curated block layer into raw primitives + free Tailwind. Stay on the blocks; reach for primitives only when no block fits, and even then keep colors on semantic tokens.
|
|
86
277
|
|
|
87
278
|
### Accessibility (required)
|
|
88
279
|
|
|
@@ -108,7 +299,8 @@ Theming helpers (import from the package root or \`/app\`): \`createTimbalTheme\
|
|
|
108
299
|
| \`useAppShellChat\` | Custom open/close trigger when \`hideChatTrigger\` on shell. |
|
|
109
300
|
| \`Page\` | Page title, description, \`breadcrumbs\`, \`actions\`, children. |
|
|
110
301
|
| \`Section\` | Titled block inside a page. |
|
|
111
|
-
| \`SubNav\` |
|
|
302
|
+
| \`SubNav\` | **Section switcher** (Overview / Reports pill bar): \`items\`, \`activeId\`, \`onChange\`. Never use Radix/shadcn \`Tabs\` \u2014 it is not in this package. Switch panels with state or the router. |
|
|
303
|
+
| **Menus** | **Select** = short list, no search. **Combobox** = searchable (same trigger as Select). **Command** only inside \`PopoverContent variant="list"\` or Combobox \u2014 never padded default Popover. See \`examples/app-kit/src/recipes/primitives-catalog.ts\`. |
|
|
112
304
|
| \`Breadcrumbs\` | Trail: \`items: [{ label, href? }]\`. |
|
|
113
305
|
| \`Button\` | Actions \u2014 \`variant="secondary"\` for catalog/secondary CTAs; \`variant="default"\` for primary. |
|
|
114
306
|
| \`StatTile\` | Single KPI in its own card (grid of scattered stats). Prefer \`MetricRow\` for a unified overview strip. |
|
|
@@ -164,19 +356,36 @@ Theming helpers (import from the package root or \`/app\`): \`createTimbalTheme\
|
|
|
164
356
|
|
|
165
357
|
Studio chrome (\`StudioSidebar\`, \`ModeToggle\`, \u2026) lives in \`@timbal-ai/timbal-react/studio\` \u2014 optional, not required for every dashboard.
|
|
166
358
|
|
|
167
|
-
###
|
|
359
|
+
### Block recipes \u2014 compose these (don't clone wholesale)
|
|
168
360
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
361
|
+
Ready-made **section patterns** assembled from the components above. Each is a composition to rebuild in your own domain with your data \u2014 **not** an importable component. Reach for a block before dropping to raw primitives.
|
|
362
|
+
|
|
363
|
+
**Settings**
|
|
364
|
+
- **Project settings** \u2014 General / Usage / Danger sections; the floating save bar appears on first edit. Compose \`SettingsSection\` + \`FieldInput\`/\`FieldSwitch\` + \`FieldRow\` + \`InfoCard\` + \`DangerZone\` + \`FloatingUnsavedChangesBar\`.
|
|
365
|
+
- **Settings form** \u2014 compact stacked form for one concern (profile, billing). Compose \`FormSection\` + \`FieldInput\`/\`FieldSelect\`/\`FieldTextarea\`.
|
|
366
|
+
|
|
367
|
+
**Data & metrics**
|
|
368
|
+
- **Metrics row** \u2014 KPI strip in one elevated card. Compose \`MetricRow\` + \`MetricTile\`.
|
|
369
|
+
- **Analytics card** \u2014 selectable KPI tiles driving a shared chart. Compose \`MetricChartCard\` + \`LineAreaChart\`.
|
|
370
|
+
- **Charts panel** \u2014 embedded chart artifact. Compose \`ChartPanel\` + \`ChartArtifactView\`.
|
|
371
|
+
- **Table + filters** \u2014 \`FilterBar\` above a sortable \`DataTable\` (+ \`StatusBadge\` in cells).
|
|
372
|
+
|
|
373
|
+
**Collections**
|
|
374
|
+
- **Integrations grid** \u2014 connector catalog + connected list. Compose \`IntegrationCard\` + \`PlanBadge\` + \`ConnectionRowList\` (\`IntegrationsEmptyState\` when empty).
|
|
375
|
+
- **Resource gallery** \u2014 project / agent / dataset cards. Compose \`ResourceCard\` + \`StatusDot\` + \`Sparkline\`.
|
|
376
|
+
|
|
377
|
+
**Overlays & flows** (animate automatically)
|
|
378
|
+
- **Confirm & destructive** \u2014 confirm/cancel modal or destructive alert. Compose \`AppConfirmDialog\` (or \`AlertDialog\`) + \`Button\`; never hand-roll a \`Dialog\` for confirms.
|
|
379
|
+
- **Detail sheet** \u2014 slide-over edit panel without leaving the list. Compose \`Sheet\` + \`Field*\` + \`Button\` + \`Separator\`.
|
|
380
|
+
|
|
381
|
+
**States & auth**
|
|
382
|
+
- **Empty states** \u2014 no-data / no-results / first-run. Compose \`EmptyState\` + \`Card\` + \`Button\`.
|
|
383
|
+
- **Sign-in card** \u2014 centered auth entry. Compose \`Card\` + \`Input\` + \`Label\` + \`Button\`.
|
|
384
|
+
|
|
385
|
+
**Shells & theming**
|
|
386
|
+
- **Minimal shell** \u2014 \`AppShell\` + \`Page\` (no sidebar/chat).
|
|
387
|
+
- **Copilot overlay** \u2014 \`AppShell\` + floating \`AppChatPanel\`.
|
|
388
|
+
- **Theme presets** \u2014 \`ThemePresetGallery\` + \`applyTimbalTheme\` (never hand-author OKLCH).
|
|
180
389
|
|
|
181
390
|
### Typical compositions
|
|
182
391
|
|
|
@@ -187,6 +396,7 @@ Studio chrome (\`StudioSidebar\`, \`ModeToggle\`, \u2026) lives in \`@timbal-ai/
|
|
|
187
396
|
- **Integrations** \u2014 grid of \`IntegrationCard\`; \`ConnectionRowList\` for connected providers; \`IntegrationsEmptyState\` when empty.
|
|
188
397
|
- **Resource gallery** \u2014 grid of \`ResourceCard\`.
|
|
189
398
|
- **Copilot-assisted app** \u2014 \`AppCopilotProvider\` + \`AppShell\` with \`chat={<AppChatPanel workforceId="\u2026" />}\`.
|
|
399
|
+
- **Motion is automatic** \u2014 Dialog, AlertDialog, Sheet, Popover, DropdownMenu, Select, Tooltip, Toast, and Accordion/Collapsible animate out of the box (fade/zoom/slide/height) via the engine inlined in \`styles.css\`. Do not add a separate animation library or hand-write \`@keyframes\`.
|
|
190
400
|
|
|
191
401
|
### Example imports
|
|
192
402
|
|
|
@@ -222,6 +432,211 @@ import {
|
|
|
222
432
|
- For rich in-chat widgets, use **artifacts** (\`ARTIFACT_AGENT_INSTRUCTIONS\`) \u2014 app kit is for the **host application shell**.
|
|
223
433
|
`.trim();
|
|
224
434
|
|
|
435
|
+
// src/design/ui-lint.ts
|
|
436
|
+
var PALETTE_GROUP = TAILWIND_PALETTE_COLORS.join("|");
|
|
437
|
+
var PREFIX_GROUP = COLOR_UTILITY_PREFIXES.join("|");
|
|
438
|
+
var RAW_COLOR_RE = new RegExp(
|
|
439
|
+
`(?:^|[\\s"'\`:])(?:[a-z-]+:)*(?:${PREFIX_GROUP})-(?:${PALETTE_GROUP})-\\d{2,3}(?:/\\d{1,3})?`,
|
|
440
|
+
"g"
|
|
441
|
+
);
|
|
442
|
+
var COLOR_LITERAL_RE = /#[0-9a-fA-F]{3,8}\b|\b(?:oklch|rgba?|hsla?)\s*\(/g;
|
|
443
|
+
var INLINE_STYLE_COLOR_RE = /style=\{\{[^}]*\b(?:color|background|backgroundColor|borderColor|fill|stroke)\b/;
|
|
444
|
+
var BOLD_VALUE_RE = /text-(?:xl|2xl|3xl|4xl|5xl|6xl)[^"'`]*\bfont-(?:bold|extrabold|black|semibold)|font-(?:bold|extrabold|black|semibold)[^"'`]*text-(?:xl|2xl|3xl|4xl|5xl|6xl)/;
|
|
445
|
+
var GRADIENT_RE = /\bbg-(?:gradient|linear|radial|conic)-/;
|
|
446
|
+
var GRADIENT_DIRECTIONS = /* @__PURE__ */ new Set([
|
|
447
|
+
"t",
|
|
448
|
+
"tr",
|
|
449
|
+
"r",
|
|
450
|
+
"br",
|
|
451
|
+
"b",
|
|
452
|
+
"bl",
|
|
453
|
+
"l",
|
|
454
|
+
"tl"
|
|
455
|
+
]);
|
|
456
|
+
var ICON_IMPORT_RE = /from\s+["']lucide-react["']/;
|
|
457
|
+
var RAW_CONTROL_SURFACE_RE = /\bborder-input\b/;
|
|
458
|
+
var RESERVED_GRADIENT_SET = new Set(RESERVED_GRADIENT_TOKENS);
|
|
459
|
+
function stripVariants(util) {
|
|
460
|
+
return util.replace(/^(?:[a-z-]+:)*/, "");
|
|
461
|
+
}
|
|
462
|
+
function isCommentOrImport(line) {
|
|
463
|
+
const t = line.trim();
|
|
464
|
+
return t.startsWith("//") || t.startsWith("*") || t.startsWith("/*") || t.startsWith("import ") || t.startsWith("export ");
|
|
465
|
+
}
|
|
466
|
+
function lintGeneratedUi(source, options = {}) {
|
|
467
|
+
const maxIcons = options.maxIconsPerView ?? SLOP_BUDGETS.maxIconsPerView;
|
|
468
|
+
const maxRowDividers = options.maxRowDividers ?? SLOP_BUDGETS.maxRowDividers;
|
|
469
|
+
const findings = [];
|
|
470
|
+
const lines = source.split("\n");
|
|
471
|
+
let usesLucide = false;
|
|
472
|
+
let iconUsageCount = 0;
|
|
473
|
+
let dividerRunCount = 0;
|
|
474
|
+
const lucideNames = /* @__PURE__ */ new Set();
|
|
475
|
+
for (let i = 0; i < lines.length; i++) {
|
|
476
|
+
const line = lines[i];
|
|
477
|
+
const lineNo = i + 1;
|
|
478
|
+
if (ICON_IMPORT_RE.test(line)) {
|
|
479
|
+
usesLucide = true;
|
|
480
|
+
const named = line.match(/\{([^}]*)\}/);
|
|
481
|
+
if (named) {
|
|
482
|
+
for (const raw of named[1].split(",")) {
|
|
483
|
+
const name = raw.trim().split(/\s+as\s+/)[0].trim();
|
|
484
|
+
if (name) lucideNames.add(name);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
if (isCommentOrImport(line)) continue;
|
|
490
|
+
const rawColors = line.match(RAW_COLOR_RE);
|
|
491
|
+
if (rawColors) {
|
|
492
|
+
for (const m of rawColors) {
|
|
493
|
+
findings.push({
|
|
494
|
+
rule: "raw-color",
|
|
495
|
+
severity: "error",
|
|
496
|
+
line: lineNo,
|
|
497
|
+
message: "Hardcoded palette color. Use a semantic token (text-primary, bg-muted, border-border, text-muted-foreground, \u2026) so dark mode and rebranding work.",
|
|
498
|
+
snippet: m.trim().replace(/^["'`:\s]+/, "")
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
const literals = line.match(COLOR_LITERAL_RE);
|
|
503
|
+
if (literals) {
|
|
504
|
+
findings.push({
|
|
505
|
+
rule: "color-literal",
|
|
506
|
+
severity: "error",
|
|
507
|
+
line: lineNo,
|
|
508
|
+
message: "Hardcoded color literal. Colors must come from the theme generator (createTimbalTheme) and semantic tokens \u2014 never inline hex/oklch/rgb.",
|
|
509
|
+
snippet: line.trim().slice(0, 120)
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
if (INLINE_STYLE_COLOR_RE.test(line)) {
|
|
513
|
+
findings.push({
|
|
514
|
+
rule: "inline-style-color",
|
|
515
|
+
severity: "error",
|
|
516
|
+
line: lineNo,
|
|
517
|
+
message: "Inline style color. Move color to a semantic Tailwind token on className.",
|
|
518
|
+
snippet: line.trim().slice(0, 120)
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
if (RAW_CONTROL_SURFACE_RE.test(line)) {
|
|
522
|
+
findings.push({
|
|
523
|
+
rule: "raw-control-surface",
|
|
524
|
+
severity: "warn",
|
|
525
|
+
line: lineNo,
|
|
526
|
+
message: "Hand-rolled control surface (border-input). Use a kit control \u2014 SearchInput, Select, DropdownMenu, FieldInput, FieldSelect \u2014 so it matches every other control.",
|
|
527
|
+
snippet: line.trim().slice(0, 120)
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
if (BOLD_VALUE_RE.test(line)) {
|
|
531
|
+
findings.push({
|
|
532
|
+
rule: "bold-metric",
|
|
533
|
+
severity: "warn",
|
|
534
|
+
line: lineNo,
|
|
535
|
+
message: "Bold large value. House style: metric values use font-normal, not bold \u2014 bold giant numbers read as a template.",
|
|
536
|
+
snippet: line.trim().slice(0, 120)
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
if (GRADIENT_RE.test(line)) {
|
|
540
|
+
const fromTo = line.match(
|
|
541
|
+
new RegExp(`(?:from|via|to)-([a-z-]+)`, "g")
|
|
542
|
+
);
|
|
543
|
+
const colorStops = (fromTo ?? []).map((u) => stripVariants(u).replace(/^(?:from|via|to)-/, "")).filter((token) => !GRADIENT_DIRECTIONS.has(token));
|
|
544
|
+
const allReserved = colorStops.length > 0 && colorStops.every((token) => RESERVED_GRADIENT_SET.has(token));
|
|
545
|
+
if (!allReserved) {
|
|
546
|
+
findings.push({
|
|
547
|
+
rule: "data-gradient",
|
|
548
|
+
severity: "warn",
|
|
549
|
+
line: lineNo,
|
|
550
|
+
message: "Gradient outside chrome. Gradients are reserved for buttons / elevated / modal / playground \u2014 never a data card, tile, or table.",
|
|
551
|
+
snippet: line.trim().slice(0, 120)
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
if (/\b(?:border-t|border-b|divide-y)\b/.test(line)) {
|
|
556
|
+
dividerRunCount++;
|
|
557
|
+
if (dividerRunCount === maxRowDividers + 1) {
|
|
558
|
+
findings.push({
|
|
559
|
+
rule: "row-divider",
|
|
560
|
+
severity: "warn",
|
|
561
|
+
line: lineNo,
|
|
562
|
+
message: "Divider on every row. Prefer spacing (gap-*) or zebra striping over a rule under each list item.",
|
|
563
|
+
snippet: line.trim().slice(0, 120)
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
} else if (line.trim() !== "" && !line.includes("className")) {
|
|
567
|
+
if (!/^\s*[)>}/]/.test(line)) dividerRunCount = 0;
|
|
568
|
+
}
|
|
569
|
+
if (usesLucide && lucideNames.size > 0) {
|
|
570
|
+
for (const name of lucideNames) {
|
|
571
|
+
const usage = new RegExp(`<${name}\\b`, "g");
|
|
572
|
+
const hits = line.match(usage);
|
|
573
|
+
if (hits) iconUsageCount += hits.length;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
if (usesLucide && iconUsageCount > maxIcons) {
|
|
578
|
+
findings.push({
|
|
579
|
+
rule: "icon-spam",
|
|
580
|
+
severity: "warn",
|
|
581
|
+
line: 1,
|
|
582
|
+
message: `Too many icons (${iconUsageCount} > ${maxIcons}). Icons should mark actions/nav/status \u2014 not decorate every label, tile, and card.`,
|
|
583
|
+
snippet: `${iconUsageCount} lucide-react icon usages`
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
const effectiveErrors = findings.filter(
|
|
587
|
+
(f) => f.severity === "error" || options.strict && f.severity === "warn"
|
|
588
|
+
).length;
|
|
589
|
+
return {
|
|
590
|
+
findings,
|
|
591
|
+
errorCount: findings.filter((f) => f.severity === "error").length,
|
|
592
|
+
warnCount: findings.filter((f) => f.severity === "warn").length,
|
|
593
|
+
ok: effectiveErrors === 0
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
function formatLintReport(findings) {
|
|
597
|
+
if (findings.length === 0) return "";
|
|
598
|
+
const lines = findings.slice().sort((a, b) => a.line - b.line).map((f) => {
|
|
599
|
+
const tag = f.severity === "error" ? "ERROR" : "warn ";
|
|
600
|
+
return ` ${tag} L${f.line} [${f.rule}] ${f.message}
|
|
601
|
+
\u2192 ${f.snippet}`;
|
|
602
|
+
});
|
|
603
|
+
const errs = findings.filter((f) => f.severity === "error").length;
|
|
604
|
+
const warns = findings.filter((f) => f.severity === "warn").length;
|
|
605
|
+
return `Anti-slop review: ${errs} error(s), ${warns} warning(s)
|
|
606
|
+
${lines.join("\n")}`;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// src/design/ui-review.ts
|
|
610
|
+
function reviewGeneratedUi(source, options = {}) {
|
|
611
|
+
const lint = lintGeneratedUi(source, options);
|
|
612
|
+
const report = formatLintReport(lint.findings);
|
|
613
|
+
if (lint.ok) {
|
|
614
|
+
return { lint, passed: true, report, revisionPrompt: null };
|
|
615
|
+
}
|
|
616
|
+
const revisionPrompt = [
|
|
617
|
+
"The generated UI failed the Timbal anti-slop review. Fix every issue below, then return the corrected code only.",
|
|
618
|
+
"",
|
|
619
|
+
report,
|
|
620
|
+
"",
|
|
621
|
+
"Rules: colors come only from semantic tokens (text-primary, bg-muted, border-border, text-muted-foreground, \u2026) \u2014 never palette colors, hex, or oklch. Icons mark actions/nav/status, not decoration. Metric values use font-normal. No gradients on data surfaces. No divider under every row. Do not change anything that already passed."
|
|
622
|
+
].join("\n");
|
|
623
|
+
return { lint, passed: false, report, revisionPrompt };
|
|
624
|
+
}
|
|
625
|
+
var UI_REVIEW_AGENT_INSTRUCTIONS = `
|
|
626
|
+
## Self-review before returning UI (anti-slop)
|
|
627
|
+
|
|
628
|
+
Before you output any generated UI code, silently re-read it and fix anything that matches the slop checklist \u2014 this is the same rubric an automated linter applies, so output that fails it will be rejected and sent back:
|
|
629
|
+
|
|
630
|
+
- **No hardcoded colors.** Every color is a semantic token (\`text-primary\`, \`bg-muted\`, \`border-border\`, \`text-muted-foreground\`, \`bg-destructive\`, \u2026). No \`text-blue-600\`, no \`#hex\`, no \`oklch(...)\`, no \`style={{ color }}\`.
|
|
631
|
+
- **No decorative icons.** An icon must mark an action, nav target, or status. Remove icons that sit beside a label that already says the thing. Aim for very few icons per view.
|
|
632
|
+
- **Muted, sparse trends.** No colored up/down pill on every metric. Show a trend only when the change is the point.
|
|
633
|
+
- **Normal-weight values.** Metric numbers use \`font-normal\`, never \`font-bold\` at large sizes.
|
|
634
|
+
- **No card-in-card, no per-row dividers, no gradients on data surfaces.** Group with spacing/Sections; reserve gradients for chrome.
|
|
635
|
+
- **Compose from blocks.** Prefer \`MetricRow\` / \`MetricChartCard\` / \`DataTable\` / \`IntegrationCard\` over hand-assembled primitives.
|
|
636
|
+
|
|
637
|
+
If a check fails, fix it and re-read once more. Only return code that would pass clean.
|
|
638
|
+
`.trim();
|
|
639
|
+
|
|
225
640
|
// src/design/oklch.ts
|
|
226
641
|
var clamp = (n, min, max) => Math.min(max, Math.max(min, n));
|
|
227
642
|
var round = (n, digits) => {
|
|
@@ -359,6 +774,38 @@ function relativeLuminance(color) {
|
|
|
359
774
|
}
|
|
360
775
|
|
|
361
776
|
// src/design/theme.ts
|
|
777
|
+
var SHADOW_PRESETS = {
|
|
778
|
+
none: {
|
|
779
|
+
lightCard: "none",
|
|
780
|
+
lightElevated: "none",
|
|
781
|
+
darkCard: "none",
|
|
782
|
+
darkElevated: "none"
|
|
783
|
+
},
|
|
784
|
+
hairline: {
|
|
785
|
+
lightCard: "0 0 0 1px rgba(15, 23, 42, 0.06)",
|
|
786
|
+
lightElevated: "0 1px 2px rgba(15, 23, 42, 0.06)",
|
|
787
|
+
darkCard: "0 0 0 1px rgba(255, 255, 255, 0.06)",
|
|
788
|
+
darkElevated: "0 2px 8px rgba(0, 0, 0, 0.4)"
|
|
789
|
+
},
|
|
790
|
+
soft: {
|
|
791
|
+
lightCard: "0 1px 2px rgba(15, 23, 42, 0.04)",
|
|
792
|
+
lightElevated: "0 8px 30px rgba(15, 23, 42, 0.07)",
|
|
793
|
+
darkCard: "0 1px 2px rgba(0, 0, 0, 0.3)",
|
|
794
|
+
darkElevated: "0 10px 34px rgba(0, 0, 0, 0.45)"
|
|
795
|
+
},
|
|
796
|
+
medium: {
|
|
797
|
+
lightCard: "0 1px 2px -0.5px rgba(0, 0, 0, 0.05)",
|
|
798
|
+
lightElevated: "0 4px 24px rgba(0, 0, 0, 0.06)",
|
|
799
|
+
darkCard: "0 1px 3px rgba(0, 0, 0, 0.22)",
|
|
800
|
+
darkElevated: "0 4px 24px rgba(0, 0, 0, 0.35)"
|
|
801
|
+
},
|
|
802
|
+
strong: {
|
|
803
|
+
lightCard: "0 2px 6px rgba(15, 23, 42, 0.10)",
|
|
804
|
+
lightElevated: "0 16px 48px rgba(15, 23, 42, 0.16)",
|
|
805
|
+
darkCard: "0 2px 6px rgba(0, 0, 0, 0.4)",
|
|
806
|
+
darkElevated: "0 18px 50px rgba(0, 0, 0, 0.6)"
|
|
807
|
+
}
|
|
808
|
+
};
|
|
362
809
|
function primaryForMode(brand, mode) {
|
|
363
810
|
if (mode === "light") {
|
|
364
811
|
return { ...brand, l: Math.min(Math.max(brand.l, 0.42), 0.68) };
|
|
@@ -394,8 +841,26 @@ function createTimbalTheme(intent) {
|
|
|
394
841
|
const light = {};
|
|
395
842
|
const dark = {};
|
|
396
843
|
const root = {};
|
|
844
|
+
let fontFamily;
|
|
845
|
+
let fontImportUrl;
|
|
397
846
|
if (typeof intent.radius === "number") {
|
|
398
847
|
root["--radius"] = `${intent.radius}rem`;
|
|
848
|
+
root["--radius-2xl"] = `${Math.max(intent.radius + 0.25, 0)}rem`;
|
|
849
|
+
}
|
|
850
|
+
if (intent.typography) {
|
|
851
|
+
const { sans, display, mono, importUrl } = intent.typography;
|
|
852
|
+
root["--font-sans"] = sans;
|
|
853
|
+
if (display) root["--font-display"] = display;
|
|
854
|
+
if (mono) root["--font-mono"] = mono;
|
|
855
|
+
fontFamily = sans;
|
|
856
|
+
fontImportUrl = importUrl;
|
|
857
|
+
}
|
|
858
|
+
if (intent.shadow) {
|
|
859
|
+
const s = SHADOW_PRESETS[intent.shadow];
|
|
860
|
+
light["--shadow-card-value"] = s.lightCard;
|
|
861
|
+
light["--shadow-card-elevated-value"] = s.lightElevated;
|
|
862
|
+
dark["--shadow-card-value"] = s.darkCard;
|
|
863
|
+
dark["--shadow-card-elevated-value"] = s.darkElevated;
|
|
399
864
|
}
|
|
400
865
|
const primaryLight = primaryForMode(brand, "light");
|
|
401
866
|
const primaryDark = primaryForMode(brand, "dark");
|
|
@@ -493,7 +958,7 @@ function createTimbalTheme(intent) {
|
|
|
493
958
|
);
|
|
494
959
|
}
|
|
495
960
|
}
|
|
496
|
-
return { light, dark, root };
|
|
961
|
+
return { light, dark, root, fontFamily, fontImportUrl };
|
|
497
962
|
}
|
|
498
963
|
function declarations(map, indent) {
|
|
499
964
|
return Object.entries(map).map(([name, value]) => `${indent}${name}: ${value};`).join("\n");
|
|
@@ -516,6 +981,11 @@ ${declarations(theme.dark, indent)}
|
|
|
516
981
|
}`
|
|
517
982
|
);
|
|
518
983
|
}
|
|
984
|
+
if (theme.fontFamily) {
|
|
985
|
+
blocks.push(`${sel} {
|
|
986
|
+
${indent}font-family: var(--font-sans);
|
|
987
|
+
}`);
|
|
988
|
+
}
|
|
519
989
|
} else {
|
|
520
990
|
if (Object.keys(lightVars).length) {
|
|
521
991
|
blocks.push(`:root {
|
|
@@ -527,13 +997,43 @@ ${declarations(lightVars, indent)}
|
|
|
527
997
|
${declarations(theme.dark, indent)}
|
|
528
998
|
}`);
|
|
529
999
|
}
|
|
1000
|
+
if (theme.fontFamily) {
|
|
1001
|
+
blocks.push(`:root,
|
|
1002
|
+
body {
|
|
1003
|
+
${indent}font-family: var(--font-sans);
|
|
1004
|
+
}`);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
const css = blocks.join("\n\n");
|
|
1008
|
+
if (options.includeFontImport && theme.fontImportUrl) {
|
|
1009
|
+
return `@import url("${theme.fontImportUrl}");
|
|
1010
|
+
|
|
1011
|
+
${css}`;
|
|
530
1012
|
}
|
|
531
|
-
return
|
|
1013
|
+
return css;
|
|
532
1014
|
}
|
|
533
1015
|
var RUNTIME_STYLE_ID = "timbal-theme-runtime";
|
|
1016
|
+
var FONT_LINK_ATTR = "data-timbal-theme-font";
|
|
1017
|
+
function ensureThemeFontLink(url) {
|
|
1018
|
+
if (typeof document === "undefined") return;
|
|
1019
|
+
const existing = document.head.querySelector(
|
|
1020
|
+
`link[${FONT_LINK_ATTR}]`
|
|
1021
|
+
);
|
|
1022
|
+
if (!url) {
|
|
1023
|
+
existing?.remove();
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
if (existing?.getAttribute("href") === url) return;
|
|
1027
|
+
const link = existing ?? document.createElement("link");
|
|
1028
|
+
link.rel = "stylesheet";
|
|
1029
|
+
link.href = url;
|
|
1030
|
+
link.setAttribute(FONT_LINK_ATTR, "");
|
|
1031
|
+
if (!existing) document.head.appendChild(link);
|
|
1032
|
+
}
|
|
534
1033
|
function applyTimbalTheme(theme) {
|
|
535
1034
|
if (typeof document === "undefined") return () => {
|
|
536
1035
|
};
|
|
1036
|
+
ensureThemeFontLink(theme.fontImportUrl);
|
|
537
1037
|
let el = document.getElementById(RUNTIME_STYLE_ID);
|
|
538
1038
|
if (!el) {
|
|
539
1039
|
el = document.createElement("style");
|
|
@@ -544,11 +1044,13 @@ function applyTimbalTheme(theme) {
|
|
|
544
1044
|
el.textContent = themeToCss(theme);
|
|
545
1045
|
return () => {
|
|
546
1046
|
el?.parentNode?.removeChild(el);
|
|
1047
|
+
ensureThemeFontLink(void 0);
|
|
547
1048
|
};
|
|
548
1049
|
}
|
|
549
1050
|
function clearTimbalTheme() {
|
|
550
1051
|
if (typeof document === "undefined") return;
|
|
551
1052
|
document.getElementById(RUNTIME_STYLE_ID)?.remove();
|
|
1053
|
+
ensureThemeFontLink(void 0);
|
|
552
1054
|
}
|
|
553
1055
|
function isDev() {
|
|
554
1056
|
if (typeof process !== "undefined" && process.env?.NODE_ENV === "production") {
|
|
@@ -559,48 +1061,122 @@ function isDev() {
|
|
|
559
1061
|
|
|
560
1062
|
// src/design/theme-presets.ts
|
|
561
1063
|
var EMPTY_TOKENS = { light: {}, dark: {}, root: {} };
|
|
1064
|
+
var FONT_URL = {
|
|
1065
|
+
geist: "https://fonts.googleapis.com/css2?family=Geist:wght@400..600&display=swap",
|
|
1066
|
+
sora: "https://fonts.googleapis.com/css2?family=Sora:wght@400..600&display=swap",
|
|
1067
|
+
lexend: "https://fonts.googleapis.com/css2?family=Lexend:wght@400..600&display=swap",
|
|
1068
|
+
inter: "https://fonts.googleapis.com/css2?family=Inter:wght@400..600&display=swap",
|
|
1069
|
+
fraunces: "https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400..600&display=swap",
|
|
1070
|
+
jetbrains: "https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400..600&display=swap"
|
|
1071
|
+
};
|
|
1072
|
+
var STACK = {
|
|
1073
|
+
geist: '"Geist", ui-sans-serif, system-ui, sans-serif',
|
|
1074
|
+
sora: '"Sora", ui-sans-serif, system-ui, sans-serif',
|
|
1075
|
+
lexend: '"Lexend", ui-sans-serif, system-ui, sans-serif',
|
|
1076
|
+
inter: '"Inter", ui-sans-serif, system-ui, sans-serif',
|
|
1077
|
+
fraunces: '"Fraunces", ui-serif, Georgia, "Times New Roman", serif',
|
|
1078
|
+
jetbrains: '"JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace'
|
|
1079
|
+
};
|
|
562
1080
|
var TIMBAL_THEME_PRESETS = [
|
|
563
1081
|
{
|
|
564
1082
|
id: "platform",
|
|
565
1083
|
label: "Platform",
|
|
566
|
-
description: "Shipped neutral monochrome \u2014 the Timbal Platform default. Calm, brand-agnostic.",
|
|
1084
|
+
description: "Shipped neutral monochrome \u2014 the Timbal Platform default. Calm, brand-agnostic, system font.",
|
|
567
1085
|
swatch: "oklch(0.205 0 0)",
|
|
1086
|
+
font: null,
|
|
568
1087
|
tokens: EMPTY_TOKENS
|
|
569
1088
|
},
|
|
570
1089
|
{
|
|
571
1090
|
id: "indigo",
|
|
572
1091
|
label: "Indigo",
|
|
573
|
-
description: "Cool, trustworthy blue-violet \u2014
|
|
1092
|
+
description: "Cool, trustworthy blue-violet, Geist type, generous radius, soft shadows \u2014 analytics & ops dashboards.",
|
|
574
1093
|
swatch: "#4f46e5",
|
|
575
|
-
|
|
1094
|
+
font: "Geist",
|
|
1095
|
+
tokens: createTimbalTheme({
|
|
1096
|
+
brand: "#4f46e5",
|
|
1097
|
+
radius: 0.875,
|
|
1098
|
+
shadow: "soft",
|
|
1099
|
+
typography: { sans: STACK.geist, importUrl: FONT_URL.geist }
|
|
1100
|
+
})
|
|
576
1101
|
},
|
|
577
1102
|
{
|
|
578
1103
|
id: "violet",
|
|
579
1104
|
label: "Violet",
|
|
580
|
-
description: "Vivid purple \u2014 expressive
|
|
1105
|
+
description: "Vivid purple, Sora type, rounded, soft shadows \u2014 expressive product / marketing surfaces.",
|
|
581
1106
|
swatch: "#7c3aed",
|
|
582
|
-
|
|
1107
|
+
font: "Sora",
|
|
1108
|
+
tokens: createTimbalTheme({
|
|
1109
|
+
brand: "#7c3aed",
|
|
1110
|
+
radius: 1,
|
|
1111
|
+
shadow: "soft",
|
|
1112
|
+
typography: { sans: STACK.sora, importUrl: FONT_URL.sora }
|
|
1113
|
+
})
|
|
583
1114
|
},
|
|
584
1115
|
{
|
|
585
1116
|
id: "forest",
|
|
586
1117
|
label: "Forest",
|
|
587
|
-
description: "Grounded green \u2014 finance, sustainability, status-positive apps.",
|
|
1118
|
+
description: "Grounded green, Lexend type, compact radius \u2014 finance, sustainability, status-positive apps.",
|
|
588
1119
|
swatch: "#16a34a",
|
|
589
|
-
|
|
1120
|
+
font: "Lexend",
|
|
1121
|
+
tokens: createTimbalTheme({
|
|
1122
|
+
brand: "#16a34a",
|
|
1123
|
+
radius: 0.625,
|
|
1124
|
+
shadow: "soft",
|
|
1125
|
+
typography: { sans: STACK.lexend, importUrl: FONT_URL.lexend }
|
|
1126
|
+
})
|
|
590
1127
|
},
|
|
591
1128
|
{
|
|
592
1129
|
id: "warm",
|
|
593
1130
|
label: "Warm",
|
|
594
|
-
description: "Energetic orange \u2014 consumer, creative, high-engagement tools.",
|
|
1131
|
+
description: "Energetic orange, Lexend type, friendly radius \u2014 consumer, creative, high-engagement tools.",
|
|
595
1132
|
swatch: "#ea580c",
|
|
596
|
-
|
|
1133
|
+
font: "Lexend",
|
|
1134
|
+
tokens: createTimbalTheme({
|
|
1135
|
+
brand: "#ea580c",
|
|
1136
|
+
radius: 0.875,
|
|
1137
|
+
shadow: "soft",
|
|
1138
|
+
typography: { sans: STACK.lexend, importUrl: FONT_URL.lexend }
|
|
1139
|
+
})
|
|
597
1140
|
},
|
|
598
1141
|
{
|
|
599
1142
|
id: "slate",
|
|
600
1143
|
label: "Slate",
|
|
601
|
-
description: "Muted
|
|
1144
|
+
description: "Muted enterprise gray-blue, Inter type, tight radius, hairline shadows, tinted neutrals.",
|
|
602
1145
|
swatch: "#475569",
|
|
603
|
-
|
|
1146
|
+
font: "Inter",
|
|
1147
|
+
tokens: createTimbalTheme({
|
|
1148
|
+
brand: "#475569",
|
|
1149
|
+
radius: 0.5,
|
|
1150
|
+
shadow: "hairline",
|
|
1151
|
+
tintNeutrals: true,
|
|
1152
|
+
typography: { sans: STACK.inter, importUrl: FONT_URL.inter }
|
|
1153
|
+
})
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
id: "folio",
|
|
1157
|
+
label: "Folio",
|
|
1158
|
+
description: "Editorial serif (Fraunces), near-sharp corners, hairline shadows \u2014 content / docs / reports.",
|
|
1159
|
+
swatch: "#9a3412",
|
|
1160
|
+
font: "Fraunces",
|
|
1161
|
+
tokens: createTimbalTheme({
|
|
1162
|
+
brand: "#9a3412",
|
|
1163
|
+
radius: 0.25,
|
|
1164
|
+
shadow: "hairline",
|
|
1165
|
+
typography: { sans: STACK.fraunces, importUrl: FONT_URL.fraunces }
|
|
1166
|
+
})
|
|
1167
|
+
},
|
|
1168
|
+
{
|
|
1169
|
+
id: "carbon",
|
|
1170
|
+
label: "Carbon",
|
|
1171
|
+
description: "Terminal monospace (JetBrains Mono), crisp corners, green accent \u2014 developer / infra tools.",
|
|
1172
|
+
swatch: "#15803d",
|
|
1173
|
+
font: "JetBrains Mono",
|
|
1174
|
+
tokens: createTimbalTheme({
|
|
1175
|
+
brand: "#15803d",
|
|
1176
|
+
radius: 0.375,
|
|
1177
|
+
shadow: "hairline",
|
|
1178
|
+
typography: { sans: STACK.jetbrains, importUrl: FONT_URL.jetbrains }
|
|
1179
|
+
})
|
|
604
1180
|
}
|
|
605
1181
|
];
|
|
606
1182
|
var PRESET_BY_ID = new Map(
|
|
@@ -641,18 +1217,29 @@ The package ships a complete light + dark token system (\`styles.css\`). Compone
|
|
|
641
1217
|
|
|
642
1218
|
**Never write \`oklch(...)\` / hex literals or hand-author paired \`:root\` + \`.dark\` blocks.** Express intent and let the package derive a complete, contrast-correct, paired palette.
|
|
643
1219
|
|
|
644
|
-
###
|
|
1220
|
+
### Generate a full personality (color + roundness + fonts + shadows)
|
|
645
1221
|
|
|
646
1222
|
\`\`\`ts
|
|
647
1223
|
import { createTimbalTheme, themeToCss } from "@timbal-ai/timbal-react";
|
|
648
1224
|
|
|
649
|
-
const theme = createTimbalTheme({
|
|
650
|
-
|
|
651
|
-
|
|
1225
|
+
const theme = createTimbalTheme({
|
|
1226
|
+
brand: "#4f46e5",
|
|
1227
|
+
radius: 0.875, // corner roundness in rem (sets --radius + --radius-2xl)
|
|
1228
|
+
shadow: "soft", // "none" | "hairline" | "soft" | "medium" | "strong"
|
|
1229
|
+
tintNeutrals: false, // tint background/border toward the brand hue
|
|
1230
|
+
accent: "#10b981", // optional secondary accent
|
|
1231
|
+
typography: { // optional \u2014 re-skins every component's font
|
|
1232
|
+
sans: '"Geist", ui-sans-serif, system-ui, sans-serif',
|
|
1233
|
+
importUrl: "https://fonts.googleapis.com/css2?family=Geist:wght@400..600&display=swap",
|
|
1234
|
+
// display?, mono? also supported
|
|
1235
|
+
},
|
|
1236
|
+
});
|
|
1237
|
+
const css = themeToCss(theme); // paired light + dark, guaranteed in sync
|
|
652
1238
|
\`\`\`
|
|
653
1239
|
|
|
654
|
-
-
|
|
655
|
-
-
|
|
1240
|
+
- \`createTimbalTheme\` derives \`--primary\`, its foreground, ring, the full button gradient, and a soft playground tint from \`brand\`. \`radius\` sets roundness, \`shadow\` sets card depth, \`typography\` sets fonts. You only supply intent \u2014 never raw OKLCH.
|
|
1241
|
+
- For a real company, look up the actual brand hex first (brandfetch / "<company> brand color hex").
|
|
1242
|
+
- **Web fonts must be loaded.** \`applyTimbalTheme\` / \`TimbalThemeStyle\` inject the \`<link>\` for \`typography.importUrl\` automatically. For build-time \`themeToCss\`, add the \`<link rel="stylesheet" href="\u2026">\` to your \`index.html\` yourself (or pass \`themeToCss(theme, { includeFontImport: true })\` when the result is a standalone stylesheet).
|
|
656
1243
|
|
|
657
1244
|
### Apply a theme
|
|
658
1245
|
|
|
@@ -669,14 +1256,18 @@ import { TIMBAL_THEME_PRESETS, applyThemePreset } from "@timbal-ai/timbal-react"
|
|
|
669
1256
|
// TIMBAL_THEME_PRESETS: { id, label, description, swatch, tokens }[]
|
|
670
1257
|
\`\`\`
|
|
671
1258
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
|
675
|
-
|
|
676
|
-
| \`
|
|
677
|
-
| \`
|
|
678
|
-
| \`
|
|
679
|
-
| \`
|
|
1259
|
+
Each preset is a **full personality** (color + radius + shadows + font), not just a color:
|
|
1260
|
+
|
|
1261
|
+
| Preset id | Personality |
|
|
1262
|
+
|-----------|-------------|
|
|
1263
|
+
| \`platform\` | Neutral monochrome, system font (the default \u2014 no brand) |
|
|
1264
|
+
| \`indigo\` | Blue-violet, Geist, generous radius, soft shadows \u2014 analytics / ops |
|
|
1265
|
+
| \`violet\` | Purple, Sora, rounded \u2014 product / marketing |
|
|
1266
|
+
| \`forest\` | Green, Lexend, compact \u2014 finance / sustainability |
|
|
1267
|
+
| \`warm\` | Orange, Lexend, friendly \u2014 consumer / creative |
|
|
1268
|
+
| \`slate\` | Enterprise gray-blue, Inter, tight radius, hairline shadows |
|
|
1269
|
+
| \`folio\` | Editorial serif (Fraunces), near-sharp corners \u2014 content / docs |
|
|
1270
|
+
| \`carbon\` | Terminal monospace (JetBrains Mono), green accent \u2014 dev / infra |
|
|
680
1271
|
|
|
681
1272
|
- To present options visually, render \`<ThemePresetGallery value={id} onSelect={setId} />\` \u2014 each swatch previews real components (Button + metric tile) scoped via \`data-timbal-theme\`, so the live app doesn't change until the user picks.
|
|
682
1273
|
- On selection, call \`applyThemePreset(id)\` (persists to \`localStorage\` and restores on reload).
|
|
@@ -689,7 +1280,7 @@ import { TIMBAL_THEME_PRESETS, applyThemePreset } from "@timbal-ai/timbal-react"
|
|
|
689
1280
|
`.trim();
|
|
690
1281
|
|
|
691
1282
|
// src/app/theme/TimbalThemeStyle.tsx
|
|
692
|
-
import { jsx } from "react/jsx-runtime";
|
|
1283
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
693
1284
|
var TimbalThemeStyle = ({
|
|
694
1285
|
theme,
|
|
695
1286
|
preset,
|
|
@@ -700,18 +1291,21 @@ var TimbalThemeStyle = ({
|
|
|
700
1291
|
if (!tokens) return null;
|
|
701
1292
|
const css = themeToCss(tokens, scope ? { scope } : void 0);
|
|
702
1293
|
if (!css) return null;
|
|
703
|
-
return /* @__PURE__ */
|
|
704
|
-
"
|
|
705
|
-
|
|
706
|
-
"
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
1294
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1295
|
+
tokens.fontImportUrl ? /* @__PURE__ */ jsx("link", { rel: "stylesheet", href: tokens.fontImportUrl }) : null,
|
|
1296
|
+
/* @__PURE__ */ jsx(
|
|
1297
|
+
"style",
|
|
1298
|
+
{
|
|
1299
|
+
"data-timbal-theme-style": scope ?? "root",
|
|
1300
|
+
nonce,
|
|
1301
|
+
dangerouslySetInnerHTML: { __html: css }
|
|
1302
|
+
}
|
|
1303
|
+
)
|
|
1304
|
+
] });
|
|
711
1305
|
};
|
|
712
1306
|
|
|
713
1307
|
// src/app/data/metrics-shared.tsx
|
|
714
|
-
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
1308
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
715
1309
|
var metricCardShellClass = cn(
|
|
716
1310
|
studioIntegrationCardClass,
|
|
717
1311
|
"aui-app-metric-card shadow-none",
|
|
@@ -729,8 +1323,8 @@ var MetricCardHeader = ({
|
|
|
729
1323
|
actions
|
|
730
1324
|
}) => {
|
|
731
1325
|
if (!title && !description && !actions) return null;
|
|
732
|
-
return /* @__PURE__ */
|
|
733
|
-
/* @__PURE__ */
|
|
1326
|
+
return /* @__PURE__ */ jsxs2("header", { className: metricCardHeaderClass, children: [
|
|
1327
|
+
/* @__PURE__ */ jsxs2("div", { className: "min-w-0", children: [
|
|
734
1328
|
title ? /* @__PURE__ */ jsx2("h3", { id: titleId, className: "text-base font-normal text-foreground", children: title }) : null,
|
|
735
1329
|
description ? /* @__PURE__ */ jsx2("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
|
|
736
1330
|
] }),
|
|
@@ -755,7 +1349,7 @@ function metricTilesGridColsClass(n) {
|
|
|
755
1349
|
}
|
|
756
1350
|
|
|
757
1351
|
// src/app/data/MetricTile.tsx
|
|
758
|
-
import { Fragment, jsx as jsx3, jsxs as
|
|
1352
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
759
1353
|
var trendToneClass = {
|
|
760
1354
|
up: "border-border/80 bg-muted/40 text-muted-foreground",
|
|
761
1355
|
down: "border-border/80 bg-muted/40 text-muted-foreground",
|
|
@@ -779,7 +1373,7 @@ var MetricTile = ({
|
|
|
779
1373
|
ariaLabel,
|
|
780
1374
|
className
|
|
781
1375
|
}) => {
|
|
782
|
-
const content = /* @__PURE__ */
|
|
1376
|
+
const content = /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
783
1377
|
active ? /* @__PURE__ */ jsx3(
|
|
784
1378
|
"span",
|
|
785
1379
|
{
|
|
@@ -788,8 +1382,8 @@ var MetricTile = ({
|
|
|
788
1382
|
}
|
|
789
1383
|
) : null,
|
|
790
1384
|
/* @__PURE__ */ jsx3("span", { className: "text-xs font-normal text-muted-foreground", children: label }),
|
|
791
|
-
/* @__PURE__ */
|
|
792
|
-
/* @__PURE__ */
|
|
1385
|
+
/* @__PURE__ */ jsxs3("span", { className: "flex items-center gap-2", children: [
|
|
1386
|
+
/* @__PURE__ */ jsxs3("span", { className: "flex items-baseline gap-1", children: [
|
|
793
1387
|
/* @__PURE__ */ jsx3("span", { className: "text-2xl font-normal tracking-tight text-foreground tabular-nums", children: value }),
|
|
794
1388
|
unit ? /* @__PURE__ */ jsx3("span", { className: "text-xs font-normal text-muted-foreground", children: unit }) : null
|
|
795
1389
|
] }),
|
|
@@ -823,7 +1417,7 @@ var MetricTile = ({
|
|
|
823
1417
|
};
|
|
824
1418
|
|
|
825
1419
|
// src/app/theme/ThemePresetGallery.tsx
|
|
826
|
-
import { jsx as jsx4, jsxs as
|
|
1420
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
827
1421
|
var ThemePresetGallery = ({
|
|
828
1422
|
value,
|
|
829
1423
|
onSelect,
|
|
@@ -842,9 +1436,9 @@ var ThemePresetGallery = ({
|
|
|
842
1436
|
),
|
|
843
1437
|
children: items.map((preset) => {
|
|
844
1438
|
const selected = value === preset.id;
|
|
845
|
-
return /* @__PURE__ */
|
|
1439
|
+
return /* @__PURE__ */ jsxs4("div", { "data-timbal-theme": preset.id, children: [
|
|
846
1440
|
/* @__PURE__ */ jsx4(TimbalThemeStyle, { preset: preset.id, scope: preset.id }),
|
|
847
|
-
/* @__PURE__ */
|
|
1441
|
+
/* @__PURE__ */ jsxs4(
|
|
848
1442
|
"button",
|
|
849
1443
|
{
|
|
850
1444
|
type: "button",
|
|
@@ -858,8 +1452,8 @@ var ThemePresetGallery = ({
|
|
|
858
1452
|
selected ? "border-primary ring-2 ring-primary/30" : "border-border hover:border-foreground/30"
|
|
859
1453
|
),
|
|
860
1454
|
children: [
|
|
861
|
-
/* @__PURE__ */
|
|
862
|
-
/* @__PURE__ */
|
|
1455
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between gap-2", children: [
|
|
1456
|
+
/* @__PURE__ */ jsxs4("span", { className: "flex items-center gap-2", children: [
|
|
863
1457
|
/* @__PURE__ */ jsx4(
|
|
864
1458
|
"span",
|
|
865
1459
|
{
|
|
@@ -873,8 +1467,12 @@ var ThemePresetGallery = ({
|
|
|
873
1467
|
selected ? /* @__PURE__ */ jsx4("span", { className: "text-xs font-medium text-primary", children: "Selected" }) : null
|
|
874
1468
|
] }),
|
|
875
1469
|
/* @__PURE__ */ jsx4("p", { className: "text-xs leading-snug text-muted-foreground", children: preset.description }),
|
|
876
|
-
/* @__PURE__ */
|
|
877
|
-
|
|
1470
|
+
preset.font ? /* @__PURE__ */ jsxs4("span", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: [
|
|
1471
|
+
"Aa \xB7 ",
|
|
1472
|
+
preset.font
|
|
1473
|
+
] }) : null,
|
|
1474
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2 rounded-lg border border-border bg-background p-2", children: [
|
|
1475
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
878
1476
|
/* @__PURE__ */ jsx4(Button, { size: "xs", className: "pointer-events-none", children: "Primary" }),
|
|
879
1477
|
/* @__PURE__ */ jsx4("span", { className: "size-5 rounded-md bg-primary", "aria-hidden": true }),
|
|
880
1478
|
/* @__PURE__ */ jsx4("span", { className: "size-5 rounded-md bg-muted", "aria-hidden": true }),
|
|
@@ -930,18 +1528,13 @@ var appFilterBarClass = cn(
|
|
|
930
1528
|
"flex flex-wrap items-center gap-2",
|
|
931
1529
|
studioTopbarPillHeightClass
|
|
932
1530
|
);
|
|
933
|
-
var appSearchInputClass =
|
|
1531
|
+
var appSearchInputClass = controlClass({}, "inline-flex items-center gap-2");
|
|
934
1532
|
var appBreadcrumbsClass = "flex flex-wrap items-center gap-1.5 text-sm text-muted-foreground";
|
|
935
1533
|
var appBreadcrumbLinkClass = "transition-colors hover:text-foreground";
|
|
936
1534
|
var appFieldClass = "flex flex-col gap-1.5";
|
|
937
1535
|
var appFieldLabelClass = "text-sm font-medium text-foreground";
|
|
938
1536
|
var appFieldHintClass = "text-xs text-muted-foreground";
|
|
939
|
-
var appInputClass =
|
|
940
|
-
studioSecondaryChromeClass,
|
|
941
|
-
"h-10 w-full rounded-lg px-3 text-sm text-foreground outline-none",
|
|
942
|
-
"placeholder:text-muted-foreground/70",
|
|
943
|
-
"focus-visible:ring-2 focus-visible:ring-foreground/10"
|
|
944
|
-
);
|
|
1537
|
+
var appInputClass = controlClass({}, "w-full");
|
|
945
1538
|
var appEmptyStateClass = cn(
|
|
946
1539
|
appSurfaceCardClass,
|
|
947
1540
|
"flex flex-col items-center justify-center gap-2 py-12 text-center"
|
|
@@ -961,7 +1554,7 @@ function useAppShellChat() {
|
|
|
961
1554
|
// src/app/layout/AppShell.tsx
|
|
962
1555
|
import { motion, useReducedMotion } from "motion/react";
|
|
963
1556
|
import { useCallback, useState } from "react";
|
|
964
|
-
import { jsx as jsx5, jsxs as
|
|
1557
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
965
1558
|
var floatingTriggerClass = cn(
|
|
966
1559
|
"aui-app-shell-chat-trigger-fixed fixed z-50 rounded-full px-5 py-2.5 text-sm font-medium shadow-card-elevated",
|
|
967
1560
|
"bg-primary text-primary-foreground transition-colors hover:bg-primary/90",
|
|
@@ -997,7 +1590,7 @@ var AppShellBody = ({
|
|
|
997
1590
|
initial: false,
|
|
998
1591
|
animate: { paddingLeft: insetPadding },
|
|
999
1592
|
transition: layoutTransition,
|
|
1000
|
-
children: /* @__PURE__ */
|
|
1593
|
+
children: /* @__PURE__ */ jsxs5(
|
|
1001
1594
|
"div",
|
|
1002
1595
|
{
|
|
1003
1596
|
className: cn(
|
|
@@ -1065,7 +1658,7 @@ var AppShell = ({
|
|
|
1065
1658
|
children
|
|
1066
1659
|
}
|
|
1067
1660
|
);
|
|
1068
|
-
const tree = /* @__PURE__ */ jsx5(ShellInsetProvider, { value: sidebar ? reportShellInset : null, children: /* @__PURE__ */
|
|
1661
|
+
const tree = /* @__PURE__ */ jsx5(ShellInsetProvider, { value: sidebar ? reportShellInset : null, children: /* @__PURE__ */ jsxs5(
|
|
1069
1662
|
"div",
|
|
1070
1663
|
{
|
|
1071
1664
|
className: cn(
|
|
@@ -1120,15 +1713,15 @@ var AppShell = ({
|
|
|
1120
1713
|
};
|
|
1121
1714
|
|
|
1122
1715
|
// src/app/layout/AppShellTopbar.tsx
|
|
1123
|
-
import { jsx as jsx6, jsxs as
|
|
1716
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1124
1717
|
var AppShellTopbar = ({
|
|
1125
1718
|
start,
|
|
1126
1719
|
actions,
|
|
1127
1720
|
children,
|
|
1128
1721
|
className
|
|
1129
1722
|
}) => {
|
|
1130
|
-
return /* @__PURE__ */
|
|
1131
|
-
/* @__PURE__ */
|
|
1723
|
+
return /* @__PURE__ */ jsxs6("div", { className: cn("aui-app-shell-topbar", appShellTopbarRowClass, className), children: [
|
|
1724
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
|
|
1132
1725
|
start,
|
|
1133
1726
|
children
|
|
1134
1727
|
] }),
|
|
@@ -1166,15 +1759,15 @@ var AppShellChatTrigger = ({
|
|
|
1166
1759
|
};
|
|
1167
1760
|
|
|
1168
1761
|
// src/app/layout/PageHeader.tsx
|
|
1169
|
-
import { jsx as jsx8, jsxs as
|
|
1762
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1170
1763
|
var PageHeader = ({
|
|
1171
1764
|
title,
|
|
1172
1765
|
description,
|
|
1173
1766
|
actions,
|
|
1174
1767
|
className
|
|
1175
1768
|
}) => {
|
|
1176
|
-
return /* @__PURE__ */
|
|
1177
|
-
/* @__PURE__ */
|
|
1769
|
+
return /* @__PURE__ */ jsxs7("header", { className: cn("aui-app-page-header", appPageHeaderClass, className), children: [
|
|
1770
|
+
/* @__PURE__ */ jsxs7("div", { className: "min-w-0", children: [
|
|
1178
1771
|
/* @__PURE__ */ jsx8("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: title }),
|
|
1179
1772
|
description ? /* @__PURE__ */ jsx8("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
|
|
1180
1773
|
] }),
|
|
@@ -1183,14 +1776,14 @@ var PageHeader = ({
|
|
|
1183
1776
|
};
|
|
1184
1777
|
|
|
1185
1778
|
// src/app/layout/Page.tsx
|
|
1186
|
-
import { jsx as jsx9, jsxs as
|
|
1779
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1187
1780
|
var Page = ({
|
|
1188
1781
|
children,
|
|
1189
1782
|
breadcrumbs,
|
|
1190
1783
|
className,
|
|
1191
1784
|
...headerProps
|
|
1192
1785
|
}) => {
|
|
1193
|
-
return /* @__PURE__ */
|
|
1786
|
+
return /* @__PURE__ */ jsxs8("div", { className: cn("aui-app-page", appPageColumnClass, className), children: [
|
|
1194
1787
|
breadcrumbs,
|
|
1195
1788
|
/* @__PURE__ */ jsx9(PageHeader, { ...headerProps }),
|
|
1196
1789
|
children
|
|
@@ -1198,14 +1791,14 @@ var Page = ({
|
|
|
1198
1791
|
};
|
|
1199
1792
|
|
|
1200
1793
|
// src/app/layout/Section.tsx
|
|
1201
|
-
import { jsx as jsx10, jsxs as
|
|
1794
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1202
1795
|
var Section = ({
|
|
1203
1796
|
title,
|
|
1204
1797
|
description,
|
|
1205
1798
|
children,
|
|
1206
1799
|
className
|
|
1207
1800
|
}) => {
|
|
1208
|
-
return /* @__PURE__ */
|
|
1801
|
+
return /* @__PURE__ */ jsxs9("section", { className: cn("aui-app-section", appSectionClass, className), children: [
|
|
1209
1802
|
title ? /* @__PURE__ */ jsx10("h2", { className: appSectionTitleClass, children: title }) : null,
|
|
1210
1803
|
description ? /* @__PURE__ */ jsx10("p", { className: appSectionDescriptionClass, children: description }) : null,
|
|
1211
1804
|
children
|
|
@@ -1228,7 +1821,7 @@ function useAppCopilotContext() {
|
|
|
1228
1821
|
|
|
1229
1822
|
// src/app/chat/AppChatPanel.tsx
|
|
1230
1823
|
import { XIcon } from "lucide-react";
|
|
1231
|
-
import { jsx as jsx12, jsxs as
|
|
1824
|
+
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1232
1825
|
var shellClass = "aui-app-chat-panel flex h-full min-h-0 flex-col overflow-hidden";
|
|
1233
1826
|
var chromeClass = cn(
|
|
1234
1827
|
"aui-app-chat-panel-chrome relative z-20 flex min-h-10 shrink-0 items-center justify-end px-2 pt-2"
|
|
@@ -1273,7 +1866,7 @@ var AppChatPanel = ({
|
|
|
1273
1866
|
...rest
|
|
1274
1867
|
}) => {
|
|
1275
1868
|
const shellChat = useAppShellChat();
|
|
1276
|
-
return /* @__PURE__ */
|
|
1869
|
+
return /* @__PURE__ */ jsxs10("div", { className: cn(shellClass, className), children: [
|
|
1277
1870
|
shellChat?.collapsible ? /* @__PURE__ */ jsx12("div", { className: chromeClass, children: /* @__PURE__ */ jsx12(
|
|
1278
1871
|
"button",
|
|
1279
1872
|
{
|
|
@@ -1321,9 +1914,9 @@ var SurfaceCard = ({ children, className }) => {
|
|
|
1321
1914
|
};
|
|
1322
1915
|
|
|
1323
1916
|
// src/app/surfaces/StatTile.tsx
|
|
1324
|
-
import { jsx as jsx14, jsxs as
|
|
1917
|
+
import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1325
1918
|
var StatTile = ({ label, value, hint, className }) => {
|
|
1326
|
-
return /* @__PURE__ */
|
|
1919
|
+
return /* @__PURE__ */ jsxs11("div", { className: cn("aui-app-stat-tile", appStatTileClass, className), children: [
|
|
1327
1920
|
/* @__PURE__ */ jsx14("span", { className: appStatLabelClass, children: label }),
|
|
1328
1921
|
/* @__PURE__ */ jsx14("span", { className: appStatValueClass, children: value }),
|
|
1329
1922
|
hint ? /* @__PURE__ */ jsx14("span", { className: "text-xs text-muted-foreground", children: hint }) : null
|
|
@@ -1331,14 +1924,14 @@ var StatTile = ({ label, value, hint, className }) => {
|
|
|
1331
1924
|
};
|
|
1332
1925
|
|
|
1333
1926
|
// src/app/surfaces/EmptyState.tsx
|
|
1334
|
-
import { jsx as jsx15, jsxs as
|
|
1927
|
+
import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1335
1928
|
var EmptyState = ({
|
|
1336
1929
|
title,
|
|
1337
1930
|
description,
|
|
1338
1931
|
action,
|
|
1339
1932
|
className
|
|
1340
1933
|
}) => {
|
|
1341
|
-
return /* @__PURE__ */
|
|
1934
|
+
return /* @__PURE__ */ jsxs12("div", { className: cn("aui-app-empty-state", appEmptyStateClass, className), children: [
|
|
1342
1935
|
/* @__PURE__ */ jsx15("p", { className: appEmptyStateTitleClass, children: title }),
|
|
1343
1936
|
description ? /* @__PURE__ */ jsx15("p", { className: appEmptyStateDescriptionClass, children: description }) : null,
|
|
1344
1937
|
action
|
|
@@ -1373,7 +1966,7 @@ var StatusBadge = ({
|
|
|
1373
1966
|
};
|
|
1374
1967
|
|
|
1375
1968
|
// src/app/surfaces/AppConfirmDialog.tsx
|
|
1376
|
-
import { jsx as jsx17, jsxs as
|
|
1969
|
+
import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1377
1970
|
var bodyClass2 = "flex flex-col gap-4 p-6";
|
|
1378
1971
|
var titleClass = "pr-8";
|
|
1379
1972
|
var actionsClass = "flex flex-wrap justify-end gap-2";
|
|
@@ -1392,10 +1985,10 @@ var AppConfirmDialog = ({
|
|
|
1392
1985
|
DialogContent,
|
|
1393
1986
|
{
|
|
1394
1987
|
className: cn("gap-0 p-0 sm:max-w-md", className),
|
|
1395
|
-
children: /* @__PURE__ */
|
|
1988
|
+
children: /* @__PURE__ */ jsxs13("div", { className: bodyClass2, children: [
|
|
1396
1989
|
/* @__PURE__ */ jsx17(DialogTitle, { className: titleClass, children: title }),
|
|
1397
1990
|
description ? /* @__PURE__ */ jsx17("p", { className: "text-sm text-muted-foreground", children: description }) : null,
|
|
1398
|
-
/* @__PURE__ */
|
|
1991
|
+
/* @__PURE__ */ jsxs13("div", { className: actionsClass, children: [
|
|
1399
1992
|
/* @__PURE__ */ jsx17(
|
|
1400
1993
|
TimbalV2Button,
|
|
1401
1994
|
{
|
|
@@ -1426,7 +2019,7 @@ var AppConfirmDialog = ({
|
|
|
1426
2019
|
};
|
|
1427
2020
|
|
|
1428
2021
|
// src/app/surfaces/InfoCard.tsx
|
|
1429
|
-
import { jsx as jsx18, jsxs as
|
|
2022
|
+
import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1430
2023
|
var toneClass = {
|
|
1431
2024
|
neutral: "border-border bg-muted/40",
|
|
1432
2025
|
info: "border-primary/25 bg-primary/5",
|
|
@@ -1441,7 +2034,7 @@ var InfoCard = ({
|
|
|
1441
2034
|
action,
|
|
1442
2035
|
tone = "neutral",
|
|
1443
2036
|
className
|
|
1444
|
-
}) => /* @__PURE__ */
|
|
2037
|
+
}) => /* @__PURE__ */ jsxs14(
|
|
1445
2038
|
"div",
|
|
1446
2039
|
{
|
|
1447
2040
|
className: cn(
|
|
@@ -1451,7 +2044,7 @@ var InfoCard = ({
|
|
|
1451
2044
|
),
|
|
1452
2045
|
children: [
|
|
1453
2046
|
icon ? /* @__PURE__ */ jsx18("span", { className: "mt-0.5 shrink-0 text-muted-foreground", children: icon }) : null,
|
|
1454
|
-
/* @__PURE__ */
|
|
2047
|
+
/* @__PURE__ */ jsxs14("div", { className: "min-w-0 flex-1", children: [
|
|
1455
2048
|
title ? /* @__PURE__ */ jsx18("p", { className: "text-sm font-medium text-foreground", children: title }) : null,
|
|
1456
2049
|
children ? /* @__PURE__ */ jsx18("div", { className: cn("text-sm text-muted-foreground", title && "mt-1"), children }) : null
|
|
1457
2050
|
] }),
|
|
@@ -1461,7 +2054,7 @@ var InfoCard = ({
|
|
|
1461
2054
|
);
|
|
1462
2055
|
|
|
1463
2056
|
// src/app/surfaces/StatusDot.tsx
|
|
1464
|
-
import { jsx as jsx19, jsxs as
|
|
2057
|
+
import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1465
2058
|
var dotClass = {
|
|
1466
2059
|
online: "bg-emerald-500",
|
|
1467
2060
|
busy: "bg-amber-500",
|
|
@@ -1474,8 +2067,8 @@ var StatusDot = ({
|
|
|
1474
2067
|
label,
|
|
1475
2068
|
pulse = false,
|
|
1476
2069
|
className
|
|
1477
|
-
}) => /* @__PURE__ */
|
|
1478
|
-
/* @__PURE__ */
|
|
2070
|
+
}) => /* @__PURE__ */ jsxs15("span", { className: cn("inline-flex items-center gap-1.5", className), children: [
|
|
2071
|
+
/* @__PURE__ */ jsxs15("span", { className: "relative flex size-2", children: [
|
|
1479
2072
|
pulse ? /* @__PURE__ */ jsx19(
|
|
1480
2073
|
"span",
|
|
1481
2074
|
{
|
|
@@ -1491,7 +2084,7 @@ var StatusDot = ({
|
|
|
1491
2084
|
] });
|
|
1492
2085
|
|
|
1493
2086
|
// src/app/surfaces/DescriptionList.tsx
|
|
1494
|
-
import { jsx as jsx20, jsxs as
|
|
2087
|
+
import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1495
2088
|
var DescriptionList = ({
|
|
1496
2089
|
items,
|
|
1497
2090
|
stacked = false,
|
|
@@ -1503,7 +2096,7 @@ var DescriptionList = ({
|
|
|
1503
2096
|
"divide-y divide-border rounded-xl border border-border bg-card",
|
|
1504
2097
|
className
|
|
1505
2098
|
),
|
|
1506
|
-
children: items.map((item, i) => /* @__PURE__ */
|
|
2099
|
+
children: items.map((item, i) => /* @__PURE__ */ jsxs16(
|
|
1507
2100
|
"div",
|
|
1508
2101
|
{
|
|
1509
2102
|
className: cn(
|
|
@@ -1532,7 +2125,7 @@ var DescriptionList = ({
|
|
|
1532
2125
|
// src/app/surfaces/ExpandableSection.tsx
|
|
1533
2126
|
import { useId, useState as useState2 } from "react";
|
|
1534
2127
|
import { AnimatePresence, motion as motion2, useReducedMotion as useReducedMotion2 } from "motion/react";
|
|
1535
|
-
import { jsx as jsx21, jsxs as
|
|
2128
|
+
import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1536
2129
|
var Chevron = ({ open }) => /* @__PURE__ */ jsx21(
|
|
1537
2130
|
"svg",
|
|
1538
2131
|
{
|
|
@@ -1568,8 +2161,8 @@ var ExpandableSection = ({
|
|
|
1568
2161
|
if (openProp == null) setInternalOpen((o) => !o);
|
|
1569
2162
|
onOpenChange?.(!open);
|
|
1570
2163
|
};
|
|
1571
|
-
return /* @__PURE__ */
|
|
1572
|
-
/* @__PURE__ */
|
|
2164
|
+
return /* @__PURE__ */ jsxs17("div", { className: cn("border-b border-border last:border-0", className), children: [
|
|
2165
|
+
/* @__PURE__ */ jsxs17(
|
|
1573
2166
|
"button",
|
|
1574
2167
|
{
|
|
1575
2168
|
type: "button",
|
|
@@ -1578,7 +2171,7 @@ var ExpandableSection = ({
|
|
|
1578
2171
|
"aria-controls": panelId,
|
|
1579
2172
|
className: "flex w-full items-center justify-between gap-3 bg-transparent px-4 py-3 text-left hover:bg-transparent active:bg-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-foreground/10",
|
|
1580
2173
|
children: [
|
|
1581
|
-
/* @__PURE__ */
|
|
2174
|
+
/* @__PURE__ */ jsxs17("span", { className: "flex min-w-0 items-center gap-3", children: [
|
|
1582
2175
|
icon ? /* @__PURE__ */ jsx21("span", { className: "flex size-8 items-center justify-center rounded-lg border border-border bg-muted text-muted-foreground", children: icon }) : null,
|
|
1583
2176
|
/* @__PURE__ */ jsx21("span", { className: "truncate text-sm font-medium text-foreground", children: title }),
|
|
1584
2177
|
count != null ? /* @__PURE__ */ jsx21("span", { className: "rounded-full border border-border bg-muted px-2 py-0.5 text-xs text-muted-foreground", children: count }) : null
|
|
@@ -1604,7 +2197,7 @@ var ExpandableSection = ({
|
|
|
1604
2197
|
};
|
|
1605
2198
|
|
|
1606
2199
|
// src/app/surfaces/ResourceCard.tsx
|
|
1607
|
-
import { Fragment as
|
|
2200
|
+
import { Fragment as Fragment3, jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1608
2201
|
var resourceCardShellClass = cn(
|
|
1609
2202
|
"flex min-h-[8.5rem] flex-col rounded-2xl p-4 text-left font-normal",
|
|
1610
2203
|
TIMBAL_V2_ELEVATED_SURFACE
|
|
@@ -1629,16 +2222,16 @@ var ResourceCard = ({
|
|
|
1629
2222
|
ariaLabel,
|
|
1630
2223
|
className
|
|
1631
2224
|
}) => {
|
|
1632
|
-
const body = /* @__PURE__ */
|
|
1633
|
-
/* @__PURE__ */
|
|
2225
|
+
const body = /* @__PURE__ */ jsxs18(Fragment3, { children: [
|
|
2226
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex items-start gap-3", children: [
|
|
1634
2227
|
media ? /* @__PURE__ */ jsx22("span", { className: mediaShellClass, children: media }) : null,
|
|
1635
|
-
/* @__PURE__ */
|
|
2228
|
+
/* @__PURE__ */ jsxs18("div", { className: "min-w-0 flex-1 pt-0.5", children: [
|
|
1636
2229
|
/* @__PURE__ */ jsx22("p", { className: "truncate text-sm font-normal leading-snug text-foreground", children: title }),
|
|
1637
2230
|
subtitle ? /* @__PURE__ */ jsx22("p", { className: "mt-1 line-clamp-2 text-xs font-normal text-muted-foreground", children: subtitle }) : null
|
|
1638
2231
|
] }),
|
|
1639
2232
|
badge ? /* @__PURE__ */ jsx22("span", { className: "shrink-0 pt-0.5", children: badge }) : null
|
|
1640
2233
|
] }),
|
|
1641
|
-
footer || action ? /* @__PURE__ */
|
|
2234
|
+
footer || action ? /* @__PURE__ */ jsxs18("div", { className: "mt-auto flex items-center justify-between gap-3 border-t border-border/40 pt-3 text-xs font-normal text-muted-foreground", children: [
|
|
1642
2235
|
/* @__PURE__ */ jsx22("span", { className: "min-w-0 truncate", children: footer }),
|
|
1643
2236
|
action ? /* @__PURE__ */ jsx22("span", { className: "shrink-0 opacity-80", children: action }) : null
|
|
1644
2237
|
] }) : null
|
|
@@ -1650,12 +2243,12 @@ var ResourceCard = ({
|
|
|
1650
2243
|
};
|
|
1651
2244
|
|
|
1652
2245
|
// src/app/settings/SettingsSection.tsx
|
|
1653
|
-
import { jsx as jsx23, jsxs as
|
|
2246
|
+
import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1654
2247
|
var SettingsSectionHeader = ({
|
|
1655
2248
|
title,
|
|
1656
2249
|
description,
|
|
1657
2250
|
className
|
|
1658
|
-
}) => /* @__PURE__ */
|
|
2251
|
+
}) => /* @__PURE__ */ jsxs19("div", { className: cn("flex flex-col", className), children: [
|
|
1659
2252
|
/* @__PURE__ */ jsx23("h3", { className: "text-[17px] font-medium leading-tight text-foreground", children: title }),
|
|
1660
2253
|
description ? /* @__PURE__ */ jsx23("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
|
|
1661
2254
|
] });
|
|
@@ -1666,7 +2259,7 @@ var SettingsSection = ({
|
|
|
1666
2259
|
children,
|
|
1667
2260
|
noBorderTop = false,
|
|
1668
2261
|
className
|
|
1669
|
-
}) => /* @__PURE__ */
|
|
2262
|
+
}) => /* @__PURE__ */ jsxs19(
|
|
1670
2263
|
"section",
|
|
1671
2264
|
{
|
|
1672
2265
|
className: cn(
|
|
@@ -1675,7 +2268,7 @@ var SettingsSection = ({
|
|
|
1675
2268
|
className
|
|
1676
2269
|
),
|
|
1677
2270
|
children: [
|
|
1678
|
-
/* @__PURE__ */
|
|
2271
|
+
/* @__PURE__ */ jsxs19("div", { className: "min-w-0", children: [
|
|
1679
2272
|
/* @__PURE__ */ jsx23("h2", { className: "text-sm font-medium text-foreground", children: title }),
|
|
1680
2273
|
description ? /* @__PURE__ */ jsx23("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null,
|
|
1681
2274
|
descriptionFooter ? /* @__PURE__ */ jsx23("div", { className: "mt-3 min-w-0", children: descriptionFooter }) : null
|
|
@@ -1686,7 +2279,7 @@ var SettingsSection = ({
|
|
|
1686
2279
|
);
|
|
1687
2280
|
|
|
1688
2281
|
// src/app/settings/FieldRow.tsx
|
|
1689
|
-
import { jsx as jsx24, jsxs as
|
|
2282
|
+
import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1690
2283
|
var FieldRow = ({
|
|
1691
2284
|
label,
|
|
1692
2285
|
children,
|
|
@@ -1696,7 +2289,7 @@ var FieldRow = ({
|
|
|
1696
2289
|
className
|
|
1697
2290
|
}) => {
|
|
1698
2291
|
if (inline) {
|
|
1699
|
-
return /* @__PURE__ */
|
|
2292
|
+
return /* @__PURE__ */ jsxs20(
|
|
1700
2293
|
"div",
|
|
1701
2294
|
{
|
|
1702
2295
|
className: cn(
|
|
@@ -1704,7 +2297,7 @@ var FieldRow = ({
|
|
|
1704
2297
|
className
|
|
1705
2298
|
),
|
|
1706
2299
|
children: [
|
|
1707
|
-
/* @__PURE__ */
|
|
2300
|
+
/* @__PURE__ */ jsxs20("div", { className: "min-w-0", children: [
|
|
1708
2301
|
/* @__PURE__ */ jsx24(
|
|
1709
2302
|
"label",
|
|
1710
2303
|
{
|
|
@@ -1720,7 +2313,7 @@ var FieldRow = ({
|
|
|
1720
2313
|
}
|
|
1721
2314
|
);
|
|
1722
2315
|
}
|
|
1723
|
-
return /* @__PURE__ */
|
|
2316
|
+
return /* @__PURE__ */ jsxs20("div", { className: cn("flex flex-col gap-1.5", className), children: [
|
|
1724
2317
|
/* @__PURE__ */ jsx24("label", { htmlFor, className: "text-sm font-medium text-foreground", children: label }),
|
|
1725
2318
|
children,
|
|
1726
2319
|
description ? /* @__PURE__ */ jsx24("p", { className: "text-xs text-muted-foreground", children: description }) : null
|
|
@@ -1731,7 +2324,7 @@ var FieldRow = ({
|
|
|
1731
2324
|
import { useEffect, useState as useState3 } from "react";
|
|
1732
2325
|
import { createPortal } from "react-dom";
|
|
1733
2326
|
import { AnimatePresence as AnimatePresence2, motion as motion3, useReducedMotion as useReducedMotion3 } from "motion/react";
|
|
1734
|
-
import { jsx as jsx25, jsxs as
|
|
2327
|
+
import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1735
2328
|
var FloatingUnsavedChangesBar = ({
|
|
1736
2329
|
visible,
|
|
1737
2330
|
message = "Unsaved changes",
|
|
@@ -1749,7 +2342,7 @@ var FloatingUnsavedChangesBar = ({
|
|
|
1749
2342
|
useEffect(() => setMounted(true), []);
|
|
1750
2343
|
if (!mounted || typeof document === "undefined") return null;
|
|
1751
2344
|
return createPortal(
|
|
1752
|
-
/* @__PURE__ */ jsx25(AnimatePresence2, { children: visible ? /* @__PURE__ */ jsx25("div", { className: "pointer-events-none fixed inset-x-0 bottom-5 z-50 flex justify-center px-4", children: /* @__PURE__ */
|
|
2345
|
+
/* @__PURE__ */ jsx25(AnimatePresence2, { children: visible ? /* @__PURE__ */ jsx25("div", { className: "pointer-events-none fixed inset-x-0 bottom-5 z-50 flex justify-center px-4", children: /* @__PURE__ */ jsxs21(
|
|
1753
2346
|
motion3.div,
|
|
1754
2347
|
{
|
|
1755
2348
|
role: "region",
|
|
@@ -1764,7 +2357,7 @@ var FloatingUnsavedChangesBar = ({
|
|
|
1764
2357
|
),
|
|
1765
2358
|
children: [
|
|
1766
2359
|
/* @__PURE__ */ jsx25("span", { className: "text-sm text-muted-foreground", children: message }),
|
|
1767
|
-
/* @__PURE__ */
|
|
2360
|
+
/* @__PURE__ */ jsxs21("span", { className: "flex items-center gap-1.5", children: [
|
|
1768
2361
|
/* @__PURE__ */ jsx25(Button, { variant: "ghost", size: "sm", onClick: onDiscard, disabled: isSaving, children: discardLabel }),
|
|
1769
2362
|
/* @__PURE__ */ jsx25(Button, { size: "sm", onClick: onSave, disabled: saveDisabled || isSaving, children: isSaving ? "Saving\u2026" : saveLabel })
|
|
1770
2363
|
] })
|
|
@@ -1776,13 +2369,13 @@ var FloatingUnsavedChangesBar = ({
|
|
|
1776
2369
|
};
|
|
1777
2370
|
|
|
1778
2371
|
// src/app/settings/DangerZone.tsx
|
|
1779
|
-
import { jsx as jsx26, jsxs as
|
|
2372
|
+
import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1780
2373
|
var DangerZoneAction = ({
|
|
1781
2374
|
title,
|
|
1782
2375
|
description,
|
|
1783
2376
|
action,
|
|
1784
2377
|
className
|
|
1785
|
-
}) => /* @__PURE__ */
|
|
2378
|
+
}) => /* @__PURE__ */ jsxs22(
|
|
1786
2379
|
"div",
|
|
1787
2380
|
{
|
|
1788
2381
|
className: cn(
|
|
@@ -1790,7 +2383,7 @@ var DangerZoneAction = ({
|
|
|
1790
2383
|
className
|
|
1791
2384
|
),
|
|
1792
2385
|
children: [
|
|
1793
|
-
/* @__PURE__ */
|
|
2386
|
+
/* @__PURE__ */ jsxs22("div", { className: "min-w-0", children: [
|
|
1794
2387
|
/* @__PURE__ */ jsx26("p", { className: "text-sm font-medium text-foreground", children: title }),
|
|
1795
2388
|
description ? /* @__PURE__ */ jsx26("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
|
|
1796
2389
|
] }),
|
|
@@ -1803,7 +2396,7 @@ var DangerZone = ({
|
|
|
1803
2396
|
description,
|
|
1804
2397
|
children,
|
|
1805
2398
|
className
|
|
1806
|
-
}) => /* @__PURE__ */
|
|
2399
|
+
}) => /* @__PURE__ */ jsxs22(
|
|
1807
2400
|
"section",
|
|
1808
2401
|
{
|
|
1809
2402
|
className: cn(
|
|
@@ -1811,7 +2404,7 @@ var DangerZone = ({
|
|
|
1811
2404
|
className
|
|
1812
2405
|
),
|
|
1813
2406
|
children: [
|
|
1814
|
-
(title || description) && /* @__PURE__ */
|
|
2407
|
+
(title || description) && /* @__PURE__ */ jsxs22("header", { className: "border-b border-destructive/20 bg-destructive/5 px-4 py-3", children: [
|
|
1815
2408
|
title ? /* @__PURE__ */ jsx26("h3", { className: "text-sm font-semibold text-destructive", children: title }) : null,
|
|
1816
2409
|
description ? /* @__PURE__ */ jsx26("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
|
|
1817
2410
|
] }),
|
|
@@ -1822,7 +2415,7 @@ var DangerZone = ({
|
|
|
1822
2415
|
|
|
1823
2416
|
// src/app/integrations/IntegrationCard.tsx
|
|
1824
2417
|
import { useId as useId2 } from "react";
|
|
1825
|
-
import { Fragment as
|
|
2418
|
+
import { Fragment as Fragment4, jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
1826
2419
|
var INTEGRATION_CATALOG_CARD_HEIGHT_CLASS = "h-[12.25rem] min-h-[12.25rem] max-h-[12.25rem]";
|
|
1827
2420
|
var statusLabel = {
|
|
1828
2421
|
available: null,
|
|
@@ -1863,11 +2456,11 @@ var IntegrationCard = ({
|
|
|
1863
2456
|
const titleId = useId2();
|
|
1864
2457
|
const locked = status === "locked";
|
|
1865
2458
|
const dimmed = status === "disabled" || locked;
|
|
1866
|
-
const body = /* @__PURE__ */
|
|
1867
|
-
/* @__PURE__ */
|
|
2459
|
+
const body = /* @__PURE__ */ jsxs23("div", { className: "flex h-full min-h-0 flex-col", children: [
|
|
2460
|
+
/* @__PURE__ */ jsxs23("div", { className: "flex shrink-0 items-start gap-3 pr-2", children: [
|
|
1868
2461
|
logo ? /* @__PURE__ */ jsx27("span", { className: logoShellClass, "aria-hidden": Boolean(ariaLabel), children: logo }) : null,
|
|
1869
|
-
/* @__PURE__ */ jsx27("div", { className: "min-w-0 flex-1 pt-0.5", children: /* @__PURE__ */
|
|
1870
|
-
/* @__PURE__ */
|
|
2462
|
+
/* @__PURE__ */ jsx27("div", { className: "min-w-0 flex-1 pt-0.5", children: /* @__PURE__ */ jsxs23("div", { className: "flex items-start justify-between gap-2", children: [
|
|
2463
|
+
/* @__PURE__ */ jsxs23("div", { className: "min-w-0", children: [
|
|
1871
2464
|
/* @__PURE__ */ jsx27(
|
|
1872
2465
|
"h4",
|
|
1873
2466
|
{
|
|
@@ -1891,7 +2484,7 @@ var IntegrationCard = ({
|
|
|
1891
2484
|
children: description
|
|
1892
2485
|
}
|
|
1893
2486
|
) : null,
|
|
1894
|
-
action ? /* @__PURE__ */
|
|
2487
|
+
action ? /* @__PURE__ */ jsxs23(Fragment4, { children: [
|
|
1895
2488
|
/* @__PURE__ */ jsx27("div", { className: "min-h-0 flex-1", "aria-hidden": true }),
|
|
1896
2489
|
/* @__PURE__ */ jsx27("div", { className: "relative mt-3 shrink-0", children: action })
|
|
1897
2490
|
] }) : null
|
|
@@ -1925,7 +2518,7 @@ var IntegrationCard = ({
|
|
|
1925
2518
|
|
|
1926
2519
|
// src/app/integrations/IntegrationsEmptyState.tsx
|
|
1927
2520
|
import { useId as useId3 } from "react";
|
|
1928
|
-
import { jsx as jsx28, jsxs as
|
|
2521
|
+
import { jsx as jsx28, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
1929
2522
|
var IntegrationsEmptyState = ({
|
|
1930
2523
|
title = "No integrations yet",
|
|
1931
2524
|
description = "Connect a provider to start syncing data and powering your workforce.",
|
|
@@ -1934,7 +2527,7 @@ var IntegrationsEmptyState = ({
|
|
|
1934
2527
|
className
|
|
1935
2528
|
}) => {
|
|
1936
2529
|
const titleId = useId3();
|
|
1937
|
-
return /* @__PURE__ */
|
|
2530
|
+
return /* @__PURE__ */ jsxs24(
|
|
1938
2531
|
"section",
|
|
1939
2532
|
{
|
|
1940
2533
|
className: cn(
|
|
@@ -1970,7 +2563,7 @@ var planBadgeClass = "inline-flex h-5 max-w-full shrink-0 items-center rounded-m
|
|
|
1970
2563
|
var PlanBadge = ({ children, className }) => /* @__PURE__ */ jsx29("span", { className: cn(planBadgeClass, className), children });
|
|
1971
2564
|
|
|
1972
2565
|
// src/app/integrations/ConnectionRow.tsx
|
|
1973
|
-
import { Fragment as
|
|
2566
|
+
import { Fragment as Fragment5, jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
1974
2567
|
var logoShellClass2 = cn(
|
|
1975
2568
|
"flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-lg",
|
|
1976
2569
|
TIMBAL_V2_LOGO_TILE
|
|
@@ -1985,9 +2578,9 @@ var ConnectionRow = ({
|
|
|
1985
2578
|
ariaLabel,
|
|
1986
2579
|
className
|
|
1987
2580
|
}) => {
|
|
1988
|
-
const inner = /* @__PURE__ */
|
|
2581
|
+
const inner = /* @__PURE__ */ jsxs25(Fragment5, { children: [
|
|
1989
2582
|
logo ? /* @__PURE__ */ jsx30("span", { className: logoShellClass2, children: logo }) : null,
|
|
1990
|
-
/* @__PURE__ */
|
|
2583
|
+
/* @__PURE__ */ jsxs25("div", { className: "min-w-0 flex-1", children: [
|
|
1991
2584
|
/* @__PURE__ */ jsx30("p", { className: "truncate text-sm font-normal text-foreground", children: name }),
|
|
1992
2585
|
meta ? /* @__PURE__ */ jsx30("p", { className: "truncate text-xs text-muted-foreground", children: meta }) : null
|
|
1993
2586
|
] }),
|
|
@@ -2059,11 +2652,11 @@ var SubNav = ({
|
|
|
2059
2652
|
};
|
|
2060
2653
|
|
|
2061
2654
|
// src/app/navigation/Breadcrumbs.tsx
|
|
2062
|
-
import { jsx as jsx33, jsxs as
|
|
2655
|
+
import { jsx as jsx33, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2063
2656
|
var Breadcrumbs = ({ items, className }) => {
|
|
2064
2657
|
return /* @__PURE__ */ jsx33("nav", { className: cn("aui-app-breadcrumbs", appBreadcrumbsClass, className), "aria-label": "Breadcrumb", children: /* @__PURE__ */ jsx33("ol", { className: "flex flex-wrap items-center gap-1.5", children: items.map((item, index) => {
|
|
2065
2658
|
const isLast = index === items.length - 1;
|
|
2066
|
-
return /* @__PURE__ */
|
|
2659
|
+
return /* @__PURE__ */ jsxs26("li", { className: "inline-flex items-center gap-1.5", children: [
|
|
2067
2660
|
index > 0 ? /* @__PURE__ */ jsx33("span", { className: "text-muted-foreground/50", "aria-hidden": true, children: "/" }) : null,
|
|
2068
2661
|
isLast ? /* @__PURE__ */ jsx33("span", { className: "text-foreground", "aria-current": "page", children: item.label }) : item.href ? /* @__PURE__ */ jsx33("a", { href: item.href, className: appBreadcrumbLinkClass, children: item.label }) : /* @__PURE__ */ jsx33(
|
|
2069
2662
|
"button",
|
|
@@ -2079,7 +2672,7 @@ var Breadcrumbs = ({ items, className }) => {
|
|
|
2079
2672
|
};
|
|
2080
2673
|
|
|
2081
2674
|
// src/app/forms/Field.tsx
|
|
2082
|
-
import { jsx as jsx34, jsxs as
|
|
2675
|
+
import { jsx as jsx34, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2083
2676
|
var Field = ({
|
|
2084
2677
|
label,
|
|
2085
2678
|
hint,
|
|
@@ -2088,7 +2681,7 @@ var Field = ({
|
|
|
2088
2681
|
className,
|
|
2089
2682
|
htmlFor
|
|
2090
2683
|
}) => {
|
|
2091
|
-
return /* @__PURE__ */
|
|
2684
|
+
return /* @__PURE__ */ jsxs27("div", { className: cn("aui-app-field", appFieldClass, className), children: [
|
|
2092
2685
|
/* @__PURE__ */ jsx34("label", { className: appFieldLabelClass, htmlFor, children: label }),
|
|
2093
2686
|
children,
|
|
2094
2687
|
hint && !error ? /* @__PURE__ */ jsx34("p", { className: appFieldHintClass, children: hint }) : null,
|
|
@@ -2165,7 +2758,7 @@ var FieldTextarea = ({
|
|
|
2165
2758
|
|
|
2166
2759
|
// src/app/forms/FieldSelect.tsx
|
|
2167
2760
|
import { ChevronDownIcon } from "lucide-react";
|
|
2168
|
-
import { jsx as jsx36, jsxs as
|
|
2761
|
+
import { jsx as jsx36, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
2169
2762
|
var selectWrapClass = "relative";
|
|
2170
2763
|
var selectClass = cn(
|
|
2171
2764
|
appInputClass,
|
|
@@ -2190,7 +2783,7 @@ var FieldSelect = ({
|
|
|
2190
2783
|
error,
|
|
2191
2784
|
htmlFor: selectId,
|
|
2192
2785
|
className: fieldClassName,
|
|
2193
|
-
children: /* @__PURE__ */
|
|
2786
|
+
children: /* @__PURE__ */ jsxs28("div", { className: selectWrapClass, children: [
|
|
2194
2787
|
/* @__PURE__ */ jsx36(
|
|
2195
2788
|
"select",
|
|
2196
2789
|
{
|
|
@@ -2214,7 +2807,7 @@ var FieldSelect = ({
|
|
|
2214
2807
|
};
|
|
2215
2808
|
|
|
2216
2809
|
// src/app/forms/FieldSwitch.tsx
|
|
2217
|
-
import { jsx as jsx37, jsxs as
|
|
2810
|
+
import { jsx as jsx37, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
2218
2811
|
var trackClass = cn(
|
|
2219
2812
|
"relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-[background,box-shadow,border-color] duration-200",
|
|
2220
2813
|
"peer-focus-visible:ring-2 peer-focus-visible:ring-foreground/10",
|
|
@@ -2235,7 +2828,7 @@ var FieldSwitch = ({
|
|
|
2235
2828
|
...props
|
|
2236
2829
|
}) => {
|
|
2237
2830
|
const inputId = id ?? props.name ?? "switch";
|
|
2238
|
-
return /* @__PURE__ */
|
|
2831
|
+
return /* @__PURE__ */ jsxs29(
|
|
2239
2832
|
"label",
|
|
2240
2833
|
{
|
|
2241
2834
|
className: cn(
|
|
@@ -2244,7 +2837,7 @@ var FieldSwitch = ({
|
|
|
2244
2837
|
),
|
|
2245
2838
|
htmlFor: inputId,
|
|
2246
2839
|
children: [
|
|
2247
|
-
/* @__PURE__ */
|
|
2840
|
+
/* @__PURE__ */ jsxs29("span", { className: "relative mt-0.5", children: [
|
|
2248
2841
|
/* @__PURE__ */ jsx37(
|
|
2249
2842
|
"input",
|
|
2250
2843
|
{
|
|
@@ -2257,7 +2850,7 @@ var FieldSwitch = ({
|
|
|
2257
2850
|
),
|
|
2258
2851
|
/* @__PURE__ */ jsx37("span", { className: trackClass, "aria-hidden": true, children: /* @__PURE__ */ jsx37("span", { className: thumbClass }) })
|
|
2259
2852
|
] }),
|
|
2260
|
-
/* @__PURE__ */
|
|
2853
|
+
/* @__PURE__ */ jsxs29("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
|
|
2261
2854
|
/* @__PURE__ */ jsx37("span", { className: "text-sm font-medium text-foreground", children: label }),
|
|
2262
2855
|
description ? /* @__PURE__ */ jsx37("span", { className: "text-xs text-muted-foreground", children: description }) : null
|
|
2263
2856
|
] })
|
|
@@ -2268,13 +2861,13 @@ var FieldSwitch = ({
|
|
|
2268
2861
|
|
|
2269
2862
|
// src/app/forms/SearchInput.tsx
|
|
2270
2863
|
import { SearchIcon } from "lucide-react";
|
|
2271
|
-
import { jsx as jsx38, jsxs as
|
|
2864
|
+
import { jsx as jsx38, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
2272
2865
|
var SearchInput = ({
|
|
2273
2866
|
className,
|
|
2274
2867
|
placeholder = "Search\u2026",
|
|
2275
2868
|
...props
|
|
2276
2869
|
}) => {
|
|
2277
|
-
return /* @__PURE__ */
|
|
2870
|
+
return /* @__PURE__ */ jsxs30(
|
|
2278
2871
|
"label",
|
|
2279
2872
|
{
|
|
2280
2873
|
className: cn(
|
|
@@ -2299,9 +2892,9 @@ var SearchInput = ({
|
|
|
2299
2892
|
};
|
|
2300
2893
|
|
|
2301
2894
|
// src/app/forms/FormSection.tsx
|
|
2302
|
-
import { jsx as jsx39, jsxs as
|
|
2895
|
+
import { jsx as jsx39, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
2303
2896
|
var FormSection = ({ title, children, className }) => {
|
|
2304
|
-
return /* @__PURE__ */
|
|
2897
|
+
return /* @__PURE__ */ jsxs31("fieldset", { className: cn("aui-app-form-section", appSectionClass, "border-0 p-0", className), children: [
|
|
2305
2898
|
title ? /* @__PURE__ */ jsx39("legend", { className: cn(appSectionTitleClass, "mb-3 px-0"), children: title }) : null,
|
|
2306
2899
|
/* @__PURE__ */ jsx39("div", { className: "flex flex-col gap-4", children })
|
|
2307
2900
|
] });
|
|
@@ -2324,7 +2917,7 @@ var FilterBar = ({ children, className }) => {
|
|
|
2324
2917
|
// src/app/data/DataTable.tsx
|
|
2325
2918
|
import { useMemo, useState as useState4 } from "react";
|
|
2326
2919
|
import { ArrowDownIcon, ArrowUpDownIcon, ArrowUpIcon } from "lucide-react";
|
|
2327
|
-
import { jsx as jsx41, jsxs as
|
|
2920
|
+
import { jsx as jsx41, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
2328
2921
|
var shellClass2 = "overflow-hidden rounded-xl border border-border bg-card shadow-card";
|
|
2329
2922
|
var tableClass = "w-full border-collapse bg-transparent text-sm";
|
|
2330
2923
|
var headCellClass = "border-b border-border/60 bg-transparent px-4 py-2.5 text-left text-xs font-medium uppercase tracking-wide text-muted-foreground";
|
|
@@ -2424,12 +3017,12 @@ function DataTable({
|
|
|
2424
3017
|
}
|
|
2425
3018
|
const rowCountText = rowCountLabel?.(sortedRows.length) ?? `${sortedRows.length} row${sortedRows.length === 1 ? "" : "s"}`;
|
|
2426
3019
|
const hasFoot = Boolean((showRowCount || footer) && sortedRows.length > 0);
|
|
2427
|
-
return /* @__PURE__ */ jsx41("div", { className: cn("aui-app-data-table", shellClass2, className), children: /* @__PURE__ */ jsx41("div", { className: "overflow-x-auto", children: /* @__PURE__ */
|
|
3020
|
+
return /* @__PURE__ */ jsx41("div", { className: cn("aui-app-data-table", shellClass2, className), children: /* @__PURE__ */ jsx41("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs32("table", { className: tableClass, children: [
|
|
2428
3021
|
caption ? /* @__PURE__ */ jsx41("caption", { className: "sr-only", children: caption }) : null,
|
|
2429
3022
|
/* @__PURE__ */ jsx41("thead", { className: cn(stickyHeader && stickyHeadClass), children: /* @__PURE__ */ jsx41("tr", { children: columns.map((col) => {
|
|
2430
3023
|
const isSorted = sort?.columnId === col.id;
|
|
2431
3024
|
const ariaSort = col.sortable ? isSorted ? sort.direction === "asc" ? "ascending" : "descending" : "none" : void 0;
|
|
2432
|
-
const headerContent = col.sortable ? /* @__PURE__ */
|
|
3025
|
+
const headerContent = col.sortable ? /* @__PURE__ */ jsxs32(
|
|
2433
3026
|
"button",
|
|
2434
3027
|
{
|
|
2435
3028
|
type: "button",
|
|
@@ -2457,7 +3050,7 @@ function DataTable({
|
|
|
2457
3050
|
col.id
|
|
2458
3051
|
);
|
|
2459
3052
|
}) }) }),
|
|
2460
|
-
/* @__PURE__ */ jsx41("tbody", { className: cn(!hasFoot && "[&_tr:last-child_td]:border-b-0"), children: sortedRows.length === 0 ? /* @__PURE__ */ jsx41("tr", { children: /* @__PURE__ */ jsx41("td", { colSpan: columns.length, className: emptyCellClass, children: /* @__PURE__ */
|
|
3053
|
+
/* @__PURE__ */ jsx41("tbody", { className: cn(!hasFoot && "[&_tr:last-child_td]:border-b-0"), children: sortedRows.length === 0 ? /* @__PURE__ */ jsx41("tr", { children: /* @__PURE__ */ jsx41("td", { colSpan: columns.length, className: emptyCellClass, children: /* @__PURE__ */ jsxs32("div", { className: "flex flex-col items-center gap-1", children: [
|
|
2461
3054
|
/* @__PURE__ */ jsx41("p", { className: "font-medium text-foreground", children: emptyTitle }),
|
|
2462
3055
|
emptyDescription ? /* @__PURE__ */ jsx41("p", { className: "max-w-sm text-muted-foreground", children: emptyDescription }) : null
|
|
2463
3056
|
] }) }) }) : sortedRows.map((row) => /* @__PURE__ */ jsx41(
|
|
@@ -2490,7 +3083,7 @@ function DataTable({
|
|
|
2490
3083
|
},
|
|
2491
3084
|
getRowKey(row)
|
|
2492
3085
|
)) }),
|
|
2493
|
-
hasFoot ? /* @__PURE__ */ jsx41("tfoot", { children: /* @__PURE__ */ jsx41("tr", { children: /* @__PURE__ */ jsx41("td", { colSpan: columns.length, className: footCellClass, children: /* @__PURE__ */
|
|
3086
|
+
hasFoot ? /* @__PURE__ */ jsx41("tfoot", { children: /* @__PURE__ */ jsx41("tr", { children: /* @__PURE__ */ jsx41("td", { colSpan: columns.length, className: footCellClass, children: /* @__PURE__ */ jsxs32(
|
|
2494
3087
|
"div",
|
|
2495
3088
|
{
|
|
2496
3089
|
className: cn(
|
|
@@ -2508,7 +3101,7 @@ function DataTable({
|
|
|
2508
3101
|
|
|
2509
3102
|
// src/app/data/ChartPanel.tsx
|
|
2510
3103
|
import { useId as useId4 } from "react";
|
|
2511
|
-
import { jsx as jsx42, jsxs as
|
|
3104
|
+
import { jsx as jsx42, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
2512
3105
|
var ChartPanel = ({
|
|
2513
3106
|
title,
|
|
2514
3107
|
description,
|
|
@@ -2522,7 +3115,7 @@ var ChartPanel = ({
|
|
|
2522
3115
|
const resolvedTitle = title ?? artifact?.title;
|
|
2523
3116
|
const hasHeader = Boolean(resolvedTitle || description || actions);
|
|
2524
3117
|
const body = children ?? (artifact ? /* @__PURE__ */ jsx42(ChartArtifactView, { artifact, embedded: true, height }) : null);
|
|
2525
|
-
return /* @__PURE__ */
|
|
3118
|
+
return /* @__PURE__ */ jsxs33(
|
|
2526
3119
|
"section",
|
|
2527
3120
|
{
|
|
2528
3121
|
className: cn(metricCardShellClass, "aui-app-chart-panel", className),
|
|
@@ -2562,7 +3155,7 @@ var ChartPanel = ({
|
|
|
2562
3155
|
|
|
2563
3156
|
// src/app/data/MetricRow.tsx
|
|
2564
3157
|
import { useId as useId5, useState as useState5 } from "react";
|
|
2565
|
-
import { jsx as jsx43, jsxs as
|
|
3158
|
+
import { jsx as jsx43, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
2566
3159
|
var MetricRow = ({
|
|
2567
3160
|
title,
|
|
2568
3161
|
description,
|
|
@@ -2584,7 +3177,7 @@ var MetricRow = ({
|
|
|
2584
3177
|
if (activeMetricId == null) setInternalId(id);
|
|
2585
3178
|
onMetricChange?.(id);
|
|
2586
3179
|
};
|
|
2587
|
-
return /* @__PURE__ */
|
|
3180
|
+
return /* @__PURE__ */ jsxs34(
|
|
2588
3181
|
"section",
|
|
2589
3182
|
{
|
|
2590
3183
|
className: cn(metricCardShellClass, className),
|
|
@@ -2632,7 +3225,7 @@ var MetricRow = ({
|
|
|
2632
3225
|
|
|
2633
3226
|
// src/app/data/MetricChartCard.tsx
|
|
2634
3227
|
import { useId as useId6, useState as useState6 } from "react";
|
|
2635
|
-
import { jsx as jsx44, jsxs as
|
|
3228
|
+
import { jsx as jsx44, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
2636
3229
|
var MetricChartCard = ({
|
|
2637
3230
|
title,
|
|
2638
3231
|
description,
|
|
@@ -2662,7 +3255,7 @@ var MetricChartCard = ({
|
|
|
2662
3255
|
};
|
|
2663
3256
|
const hasHeader = Boolean(title || description || actions);
|
|
2664
3257
|
const chartAriaLabel = typeof active?.label === "string" ? `${active.label} over time` : "Metric chart";
|
|
2665
|
-
return /* @__PURE__ */
|
|
3258
|
+
return /* @__PURE__ */ jsxs35(
|
|
2666
3259
|
"section",
|
|
2667
3260
|
{
|
|
2668
3261
|
className: cn(metricCardShellClass, className),
|
|
@@ -2739,7 +3332,7 @@ var MetricChartCard = ({
|
|
|
2739
3332
|
|
|
2740
3333
|
// src/charts/sparkline.tsx
|
|
2741
3334
|
import { useId as useId7 } from "react";
|
|
2742
|
-
import { Fragment as
|
|
3335
|
+
import { Fragment as Fragment6, jsx as jsx45, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
2743
3336
|
var Sparkline = ({
|
|
2744
3337
|
data,
|
|
2745
3338
|
dataKey = "value",
|
|
@@ -2766,7 +3359,7 @@ var Sparkline = ({
|
|
|
2766
3359
|
x: pad + (values.length > 1 ? i / (values.length - 1) * innerW : innerW / 2),
|
|
2767
3360
|
y: pad + innerH - (v - min) / range * innerH
|
|
2768
3361
|
}));
|
|
2769
|
-
return /* @__PURE__ */
|
|
3362
|
+
return /* @__PURE__ */ jsxs36(
|
|
2770
3363
|
"svg",
|
|
2771
3364
|
{
|
|
2772
3365
|
width,
|
|
@@ -2777,8 +3370,8 @@ var Sparkline = ({
|
|
|
2777
3370
|
"aria-label": ariaLabel,
|
|
2778
3371
|
preserveAspectRatio: "none",
|
|
2779
3372
|
children: [
|
|
2780
|
-
area && /* @__PURE__ */
|
|
2781
|
-
/* @__PURE__ */ jsx45("defs", { children: /* @__PURE__ */
|
|
3373
|
+
area && /* @__PURE__ */ jsxs36(Fragment6, { children: [
|
|
3374
|
+
/* @__PURE__ */ jsx45("defs", { children: /* @__PURE__ */ jsxs36("linearGradient", { id: `${uid}-spark`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
|
|
2782
3375
|
/* @__PURE__ */ jsx45("stop", { offset: "0%", style: { stopColor: color, stopOpacity: 0.25 } }),
|
|
2783
3376
|
/* @__PURE__ */ jsx45("stop", { offset: "100%", style: { stopColor: color, stopOpacity: 0 } })
|
|
2784
3377
|
] }) }),
|
|
@@ -2801,9 +3394,20 @@ var Sparkline = ({
|
|
|
2801
3394
|
};
|
|
2802
3395
|
|
|
2803
3396
|
export {
|
|
3397
|
+
SEMANTIC_COLOR_TOKENS,
|
|
3398
|
+
RESERVED_GRADIENT_TOKENS,
|
|
3399
|
+
TAILWIND_PALETTE_COLORS,
|
|
3400
|
+
COLOR_UTILITY_PREFIXES,
|
|
3401
|
+
SLOP_BUDGETS,
|
|
3402
|
+
HOUSE_RULES,
|
|
2804
3403
|
APP_KIT_AGENT_INSTRUCTIONS,
|
|
3404
|
+
lintGeneratedUi,
|
|
3405
|
+
formatLintReport,
|
|
3406
|
+
reviewGeneratedUi,
|
|
3407
|
+
UI_REVIEW_AGENT_INSTRUCTIONS,
|
|
2805
3408
|
createTimbalTheme,
|
|
2806
3409
|
themeToCss,
|
|
3410
|
+
ensureThemeFontLink,
|
|
2807
3411
|
applyTimbalTheme,
|
|
2808
3412
|
clearTimbalTheme,
|
|
2809
3413
|
TIMBAL_THEME_PRESETS,
|