@timbal-ai/timbal-react 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +24 -5
  3. package/dist/app.cjs +2282 -738
  4. package/dist/app.d.cts +4 -1
  5. package/dist/app.d.ts +4 -1
  6. package/dist/app.esm.js +58 -5
  7. package/dist/button-CIKzUrJI.d.cts +18 -0
  8. package/dist/button-CIKzUrJI.d.ts +18 -0
  9. package/dist/chart-artifact-BFDz8Tf9.d.ts +756 -0
  10. package/dist/chart-artifact-bWUa-iSG.d.cts +756 -0
  11. package/dist/chat.cjs +872 -562
  12. package/dist/chat.d.cts +2 -2
  13. package/dist/chat.d.ts +2 -2
  14. package/dist/chat.esm.js +3 -3
  15. package/dist/{chunk-4TCJQSIX.esm.js → chunk-2XZ3S4OP.esm.js} +14 -3
  16. package/dist/chunk-533MK5EA.esm.js +2294 -0
  17. package/dist/{chunk-OVHR7J3J.esm.js → chunk-7O5VY3TP.esm.js} +38 -11
  18. package/dist/{chunk-WLTW56MC.esm.js → chunk-N3PYVTY5.esm.js} +2 -2
  19. package/dist/{chunk-IYENDIRY.esm.js → chunk-TDIJHV4I.esm.js} +1 -1
  20. package/dist/{chunk-YJQLLFKP.esm.js → chunk-TLUF2RUL.esm.js} +813 -507
  21. package/dist/{chunk-OFHLFNJH.esm.js → chunk-Z27GBSOT.esm.js} +3 -1
  22. package/dist/index.cjs +2587 -1016
  23. package/dist/index.d.cts +6 -5
  24. package/dist/index.d.ts +6 -5
  25. package/dist/index.esm.js +57 -7
  26. package/dist/{layout-CQWngNQ7.d.ts → layout-BTJyU8wd.d.ts} +1 -1
  27. package/dist/{layout-B9VayJhZ.d.cts → layout-C2G-FcER.d.cts} +1 -1
  28. package/dist/studio.cjs +1127 -788
  29. package/dist/studio.d.cts +1 -1
  30. package/dist/studio.d.ts +1 -1
  31. package/dist/studio.esm.js +6 -6
  32. package/dist/{timbal-v2-button-F4-z7m33.d.ts → timbal-v2-button-CNfdwGq4.d.cts} +1 -1
  33. package/dist/{timbal-v2-button-F4-z7m33.d.cts → timbal-v2-button-CNfdwGq4.d.ts} +1 -1
  34. package/dist/ui.cjs +12 -3
  35. package/dist/ui.d.cts +5 -16
  36. package/dist/ui.d.ts +5 -16
  37. package/dist/ui.esm.js +2 -2
  38. package/dist/{welcome-BOizSp5h.d.ts → welcome-BBmB3tl7.d.ts} +4 -3
  39. package/dist/{welcome--80i_O0p.d.cts → welcome-C89Mgdaw.d.cts} +4 -3
  40. package/package.json +2 -1
  41. package/vite/local-dev.mjs +91 -5
  42. package/dist/chart-artifact-C71dk4xI.d.ts +0 -329
  43. package/dist/chart-artifact-CPEpOmtV.d.cts +0 -329
  44. package/dist/chunk-M4V6Q6XO.esm.js +0 -1082
@@ -0,0 +1,2294 @@
1
+ import {
2
+ SIDEBAR_INSET_PX_EXPANDED,
3
+ ShellInsetProvider,
4
+ studioChromeShellStyle,
5
+ studioSidebarWidthTransition
6
+ } from "./chunk-Z27GBSOT.esm.js";
7
+ import {
8
+ ChartArtifactView,
9
+ LineAreaChart,
10
+ Thread,
11
+ TimbalRuntimeProvider,
12
+ monotoneAreaPath,
13
+ monotoneLinePath,
14
+ studioIntegrationCardClass,
15
+ studioSearchChromeClass,
16
+ studioSecondaryChromeClass,
17
+ studioTopbarPillHeightClass,
18
+ toNum
19
+ } from "./chunk-TLUF2RUL.esm.js";
20
+ import {
21
+ PillSegmentedTabs
22
+ } from "./chunk-TDIJHV4I.esm.js";
23
+ import {
24
+ Button,
25
+ Dialog,
26
+ DialogContent,
27
+ DialogTitle,
28
+ TIMBAL_V2_ELEVATED_SURFACE,
29
+ TIMBAL_V2_LOGO_TILE,
30
+ TIMBAL_V2_SWITCH_THUMB,
31
+ TIMBAL_V2_SWITCH_TRACK_OFF,
32
+ TimbalV2Button,
33
+ cn
34
+ } from "./chunk-2XZ3S4OP.esm.js";
35
+
36
+ // src/app/agent-instructions.ts
37
+ var APP_KIT_AGENT_INSTRUCTIONS = `
38
+ ## App kit (@timbal-ai/timbal-react/app)
39
+
40
+ Build **dashboard and operations UIs** with React components. Import from \`@timbal-ai/timbal-react/app\` (or the main package entry if your app already uses it).
41
+
42
+ ### Creative freedom (read this first)
43
+
44
+ You are **not** required to copy any example layout, page title, section order, or visual theme.
45
+
46
+ - **Do** invent layouts that fit the user's domain (CRM, inventory, billing, internal tools, etc.).
47
+ - **Do** pick only the components you need; skip shell, sidebar, or copilot when the task does not need them.
48
+ - **Do** vary density, grid columns, navigation patterns, and copy \u2014 as long as you follow the **design guidelines** below.
49
+ - **Do not** treat \`examples/app-kit/reference/\` as a template to clone (no default "Operations" dashboard with sidebar + three KPI tiles + SubNav + table unless the user asked for that).
50
+ - **Do** use \`examples/app-kit/recipes/\` as **short grammar examples** (one concern per file), not as a full app blueprint.
51
+
52
+ When in doubt: compose from the **component menu** + **guidelines**, then adapt creatively to the request.
53
+
54
+ ### Module layout (source folders)
55
+
56
+ Presentational groups \u2014 import from the package root, not from these paths:
57
+
58
+ | Folder | Components |
59
+ |--------|------------|
60
+ | \`data/\` | \`MetricRow\`, \`MetricChartCard\`, \`MetricTile\`, \`DataTable\`, \`FilterBar\`, \`ChartPanel\` |
61
+ | \`integrations/\` | \`IntegrationCard\`, \`ConnectionRow\`, \`ConnectionRowList\`, \`IntegrationsEmptyState\`, \`PlanBadge\` |
62
+ | \`settings/\` | \`SettingsSection\`, \`FieldRow\`, \`DangerZone\`, \`FloatingUnsavedChangesBar\` |
63
+ | \`surfaces/\` | \`StatTile\`, \`InfoCard\`, \`ResourceCard\`, \`DescriptionList\`, \`ExpandableSection\`, \`StatusDot\`, \`StatusBadge\`, \`EmptyState\` |
64
+ | \`layout/\` | \`AppShell\`, \`Page\`, \`Section\` |
65
+ | \`charts\` (re-exported) | \`LineAreaChart\`, \`Sparkline\`, \`CHART_PALETTE\` |
66
+
67
+ Also re-exported: \`Button\`, \`TimbalChat\`, \`ChartArtifactView\`, \`APP_KIT_AGENT_INSTRUCTIONS\`.
68
+
69
+ ### Design guidelines (required)
70
+
71
+ | Area | Rule |
72
+ |------|------|
73
+ | **Copilot** | Use \`AppCopilotProvider\` for page context (\`useAppCopilotContext\`). Copilot is a **floating overlay** via \`AppShell\` \`chat={<AppChatPanel />}\` \u2014 not a sidebar column that shrinks main content. |
74
+ | **Chat panel** | \`AppChatPanel\` only; \`Thread\` uses \`variant="panel"\` internally. Dismiss with **X**; trigger is a **text-only** pill (e.g. "Assistant") \u2014 **no** MessageSquare or chat icons on the shell trigger. |
75
+ | **Context** | Do not show raw JSON context in the panel header; keep context in \`AppCopilotProvider\` only. |
76
+ | **Theming** | Use semantic Tailwind tokens (\`bg-background\`, \`text-foreground\`, \`border-border\`, \`bg-elevated-from\`, etc.) from the host app's \`styles.css\`. Optional: \`import "@timbal-ai/timbal-react/styles.css"\`. |
77
+ | **Layout chrome** | \`Page\` \u2192 \`Section\` for main content hierarchy. \`AppShellTopbar\` for global actions (auth, theme). |
78
+ | **Data** | Prefer \`DataTable\` with typed \`columns\` / \`rows\` / \`getRowKey\`; use \`ChartPanel\` with \`ChartArtifact\` for charts. |
79
+ | **Modals** | Use \`AppConfirmDialog\` for destructive/export confirmations. |
80
+ | **Metrics** | Overview KPIs \u2192 \`MetricRow\` or \`MetricChartCard\` (not four separate heavy cards). Values use **normal** font weight, not bold. |
81
+ | **Integrations** | Catalog \u2192 \`IntegrationCard\` grid; connected list \u2192 \`ConnectionRow\` inside \`ConnectionRowList\`. Footer CTAs: \`Button variant="secondary"\`. |
82
+ | **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). |
83
+
84
+ ### Accessibility (required)
85
+
86
+ | Area | Rule |
87
+ |------|------|
88
+ | **Headings** | Use \`Page\` / \`Section\` titles for hierarchy. Card titles inside premade components are already \`h3\`/\`h4\`. |
89
+ | **Selectable metrics** | \`MetricChartCard\` / \`MetricRow\` tiles are buttons with \`aria-pressed\`. Pass \`metricsAriaLabel\` when the default "Metrics" is too vague. |
90
+ | **Charts** | \`LineAreaChart\` exposes \`role="img"\` + \`aria-label\`; \`MetricChartCard\` updates the chart label when the active metric changes (\`aria-live\` on the plot region). |
91
+ | **Integration cards** | Whole-card click \u2192 \`onClick\` only (no nested footer button). With footer \`action\`, render a static \`article\` \u2014 do not wrap the CTA in a card button. Pass \`ariaLabel\` when \`name\` is not plain text. |
92
+ | **Lists** | Wrap \`ConnectionRow\` in \`ConnectionRowList\` (\`role="list"\`); rows expose \`role="listitem"\`. |
93
+ | **Status** | Pair \`StatusDot\` / \`StatusBadge\` with visible text \u2014 do not rely on color alone. |
94
+ | **Forms** | Use \`Field*\` components; errors use \`role="alert"\`. |
95
+ | **Custom labels** | \`ariaLabel\` props exist on \`MetricTile\`, \`IntegrationCard\`, \`ConnectionRow\`, \`ResourceCard\` when slots are icons or rich nodes. |
96
+
97
+ ### Component menu
98
+
99
+ | Component | Use for |
100
+ |-----------|---------|
101
+ | \`AppShell\` | Shell: optional \`sidebar\`, \`topbar\`, main \`children\`, optional floating \`chat\`. Props: \`chatTriggerLabel\`, \`chatCollapsible\`, \`chatWidth\`, \`chatHeight\`, controlled \`chatOpen\`. |
102
+ | \`AppShellTopbar\` | Full-width top bar: \`start\`, \`actions\` slots. |
103
+ | \`AppCopilotProvider\` | React context for copilot-aware tools (page, filters, selection, etc.). |
104
+ | \`AppChatPanel\` | Floating thread: \`workforceId\`, \`welcome\`, \`debug\`. |
105
+ | \`useAppShellChat\` | Custom open/close trigger when \`hideChatTrigger\` on shell. |
106
+ | \`Page\` | Page title, description, \`breadcrumbs\`, \`actions\`, children. |
107
+ | \`Section\` | Titled block inside a page. |
108
+ | \`SubNav\` | In-page tabs: \`items\`, \`activeId\`, \`onChange\`. |
109
+ | \`Breadcrumbs\` | Trail: \`items: [{ label, href? }]\`. |
110
+ | \`Button\` | Actions \u2014 \`variant="secondary"\` for catalog/secondary CTAs; \`variant="default"\` for primary. |
111
+ | \`StatTile\` | Single KPI in its own card (grid of scattered stats). Prefer \`MetricRow\` for a unified overview strip. |
112
+ | \`StatusBadge\` | Status pill: \`tone\` (\`success\`, \`warn\`, \u2026), children. |
113
+ | \`FilterBar\` | Horizontal filter row (wraps \`SearchInput\`, buttons, etc.). |
114
+ | \`SearchInput\` | Filter field with consistent app styling. |
115
+ | \`DataTable\` | Sortable table: \`columns\`, \`rows\`, \`getRowKey\`, optional \`sort\` / \`onSortChange\`, \`emptyTitle\`, \`showRowCount\`, \`caption\` for screen readers. |
116
+ | \`ChartPanel\` | Same shell as \`MetricChartCard\`: title row (\`px-4 pt-4\`), flush plot (\`pt-2\` only). Pass \`title\` + \`artifact\` (omit \`artifact.title\` to avoid duplicates) or \`children\`. |
117
+ | \`FieldInput\`, \`FieldTextarea\`, \`FieldSelect\`, \`FieldSwitch\` | Settings-style forms with labels and hints. |
118
+ | \`FormSection\` | Grouped form block. |
119
+ | \`AppConfirmDialog\` | Confirm/cancel modal: \`open\`, \`onOpenChange\`, \`title\`, \`description\`, \`onConfirm\`. |
120
+ | \`SurfaceCard\`, \`EmptyState\` | Generic surfaces when needed. |
121
+ | \`TimbalChat\` | Re-export if you need chat outside \`AppChatPanel\`. |
122
+
123
+ #### Charts & metrics
124
+
125
+ | Component | Use for |
126
+ |-----------|---------|
127
+ | \`LineAreaChart\` | Chart engine. Props: \`data\`, \`xKey\`, \`series\`, \`variant\` (\`"area"\`), \`layout\` (\`"flush"\`), \`height\`, \`ariaLabel\`, \`formatX\`, \`formatValue\`. |
128
+ | \`Sparkline\` | Tiny inline trend (table cells): \`data\`, \`color\`, \`area\`. |
129
+ | \`MetricTile\` | Low-level KPI cell \u2014 prefer \`MetricRow\` / \`MetricChartCard\` instead of hand-wiring tiles. |
130
+ | \`MetricRow\` | KPI strip in one elevated card (no chart). Props: \`metrics: [{ id, label, value, unit?, trend? }]\`, optional \`onMetricChange\`, \`metricsAriaLabel\`. |
131
+ | \`MetricChartCard\` | KPI strip + flush chart; tile click swaps series. Same metrics shape + \`data\` per metric. Default chart height 300. |
132
+
133
+ #### Settings
134
+
135
+ | Component | Use for |
136
+ |-----------|---------|
137
+ | \`SettingsSection\` | Two-column settings block: \`title\` + \`description\` rail on the left, controls on the right. |
138
+ | \`FieldRow\` | Labeled control row: \`label\`, \`description\`, \`inline\` (right-aligned control for switches). |
139
+ | \`DangerZone\` + \`DangerZoneAction\` | Destructive-actions container with destructive border. |
140
+ | \`FloatingUnsavedChangesBar\` | Portaled discard/save pill: \`visible\`, \`onDiscard\`, \`onSave\`, \`isSaving\`. |
141
+
142
+ #### Integrations & resources
143
+
144
+ | Component | Use for |
145
+ |-----------|---------|
146
+ | \`IntegrationCard\` | Catalog tile: \`logo\`, \`name\`, \`description\`, \`badge\`, \`status\`, footer \`action\` **or** whole-card \`onClick\` (never both). |
147
+ | \`ConnectionRow\` | One connected provider row: \`logo\`, \`name\`, \`meta\`, \`badge\`, \`action\`. |
148
+ | \`ConnectionRowList\` | Wrapper for rows (\`role="list"\`) \u2014 use instead of raw class strings. |
149
+ | \`IntegrationsEmptyState\` | Empty catalog hero: \`icon\`, \`title\`, \`description\`, \`action\`. |
150
+ | \`PlanBadge\` | Neutral tier chip on catalog cards. |
151
+ | \`ResourceCard\` | Project/agent/dataset card on elevated surface + logo tile: \`media\`, \`title\`, \`subtitle\`, optional \`badge\`, \`footer\` (\`StatusDot\`), \`action\` (\`Sparkline\`). |
152
+
153
+ #### Surfaces & details
154
+
155
+ | Component | Use for |
156
+ |-----------|---------|
157
+ | \`InfoCard\` | Soft callout: \`icon\`, \`title\`, body, \`action\`, \`tone\` (\`info\`/\`success\`/\`warn\`/\`danger\`). |
158
+ | \`DescriptionList\` | Read-only key/value metadata: \`items: [{ label, value }]\`, optional \`stacked\`. |
159
+ | \`ExpandableSection\` | Collapsible block: \`title\`, \`icon\`, \`count\`, animated body (\`aria-expanded\` + \`aria-controls\`). |
160
+ | \`StatusDot\` | Status indicator dot: \`tone\`, \`label\`, \`pulse\`. |
161
+
162
+ Studio chrome (\`StudioSidebar\`, \`ModeToggle\`, \u2026) lives in \`@timbal-ai/timbal-react/studio\` \u2014 optional, not required for every dashboard.
163
+
164
+ ### Recipe index (\`examples/app-kit/recipes/\`)
165
+
166
+ | Recipe file | Components to study |
167
+ |-------------|---------------------|
168
+ | \`metrics-row.tsx\` | \`Page\`, \`MetricRow\` |
169
+ | \`analytics-card.tsx\` | \`MetricChartCard\`, \`Button\` |
170
+ | \`integrations-grid.tsx\` | \`IntegrationCard\`, \`ConnectionRowList\`, \`PlanBadge\` |
171
+ | \`table-with-filters.tsx\` | \`FilterBar\`, \`DataTable\` |
172
+ | \`settings-page.tsx\` | \`SettingsSection\`, \`DangerZone\`, \`FloatingUnsavedChangesBar\` |
173
+ | \`resource-gallery.tsx\` | \`ResourceCard\`, \`StatusDot\`, \`Sparkline\` |
174
+ | \`charts-panel.tsx\` | \`ChartPanel\`, \`ChartArtifact\` |
175
+ | \`copilot-overlay.tsx\` | \`AppShell\`, \`AppChatPanel\` |
176
+
177
+ ### Typical compositions
178
+
179
+ - **Metrics overview** \u2014 \`MetricRow\` or \`MetricChartCard\` (not four isolated stat cards with bold numbers).
180
+ - **Analytics** \u2014 \`MetricChartCard\`; header action: \`Button variant="secondary" size="sm"\`.
181
+ - **Table workspace** \u2014 \`Page\` + \`FilterBar\` + \`DataTable\` (+ \`StatusBadge\` / \`StatusDot\` in cells).
182
+ - **Settings** \u2014 \`Page\` + \`SettingsSection\`s + \`DangerZone\` + \`FloatingUnsavedChangesBar\`.
183
+ - **Integrations** \u2014 grid of \`IntegrationCard\`; \`ConnectionRowList\` for connected providers; \`IntegrationsEmptyState\` when empty.
184
+ - **Resource gallery** \u2014 grid of \`ResourceCard\`.
185
+ - **Copilot-assisted app** \u2014 \`AppCopilotProvider\` + \`AppShell\` with \`chat={<AppChatPanel workforceId="\u2026" />}\`.
186
+
187
+ ### Example imports
188
+
189
+ \`\`\`tsx
190
+ import {
191
+ AppShell,
192
+ AppCopilotProvider,
193
+ AppChatPanel,
194
+ Page,
195
+ Section,
196
+ MetricRow,
197
+ MetricChartCard,
198
+ IntegrationCard,
199
+ ConnectionRow,
200
+ ConnectionRowList,
201
+ Button,
202
+ DataTable,
203
+ FilterBar,
204
+ } from "@timbal-ai/timbal-react/app";
205
+ \`\`\`
206
+
207
+ ### Examples in this repo (for humans/tools)
208
+
209
+ | Path | Purpose |
210
+ |------|---------|
211
+ | \`examples/app-kit/recipes/*\` | **Recipes** \u2014 one pattern each (~20\u201380 lines). Use for capability, not layout. |
212
+ | \`examples/app-kit/reference/operations-dashboard.tsx\` | **Reference only** \u2014 full wired app; do not treat as the default generated layout. |
213
+
214
+ ### Rules
215
+
216
+ - Prefer stable props documented above; avoid undocumented \`design/*\` class exports (\`connectionRowListClass\` is exported but \`ConnectionRowList\` is preferred).
217
+ - Match the user's domain language in titles and labels.
218
+ - For rich in-chat widgets, use **artifacts** (\`ARTIFACT_AGENT_INSTRUCTIONS\`) \u2014 app kit is for the **host application shell**.
219
+ `.trim();
220
+
221
+ // src/design/app-classes.ts
222
+ var appPageColumnClass = "mx-auto w-full max-w-6xl px-4 md:px-6";
223
+ var appShellTopbarInsetClass = "w-full px-4 md:px-6";
224
+ var appShellInsetTopClass = "pt-4 md:pt-6";
225
+ var appShellTopbarRowClass = cn(
226
+ studioTopbarPillHeightClass,
227
+ "flex w-full items-center justify-between gap-2"
228
+ );
229
+ var appShellTopbarStickyClass = cn(
230
+ "sticky top-0 z-20 shrink-0 bg-background pb-2",
231
+ appShellInsetTopClass
232
+ );
233
+ var appPageHeaderClass = cn(
234
+ "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
235
+ "pb-4 pt-2"
236
+ );
237
+ var appSectionClass = "flex flex-col gap-4 py-4";
238
+ var appSectionTitleClass = "text-lg font-semibold text-foreground";
239
+ var appSectionDescriptionClass = "text-sm text-muted-foreground";
240
+ var appSurfaceCardClass = cn(
241
+ studioIntegrationCardClass,
242
+ "p-4 md:p-5"
243
+ );
244
+ var appStatTileClass = cn(
245
+ appSurfaceCardClass,
246
+ "flex flex-col gap-1 px-4 py-3 shadow-none"
247
+ );
248
+ var appStatValueClass = "text-2xl font-normal tracking-tight text-foreground tabular-nums";
249
+ var appStatLabelClass = "text-xs font-normal text-muted-foreground";
250
+ var appFilterBarClass = cn(
251
+ "flex flex-wrap items-center gap-2",
252
+ studioTopbarPillHeightClass
253
+ );
254
+ var appSearchInputClass = cn(studioSearchChromeClass, "text-sm");
255
+ var appBreadcrumbsClass = "flex flex-wrap items-center gap-1.5 text-sm text-muted-foreground";
256
+ var appBreadcrumbLinkClass = "transition-colors hover:text-foreground";
257
+ var appFieldClass = "flex flex-col gap-1.5";
258
+ var appFieldLabelClass = "text-sm font-medium text-foreground";
259
+ var appFieldHintClass = "text-xs text-muted-foreground";
260
+ var appInputClass = cn(
261
+ studioSecondaryChromeClass,
262
+ "h-10 w-full rounded-lg px-3 text-sm text-foreground outline-none",
263
+ "placeholder:text-muted-foreground/70",
264
+ "focus-visible:ring-2 focus-visible:ring-foreground/10"
265
+ );
266
+ var appEmptyStateClass = cn(
267
+ appSurfaceCardClass,
268
+ "flex flex-col items-center justify-center gap-2 py-12 text-center"
269
+ );
270
+ var appEmptyStateTitleClass = "text-base font-medium text-foreground";
271
+ var appEmptyStateDescriptionClass = "max-w-sm text-sm text-muted-foreground";
272
+ var appChartPanelClass = cn(appSurfaceCardClass, "flex flex-col gap-3");
273
+
274
+ // src/app/layout/app-shell-chat-context.tsx
275
+ import { createContext, useContext } from "react";
276
+ var AppShellChatContext = createContext(null);
277
+ var AppShellChatProvider = AppShellChatContext.Provider;
278
+ function useAppShellChat() {
279
+ return useContext(AppShellChatContext);
280
+ }
281
+
282
+ // src/app/layout/AppShell.tsx
283
+ import { motion, useReducedMotion } from "motion/react";
284
+ import { useCallback, useState } from "react";
285
+ import { jsx, jsxs } from "react/jsx-runtime";
286
+ var floatingTriggerClass = cn(
287
+ "aui-app-shell-chat-trigger-fixed fixed z-50 rounded-full px-5 py-2.5 text-sm font-medium shadow-card-elevated",
288
+ "bg-primary text-primary-foreground transition-colors hover:bg-primary/90",
289
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
290
+ "bottom-6 right-6 max-sm:bottom-4 max-sm:right-4"
291
+ );
292
+ var floatingPanelClass = cn(
293
+ "aui-app-shell-chat-float fixed z-50 flex flex-col overflow-hidden rounded-2xl border border-border/60 shadow-card-elevated",
294
+ "bg-card/85 backdrop-blur-xl supports-backdrop-filter:bg-card/75",
295
+ // Mobile: stretch between insets (no fixed width — avoids a wide empty strip on the right)
296
+ "max-sm:inset-x-3 max-sm:top-3 max-sm:bottom-3 max-sm:w-auto",
297
+ "sm:top-6 sm:right-6 sm:bottom-6 sm:w-[var(--app-shell-chat-width)] sm:max-w-[calc(100vw-3rem)]"
298
+ );
299
+ var AppShellBody = ({
300
+ sidebar,
301
+ topbarContent,
302
+ mainClassName,
303
+ insetPaddingPx,
304
+ insetExpanded,
305
+ children
306
+ }) => {
307
+ const reducedMotion = useReducedMotion();
308
+ const layoutDirection = insetExpanded ? "expand" : "collapse";
309
+ const layoutTransition = studioSidebarWidthTransition(
310
+ !!reducedMotion,
311
+ layoutDirection
312
+ );
313
+ const insetPadding = sidebar ? insetPaddingPx : 0;
314
+ return /* @__PURE__ */ jsx(
315
+ motion.div,
316
+ {
317
+ className: "aui-app-shell-body relative z-10 flex min-h-0 min-w-0 flex-1 flex-col",
318
+ initial: false,
319
+ animate: { paddingLeft: insetPadding },
320
+ transition: layoutTransition,
321
+ children: /* @__PURE__ */ jsxs(
322
+ "div",
323
+ {
324
+ className: cn(
325
+ "aui-app-shell-scroll flex min-h-0 flex-1 flex-col overflow-y-auto",
326
+ !topbarContent && appShellInsetTopClass
327
+ ),
328
+ children: [
329
+ topbarContent ? /* @__PURE__ */ jsx("header", { className: cn("aui-app-shell-topbar-region", appShellTopbarStickyClass), children: /* @__PURE__ */ jsx("div", { className: appShellTopbarInsetClass, children: topbarContent }) }) : null,
330
+ /* @__PURE__ */ jsx("main", { className: cn("aui-app-shell-main min-w-0 flex-1", mainClassName), children })
331
+ ]
332
+ }
333
+ )
334
+ }
335
+ );
336
+ };
337
+ var AppShell = ({
338
+ sidebar,
339
+ topbar,
340
+ header,
341
+ children,
342
+ chat,
343
+ chatWidth = "24rem",
344
+ chatHeight,
345
+ chatOpen: chatOpenProp,
346
+ defaultChatOpen = false,
347
+ onChatOpenChange,
348
+ chatCollapsible = true,
349
+ chatTriggerLabel = "Assistant",
350
+ hideChatTrigger = false,
351
+ className,
352
+ mainClassName
353
+ }) => {
354
+ const topbarContent = topbar ?? header;
355
+ const hasChat = Boolean(chat);
356
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultChatOpen);
357
+ const isChatControlled = chatOpenProp !== void 0;
358
+ const chatOpen = isChatControlled ? chatOpenProp : uncontrolledOpen;
359
+ const setChatOpen = useCallback(
360
+ (open) => {
361
+ if (!isChatControlled) {
362
+ setUncontrolledOpen(open);
363
+ }
364
+ onChatOpenChange?.(open);
365
+ },
366
+ [isChatControlled, onChatOpenChange]
367
+ );
368
+ const toggleChat = useCallback(() => {
369
+ setChatOpen(!chatOpen);
370
+ }, [chatOpen, setChatOpen]);
371
+ const [insetPaddingPx, setInsetPaddingPx] = useState(
372
+ sidebar ? SIDEBAR_INSET_PX_EXPANDED : 0
373
+ );
374
+ const reportShellInset = useCallback((insetPx) => {
375
+ setInsetPaddingPx(insetPx);
376
+ }, []);
377
+ const insetExpanded = insetPaddingPx >= SIDEBAR_INSET_PX_EXPANDED;
378
+ const shellBody = /* @__PURE__ */ jsx(
379
+ AppShellBody,
380
+ {
381
+ sidebar,
382
+ topbarContent,
383
+ mainClassName,
384
+ insetPaddingPx,
385
+ insetExpanded,
386
+ children
387
+ }
388
+ );
389
+ const tree = /* @__PURE__ */ jsx(ShellInsetProvider, { value: sidebar ? reportShellInset : null, children: /* @__PURE__ */ jsxs(
390
+ "div",
391
+ {
392
+ className: cn(
393
+ "aui-app-shell relative flex h-dvh overflow-hidden bg-background",
394
+ className
395
+ ),
396
+ style: studioChromeShellStyle,
397
+ children: [
398
+ sidebar,
399
+ shellBody,
400
+ hasChat && chatOpen ? /* @__PURE__ */ jsx(
401
+ "div",
402
+ {
403
+ className: floatingPanelClass,
404
+ style: {
405
+ ["--app-shell-chat-width"]: chatWidth,
406
+ ...chatHeight ? { height: chatHeight } : void 0
407
+ },
408
+ role: "dialog",
409
+ "aria-label": typeof chatTriggerLabel === "string" ? chatTriggerLabel : "Assistant",
410
+ children: chat
411
+ }
412
+ ) : null,
413
+ hasChat && chatCollapsible && !chatOpen && !hideChatTrigger ? /* @__PURE__ */ jsx(
414
+ "button",
415
+ {
416
+ type: "button",
417
+ className: floatingTriggerClass,
418
+ onClick: () => setChatOpen(true),
419
+ "aria-expanded": false,
420
+ children: chatTriggerLabel
421
+ }
422
+ ) : null
423
+ ]
424
+ }
425
+ ) });
426
+ if (!hasChat) {
427
+ return tree;
428
+ }
429
+ return /* @__PURE__ */ jsx(
430
+ AppShellChatProvider,
431
+ {
432
+ value: {
433
+ open: chatOpen,
434
+ setOpen: setChatOpen,
435
+ toggle: toggleChat,
436
+ collapsible: chatCollapsible
437
+ },
438
+ children: tree
439
+ }
440
+ );
441
+ };
442
+
443
+ // src/app/layout/AppShellTopbar.tsx
444
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
445
+ var AppShellTopbar = ({
446
+ start,
447
+ actions,
448
+ children,
449
+ className
450
+ }) => {
451
+ return /* @__PURE__ */ jsxs2("div", { className: cn("aui-app-shell-topbar", appShellTopbarRowClass, className), children: [
452
+ /* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
453
+ start,
454
+ children
455
+ ] }),
456
+ actions ? /* @__PURE__ */ jsx2("div", { className: "aui-app-shell-topbar-actions flex shrink-0 items-center gap-2", children: actions }) : null
457
+ ] });
458
+ };
459
+
460
+ // src/app/layout/AppShellChatTrigger.tsx
461
+ import { jsx as jsx3 } from "react/jsx-runtime";
462
+ var floatingPositionClass = "fixed bottom-6 right-6 z-50 max-sm:bottom-4 max-sm:right-4";
463
+ var AppShellChatTrigger = ({
464
+ className,
465
+ label = "Assistant",
466
+ placement = "inline"
467
+ }) => {
468
+ const shellChat = useAppShellChat();
469
+ if (!shellChat || shellChat.open) return null;
470
+ return /* @__PURE__ */ jsx3(
471
+ TimbalV2Button,
472
+ {
473
+ type: "button",
474
+ variant: placement === "floating" ? "primary" : "secondary",
475
+ size: "sm",
476
+ className: cn(
477
+ "aui-app-shell-chat-trigger",
478
+ placement === "floating" && floatingPositionClass,
479
+ placement === "floating" && "shadow-card-elevated",
480
+ className
481
+ ),
482
+ onClick: () => shellChat.setOpen(true),
483
+ "aria-expanded": false,
484
+ children: label
485
+ }
486
+ );
487
+ };
488
+
489
+ // src/app/layout/PageHeader.tsx
490
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
491
+ var PageHeader = ({
492
+ title,
493
+ description,
494
+ actions,
495
+ className
496
+ }) => {
497
+ return /* @__PURE__ */ jsxs3("header", { className: cn("aui-app-page-header", appPageHeaderClass, className), children: [
498
+ /* @__PURE__ */ jsxs3("div", { className: "min-w-0", children: [
499
+ /* @__PURE__ */ jsx4("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: title }),
500
+ description ? /* @__PURE__ */ jsx4("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
501
+ ] }),
502
+ actions ? /* @__PURE__ */ jsx4("div", { className: "aui-app-page-header-actions flex shrink-0 flex-wrap items-center gap-2", children: actions }) : null
503
+ ] });
504
+ };
505
+
506
+ // src/app/layout/Page.tsx
507
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
508
+ var Page = ({
509
+ children,
510
+ breadcrumbs,
511
+ className,
512
+ ...headerProps
513
+ }) => {
514
+ return /* @__PURE__ */ jsxs4("div", { className: cn("aui-app-page", appPageColumnClass, className), children: [
515
+ breadcrumbs,
516
+ /* @__PURE__ */ jsx5(PageHeader, { ...headerProps }),
517
+ children
518
+ ] });
519
+ };
520
+
521
+ // src/app/layout/Section.tsx
522
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
523
+ var Section = ({
524
+ title,
525
+ description,
526
+ children,
527
+ className
528
+ }) => {
529
+ return /* @__PURE__ */ jsxs5("section", { className: cn("aui-app-section", appSectionClass, className), children: [
530
+ title ? /* @__PURE__ */ jsx6("h2", { className: appSectionTitleClass, children: title }) : null,
531
+ description ? /* @__PURE__ */ jsx6("p", { className: appSectionDescriptionClass, children: description }) : null,
532
+ children
533
+ ] });
534
+ };
535
+
536
+ // src/app/copilot/app-copilot-context.tsx
537
+ import { createContext as createContext2, useContext as useContext2 } from "react";
538
+ import { jsx as jsx7 } from "react/jsx-runtime";
539
+ var AppCopilotContext = createContext2(null);
540
+ var AppCopilotProvider = ({
541
+ value,
542
+ children
543
+ }) => {
544
+ return /* @__PURE__ */ jsx7(AppCopilotContext.Provider, { value, children });
545
+ };
546
+ function useAppCopilotContext() {
547
+ return useContext2(AppCopilotContext) ?? {};
548
+ }
549
+
550
+ // src/app/chat/AppChatPanel.tsx
551
+ import { XIcon } from "lucide-react";
552
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
553
+ var shellClass = "aui-app-chat-panel flex h-full min-h-0 flex-col overflow-hidden";
554
+ var chromeClass = cn(
555
+ "aui-app-chat-panel-chrome relative z-20 flex min-h-12 shrink-0 items-center justify-end",
556
+ "bg-card/90 px-2 pt-3 pb-3 backdrop-blur-sm"
557
+ );
558
+ var closeButtonClass = cn(
559
+ "aui-app-chat-panel-close flex size-8 shrink-0 items-center justify-center rounded-md",
560
+ "text-muted-foreground transition-colors hover:bg-foreground/5 hover:text-foreground",
561
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-transparent"
562
+ );
563
+ var bodyClass = cn(
564
+ "aui-app-chat-panel-body relative min-h-0 flex-1 overflow-hidden",
565
+ "[&_.aui-thread-root]:h-full",
566
+ "[&_.aui-thread-viewport]:scrollbar-thin",
567
+ "[&_.aui-thread-viewport]:[scrollbar-color:var(--border)_transparent]",
568
+ // Reserve the scrollbar gutter on BOTH edges so the composer + messages
569
+ // stay symmetric (otherwise the right-side scrollbar adds a phantom inset).
570
+ "[&_.aui-thread-viewport]:[scrollbar-gutter:stable_both-edges]",
571
+ // Tighter symmetric horizontal inset for panel + composer
572
+ "[&_.aui-thread-viewport]:!px-2",
573
+ "[&_.aui-thread-viewport]:!pt-2",
574
+ "[&_.aui-user-message-root]:!px-0",
575
+ "[&_.aui-composer-input]:!px-2",
576
+ "[&_.aui-composer-action-wrapper]:!px-2"
577
+ );
578
+ var AppChatPanel = ({
579
+ className,
580
+ workforceId,
581
+ baseUrl,
582
+ fetch,
583
+ attachments,
584
+ attachmentsUploadUrl,
585
+ attachmentsAccept,
586
+ debug,
587
+ welcome,
588
+ suggestions,
589
+ composerPlaceholder,
590
+ components,
591
+ artifacts,
592
+ onArtifactEvent,
593
+ ...rest
594
+ }) => {
595
+ const shellChat = useAppShellChat();
596
+ return /* @__PURE__ */ jsxs6("div", { className: cn(shellClass, className), children: [
597
+ shellChat?.collapsible ? /* @__PURE__ */ jsx8("div", { className: chromeClass, children: /* @__PURE__ */ jsx8(
598
+ "button",
599
+ {
600
+ type: "button",
601
+ className: closeButtonClass,
602
+ onClick: () => shellChat.setOpen(false),
603
+ "aria-label": "Close assistant",
604
+ children: /* @__PURE__ */ jsx8(XIcon, { className: "size-4", "aria-hidden": true })
605
+ }
606
+ ) }) : null,
607
+ /* @__PURE__ */ jsx8("div", { className: bodyClass, children: /* @__PURE__ */ jsx8(
608
+ TimbalRuntimeProvider,
609
+ {
610
+ workforceId,
611
+ baseUrl,
612
+ fetch,
613
+ attachments,
614
+ attachmentsUploadUrl,
615
+ attachmentsAccept,
616
+ debug,
617
+ children: /* @__PURE__ */ jsx8(
618
+ Thread,
619
+ {
620
+ variant: "panel",
621
+ className: "aui-app-chat-panel-thread",
622
+ welcome,
623
+ suggestions,
624
+ composerPlaceholder,
625
+ components,
626
+ artifacts,
627
+ onArtifactEvent,
628
+ ...rest
629
+ }
630
+ )
631
+ }
632
+ ) })
633
+ ] });
634
+ };
635
+
636
+ // src/app/surfaces/SurfaceCard.tsx
637
+ import { jsx as jsx9 } from "react/jsx-runtime";
638
+ var SurfaceCard = ({ children, className }) => {
639
+ return /* @__PURE__ */ jsx9("div", { className: cn("aui-app-surface-card", appSurfaceCardClass, className), children });
640
+ };
641
+
642
+ // src/app/surfaces/StatTile.tsx
643
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
644
+ var StatTile = ({ label, value, hint, className }) => {
645
+ return /* @__PURE__ */ jsxs7("div", { className: cn("aui-app-stat-tile", appStatTileClass, className), children: [
646
+ /* @__PURE__ */ jsx10("span", { className: appStatLabelClass, children: label }),
647
+ /* @__PURE__ */ jsx10("span", { className: appStatValueClass, children: value }),
648
+ hint ? /* @__PURE__ */ jsx10("span", { className: "text-xs text-muted-foreground", children: hint }) : null
649
+ ] });
650
+ };
651
+
652
+ // src/app/surfaces/EmptyState.tsx
653
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
654
+ var EmptyState = ({
655
+ title,
656
+ description,
657
+ action,
658
+ className
659
+ }) => {
660
+ return /* @__PURE__ */ jsxs8("div", { className: cn("aui-app-empty-state", appEmptyStateClass, className), children: [
661
+ /* @__PURE__ */ jsx11("p", { className: appEmptyStateTitleClass, children: title }),
662
+ description ? /* @__PURE__ */ jsx11("p", { className: appEmptyStateDescriptionClass, children: description }) : null,
663
+ action
664
+ ] });
665
+ };
666
+
667
+ // src/app/surfaces/StatusBadge.tsx
668
+ import { jsx as jsx12 } from "react/jsx-runtime";
669
+ var statusBadgeToneClass = {
670
+ default: "bg-muted text-foreground",
671
+ primary: "bg-primary/10 text-primary",
672
+ success: "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400",
673
+ warn: "bg-amber-500/10 text-amber-600 dark:text-amber-400",
674
+ muted: "bg-muted/80 text-muted-foreground"
675
+ };
676
+ var StatusBadge = ({
677
+ children,
678
+ tone = "default",
679
+ className
680
+ }) => {
681
+ return /* @__PURE__ */ jsx12(
682
+ "span",
683
+ {
684
+ className: cn(
685
+ "aui-app-status-badge inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium",
686
+ statusBadgeToneClass[tone],
687
+ className
688
+ ),
689
+ children
690
+ }
691
+ );
692
+ };
693
+
694
+ // src/app/surfaces/AppConfirmDialog.tsx
695
+ import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
696
+ var bodyClass2 = "flex flex-col gap-4 p-6";
697
+ var titleClass = "pr-8";
698
+ var actionsClass = "flex flex-wrap justify-end gap-2";
699
+ var AppConfirmDialog = ({
700
+ open,
701
+ onOpenChange,
702
+ title,
703
+ description,
704
+ confirmLabel = "Confirm",
705
+ cancelLabel = "Cancel",
706
+ onConfirm,
707
+ destructive = false,
708
+ className
709
+ }) => {
710
+ return /* @__PURE__ */ jsx13(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsx13(
711
+ DialogContent,
712
+ {
713
+ className: cn("gap-0 p-0 sm:max-w-md", className),
714
+ children: /* @__PURE__ */ jsxs9("div", { className: bodyClass2, children: [
715
+ /* @__PURE__ */ jsx13(DialogTitle, { className: titleClass, children: title }),
716
+ description ? /* @__PURE__ */ jsx13("p", { className: "text-sm text-muted-foreground", children: description }) : null,
717
+ /* @__PURE__ */ jsxs9("div", { className: actionsClass, children: [
718
+ /* @__PURE__ */ jsx13(
719
+ TimbalV2Button,
720
+ {
721
+ type: "button",
722
+ variant: "secondary",
723
+ size: "sm",
724
+ onClick: () => onOpenChange(false),
725
+ children: cancelLabel
726
+ }
727
+ ),
728
+ /* @__PURE__ */ jsx13(
729
+ TimbalV2Button,
730
+ {
731
+ type: "button",
732
+ variant: destructive ? "destructive" : "primary",
733
+ size: "sm",
734
+ onClick: () => {
735
+ onConfirm();
736
+ onOpenChange(false);
737
+ },
738
+ children: confirmLabel
739
+ }
740
+ )
741
+ ] })
742
+ ] })
743
+ }
744
+ ) });
745
+ };
746
+
747
+ // src/app/surfaces/InfoCard.tsx
748
+ import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
749
+ var toneClass = {
750
+ neutral: "border-border bg-muted/40",
751
+ info: "border-primary/25 bg-primary/5",
752
+ success: "border-emerald-500/25 bg-emerald-500/5",
753
+ warn: "border-amber-500/25 bg-amber-500/5",
754
+ danger: "border-destructive/25 bg-destructive/5"
755
+ };
756
+ var InfoCard = ({
757
+ title,
758
+ children,
759
+ icon,
760
+ action,
761
+ tone = "neutral",
762
+ className
763
+ }) => /* @__PURE__ */ jsxs10(
764
+ "div",
765
+ {
766
+ className: cn(
767
+ "flex items-start gap-3 rounded-xl border p-4",
768
+ toneClass[tone],
769
+ className
770
+ ),
771
+ children: [
772
+ icon ? /* @__PURE__ */ jsx14("span", { className: "mt-0.5 shrink-0 text-muted-foreground", children: icon }) : null,
773
+ /* @__PURE__ */ jsxs10("div", { className: "min-w-0 flex-1", children: [
774
+ title ? /* @__PURE__ */ jsx14("p", { className: "text-sm font-medium text-foreground", children: title }) : null,
775
+ children ? /* @__PURE__ */ jsx14("div", { className: cn("text-sm text-muted-foreground", title && "mt-1"), children }) : null
776
+ ] }),
777
+ action ? /* @__PURE__ */ jsx14("div", { className: "shrink-0", children: action }) : null
778
+ ]
779
+ }
780
+ );
781
+
782
+ // src/app/surfaces/StatusDot.tsx
783
+ import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
784
+ var dotClass = {
785
+ online: "bg-emerald-500",
786
+ busy: "bg-amber-500",
787
+ offline: "bg-muted-foreground/50",
788
+ error: "bg-destructive",
789
+ neutral: "bg-muted-foreground"
790
+ };
791
+ var StatusDot = ({
792
+ tone = "neutral",
793
+ label,
794
+ pulse = false,
795
+ className
796
+ }) => /* @__PURE__ */ jsxs11("span", { className: cn("inline-flex items-center gap-1.5", className), children: [
797
+ /* @__PURE__ */ jsxs11("span", { className: "relative flex size-2", children: [
798
+ pulse ? /* @__PURE__ */ jsx15(
799
+ "span",
800
+ {
801
+ className: cn(
802
+ "absolute inline-flex size-full animate-ping rounded-full opacity-60",
803
+ dotClass[tone]
804
+ )
805
+ }
806
+ ) : null,
807
+ /* @__PURE__ */ jsx15("span", { className: cn("relative inline-flex size-2 rounded-full", dotClass[tone]) })
808
+ ] }),
809
+ label ? /* @__PURE__ */ jsx15("span", { className: "text-xs text-muted-foreground", children: label }) : null
810
+ ] });
811
+
812
+ // src/app/surfaces/DescriptionList.tsx
813
+ import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
814
+ var DescriptionList = ({
815
+ items,
816
+ stacked = false,
817
+ className
818
+ }) => /* @__PURE__ */ jsx16(
819
+ "dl",
820
+ {
821
+ className: cn(
822
+ "divide-y divide-border rounded-xl border border-border bg-card",
823
+ className
824
+ ),
825
+ children: items.map((item, i) => /* @__PURE__ */ jsxs12(
826
+ "div",
827
+ {
828
+ className: cn(
829
+ "px-4 py-3",
830
+ stacked ? "flex flex-col gap-0.5" : "flex items-center justify-between gap-4"
831
+ ),
832
+ children: [
833
+ /* @__PURE__ */ jsx16("dt", { className: "text-sm text-muted-foreground", children: item.label }),
834
+ /* @__PURE__ */ jsx16(
835
+ "dd",
836
+ {
837
+ className: cn(
838
+ "text-sm text-foreground",
839
+ !stacked && "text-right tabular-nums"
840
+ ),
841
+ children: item.value
842
+ }
843
+ )
844
+ ]
845
+ },
846
+ i
847
+ ))
848
+ }
849
+ );
850
+
851
+ // src/app/surfaces/ExpandableSection.tsx
852
+ import { useId, useState as useState2 } from "react";
853
+ import { AnimatePresence, motion as motion2, useReducedMotion as useReducedMotion2 } from "motion/react";
854
+ import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
855
+ var Chevron = ({ open }) => /* @__PURE__ */ jsx17(
856
+ "svg",
857
+ {
858
+ viewBox: "0 0 24 24",
859
+ className: cn(
860
+ "size-4 text-muted-foreground transition-transform duration-200",
861
+ open && "rotate-180"
862
+ ),
863
+ fill: "none",
864
+ stroke: "currentColor",
865
+ strokeWidth: 2,
866
+ strokeLinecap: "round",
867
+ strokeLinejoin: "round",
868
+ "aria-hidden": true,
869
+ children: /* @__PURE__ */ jsx17("path", { d: "m6 9 6 6 6-6" })
870
+ }
871
+ );
872
+ var ExpandableSection = ({
873
+ title,
874
+ icon,
875
+ count,
876
+ children,
877
+ open: openProp,
878
+ defaultOpen = false,
879
+ onOpenChange,
880
+ className
881
+ }) => {
882
+ const reduceMotion = useReducedMotion2();
883
+ const panelId = useId();
884
+ const [internalOpen, setInternalOpen] = useState2(defaultOpen);
885
+ const open = openProp ?? internalOpen;
886
+ const toggle = () => {
887
+ if (openProp == null) setInternalOpen((o) => !o);
888
+ onOpenChange?.(!open);
889
+ };
890
+ return /* @__PURE__ */ jsxs13("div", { className: cn("border-b border-border last:border-0", className), children: [
891
+ /* @__PURE__ */ jsxs13(
892
+ "button",
893
+ {
894
+ type: "button",
895
+ onClick: toggle,
896
+ "aria-expanded": open,
897
+ "aria-controls": panelId,
898
+ 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",
899
+ children: [
900
+ /* @__PURE__ */ jsxs13("span", { className: "flex min-w-0 items-center gap-3", children: [
901
+ icon ? /* @__PURE__ */ jsx17("span", { className: "flex size-8 items-center justify-center rounded-lg border border-border bg-muted text-muted-foreground", children: icon }) : null,
902
+ /* @__PURE__ */ jsx17("span", { className: "truncate text-sm font-medium text-foreground", children: title }),
903
+ count != null ? /* @__PURE__ */ jsx17("span", { className: "rounded-full border border-border bg-muted px-2 py-0.5 text-xs text-muted-foreground", children: count }) : null
904
+ ] }),
905
+ /* @__PURE__ */ jsx17(Chevron, { open })
906
+ ]
907
+ }
908
+ ),
909
+ /* @__PURE__ */ jsx17(AnimatePresence, { initial: false, children: open ? /* @__PURE__ */ jsx17(
910
+ motion2.div,
911
+ {
912
+ id: panelId,
913
+ initial: reduceMotion ? void 0 : { height: 0, opacity: 0 },
914
+ animate: { height: "auto", opacity: 1 },
915
+ exit: reduceMotion ? void 0 : { height: 0, opacity: 0 },
916
+ transition: { duration: 0.2, ease: "easeOut" },
917
+ className: "overflow-hidden",
918
+ children: /* @__PURE__ */ jsx17("div", { className: "bg-muted/20", children })
919
+ },
920
+ "body"
921
+ ) : null })
922
+ ] });
923
+ };
924
+
925
+ // src/app/surfaces/ResourceCard.tsx
926
+ import { Fragment, jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
927
+ var resourceCardShellClass = cn(
928
+ "flex min-h-[8.5rem] flex-col rounded-2xl p-4 text-left font-normal",
929
+ TIMBAL_V2_ELEVATED_SURFACE
930
+ );
931
+ var mediaShellClass = cn(
932
+ "flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-xl text-sm font-normal text-foreground",
933
+ TIMBAL_V2_LOGO_TILE
934
+ );
935
+ var resourceCardInteractiveClass = cn(
936
+ resourceCardShellClass,
937
+ "cursor-pointer bg-transparent hover:bg-transparent active:bg-transparent",
938
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
939
+ );
940
+ var ResourceCard = ({
941
+ title,
942
+ subtitle,
943
+ media,
944
+ badge,
945
+ footer,
946
+ action,
947
+ onClick,
948
+ ariaLabel,
949
+ className
950
+ }) => {
951
+ const body = /* @__PURE__ */ jsxs14(Fragment, { children: [
952
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-start gap-3", children: [
953
+ media ? /* @__PURE__ */ jsx18("span", { className: mediaShellClass, children: media }) : null,
954
+ /* @__PURE__ */ jsxs14("div", { className: "min-w-0 flex-1 pt-0.5", children: [
955
+ /* @__PURE__ */ jsx18("p", { className: "truncate text-sm font-normal leading-snug text-foreground", children: title }),
956
+ subtitle ? /* @__PURE__ */ jsx18("p", { className: "mt-1 line-clamp-2 text-xs font-normal text-muted-foreground", children: subtitle }) : null
957
+ ] }),
958
+ badge ? /* @__PURE__ */ jsx18("span", { className: "shrink-0 pt-0.5", children: badge }) : null
959
+ ] }),
960
+ footer || action ? /* @__PURE__ */ jsxs14("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: [
961
+ /* @__PURE__ */ jsx18("span", { className: "min-w-0 truncate", children: footer }),
962
+ action ? /* @__PURE__ */ jsx18("span", { className: "shrink-0 opacity-80", children: action }) : null
963
+ ] }) : null
964
+ ] });
965
+ if (onClick) {
966
+ return /* @__PURE__ */ jsx18("button", { type: "button", onClick, "aria-label": ariaLabel, className: cn(resourceCardInteractiveClass, className), children: body });
967
+ }
968
+ return /* @__PURE__ */ jsx18("article", { className: cn(resourceCardShellClass, className), children: body });
969
+ };
970
+
971
+ // src/app/settings/SettingsSection.tsx
972
+ import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
973
+ var SettingsSectionHeader = ({
974
+ title,
975
+ description,
976
+ className
977
+ }) => /* @__PURE__ */ jsxs15("div", { className: cn("flex flex-col", className), children: [
978
+ /* @__PURE__ */ jsx19("h3", { className: "text-[17px] font-medium leading-tight text-foreground", children: title }),
979
+ description ? /* @__PURE__ */ jsx19("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
980
+ ] });
981
+ var SettingsSection = ({
982
+ title,
983
+ description,
984
+ descriptionFooter,
985
+ children,
986
+ noBorderTop = false,
987
+ className
988
+ }) => /* @__PURE__ */ jsxs15(
989
+ "section",
990
+ {
991
+ className: cn(
992
+ "grid grid-cols-1 gap-y-4 lg:grid-cols-[minmax(200px,280px)_minmax(0,1fr)] lg:gap-x-12 lg:gap-y-0",
993
+ noBorderTop ? "pb-6" : "border-t border-border py-6",
994
+ className
995
+ ),
996
+ children: [
997
+ /* @__PURE__ */ jsxs15("div", { className: "min-w-0", children: [
998
+ /* @__PURE__ */ jsx19("h2", { className: "text-sm font-medium text-foreground", children: title }),
999
+ description ? /* @__PURE__ */ jsx19("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null,
1000
+ descriptionFooter ? /* @__PURE__ */ jsx19("div", { className: "mt-3 min-w-0", children: descriptionFooter }) : null
1001
+ ] }),
1002
+ /* @__PURE__ */ jsx19("div", { className: "min-w-0 space-y-3", children })
1003
+ ]
1004
+ }
1005
+ );
1006
+
1007
+ // src/app/settings/FieldRow.tsx
1008
+ import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
1009
+ var FieldRow = ({
1010
+ label,
1011
+ children,
1012
+ description,
1013
+ inline = false,
1014
+ htmlFor,
1015
+ className
1016
+ }) => {
1017
+ if (inline) {
1018
+ return /* @__PURE__ */ jsxs16(
1019
+ "div",
1020
+ {
1021
+ className: cn(
1022
+ "flex items-center justify-between gap-4 rounded-lg border border-border bg-card px-3.5 py-3",
1023
+ className
1024
+ ),
1025
+ children: [
1026
+ /* @__PURE__ */ jsxs16("div", { className: "min-w-0", children: [
1027
+ /* @__PURE__ */ jsx20(
1028
+ "label",
1029
+ {
1030
+ htmlFor,
1031
+ className: "block text-sm font-medium text-foreground",
1032
+ children: label
1033
+ }
1034
+ ),
1035
+ description ? /* @__PURE__ */ jsx20("p", { className: "mt-0.5 text-xs text-muted-foreground", children: description }) : null
1036
+ ] }),
1037
+ /* @__PURE__ */ jsx20("div", { className: "shrink-0", children })
1038
+ ]
1039
+ }
1040
+ );
1041
+ }
1042
+ return /* @__PURE__ */ jsxs16("div", { className: cn("flex flex-col gap-1.5", className), children: [
1043
+ /* @__PURE__ */ jsx20("label", { htmlFor, className: "text-sm font-medium text-foreground", children: label }),
1044
+ children,
1045
+ description ? /* @__PURE__ */ jsx20("p", { className: "text-xs text-muted-foreground", children: description }) : null
1046
+ ] });
1047
+ };
1048
+
1049
+ // src/app/settings/FloatingUnsavedChangesBar.tsx
1050
+ import { useEffect, useState as useState3 } from "react";
1051
+ import { createPortal } from "react-dom";
1052
+ import { AnimatePresence as AnimatePresence2, motion as motion3, useReducedMotion as useReducedMotion3 } from "motion/react";
1053
+ import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
1054
+ var FloatingUnsavedChangesBar = ({
1055
+ visible,
1056
+ message = "Unsaved changes",
1057
+ discardLabel = "Discard",
1058
+ saveLabel = "Save changes",
1059
+ isSaving = false,
1060
+ saveDisabled = false,
1061
+ onDiscard,
1062
+ onSave,
1063
+ ariaLabel = "Unsaved changes",
1064
+ className
1065
+ }) => {
1066
+ const reduceMotion = useReducedMotion3();
1067
+ const [mounted, setMounted] = useState3(false);
1068
+ useEffect(() => setMounted(true), []);
1069
+ if (!mounted || typeof document === "undefined") return null;
1070
+ return createPortal(
1071
+ /* @__PURE__ */ jsx21(AnimatePresence2, { children: visible ? /* @__PURE__ */ jsx21("div", { className: "pointer-events-none fixed inset-x-0 bottom-5 z-50 flex justify-center px-4", children: /* @__PURE__ */ jsxs17(
1072
+ motion3.div,
1073
+ {
1074
+ role: "region",
1075
+ "aria-label": ariaLabel,
1076
+ initial: reduceMotion ? { opacity: 0 } : { opacity: 0, y: 28, scale: 0.94 },
1077
+ animate: { opacity: 1, y: 0, scale: 1 },
1078
+ exit: reduceMotion ? { opacity: 0 } : { opacity: 0, y: 18, scale: 0.96 },
1079
+ transition: { type: "spring", stiffness: 420, damping: 32 },
1080
+ className: cn(
1081
+ "pointer-events-auto inline-flex max-w-[calc(100vw-2rem)] items-center gap-3 rounded-full border border-border bg-card py-1.5 pl-4 pr-1.5 shadow-card-elevated",
1082
+ className
1083
+ ),
1084
+ children: [
1085
+ /* @__PURE__ */ jsx21("span", { className: "text-sm text-muted-foreground", children: message }),
1086
+ /* @__PURE__ */ jsxs17("span", { className: "flex items-center gap-1.5", children: [
1087
+ /* @__PURE__ */ jsx21(Button, { variant: "ghost", size: "sm", onClick: onDiscard, disabled: isSaving, children: discardLabel }),
1088
+ /* @__PURE__ */ jsx21(Button, { size: "sm", onClick: onSave, disabled: saveDisabled || isSaving, children: isSaving ? "Saving\u2026" : saveLabel })
1089
+ ] })
1090
+ ]
1091
+ }
1092
+ ) }) : null }),
1093
+ document.body
1094
+ );
1095
+ };
1096
+
1097
+ // src/app/settings/DangerZone.tsx
1098
+ import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
1099
+ var DangerZoneAction = ({
1100
+ title,
1101
+ description,
1102
+ action,
1103
+ className
1104
+ }) => /* @__PURE__ */ jsxs18(
1105
+ "div",
1106
+ {
1107
+ className: cn(
1108
+ "flex flex-col gap-3 px-4 py-3.5 sm:flex-row sm:items-center sm:justify-between",
1109
+ className
1110
+ ),
1111
+ children: [
1112
+ /* @__PURE__ */ jsxs18("div", { className: "min-w-0", children: [
1113
+ /* @__PURE__ */ jsx22("p", { className: "text-sm font-medium text-foreground", children: title }),
1114
+ description ? /* @__PURE__ */ jsx22("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
1115
+ ] }),
1116
+ /* @__PURE__ */ jsx22("div", { className: "shrink-0", children: action })
1117
+ ]
1118
+ }
1119
+ );
1120
+ var DangerZone = ({
1121
+ title = "Danger zone",
1122
+ description,
1123
+ children,
1124
+ className
1125
+ }) => /* @__PURE__ */ jsxs18(
1126
+ "section",
1127
+ {
1128
+ className: cn(
1129
+ "overflow-hidden rounded-xl border border-destructive/30",
1130
+ className
1131
+ ),
1132
+ children: [
1133
+ (title || description) && /* @__PURE__ */ jsxs18("header", { className: "border-b border-destructive/20 bg-destructive/5 px-4 py-3", children: [
1134
+ title ? /* @__PURE__ */ jsx22("h3", { className: "text-sm font-semibold text-destructive", children: title }) : null,
1135
+ description ? /* @__PURE__ */ jsx22("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
1136
+ ] }),
1137
+ /* @__PURE__ */ jsx22("div", { className: "divide-y divide-border bg-card", children })
1138
+ ]
1139
+ }
1140
+ );
1141
+
1142
+ // src/app/integrations/IntegrationCard.tsx
1143
+ import { useId as useId2 } from "react";
1144
+ import { Fragment as Fragment2, jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
1145
+ var INTEGRATION_CATALOG_CARD_HEIGHT_CLASS = "h-[12.25rem] min-h-[12.25rem] max-h-[12.25rem]";
1146
+ var statusLabel = {
1147
+ available: null,
1148
+ connected: "Connected",
1149
+ disabled: "Disabled",
1150
+ locked: "Locked"
1151
+ };
1152
+ var catalogCardShellClass = cn(
1153
+ "group relative box-border flex flex-col overflow-hidden rounded-2xl px-4 pb-4 pt-4 text-left font-normal",
1154
+ INTEGRATION_CATALOG_CARD_HEIGHT_CLASS,
1155
+ TIMBAL_V2_ELEVATED_SURFACE,
1156
+ "transition-opacity duration-200 ease-out"
1157
+ );
1158
+ var catalogCardInteractiveClass = cn(
1159
+ catalogCardShellClass,
1160
+ "cursor-pointer bg-transparent hover:bg-transparent active:bg-transparent",
1161
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
1162
+ );
1163
+ var catalogCardMutedClass = cn(
1164
+ "border-border/60 saturate-[0.72]",
1165
+ "from-muted/80 to-muted/50 dark:border-white/[0.06] dark:from-white/[0.04] dark:to-white/[0.02]"
1166
+ );
1167
+ var logoShellClass = cn(
1168
+ "relative flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-xl",
1169
+ TIMBAL_V2_LOGO_TILE
1170
+ );
1171
+ var IntegrationCard = ({
1172
+ name,
1173
+ description,
1174
+ logo,
1175
+ badge,
1176
+ status = "available",
1177
+ action,
1178
+ onClick,
1179
+ ariaLabel,
1180
+ className
1181
+ }) => {
1182
+ const titleId = useId2();
1183
+ const locked = status === "locked";
1184
+ const dimmed = status === "disabled" || locked;
1185
+ const body = /* @__PURE__ */ jsxs19("div", { className: "flex h-full min-h-0 flex-col", children: [
1186
+ /* @__PURE__ */ jsxs19("div", { className: "flex shrink-0 items-start gap-3 pr-2", children: [
1187
+ logo ? /* @__PURE__ */ jsx23("span", { className: logoShellClass, "aria-hidden": Boolean(ariaLabel), children: logo }) : null,
1188
+ /* @__PURE__ */ jsx23("div", { className: "min-w-0 flex-1 pt-0.5", children: /* @__PURE__ */ jsxs19("div", { className: "flex items-start justify-between gap-2", children: [
1189
+ /* @__PURE__ */ jsxs19("div", { className: "min-w-0", children: [
1190
+ /* @__PURE__ */ jsx23(
1191
+ "h4",
1192
+ {
1193
+ id: onClick && !action ? void 0 : titleId,
1194
+ className: "truncate text-sm font-normal leading-snug text-foreground",
1195
+ children: name
1196
+ }
1197
+ ),
1198
+ statusLabel[status] ? /* @__PURE__ */ jsx23("p", { className: "mt-0.5 text-xs text-muted-foreground", children: statusLabel[status] }) : null
1199
+ ] }),
1200
+ badge ? /* @__PURE__ */ jsx23("span", { className: "shrink-0", children: badge }) : null
1201
+ ] }) })
1202
+ ] }),
1203
+ description ? /* @__PURE__ */ jsx23(
1204
+ "p",
1205
+ {
1206
+ className: cn(
1207
+ "mt-3 line-clamp-3 shrink-0 text-sm leading-relaxed text-muted-foreground",
1208
+ dimmed && "text-muted-foreground/80"
1209
+ ),
1210
+ children: description
1211
+ }
1212
+ ) : null,
1213
+ action ? /* @__PURE__ */ jsxs19(Fragment2, { children: [
1214
+ /* @__PURE__ */ jsx23("div", { className: "min-h-0 flex-1", "aria-hidden": true }),
1215
+ /* @__PURE__ */ jsx23("div", { className: "relative mt-3 shrink-0", children: action })
1216
+ ] }) : null
1217
+ ] });
1218
+ const shellClass3 = cn(
1219
+ catalogCardShellClass,
1220
+ dimmed && catalogCardMutedClass,
1221
+ locked && "cursor-default opacity-75",
1222
+ className
1223
+ );
1224
+ if (onClick && !action) {
1225
+ return /* @__PURE__ */ jsx23(
1226
+ "button",
1227
+ {
1228
+ type: "button",
1229
+ onClick,
1230
+ disabled: locked,
1231
+ "aria-label": ariaLabel,
1232
+ className: cn(
1233
+ catalogCardInteractiveClass,
1234
+ dimmed && catalogCardMutedClass,
1235
+ locked && "cursor-default opacity-75",
1236
+ className
1237
+ ),
1238
+ children: body
1239
+ }
1240
+ );
1241
+ }
1242
+ return /* @__PURE__ */ jsx23("article", { className: shellClass3, "aria-labelledby": titleId, children: body });
1243
+ };
1244
+
1245
+ // src/app/integrations/IntegrationsEmptyState.tsx
1246
+ import { useId as useId3 } from "react";
1247
+ import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
1248
+ var IntegrationsEmptyState = ({
1249
+ title = "No integrations yet",
1250
+ description = "Connect a provider to start syncing data and powering your workforce.",
1251
+ icon,
1252
+ action,
1253
+ className
1254
+ }) => {
1255
+ const titleId = useId3();
1256
+ return /* @__PURE__ */ jsxs20(
1257
+ "section",
1258
+ {
1259
+ className: cn(
1260
+ "flex flex-col items-center justify-center gap-3 rounded-2xl px-6 py-14 text-center",
1261
+ TIMBAL_V2_ELEVATED_SURFACE,
1262
+ className
1263
+ ),
1264
+ "aria-labelledby": titleId,
1265
+ children: [
1266
+ icon ? /* @__PURE__ */ jsx24(
1267
+ "span",
1268
+ {
1269
+ className: cn(
1270
+ "flex size-14 items-center justify-center overflow-hidden rounded-2xl",
1271
+ TIMBAL_V2_LOGO_TILE,
1272
+ "text-muted-foreground"
1273
+ ),
1274
+ "aria-hidden": true,
1275
+ children: icon
1276
+ }
1277
+ ) : null,
1278
+ /* @__PURE__ */ jsx24("h3", { id: titleId, className: "text-base font-normal text-foreground", children: title }),
1279
+ description ? /* @__PURE__ */ jsx24("p", { className: "max-w-sm text-sm text-muted-foreground", children: description }) : null,
1280
+ action ? /* @__PURE__ */ jsx24("div", { className: "mt-1", children: action }) : null
1281
+ ]
1282
+ }
1283
+ );
1284
+ };
1285
+
1286
+ // src/app/integrations/PlanBadge.tsx
1287
+ import { jsx as jsx25 } from "react/jsx-runtime";
1288
+ var planBadgeClass = "inline-flex h-5 max-w-full shrink-0 items-center rounded-md border border-border bg-muted/90 px-2 text-[11px] font-normal text-muted-foreground dark:border-white/10 dark:bg-white/5 dark:text-muted-foreground";
1289
+ var PlanBadge = ({ children, className }) => /* @__PURE__ */ jsx25("span", { className: cn(planBadgeClass, className), children });
1290
+
1291
+ // src/app/integrations/ConnectionRow.tsx
1292
+ import { Fragment as Fragment3, jsx as jsx26, jsxs as jsxs21 } from "react/jsx-runtime";
1293
+ var logoShellClass2 = cn(
1294
+ "flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-lg",
1295
+ TIMBAL_V2_LOGO_TILE
1296
+ );
1297
+ var ConnectionRow = ({
1298
+ name,
1299
+ logo,
1300
+ meta,
1301
+ badge,
1302
+ action,
1303
+ onClick,
1304
+ ariaLabel,
1305
+ className
1306
+ }) => {
1307
+ const inner = /* @__PURE__ */ jsxs21(Fragment3, { children: [
1308
+ logo ? /* @__PURE__ */ jsx26("span", { className: logoShellClass2, children: logo }) : null,
1309
+ /* @__PURE__ */ jsxs21("div", { className: "min-w-0 flex-1", children: [
1310
+ /* @__PURE__ */ jsx26("p", { className: "truncate text-sm font-normal text-foreground", children: name }),
1311
+ meta ? /* @__PURE__ */ jsx26("p", { className: "truncate text-xs text-muted-foreground", children: meta }) : null
1312
+ ] }),
1313
+ badge ? /* @__PURE__ */ jsx26("span", { className: "shrink-0", children: badge }) : null,
1314
+ action ? /* @__PURE__ */ jsx26("span", { className: "shrink-0", children: action }) : null
1315
+ ] });
1316
+ const rowClass2 = cn(
1317
+ "flex w-full items-center gap-3 px-4 py-3 text-left",
1318
+ onClick && "cursor-pointer bg-transparent hover:bg-transparent active:bg-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-foreground/10",
1319
+ className
1320
+ );
1321
+ if (onClick) {
1322
+ return /* @__PURE__ */ jsx26(
1323
+ "button",
1324
+ {
1325
+ type: "button",
1326
+ role: "listitem",
1327
+ onClick,
1328
+ "aria-label": ariaLabel,
1329
+ className: rowClass2,
1330
+ children: inner
1331
+ }
1332
+ );
1333
+ }
1334
+ return /* @__PURE__ */ jsx26("div", { role: "listitem", className: rowClass2, children: inner });
1335
+ };
1336
+ var connectionRowListClass = cn(
1337
+ "overflow-hidden rounded-2xl",
1338
+ TIMBAL_V2_ELEVATED_SURFACE
1339
+ );
1340
+
1341
+ // src/app/integrations/ConnectionRowList.tsx
1342
+ import { jsx as jsx27 } from "react/jsx-runtime";
1343
+ var ConnectionRowList = ({
1344
+ children,
1345
+ "aria-label": ariaLabel = "Connected integrations",
1346
+ className
1347
+ }) => /* @__PURE__ */ jsx27(
1348
+ "div",
1349
+ {
1350
+ role: "list",
1351
+ "aria-label": ariaLabel,
1352
+ className: cn(connectionRowListClass, "divide-y divide-border", className),
1353
+ children
1354
+ }
1355
+ );
1356
+
1357
+ // src/app/navigation/SubNav.tsx
1358
+ import { jsx as jsx28 } from "react/jsx-runtime";
1359
+ var SubNav = ({
1360
+ items,
1361
+ activeId,
1362
+ onChange,
1363
+ className,
1364
+ "aria-label": ariaLabel = "Section navigation",
1365
+ layoutId
1366
+ }) => {
1367
+ return /* @__PURE__ */ jsx28("nav", { className: cn("aui-app-sub-nav", className), "aria-label": ariaLabel, children: /* @__PURE__ */ jsx28(
1368
+ PillSegmentedTabs,
1369
+ {
1370
+ value: activeId,
1371
+ onChange,
1372
+ tabs: items.map((item) => ({ key: item.id, label: item.label })),
1373
+ trackVariant: "flush",
1374
+ layoutId: layoutId ?? "app-sub-nav-segmented",
1375
+ "aria-label": ariaLabel
1376
+ }
1377
+ ) });
1378
+ };
1379
+
1380
+ // src/app/navigation/Breadcrumbs.tsx
1381
+ import { jsx as jsx29, jsxs as jsxs22 } from "react/jsx-runtime";
1382
+ var Breadcrumbs = ({ items, className }) => {
1383
+ return /* @__PURE__ */ jsx29("nav", { className: cn("aui-app-breadcrumbs", appBreadcrumbsClass, className), "aria-label": "Breadcrumb", children: /* @__PURE__ */ jsx29("ol", { className: "flex flex-wrap items-center gap-1.5", children: items.map((item, index) => {
1384
+ const isLast = index === items.length - 1;
1385
+ return /* @__PURE__ */ jsxs22("li", { className: "inline-flex items-center gap-1.5", children: [
1386
+ index > 0 ? /* @__PURE__ */ jsx29("span", { className: "text-muted-foreground/50", "aria-hidden": true, children: "/" }) : null,
1387
+ isLast ? /* @__PURE__ */ jsx29("span", { className: "text-foreground", "aria-current": "page", children: item.label }) : item.href ? /* @__PURE__ */ jsx29("a", { href: item.href, className: appBreadcrumbLinkClass, children: item.label }) : /* @__PURE__ */ jsx29(
1388
+ "button",
1389
+ {
1390
+ type: "button",
1391
+ className: appBreadcrumbLinkClass,
1392
+ onClick: item.onClick,
1393
+ children: item.label
1394
+ }
1395
+ )
1396
+ ] }, index);
1397
+ }) }) });
1398
+ };
1399
+
1400
+ // src/app/forms/Field.tsx
1401
+ import { jsx as jsx30, jsxs as jsxs23 } from "react/jsx-runtime";
1402
+ var Field = ({
1403
+ label,
1404
+ hint,
1405
+ error,
1406
+ children,
1407
+ className,
1408
+ htmlFor
1409
+ }) => {
1410
+ return /* @__PURE__ */ jsxs23("div", { className: cn("aui-app-field", appFieldClass, className), children: [
1411
+ /* @__PURE__ */ jsx30("label", { className: appFieldLabelClass, htmlFor, children: label }),
1412
+ children,
1413
+ hint && !error ? /* @__PURE__ */ jsx30("p", { className: appFieldHintClass, children: hint }) : null,
1414
+ error ? /* @__PURE__ */ jsx30("p", { className: "text-xs text-destructive", role: "alert", children: error }) : null
1415
+ ] });
1416
+ };
1417
+ var FieldInput = ({
1418
+ label,
1419
+ hint,
1420
+ error,
1421
+ fieldClassName,
1422
+ className,
1423
+ id,
1424
+ ...inputProps
1425
+ }) => {
1426
+ const inputId = id ?? inputProps.name;
1427
+ return /* @__PURE__ */ jsx30(
1428
+ Field,
1429
+ {
1430
+ label,
1431
+ hint,
1432
+ error,
1433
+ htmlFor: inputId,
1434
+ className: fieldClassName,
1435
+ children: /* @__PURE__ */ jsx30(
1436
+ "input",
1437
+ {
1438
+ id: inputId,
1439
+ className: cn(appInputClass, className),
1440
+ "aria-invalid": error ? true : void 0,
1441
+ ...inputProps
1442
+ }
1443
+ )
1444
+ }
1445
+ );
1446
+ };
1447
+
1448
+ // src/app/forms/FieldTextarea.tsx
1449
+ import { jsx as jsx31 } from "react/jsx-runtime";
1450
+ var textareaClass = cn(
1451
+ appInputClass,
1452
+ "min-h-[5.5rem] resize-y py-2.5 leading-relaxed"
1453
+ );
1454
+ var FieldTextarea = ({
1455
+ label,
1456
+ hint,
1457
+ error,
1458
+ fieldClassName,
1459
+ className,
1460
+ id,
1461
+ ...props
1462
+ }) => {
1463
+ const textareaId = id ?? props.name;
1464
+ return /* @__PURE__ */ jsx31(
1465
+ Field,
1466
+ {
1467
+ label,
1468
+ hint,
1469
+ error,
1470
+ htmlFor: textareaId,
1471
+ className: fieldClassName,
1472
+ children: /* @__PURE__ */ jsx31(
1473
+ "textarea",
1474
+ {
1475
+ id: textareaId,
1476
+ className: cn(textareaClass, className),
1477
+ "aria-invalid": error ? true : void 0,
1478
+ ...props
1479
+ }
1480
+ )
1481
+ }
1482
+ );
1483
+ };
1484
+
1485
+ // src/app/forms/FieldSelect.tsx
1486
+ import { ChevronDownIcon } from "lucide-react";
1487
+ import { jsx as jsx32, jsxs as jsxs24 } from "react/jsx-runtime";
1488
+ var selectWrapClass = "relative";
1489
+ var selectClass = cn(
1490
+ appInputClass,
1491
+ "appearance-none pr-9"
1492
+ );
1493
+ var FieldSelect = ({
1494
+ label,
1495
+ hint,
1496
+ error,
1497
+ fieldClassName,
1498
+ className,
1499
+ children,
1500
+ id,
1501
+ ...props
1502
+ }) => {
1503
+ const selectId = id ?? props.name;
1504
+ return /* @__PURE__ */ jsx32(
1505
+ Field,
1506
+ {
1507
+ label,
1508
+ hint,
1509
+ error,
1510
+ htmlFor: selectId,
1511
+ className: fieldClassName,
1512
+ children: /* @__PURE__ */ jsxs24("div", { className: selectWrapClass, children: [
1513
+ /* @__PURE__ */ jsx32(
1514
+ "select",
1515
+ {
1516
+ id: selectId,
1517
+ className: cn(selectClass, className),
1518
+ "aria-invalid": error ? true : void 0,
1519
+ ...props,
1520
+ children
1521
+ }
1522
+ ),
1523
+ /* @__PURE__ */ jsx32(
1524
+ ChevronDownIcon,
1525
+ {
1526
+ className: "pointer-events-none absolute top-1/2 right-3 size-4 -translate-y-1/2 text-muted-foreground",
1527
+ "aria-hidden": true
1528
+ }
1529
+ )
1530
+ ] })
1531
+ }
1532
+ );
1533
+ };
1534
+
1535
+ // src/app/forms/FieldSwitch.tsx
1536
+ import { jsx as jsx33, jsxs as jsxs25 } from "react/jsx-runtime";
1537
+ var trackClass = cn(
1538
+ "relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-[background,box-shadow,border-color] duration-200",
1539
+ "peer-focus-visible:ring-2 peer-focus-visible:ring-foreground/10",
1540
+ "peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
1541
+ TIMBAL_V2_SWITCH_TRACK_OFF,
1542
+ "peer-checked:border-foreground/15 peer-checked:from-primary-fill-from peer-checked:to-primary-fill-to peer-checked:shadow-card",
1543
+ "peer-checked:[&>span]:translate-x-4"
1544
+ );
1545
+ var thumbClass = cn(
1546
+ "pointer-events-none inline-block size-4 shrink-0 translate-x-0.5 rounded-full transition-transform",
1547
+ TIMBAL_V2_SWITCH_THUMB
1548
+ );
1549
+ var FieldSwitch = ({
1550
+ label,
1551
+ description,
1552
+ className,
1553
+ id,
1554
+ ...props
1555
+ }) => {
1556
+ const inputId = id ?? props.name ?? "switch";
1557
+ return /* @__PURE__ */ jsxs25(
1558
+ "label",
1559
+ {
1560
+ className: cn(
1561
+ "aui-app-field-switch flex cursor-pointer items-start gap-3",
1562
+ className
1563
+ ),
1564
+ htmlFor: inputId,
1565
+ children: [
1566
+ /* @__PURE__ */ jsxs25("span", { className: "relative mt-0.5", children: [
1567
+ /* @__PURE__ */ jsx33(
1568
+ "input",
1569
+ {
1570
+ id: inputId,
1571
+ type: "checkbox",
1572
+ role: "switch",
1573
+ className: "peer sr-only",
1574
+ ...props
1575
+ }
1576
+ ),
1577
+ /* @__PURE__ */ jsx33("span", { className: trackClass, "aria-hidden": true, children: /* @__PURE__ */ jsx33("span", { className: thumbClass }) })
1578
+ ] }),
1579
+ /* @__PURE__ */ jsxs25("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
1580
+ /* @__PURE__ */ jsx33("span", { className: "text-sm font-medium text-foreground", children: label }),
1581
+ description ? /* @__PURE__ */ jsx33("span", { className: "text-xs text-muted-foreground", children: description }) : null
1582
+ ] })
1583
+ ]
1584
+ }
1585
+ );
1586
+ };
1587
+
1588
+ // src/app/forms/SearchInput.tsx
1589
+ import { SearchIcon } from "lucide-react";
1590
+ import { jsx as jsx34, jsxs as jsxs26 } from "react/jsx-runtime";
1591
+ var SearchInput = ({
1592
+ className,
1593
+ placeholder = "Search\u2026",
1594
+ ...props
1595
+ }) => {
1596
+ return /* @__PURE__ */ jsxs26(
1597
+ "label",
1598
+ {
1599
+ className: cn(
1600
+ "aui-app-search-input inline-flex min-w-[12rem] items-center gap-2",
1601
+ appSearchInputClass,
1602
+ className
1603
+ ),
1604
+ children: [
1605
+ /* @__PURE__ */ jsx34(SearchIcon, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": true }),
1606
+ /* @__PURE__ */ jsx34(
1607
+ "input",
1608
+ {
1609
+ type: "search",
1610
+ placeholder,
1611
+ className: "min-w-0 flex-1 border-0 bg-transparent text-sm outline-none placeholder:text-muted-foreground/70",
1612
+ ...props
1613
+ }
1614
+ )
1615
+ ]
1616
+ }
1617
+ );
1618
+ };
1619
+
1620
+ // src/app/forms/FormSection.tsx
1621
+ import { jsx as jsx35, jsxs as jsxs27 } from "react/jsx-runtime";
1622
+ var FormSection = ({ title, children, className }) => {
1623
+ return /* @__PURE__ */ jsxs27("fieldset", { className: cn("aui-app-form-section", appSectionClass, "border-0 p-0", className), children: [
1624
+ title ? /* @__PURE__ */ jsx35("legend", { className: cn(appSectionTitleClass, "mb-3 px-0"), children: title }) : null,
1625
+ /* @__PURE__ */ jsx35("div", { className: "flex flex-col gap-4", children })
1626
+ ] });
1627
+ };
1628
+
1629
+ // src/app/data/FilterBar.tsx
1630
+ import { jsx as jsx36 } from "react/jsx-runtime";
1631
+ var FilterBar = ({ children, className }) => {
1632
+ return /* @__PURE__ */ jsx36(
1633
+ "div",
1634
+ {
1635
+ className: cn("aui-app-filter-bar", appFilterBarClass, className),
1636
+ role: "toolbar",
1637
+ "aria-label": "Filters",
1638
+ children
1639
+ }
1640
+ );
1641
+ };
1642
+
1643
+ // src/app/data/DataTable.tsx
1644
+ import { useMemo, useState as useState4 } from "react";
1645
+ import { ArrowDownIcon, ArrowUpDownIcon, ArrowUpIcon } from "lucide-react";
1646
+ import { jsx as jsx37, jsxs as jsxs28 } from "react/jsx-runtime";
1647
+ var shellClass2 = "overflow-hidden rounded-xl border border-border bg-card shadow-card";
1648
+ var tableClass = "w-full border-collapse bg-transparent text-sm";
1649
+ 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";
1650
+ var bodyCellClass = "border-b border-border/40 bg-transparent px-4 py-2.5 text-foreground";
1651
+ var rowClass = "bg-transparent transition-colors hover:bg-foreground/[0.03] data-[clickable=true]:cursor-pointer";
1652
+ var footCellClass = "border-t border-border/60 bg-transparent px-4 py-2.5 text-xs text-muted-foreground";
1653
+ var footInnerClass = "flex flex-wrap items-center gap-2";
1654
+ var emptyCellClass = "bg-transparent px-4 py-10 text-center text-sm text-muted-foreground";
1655
+ var sortButtonClass = "group inline-flex min-w-0 items-center gap-1.5 rounded-md -mx-1 px-1 py-0.5 text-left font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/10";
1656
+ var stickyHeadClass = "sticky top-0 z-[1] bg-card/95 shadow-[0_1px_0_0_hsl(var(--border)/0.5)] backdrop-blur-sm [&_th]:bg-card/95";
1657
+ var alignClass = {
1658
+ left: "text-left",
1659
+ center: "text-center",
1660
+ right: "text-right"
1661
+ };
1662
+ function compareSortValues(a, b) {
1663
+ if (a == null && b == null) return 0;
1664
+ if (a == null) return 1;
1665
+ if (b == null) return -1;
1666
+ if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();
1667
+ if (typeof a === "number" && typeof b === "number") return a - b;
1668
+ if (typeof a === "boolean" && typeof b === "boolean") return Number(a) - Number(b);
1669
+ return String(a).localeCompare(String(b), void 0, { sensitivity: "base" });
1670
+ }
1671
+ function nextSort(current, columnId) {
1672
+ if (current?.columnId !== columnId) {
1673
+ return { columnId, direction: "asc" };
1674
+ }
1675
+ if (current.direction === "asc") {
1676
+ return { columnId, direction: "desc" };
1677
+ }
1678
+ return null;
1679
+ }
1680
+ function SortIndicator({
1681
+ active,
1682
+ direction
1683
+ }) {
1684
+ const iconClass = "size-3.5 shrink-0 opacity-60 group-hover:opacity-100";
1685
+ if (!active) {
1686
+ return /* @__PURE__ */ jsx37(ArrowUpDownIcon, { className: iconClass, "aria-hidden": true });
1687
+ }
1688
+ if (direction === "desc") {
1689
+ return /* @__PURE__ */ jsx37(ArrowDownIcon, { className: iconClass, "aria-hidden": true });
1690
+ }
1691
+ return /* @__PURE__ */ jsx37(ArrowUpIcon, { className: iconClass, "aria-hidden": true });
1692
+ }
1693
+ function DataTable({
1694
+ columns,
1695
+ rows,
1696
+ getRowKey,
1697
+ emptyTitle = "No data",
1698
+ emptyDescription,
1699
+ emptyMode = "replace",
1700
+ className,
1701
+ sort: sortProp,
1702
+ defaultSort = null,
1703
+ onSortChange,
1704
+ showRowCount = false,
1705
+ rowCountLabel,
1706
+ footer,
1707
+ onRowClick,
1708
+ stickyHeader = false,
1709
+ dense = false,
1710
+ caption
1711
+ }) {
1712
+ const [uncontrolledSort, setUncontrolledSort] = useState4(
1713
+ defaultSort
1714
+ );
1715
+ const isSortControlled = sortProp !== void 0;
1716
+ const sort = isSortControlled ? sortProp : uncontrolledSort;
1717
+ const setSort = (next) => {
1718
+ if (!isSortControlled) {
1719
+ setUncontrolledSort(next);
1720
+ }
1721
+ onSortChange?.(next);
1722
+ };
1723
+ const sortedRows = useMemo(() => {
1724
+ if (!sort) return rows;
1725
+ const column = columns.find((col) => col.id === sort.columnId);
1726
+ if (!column?.sortable) return rows;
1727
+ const getValue = column.sortValue ?? ((row) => {
1728
+ const rendered = column.cell(row);
1729
+ if (rendered === null || rendered === void 0 || typeof rendered === "string" || typeof rendered === "number" || typeof rendered === "boolean") {
1730
+ return rendered;
1731
+ }
1732
+ return String(rendered);
1733
+ });
1734
+ return [...rows].sort((a, b) => {
1735
+ const cmp = compareSortValues(getValue(a), getValue(b));
1736
+ return sort.direction === "asc" ? cmp : -cmp;
1737
+ });
1738
+ }, [columns, rows, sort]);
1739
+ const cellPad = dense ? "px-3 py-2" : void 0;
1740
+ const headPad = dense ? "px-3 py-2" : void 0;
1741
+ if (rows.length === 0 && emptyMode === "replace") {
1742
+ return /* @__PURE__ */ jsx37(EmptyState, { title: emptyTitle, description: emptyDescription, className });
1743
+ }
1744
+ const rowCountText = rowCountLabel?.(sortedRows.length) ?? `${sortedRows.length} row${sortedRows.length === 1 ? "" : "s"}`;
1745
+ const hasFoot = Boolean((showRowCount || footer) && sortedRows.length > 0);
1746
+ return /* @__PURE__ */ jsx37("div", { className: cn("aui-app-data-table", shellClass2, className), children: /* @__PURE__ */ jsx37("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs28("table", { className: tableClass, children: [
1747
+ caption ? /* @__PURE__ */ jsx37("caption", { className: "sr-only", children: caption }) : null,
1748
+ /* @__PURE__ */ jsx37("thead", { className: cn(stickyHeader && stickyHeadClass), children: /* @__PURE__ */ jsx37("tr", { children: columns.map((col) => {
1749
+ const isSorted = sort?.columnId === col.id;
1750
+ const ariaSort = col.sortable ? isSorted ? sort.direction === "asc" ? "ascending" : "descending" : "none" : void 0;
1751
+ const headerContent = col.sortable ? /* @__PURE__ */ jsxs28(
1752
+ "button",
1753
+ {
1754
+ type: "button",
1755
+ className: sortButtonClass,
1756
+ onClick: () => setSort(nextSort(sort, col.id)),
1757
+ children: [
1758
+ /* @__PURE__ */ jsx37("span", { className: "truncate", children: col.header }),
1759
+ /* @__PURE__ */ jsx37(SortIndicator, { active: Boolean(isSorted), direction: sort?.direction })
1760
+ ]
1761
+ }
1762
+ ) : col.header;
1763
+ return /* @__PURE__ */ jsx37(
1764
+ "th",
1765
+ {
1766
+ scope: "col",
1767
+ "aria-sort": ariaSort,
1768
+ className: cn(
1769
+ headCellClass,
1770
+ headPad,
1771
+ col.align && alignClass[col.align],
1772
+ col.headerClassName
1773
+ ),
1774
+ children: headerContent
1775
+ },
1776
+ col.id
1777
+ );
1778
+ }) }) }),
1779
+ /* @__PURE__ */ jsx37("tbody", { className: cn(!hasFoot && "[&_tr:last-child_td]:border-b-0"), children: sortedRows.length === 0 ? /* @__PURE__ */ jsx37("tr", { children: /* @__PURE__ */ jsx37("td", { colSpan: columns.length, className: emptyCellClass, children: /* @__PURE__ */ jsxs28("div", { className: "flex flex-col items-center gap-1", children: [
1780
+ /* @__PURE__ */ jsx37("p", { className: "font-medium text-foreground", children: emptyTitle }),
1781
+ emptyDescription ? /* @__PURE__ */ jsx37("p", { className: "max-w-sm text-muted-foreground", children: emptyDescription }) : null
1782
+ ] }) }) }) : sortedRows.map((row) => /* @__PURE__ */ jsx37(
1783
+ "tr",
1784
+ {
1785
+ className: rowClass,
1786
+ "data-clickable": onRowClick ? "true" : void 0,
1787
+ onClick: onRowClick ? () => onRowClick(row) : void 0,
1788
+ onKeyDown: onRowClick ? (event) => {
1789
+ if (event.key === "Enter" || event.key === " ") {
1790
+ event.preventDefault();
1791
+ onRowClick(row);
1792
+ }
1793
+ } : void 0,
1794
+ tabIndex: onRowClick ? 0 : void 0,
1795
+ role: onRowClick ? "button" : void 0,
1796
+ children: columns.map((col) => /* @__PURE__ */ jsx37(
1797
+ "td",
1798
+ {
1799
+ className: cn(
1800
+ bodyCellClass,
1801
+ cellPad,
1802
+ col.align && alignClass[col.align],
1803
+ col.className
1804
+ ),
1805
+ children: col.cell(row)
1806
+ },
1807
+ col.id
1808
+ ))
1809
+ },
1810
+ getRowKey(row)
1811
+ )) }),
1812
+ hasFoot ? /* @__PURE__ */ jsx37("tfoot", { children: /* @__PURE__ */ jsx37("tr", { children: /* @__PURE__ */ jsx37("td", { colSpan: columns.length, className: footCellClass, children: /* @__PURE__ */ jsxs28(
1813
+ "div",
1814
+ {
1815
+ className: cn(
1816
+ footInnerClass,
1817
+ showRowCount && footer ? "justify-between" : "justify-start"
1818
+ ),
1819
+ children: [
1820
+ showRowCount ? /* @__PURE__ */ jsx37("span", { children: rowCountText }) : null,
1821
+ footer
1822
+ ]
1823
+ }
1824
+ ) }) }) }) : null
1825
+ ] }) }) });
1826
+ }
1827
+
1828
+ // src/app/data/ChartPanel.tsx
1829
+ import { useId as useId4 } from "react";
1830
+
1831
+ // src/app/data/metrics-shared.tsx
1832
+ import { jsx as jsx38, jsxs as jsxs29 } from "react/jsx-runtime";
1833
+ var metricCardShellClass = cn(
1834
+ studioIntegrationCardClass,
1835
+ "aui-app-metric-card shadow-none",
1836
+ "flex flex-col overflow-hidden"
1837
+ );
1838
+ var metricCardHeaderClass = "flex items-start justify-between gap-3 px-4 pb-1 pt-3";
1839
+ var metricTilesRowClass = "grid w-full min-w-0";
1840
+ var metricChartRegionClass = "relative min-h-0 w-full border-t border-border/40 pt-2";
1841
+ var metricChartPlotRegionClass = "relative min-h-0 w-full border-t border-border/40 px-0 pt-5 pb-3";
1842
+ var metricCellDividerClass = "border-r border-border/40";
1843
+ var MetricCardHeader = ({
1844
+ title,
1845
+ titleId,
1846
+ description,
1847
+ actions
1848
+ }) => {
1849
+ if (!title && !description && !actions) return null;
1850
+ return /* @__PURE__ */ jsxs29("header", { className: metricCardHeaderClass, children: [
1851
+ /* @__PURE__ */ jsxs29("div", { className: "min-w-0", children: [
1852
+ title ? /* @__PURE__ */ jsx38("h3", { id: titleId, className: "text-base font-normal text-foreground", children: title }) : null,
1853
+ description ? /* @__PURE__ */ jsx38("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
1854
+ ] }),
1855
+ actions ? /* @__PURE__ */ jsx38("div", { className: "shrink-0", children: actions }) : null
1856
+ ] });
1857
+ };
1858
+ function metricTilesGridColsClass(n) {
1859
+ switch (n) {
1860
+ case 1:
1861
+ return "grid-cols-1";
1862
+ case 2:
1863
+ return "grid-cols-2";
1864
+ case 3:
1865
+ return "grid-cols-3";
1866
+ case 5:
1867
+ return "grid-cols-2 sm:grid-cols-5";
1868
+ case 6:
1869
+ return "grid-cols-2 sm:grid-cols-3 lg:grid-cols-6";
1870
+ default:
1871
+ return "grid-cols-2 md:grid-cols-4";
1872
+ }
1873
+ }
1874
+
1875
+ // src/app/data/ChartPanel.tsx
1876
+ import { jsx as jsx39, jsxs as jsxs30 } from "react/jsx-runtime";
1877
+ var ChartPanel = ({
1878
+ title,
1879
+ description,
1880
+ artifact,
1881
+ children,
1882
+ actions,
1883
+ height = 300,
1884
+ className
1885
+ }) => {
1886
+ const titleId = useId4();
1887
+ const resolvedTitle = title ?? artifact?.title;
1888
+ const hasHeader = Boolean(resolvedTitle || description || actions);
1889
+ const body = children ?? (artifact ? /* @__PURE__ */ jsx39(ChartArtifactView, { artifact, embedded: true, height }) : null);
1890
+ return /* @__PURE__ */ jsxs30(
1891
+ "section",
1892
+ {
1893
+ className: cn(metricCardShellClass, "aui-app-chart-panel", className),
1894
+ "aria-labelledby": resolvedTitle ? titleId : void 0,
1895
+ children: [
1896
+ /* @__PURE__ */ jsx39(
1897
+ MetricCardHeader,
1898
+ {
1899
+ title: resolvedTitle,
1900
+ titleId,
1901
+ description,
1902
+ actions
1903
+ }
1904
+ ),
1905
+ /* @__PURE__ */ jsx39(
1906
+ "div",
1907
+ {
1908
+ className: cn(
1909
+ "relative min-h-0 w-full",
1910
+ hasHeader ? metricChartPlotRegionClass : "pt-2 pb-3"
1911
+ ),
1912
+ children: body ?? /* @__PURE__ */ jsx39(
1913
+ "div",
1914
+ {
1915
+ className: "flex items-center justify-center text-sm font-normal text-muted-foreground",
1916
+ style: { minHeight: height },
1917
+ role: "status",
1918
+ children: "No chart"
1919
+ }
1920
+ )
1921
+ }
1922
+ )
1923
+ ]
1924
+ }
1925
+ );
1926
+ };
1927
+
1928
+ // src/app/data/MetricTile.tsx
1929
+ import { Fragment as Fragment4, jsx as jsx40, jsxs as jsxs31 } from "react/jsx-runtime";
1930
+ var trendToneClass = {
1931
+ up: "border-border/80 bg-muted/40 text-muted-foreground",
1932
+ down: "border-border/80 bg-muted/40 text-muted-foreground",
1933
+ neutral: "border-border/80 bg-muted/30 text-muted-foreground"
1934
+ };
1935
+ var metricTileBaseClass = "relative flex min-w-0 flex-1 flex-col gap-1 px-4 py-3 text-left font-normal";
1936
+ var metricTileInteractiveClass = cn(
1937
+ metricTileBaseClass,
1938
+ "bg-transparent hover:bg-transparent active:bg-transparent",
1939
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-foreground/10"
1940
+ );
1941
+ var MetricTile = ({
1942
+ label,
1943
+ value,
1944
+ unit,
1945
+ trend,
1946
+ trendTone = "neutral",
1947
+ active = false,
1948
+ showDivider = false,
1949
+ onSelect,
1950
+ ariaLabel,
1951
+ className
1952
+ }) => {
1953
+ const content = /* @__PURE__ */ jsxs31(Fragment4, { children: [
1954
+ active ? /* @__PURE__ */ jsx40(
1955
+ "span",
1956
+ {
1957
+ "aria-hidden": true,
1958
+ className: "absolute inset-x-0 bottom-0 h-0.5 bg-foreground dark:bg-white"
1959
+ }
1960
+ ) : null,
1961
+ /* @__PURE__ */ jsx40("span", { className: "text-xs font-normal text-muted-foreground", children: label }),
1962
+ /* @__PURE__ */ jsxs31("span", { className: "flex items-center gap-2", children: [
1963
+ /* @__PURE__ */ jsxs31("span", { className: "flex items-baseline gap-1", children: [
1964
+ /* @__PURE__ */ jsx40("span", { className: "text-2xl font-normal tracking-tight text-foreground tabular-nums", children: value }),
1965
+ unit ? /* @__PURE__ */ jsx40("span", { className: "text-xs font-normal text-muted-foreground", children: unit }) : null
1966
+ ] }),
1967
+ trend ? /* @__PURE__ */ jsx40(
1968
+ "span",
1969
+ {
1970
+ className: cn(
1971
+ "rounded-full border px-1.5 py-0.5 text-xs font-normal",
1972
+ trendToneClass[trendTone]
1973
+ ),
1974
+ children: trend
1975
+ }
1976
+ ) : null
1977
+ ] })
1978
+ ] });
1979
+ const divider = showDivider ? metricCellDividerClass : void 0;
1980
+ if (onSelect) {
1981
+ return /* @__PURE__ */ jsx40(
1982
+ "button",
1983
+ {
1984
+ type: "button",
1985
+ onClick: onSelect,
1986
+ "aria-pressed": active,
1987
+ "aria-label": ariaLabel,
1988
+ className: cn(metricTileInteractiveClass, divider, className),
1989
+ children: content
1990
+ }
1991
+ );
1992
+ }
1993
+ return /* @__PURE__ */ jsx40("div", { className: cn(metricTileBaseClass, divider, className), children: content });
1994
+ };
1995
+
1996
+ // src/app/data/MetricRow.tsx
1997
+ import { useId as useId5, useState as useState5 } from "react";
1998
+ import { jsx as jsx41, jsxs as jsxs32 } from "react/jsx-runtime";
1999
+ var MetricRow = ({
2000
+ title,
2001
+ description,
2002
+ actions,
2003
+ metrics,
2004
+ activeMetricId,
2005
+ defaultActiveMetricId,
2006
+ onMetricChange,
2007
+ metricsAriaLabel = "Metrics",
2008
+ className
2009
+ }) => {
2010
+ const titleId = useId5();
2011
+ const selectable = onMetricChange != null || activeMetricId != null;
2012
+ const [internalId, setInternalId] = useState5(
2013
+ defaultActiveMetricId ?? metrics[0]?.id
2014
+ );
2015
+ const activeId = activeMetricId ?? internalId;
2016
+ const select = (id) => {
2017
+ if (activeMetricId == null) setInternalId(id);
2018
+ onMetricChange?.(id);
2019
+ };
2020
+ return /* @__PURE__ */ jsxs32(
2021
+ "section",
2022
+ {
2023
+ className: cn(metricCardShellClass, className),
2024
+ "aria-labelledby": title ? titleId : void 0,
2025
+ children: [
2026
+ /* @__PURE__ */ jsx41(
2027
+ MetricCardHeader,
2028
+ {
2029
+ title,
2030
+ titleId,
2031
+ description,
2032
+ actions
2033
+ }
2034
+ ),
2035
+ /* @__PURE__ */ jsx41(
2036
+ "div",
2037
+ {
2038
+ role: selectable ? "group" : void 0,
2039
+ "aria-label": selectable ? metricsAriaLabel : void 0,
2040
+ className: cn(
2041
+ metricTilesRowClass,
2042
+ metricTilesGridColsClass(metrics.length),
2043
+ (title || description || actions) && "mt-3"
2044
+ ),
2045
+ children: metrics.map((m, index) => /* @__PURE__ */ jsx41(
2046
+ MetricTile,
2047
+ {
2048
+ label: m.label,
2049
+ value: m.value,
2050
+ unit: m.unit,
2051
+ trend: m.trend,
2052
+ trendTone: m.trendTone,
2053
+ active: selectable && m.id === activeId,
2054
+ showDivider: index < metrics.length - 1,
2055
+ onSelect: selectable ? () => select(m.id) : void 0
2056
+ },
2057
+ m.id
2058
+ ))
2059
+ }
2060
+ )
2061
+ ]
2062
+ }
2063
+ );
2064
+ };
2065
+
2066
+ // src/app/data/MetricChartCard.tsx
2067
+ import { useId as useId6, useState as useState6 } from "react";
2068
+ import { jsx as jsx42, jsxs as jsxs33 } from "react/jsx-runtime";
2069
+ var MetricChartCard = ({
2070
+ title,
2071
+ description,
2072
+ actions,
2073
+ metrics,
2074
+ activeMetricId,
2075
+ defaultActiveMetricId,
2076
+ onMetricChange,
2077
+ xKey = "date",
2078
+ variant = "area",
2079
+ height = 300,
2080
+ formatX,
2081
+ formatValue,
2082
+ emptyLabel = "No data yet",
2083
+ metricsAriaLabel = "Metrics",
2084
+ className
2085
+ }) => {
2086
+ const titleId = useId6();
2087
+ const [internalId, setInternalId] = useState6(
2088
+ defaultActiveMetricId ?? metrics[0]?.id
2089
+ );
2090
+ const activeId = activeMetricId ?? internalId;
2091
+ const active = metrics.find((m) => m.id === activeId) ?? metrics[0];
2092
+ const select = (id) => {
2093
+ if (activeMetricId == null) setInternalId(id);
2094
+ onMetricChange?.(id);
2095
+ };
2096
+ const hasHeader = Boolean(title || description || actions);
2097
+ const chartAriaLabel = typeof active?.label === "string" ? `${active.label} over time` : "Metric chart";
2098
+ return /* @__PURE__ */ jsxs33(
2099
+ "section",
2100
+ {
2101
+ className: cn(metricCardShellClass, className),
2102
+ "aria-labelledby": title ? titleId : void 0,
2103
+ children: [
2104
+ /* @__PURE__ */ jsx42(
2105
+ MetricCardHeader,
2106
+ {
2107
+ title,
2108
+ titleId,
2109
+ description,
2110
+ actions
2111
+ }
2112
+ ),
2113
+ /* @__PURE__ */ jsx42(
2114
+ "div",
2115
+ {
2116
+ role: "group",
2117
+ "aria-label": metricsAriaLabel,
2118
+ className: cn(
2119
+ metricTilesRowClass,
2120
+ metricTilesGridColsClass(metrics.length),
2121
+ hasHeader && "mt-3"
2122
+ ),
2123
+ children: metrics.map((m, index) => /* @__PURE__ */ jsx42(
2124
+ MetricTile,
2125
+ {
2126
+ label: m.label,
2127
+ value: m.value,
2128
+ unit: m.unit,
2129
+ trend: m.trend,
2130
+ trendTone: m.trendTone,
2131
+ active: m.id === active?.id,
2132
+ showDivider: index < metrics.length - 1,
2133
+ onSelect: () => select(m.id)
2134
+ },
2135
+ m.id
2136
+ ))
2137
+ }
2138
+ ),
2139
+ /* @__PURE__ */ jsx42("div", { className: metricChartRegionClass, "aria-live": "polite", "aria-atomic": "true", children: active?.data && active.data.length > 0 ? /* @__PURE__ */ jsx42(
2140
+ LineAreaChart,
2141
+ {
2142
+ data: active.data,
2143
+ xKey,
2144
+ series: [
2145
+ {
2146
+ dataKey: active.dataKey ?? "value",
2147
+ label: typeof active.label === "string" ? active.label : active.id,
2148
+ color: active.color
2149
+ }
2150
+ ],
2151
+ variant,
2152
+ layout: "flush",
2153
+ height,
2154
+ formatX,
2155
+ formatValue,
2156
+ ariaLabel: chartAriaLabel
2157
+ },
2158
+ active.id
2159
+ ) : /* @__PURE__ */ jsx42(
2160
+ "div",
2161
+ {
2162
+ className: "flex w-full items-center justify-center text-sm font-normal text-muted-foreground",
2163
+ style: { height },
2164
+ role: "status",
2165
+ children: emptyLabel
2166
+ }
2167
+ ) })
2168
+ ]
2169
+ }
2170
+ );
2171
+ };
2172
+
2173
+ // src/charts/sparkline.tsx
2174
+ import { useId as useId7 } from "react";
2175
+ import { Fragment as Fragment5, jsx as jsx43, jsxs as jsxs34 } from "react/jsx-runtime";
2176
+ var Sparkline = ({
2177
+ data,
2178
+ dataKey = "value",
2179
+ color = "var(--primary, #6366f1)",
2180
+ area = true,
2181
+ width = 96,
2182
+ height = 28,
2183
+ strokeWidth = 1.5,
2184
+ className,
2185
+ ariaLabel = "Trend"
2186
+ }) => {
2187
+ const uid = useId7();
2188
+ const values = data.map((d) => typeof d === "number" ? d : toNum(d[dataKey]));
2189
+ if (values.length === 0) {
2190
+ return /* @__PURE__ */ jsx43("span", { className: cn("inline-block", className), style: { width, height } });
2191
+ }
2192
+ const pad = strokeWidth + 1;
2193
+ const min = Math.min(...values);
2194
+ const max = Math.max(...values);
2195
+ const range = max - min || 1;
2196
+ const innerW = width - pad * 2;
2197
+ const innerH = height - pad * 2;
2198
+ const points = values.map((v, i) => ({
2199
+ x: pad + (values.length > 1 ? i / (values.length - 1) * innerW : innerW / 2),
2200
+ y: pad + innerH - (v - min) / range * innerH
2201
+ }));
2202
+ return /* @__PURE__ */ jsxs34(
2203
+ "svg",
2204
+ {
2205
+ width,
2206
+ height,
2207
+ viewBox: `0 0 ${width} ${height}`,
2208
+ className: cn("block", className),
2209
+ role: "img",
2210
+ "aria-label": ariaLabel,
2211
+ preserveAspectRatio: "none",
2212
+ children: [
2213
+ area && /* @__PURE__ */ jsxs34(Fragment5, { children: [
2214
+ /* @__PURE__ */ jsx43("defs", { children: /* @__PURE__ */ jsxs34("linearGradient", { id: `${uid}-spark`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
2215
+ /* @__PURE__ */ jsx43("stop", { offset: "0%", style: { stopColor: color, stopOpacity: 0.25 } }),
2216
+ /* @__PURE__ */ jsx43("stop", { offset: "100%", style: { stopColor: color, stopOpacity: 0 } })
2217
+ ] }) }),
2218
+ /* @__PURE__ */ jsx43("path", { d: monotoneAreaPath(points, height - pad), fill: `url(#${uid}-spark)` })
2219
+ ] }),
2220
+ /* @__PURE__ */ jsx43(
2221
+ "path",
2222
+ {
2223
+ d: monotoneLinePath(points),
2224
+ fill: "none",
2225
+ stroke: color,
2226
+ strokeWidth,
2227
+ strokeLinecap: "round",
2228
+ strokeLinejoin: "round"
2229
+ }
2230
+ )
2231
+ ]
2232
+ }
2233
+ );
2234
+ };
2235
+
2236
+ export {
2237
+ APP_KIT_AGENT_INSTRUCTIONS,
2238
+ appPageColumnClass,
2239
+ appShellTopbarInsetClass,
2240
+ appShellInsetTopClass,
2241
+ appSurfaceCardClass,
2242
+ appStatTileClass,
2243
+ appFilterBarClass,
2244
+ appSearchInputClass,
2245
+ useAppShellChat,
2246
+ AppShell,
2247
+ AppShellTopbar,
2248
+ AppShellChatTrigger,
2249
+ PageHeader,
2250
+ Page,
2251
+ Section,
2252
+ AppCopilotProvider,
2253
+ useAppCopilotContext,
2254
+ AppChatPanel,
2255
+ SurfaceCard,
2256
+ StatTile,
2257
+ EmptyState,
2258
+ StatusBadge,
2259
+ AppConfirmDialog,
2260
+ InfoCard,
2261
+ StatusDot,
2262
+ DescriptionList,
2263
+ ExpandableSection,
2264
+ ResourceCard,
2265
+ SettingsSectionHeader,
2266
+ SettingsSection,
2267
+ FieldRow,
2268
+ FloatingUnsavedChangesBar,
2269
+ DangerZoneAction,
2270
+ DangerZone,
2271
+ INTEGRATION_CATALOG_CARD_HEIGHT_CLASS,
2272
+ IntegrationCard,
2273
+ IntegrationsEmptyState,
2274
+ PlanBadge,
2275
+ ConnectionRow,
2276
+ connectionRowListClass,
2277
+ ConnectionRowList,
2278
+ SubNav,
2279
+ Breadcrumbs,
2280
+ Field,
2281
+ FieldInput,
2282
+ FieldTextarea,
2283
+ FieldSelect,
2284
+ FieldSwitch,
2285
+ SearchInput,
2286
+ FormSection,
2287
+ FilterBar,
2288
+ DataTable,
2289
+ ChartPanel,
2290
+ MetricTile,
2291
+ MetricRow,
2292
+ MetricChartCard,
2293
+ Sparkline
2294
+ };