@arbidocs/blocks 0.4.145 → 0.4.147

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 (100) hide show
  1. package/dist/agents/index.cjs +266 -266
  2. package/dist/agents/index.js +9 -9
  3. package/dist/chat/index.cjs +1 -1
  4. package/dist/chat/index.js +1 -1
  5. package/dist/{chunk-ULPFRK3J.cjs → chunk-AINU3EJ6.cjs} +8 -8
  6. package/dist/{chunk-ULPFRK3J.cjs.map → chunk-AINU3EJ6.cjs.map} +1 -1
  7. package/dist/{chunk-KPO67GBR.cjs → chunk-AO7SK6WP.cjs} +32 -32
  8. package/dist/{chunk-KPO67GBR.cjs.map → chunk-AO7SK6WP.cjs.map} +1 -1
  9. package/dist/{chunk-NGDXDHLG.js → chunk-DAHCRVPM.js} +3 -3
  10. package/dist/{chunk-NGDXDHLG.js.map → chunk-DAHCRVPM.js.map} +1 -1
  11. package/dist/{chunk-FUTMU4FR.js → chunk-DGV7HQA7.js} +6 -179
  12. package/dist/chunk-DGV7HQA7.js.map +1 -0
  13. package/dist/chunk-EV7W64B6.js +134 -0
  14. package/dist/chunk-EV7W64B6.js.map +1 -0
  15. package/dist/{chunk-O6IEZDRC.cjs → chunk-GRGS5M6O.cjs} +181 -3
  16. package/dist/chunk-GRGS5M6O.cjs.map +1 -0
  17. package/dist/{chunk-J4M37SXY.js → chunk-H74HEPKT.js} +190 -7
  18. package/dist/chunk-H74HEPKT.js.map +1 -0
  19. package/dist/{chunk-NSNHRZBH.cjs → chunk-IL3WB3YX.cjs} +3 -3
  20. package/dist/{chunk-NSNHRZBH.cjs.map → chunk-IL3WB3YX.cjs.map} +1 -1
  21. package/dist/{chunk-34GDGE77.cjs → chunk-LTU35TQN.cjs} +192 -6
  22. package/dist/chunk-LTU35TQN.cjs.map +1 -0
  23. package/dist/{chunk-VVZ34A2P.js → chunk-LZRMXJIE.js} +3 -3
  24. package/dist/{chunk-VVZ34A2P.js.map → chunk-LZRMXJIE.js.map} +1 -1
  25. package/dist/{chunk-OPTBQSCV.cjs → chunk-MFY2ZNP6.cjs} +2 -181
  26. package/dist/{chunk-FUTMU4FR.js.map → chunk-MFY2ZNP6.cjs.map} +1 -1
  27. package/dist/{chunk-6HCJ3ZEU.js → chunk-R6VCPDPF.js} +178 -6
  28. package/dist/chunk-R6VCPDPF.js.map +1 -0
  29. package/dist/chunk-RXK3XP62.cjs +144 -0
  30. package/dist/chunk-RXK3XP62.cjs.map +1 -0
  31. package/dist/{chunk-KTSF4HFC.js → chunk-SUUPR735.js} +6 -6
  32. package/dist/{chunk-KTSF4HFC.js.map → chunk-SUUPR735.js.map} +1 -1
  33. package/dist/{chunk-WO7QAVTL.cjs → chunk-T5I4NRFP.cjs} +9 -9
  34. package/dist/{chunk-WO7QAVTL.cjs.map → chunk-T5I4NRFP.cjs.map} +1 -1
  35. package/dist/chunk-U37AYUPI.cjs +5371 -0
  36. package/dist/chunk-U37AYUPI.cjs.map +1 -0
  37. package/dist/{chunk-QOL3M4FJ.js → chunk-VUR63NMQ.js} +8 -8
  38. package/dist/{chunk-QOL3M4FJ.js.map → chunk-VUR63NMQ.js.map} +1 -1
  39. package/dist/chunk-WIZ7Z7FN.js +5179 -0
  40. package/dist/chunk-WIZ7Z7FN.js.map +1 -0
  41. package/dist/collaboration/index.cjs +81 -81
  42. package/dist/collaboration/index.js +3 -3
  43. package/dist/connect/docx.cjs +9 -9
  44. package/dist/connect/docx.js +6 -6
  45. package/dist/connect/index.cjs +1395 -132
  46. package/dist/connect/index.cjs.map +1 -1
  47. package/dist/connect/index.d.ts +2 -0
  48. package/dist/connect/index.js +1278 -24
  49. package/dist/connect/index.js.map +1 -1
  50. package/dist/connection/ConnectedNotificationRelevance.d.ts +4 -1
  51. package/dist/connection/connectors/ConnectedAppsSettings.d.ts +24 -0
  52. package/dist/connection/connectors/ConnectedDriveSyncDialog.d.ts +12 -0
  53. package/dist/connection/connectors/ConnectedMailboxSyncDialog.d.ts +12 -0
  54. package/dist/connection/connectors/index.d.ts +4 -0
  55. package/dist/connection/connectors/sync-labels.d.ts +21 -0
  56. package/dist/connection/settings-page/ConnectedSettingsPage.d.ts +4 -0
  57. package/dist/connection/settings-page/index.d.ts +1 -1
  58. package/dist/connection/workspace-settings/ConnectedWorkspaceSettings.d.ts +53 -0
  59. package/dist/connection/workspace-settings/index.d.ts +2 -0
  60. package/dist/connection/workspace-settings/useConnectedWorkspaceSettings.d.ts +85 -0
  61. package/dist/document-tools/index.cjs +1 -1
  62. package/dist/document-tools/index.js +1 -1
  63. package/dist/facts/index.cjs +1 -1
  64. package/dist/facts/index.js +1 -1
  65. package/dist/files/index.cjs +3 -3
  66. package/dist/files/index.js +3 -3
  67. package/dist/index.cjs +445 -618
  68. package/dist/index.cjs.map +1 -1
  69. package/dist/index.js +23 -205
  70. package/dist/index.js.map +1 -1
  71. package/dist/settings/PlanBadge.d.ts +1 -1
  72. package/dist/settings/index.cjs +551 -3716
  73. package/dist/settings/index.cjs.map +1 -1
  74. package/dist/settings/index.js +8 -3525
  75. package/dist/settings/index.js.map +1 -1
  76. package/dist/shell/index.cjs +1 -1
  77. package/dist/shell/index.js +1 -1
  78. package/dist/site/index.cjs +10 -10
  79. package/dist/site/index.js +2 -2
  80. package/dist/studio/index.cjs +3 -3
  81. package/dist/studio/index.js +3 -3
  82. package/dist/workflow-canvas/index.cjs +22 -22
  83. package/dist/workflow-canvas/index.js +9 -9
  84. package/dist/workspaces/CommonWorkspaceToggle.d.ts +15 -0
  85. package/dist/workspaces/index.cjs +38 -107
  86. package/dist/workspaces/index.cjs.map +1 -1
  87. package/dist/workspaces/index.d.ts +1 -0
  88. package/dist/workspaces/index.js +5 -102
  89. package/dist/workspaces/index.js.map +1 -1
  90. package/node_modules/@arbidocs/coedit/package.json +1 -1
  91. package/package.json +4 -4
  92. package/dist/chunk-34GDGE77.cjs.map +0 -1
  93. package/dist/chunk-5YHEHEJT.js +0 -1667
  94. package/dist/chunk-5YHEHEJT.js.map +0 -1
  95. package/dist/chunk-6HCJ3ZEU.js.map +0 -1
  96. package/dist/chunk-J4M37SXY.js.map +0 -1
  97. package/dist/chunk-O6IEZDRC.cjs.map +0 -1
  98. package/dist/chunk-OPTBQSCV.cjs.map +0 -1
  99. package/dist/chunk-U73KPFYH.cjs +0 -1717
  100. package/dist/chunk-U73KPFYH.cjs.map +0 -1
@@ -1,3536 +1,19 @@
1
1
  "use client";
2
+ export { ActiveSessionsDialog, AppearanceSettings, AvatarPicker, CONVERSATION_TITLE_MAX, CheckoutDialogFrame, ConfigSwitcherBar, ConnectionCard, ConnectionsEmpty, ConnectionsError, ConnectionsLoading, ConversationTitleSection, DEFAULT_ACTIVE_SESSIONS_DIALOG_LABELS, DEFAULT_APPEARANCE_PAIRING_IDS, DEFAULT_AVATAR_PICKER_LABELS, DEFAULT_CHECKOUT_DIALOG_LABELS, DEFAULT_CONFIG_SWITCHER_BAR_LABELS, DEFAULT_CONNECTION_CARD_LABELS, DEFAULT_CONVERSATION_TITLE_SECTION_LABELS, DEFAULT_DAILY_CAP_FIELD_LABELS, DEFAULT_DELETE_ACCOUNT_DIALOG_LABELS, DEFAULT_EMAIL_WORKSPACE_DIALOG_LABELS, DEFAULT_ERROR_DIALOG_LABELS, DEFAULT_IDENTITY_VERIFICATION_LABELS, DEFAULT_INLINE_NAME_EDITOR_LABELS, DEFAULT_INVITE_CONTACTS_DIALOG_LABELS, DEFAULT_INVOICES_DIALOG_LABELS, DEFAULT_LOGOUT_DIALOG_LABELS, DEFAULT_MEMBER_PROJECT_ROW_LABELS, DEFAULT_MODEL_SELECTION_SECTION_LABELS, DEFAULT_PASSWORD_CHANGE_DIALOG_LABELS, DEFAULT_PLAN_CARD_LABELS, DEFAULT_PROFILE_CARD_LABELS, DEFAULT_PROJECT_CARD_VIEW_LABELS, DEFAULT_PROJECT_DETAIL_HEADER_LABELS, DEFAULT_PROJECT_LIST_LABELS, DEFAULT_QUOTA_EXCEEDED_DIALOG_LABELS, DEFAULT_REFERRAL_REWARD_LABELS, DEFAULT_RENAME_DIALOG_LABELS, DEFAULT_SETTINGS_DANGER_ZONE_LABELS, DEFAULT_SHARED_PROJECTS_SECTION_LABELS, DEFAULT_SUBSCRIPTION_SECTION_LABELS, DEFAULT_SUBSCRIPTION_STATUS_LABELS, DEFAULT_SUPPORT_PANEL_LABELS, DEFAULT_SYNC_DIALOG_FOOTER_LABELS, DEFAULT_SYNC_FOLDER_ROW_LABELS, DEFAULT_TIPS_EDITOR_LABELS, DEFAULT_USAGE_LINE_CHART_LABELS, DEFAULT_USAGE_PANEL_LABELS, DEFAULT_USAGE_PROPORTION_BAR_LABELS, DEFAULT_WELCOME_DIALOG_LABELS, DEFAULT_WORK_OFFLINE_LABELS, DailyCapField, DeleteAccountDialog, DraggableHeader, EMAIL_NOTIFICATION_CATEGORIES, EMAIL_NOTIFICATION_KEYS, EmailWorkspaceDialogView, ErrorDialogView, FilterChip, FloatingPanel, IdentityVerificationCard, IdentityVerificationDialog, InlineNameEditor, InviteContactsDialog, InvoicesDialog, JsonTree, LogoutDialog, MemberProjectRow, MessageJsonTree, ModelProviderLabel, ModelSelectionSection, MoreInfo, MoreInfoList, NotificationCategoryList, PanelActionsRow, PanelAddButton, PanelFrame, PanelFrameBody, PanelNote, PanelScroll, PanelTitleBar, PasswordChangeDialog, PlanActionButton, PlanActionRow, PlanBadge, PlanCard, PlanCardGrid, PlanDialogFrame, PlanInfoPopover, PlanNotice, PlanPickerCard, PlanPickerHeader, PlanStack, PlanTierOption, PlanTransition, PremiumModelsFrame, PricingToggle, ProfileCard, ProjectCardView, ProjectDetailHeader, ProjectDetailSection, ProjectGroup, ProjectList, ProjectNameLabel, ProjectPlanPicker, ProjectRow, ProjectRowActions, PromoCodeInput, QuotaExceededDialogView, QuotaMeter, ReferralRewardBadge, ReferralRewardNote, ReferralRewardSummary, RenameDialog, RequireVerified, RowButton, RowIconAction, SegmentedControl, ServerConnectionButton, SettingsActionRow, SettingsCard, SettingsDangerZone, SettingsPage, SettingsPanel, SettingsRow, SettingsSection, SettingsSwitchGroup, SettingsSwitchRow, SharedProjectRow, SharedProjectsSection, SubscriptionSectionView, SubscriptionStatusView, SupportPanel, SupportUnavailable, SyncCheckList, SyncDialogBody, SyncDialogFooter, SyncField, SyncFolderRow, SyncFolderStatus, SyncLinkButton, SyncLinkRow, SyncLoading, SyncNote, SyncOptionToggle, SyncOptions, SyncReconnectNotice, SyncSelect, SyncSmallNote, SyncTreeFrame, ThreadPreviewDialog, TipsEditorDialog, UsageActionButton, UsageLegend, UsageLineChart, UsagePanel, UsageProportionBar, UsageQuotaHint, UsageSection, UsageSummary, UsageToggle, WelcomeDialog, WelcomeProjectPicker, WorkOfflineToggle, formatSessionTtl, resolveSettingsSection, serverConnectionView, toActiveSessionViews, toDeleteBlockerViews, toLogoutSessionViews, toNotificationCategories } from '../chunk-WIZ7Z7FN.js';
2
3
  export { ModalBreadcrumbHeader, ModalCrumbs } from '../chunk-RC6VTY2H.js';
3
- import { SettingsPanel, SettingsSection } from '../chunk-5YHEHEJT.js';
4
- export { ActiveSessionsDialog, AvatarPicker, DEFAULT_ACTIVE_SESSIONS_DIALOG_LABELS, DEFAULT_AVATAR_PICKER_LABELS, DEFAULT_DELETE_ACCOUNT_DIALOG_LABELS, DEFAULT_IDENTITY_VERIFICATION_LABELS, DEFAULT_LOGOUT_DIALOG_LABELS, DEFAULT_PASSWORD_CHANGE_DIALOG_LABELS, DEFAULT_PROFILE_CARD_LABELS, DEFAULT_SETTINGS_DANGER_ZONE_LABELS, DEFAULT_WORK_OFFLINE_LABELS, DeleteAccountDialog, DraggableHeader, EMAIL_NOTIFICATION_CATEGORIES, EMAIL_NOTIFICATION_KEYS, IdentityVerificationCard, IdentityVerificationDialog, LogoutDialog, ModelProviderLabel, NotificationCategoryList, PanelActionsRow, PanelAddButton, PanelFrame, PanelFrameBody, PanelNote, PanelScroll, PanelTitleBar, PasswordChangeDialog, PremiumModelsFrame, ProfileCard, RequireVerified, ServerConnectionButton, SettingsActionRow, SettingsCard, SettingsDangerZone, SettingsPage, SettingsPanel, SettingsRow, SettingsSection, SettingsSwitchGroup, SettingsSwitchRow, WorkOfflineToggle, formatSessionTtl, resolveSettingsSection, serverConnectionView, toActiveSessionViews, toDeleteBlockerViews, toLogoutSessionViews, toNotificationCategories } from '../chunk-5YHEHEJT.js';
5
- import { UsageMeter } from '../chunk-66TA5IBN.js';
6
- import { getFontPairing } from '../chunk-NHZK64TQ.js';
7
- import { ToneBadge } from '../chunk-5LXQPP2K.js';
4
+ import '../chunk-66TA5IBN.js';
5
+ import '../chunk-NHZK64TQ.js';
8
6
  export { MultiSelect } from '../chunk-CQCC7LE7.js';
9
7
  export { DEFAULT_DOCUMENT_PICKER_LABELS, DEFAULT_TRI_STATE_FILTER_MENU_LABELS, DocumentPicker, TriStateFilterMenu } from '../chunk-KSHKTOY2.js';
8
+ import '../chunk-5LXQPP2K.js';
10
9
  import '../chunk-VILAPTCB.js';
11
10
  export { ConfirmDialog, DEFAULT_CONFIRM_DIALOG_LABELS } from '../chunk-VHJHY7TT.js';
12
- import { TabHeader } from '../chunk-4WWM3GQ5.js';
13
- import { TONE_BORDER, TONE_SURFACE } from '../chunk-AUS3KBHJ.js';
11
+ import '../chunk-4WWM3GQ5.js';
12
+ import '../chunk-AUS3KBHJ.js';
14
13
  import '../chunk-NPLYHR2E.js';
15
- import { testIdOr, prefixedTestId } from '../chunk-7F6CBP3Z.js';
16
- import { ModalSection, ModalSectionTitle, ModalHeader, ModalScaffold } from '../chunk-TX2ZICS7.js';
14
+ import '../chunk-7F6CBP3Z.js';
17
15
  export { MODAL_SECTION_TITLE_CLASS, ModalHeader, ModalScaffold, ModalSection, ModalSectionTitle } from '../chunk-TX2ZICS7.js';
18
- import { formatCount } from '../chunk-OXF2IFXG.js';
16
+ import '../chunk-OXF2IFXG.js';
19
17
  import '../chunk-VUNV25KB.js';
20
- import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
21
- import { cn, DialogFooter, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Input, Button, Tooltip, TooltipTrigger, TooltipContent, Dialog, DialogContent, DialogTitle, DialogDescription, Label, Textarea, Checkbox, ScrollArea, DialogHeader, Badge, Progress } from '@arbidocs/react/ui';
22
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
23
- import { useMemo, useState, useId, Fragment as Fragment$1 } from 'react';
24
- import { Sun, Moon, History, Undo2, Star, Trash2, Users, Sparkles, Brain, ChevronDown, ChevronRight, Pencil, Plus, Loader2, Check, X, Info, Tag, Gift, PlugIcon, CheckCircle2, ExternalLink, ShieldCheck, Plug, AlertCircle, MessageCircleQuestionMark, BookOpen, UserPlus, ChartNoAxesColumn, FileText, Mail, Send, Clock, Infinity, ArrowRight, Copy, RefreshCw, LogOut, RotateCcw } from 'lucide-react';
25
- import { cva } from 'class-variance-authority';
26
-
27
- function SegmentedControl({
28
- value,
29
- onChange,
30
- options,
31
- ariaLabel,
32
- className
33
- }) {
34
- return /* @__PURE__ */ jsx(
35
- RadioGroupPrimitive.Root,
36
- {
37
- "data-slot": "segmented-control",
38
- "aria-label": ariaLabel,
39
- value,
40
- onValueChange: (next) => onChange(next),
41
- orientation: "horizontal",
42
- className: cn("inline-flex rounded-lg border border-border bg-background p-1", className),
43
- children: options.map((option) => /* @__PURE__ */ jsxs(
44
- RadioGroupPrimitive.Item,
45
- {
46
- value: option.value,
47
- "data-testid": option.testId,
48
- className: cn(
49
- "flex items-center gap-2 rounded-md px-4 py-1.5 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-coarse:min-h-11 pointer-coarse:py-2.5",
50
- option.value === value ? "bg-accent text-accent-foreground" : "text-muted-foreground hover:text-foreground"
51
- ),
52
- children: [
53
- option.icon,
54
- option.label
55
- ]
56
- },
57
- option.value
58
- ))
59
- }
60
- );
61
- }
62
- function FloatingPanel({
63
- panelId,
64
- mode,
65
- title,
66
- subtitle,
67
- headerIcon,
68
- headerActions,
69
- tabLabel,
70
- modeSwitcher,
71
- children,
72
- footer,
73
- onPanelClick,
74
- className
75
- }) {
76
- const isRenderFunction = typeof children === "function";
77
- const content = isRenderFunction ? children({ modeSwitcher }) : children;
78
- const footerEl = footer && /* @__PURE__ */ jsx("div", { className: "relative z-10 flex-shrink-0 border-t border-border p-3", children: footer });
79
- const body = /* @__PURE__ */ jsxs(Fragment, { children: [
80
- /* @__PURE__ */ jsx("div", { className: cn("flex-1", isRenderFunction ? "overflow-hidden" : "overflow-auto p-3"), children: content }),
81
- footerEl
82
- ] });
83
- if (mode === "popout") {
84
- return /* @__PURE__ */ jsxs(
85
- "div",
86
- {
87
- "data-slot": "floating-panel",
88
- className: cn("flex h-screen flex-col bg-background text-foreground", className),
89
- children: [
90
- !isRenderFunction && /* @__PURE__ */ jsxs("div", { className: "flex flex-shrink-0 items-start justify-between border-b border-border px-3 py-2", children: [
91
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [
92
- (title || headerIcon) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-base font-semibold text-primary", children: [
93
- headerIcon,
94
- title && /* @__PURE__ */ jsx("span", { children: title })
95
- ] }),
96
- subtitle && /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: subtitle }),
97
- headerActions && /* @__PURE__ */ jsx("div", { className: title ? "mt-1.5" : "", children: headerActions })
98
- ] }),
99
- modeSwitcher
100
- ] }),
101
- body
102
- ]
103
- }
104
- );
105
- }
106
- return /* @__PURE__ */ jsxs(
107
- "div",
108
- {
109
- "data-slot": "floating-panel",
110
- "data-mode": mode,
111
- className: cn("flex h-full flex-col bg-background text-foreground", className),
112
- onClick: onPanelClick,
113
- children: [
114
- !isRenderFunction && /* @__PURE__ */ jsx(
115
- TabHeader,
116
- {
117
- tabs: [{ id: panelId, label: tabLabel ?? title }],
118
- activeTabId: panelId,
119
- onTabChange: () => {
120
- },
121
- rightContent: modeSwitcher
122
- }
123
- ),
124
- body
125
- ]
126
- }
127
- );
128
- }
129
- var DEFAULT_CONFIG_SWITCHER_BAR_LABELS = {
130
- placeholder: "Select configuration...",
131
- save: "Save",
132
- saving: "Saving...",
133
- delete: "Delete"
134
- };
135
- var MARKERS = { default: Star, original: Undo2, "last-used": History };
136
- function ConfigSwitcherBar({
137
- options,
138
- value,
139
- onSelect,
140
- hasUnsavedChanges,
141
- unsavedTitle = "Unsaved Changes",
142
- unsavedSubtitle = "Changes will be lost if you switch",
143
- showNameField,
144
- name,
145
- onNameChange,
146
- namePlaceholder,
147
- saveLabel,
148
- onSave,
149
- isSaving,
150
- canDelete,
151
- onDelete,
152
- isDeleting,
153
- nameTestId,
154
- saveTestId,
155
- deleteTestId,
156
- labels,
157
- className
158
- }) {
159
- const l = useMemo(() => ({ ...DEFAULT_CONFIG_SWITCHER_BAR_LABELS, ...labels }), [labels]);
160
- return /* @__PURE__ */ jsx(
161
- DialogFooter,
162
- {
163
- "data-slot": "config-switcher-bar",
164
- className: cn("flex-shrink-0 border-t bg-muted px-4 py-4", className),
165
- children: /* @__PURE__ */ jsx("div", { className: "flex w-full flex-col gap-3", children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-[36px] items-center gap-3", children: [
166
- /* @__PURE__ */ jsxs(
167
- Select,
168
- {
169
- value: hasUnsavedChanges ? "unsaved" : value,
170
- onValueChange: (v) => {
171
- if (v !== "unsaved") onSelect(v);
172
- },
173
- children: [
174
- /* @__PURE__ */ jsx(SelectTrigger, { className: "h-9 min-w-0 flex-1 border-0 bg-background", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: l.placeholder }) }),
175
- /* @__PURE__ */ jsxs(SelectContent, { position: "popper", children: [
176
- hasUnsavedChanges && /* @__PURE__ */ jsx(SelectItem, { value: "unsaved", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
177
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: unsavedTitle }),
178
- /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: unsavedSubtitle })
179
- ] }) }),
180
- options.map((o) => {
181
- const Marker = o.marker ? MARKERS[o.marker] : null;
182
- return /* @__PURE__ */ jsx(SelectItem, { value: o.value, children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
183
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
184
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: o.title }),
185
- o.subtitle && /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: o.subtitle })
186
- ] }),
187
- Marker && /* @__PURE__ */ jsx(Marker, { className: "mt-0.5 size-3 flex-shrink-0 text-muted-foreground" })
188
- ] }) }, o.value);
189
- })
190
- ] })
191
- ]
192
- }
193
- ),
194
- showNameField && /* @__PURE__ */ jsxs(Fragment, { children: [
195
- /* @__PURE__ */ jsx(
196
- Input,
197
- {
198
- value: name,
199
- onChange: (e) => onNameChange(e.target.value),
200
- placeholder: namePlaceholder,
201
- className: "h-9 min-w-0 flex-1 bg-background",
202
- "data-testid": testIdOr(nameTestId, { testId: "config-name-input" })
203
- }
204
- ),
205
- /* @__PURE__ */ jsx(
206
- Button,
207
- {
208
- variant: "outline",
209
- size: "sm",
210
- className: "h-9",
211
- onClick: onSave,
212
- disabled: isSaving || !name.trim(),
213
- "data-testid": testIdOr(saveTestId, { testId: "save-config-button" }),
214
- children: isSaving ? l.saving : saveLabel
215
- }
216
- )
217
- ] }),
218
- canDelete && /* @__PURE__ */ jsxs(
219
- Button,
220
- {
221
- variant: "outline",
222
- size: "sm",
223
- className: "h-9 border-destructive text-destructive hover:bg-destructive hover:text-destructive-foreground",
224
- onClick: onDelete,
225
- disabled: isDeleting,
226
- "data-testid": testIdOr(deleteTestId, { testId: "delete-config-button" }),
227
- children: [
228
- /* @__PURE__ */ jsx(Trash2, { className: "mr-2 size-4" }),
229
- l.delete
230
- ]
231
- }
232
- )
233
- ] }) })
234
- }
235
- );
236
- }
237
- var CONVERSATION_TITLE_MAX = 50;
238
- var DEFAULT_CONVERSATION_TITLE_SECTION_LABELS = {
239
- title: "Conversation Title",
240
- placeholder: "New Conversation",
241
- share: "Share",
242
- shared: "Shared",
243
- ownerOnlyShare: "Only available to conversation owner",
244
- makePrivate: "Make private again",
245
- characters: (count, max) => `${count}/${max} characters`,
246
- saving: "Saving...",
247
- ownerOnlyEdit: "Only the conversation owner can edit the title"
248
- };
249
- var SECTION_CLASS = "rounded-md border bg-muted/50 p-4";
250
- function ConversationTitleSection({
251
- title,
252
- onTitleChange,
253
- onTitleCommit,
254
- isOwner,
255
- isShared,
256
- isSharing,
257
- isSavingTitle,
258
- onShare,
259
- onUnshare,
260
- inputTestId,
261
- shareTestId,
262
- labels
263
- }) {
264
- const l = useMemo(() => ({ ...DEFAULT_CONVERSATION_TITLE_SECTION_LABELS, ...labels }), [labels]);
265
- const trimmed = title.trim();
266
- return /* @__PURE__ */ jsxs(ModalSection, { className: SECTION_CLASS, children: [
267
- /* @__PURE__ */ jsx(ModalSectionTitle, { children: l.title }),
268
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
269
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
270
- /* @__PURE__ */ jsx(
271
- Input,
272
- {
273
- value: title,
274
- onChange: (e) => {
275
- if (isOwner && e.target.value.length <= CONVERSATION_TITLE_MAX) {
276
- onTitleChange(e.target.value);
277
- }
278
- },
279
- onBlur: () => {
280
- if (isOwner && trimmed && trimmed.length <= CONVERSATION_TITLE_MAX) onTitleCommit();
281
- },
282
- placeholder: l.placeholder,
283
- maxLength: 60,
284
- disabled: !isOwner,
285
- "data-testid": testIdOr(inputTestId, { testId: "conversation-title-input" })
286
- }
287
- ),
288
- /* @__PURE__ */ jsxs(Tooltip, { children: [
289
- /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx("span", { tabIndex: !isOwner ? 0 : void 0, children: /* @__PURE__ */ jsxs(
290
- Button,
291
- {
292
- variant: isShared ? "secondary" : "outline",
293
- size: "sm",
294
- className: "h-9 px-3 pointer-coarse:h-11",
295
- onClick: () => {
296
- if (!isOwner) return;
297
- if (isShared) onUnshare?.();
298
- else onShare();
299
- },
300
- disabled: !isOwner || isShared && !onUnshare || isSharing,
301
- title: isOwner && isShared && onUnshare ? l.makePrivate : void 0,
302
- "data-testid": testIdOr(shareTestId, { testId: "share-conversation-button" }),
303
- children: [
304
- /* @__PURE__ */ jsx(Users, { className: "mr-1 size-3" }),
305
- isShared ? l.shared : l.share
306
- ]
307
- }
308
- ) }) }),
309
- !isOwner && /* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: l.ownerOnlyShare }) })
310
- ] })
311
- ] }),
312
- isOwner ? /* @__PURE__ */ jsxs(
313
- "p",
314
- {
315
- className: cn(
316
- "text-sm",
317
- title.length > CONVERSATION_TITLE_MAX ? "text-destructive" : "text-muted-foreground"
318
- ),
319
- children: [
320
- l.characters(title.length, CONVERSATION_TITLE_MAX),
321
- isSavingTitle && /* @__PURE__ */ jsx("span", { className: "ml-2 text-primary", children: l.saving })
322
- ]
323
- }
324
- ) : /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: l.ownerOnlyEdit })
325
- ] })
326
- ] });
327
- }
328
- var DEFAULT_MODEL_SELECTION_SECTION_LABELS = {
329
- title: "AI Model Selection",
330
- description: "Select the AI model to use for this conversation",
331
- useDefaults: "Use defaults",
332
- auto: "Auto",
333
- default: "Default",
334
- lastUsed: "Last used",
335
- deepThinking: "Deep Thinking"
336
- };
337
- function ModelSelectionSection({
338
- selector,
339
- onUseDefaults,
340
- defaultsTestId,
341
- labels
342
- }) {
343
- const l = useMemo(() => ({ ...DEFAULT_MODEL_SELECTION_SECTION_LABELS, ...labels }), [labels]);
344
- const legend = [
345
- { icon: Sparkles, label: l.auto },
346
- { icon: Star, label: l.default },
347
- { icon: History, label: l.lastUsed },
348
- { icon: Brain, label: l.deepThinking }
349
- ];
350
- return /* @__PURE__ */ jsxs(ModalSection, { className: SECTION_CLASS, children: [
351
- /* @__PURE__ */ jsx(ModalSectionTitle, { children: l.title }),
352
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
353
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: l.description }),
354
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
355
- selector,
356
- /* @__PURE__ */ jsxs(
357
- Button,
358
- {
359
- variant: "outline",
360
- size: "sm",
361
- className: "flex h-8 items-center gap-1.5 px-3 pointer-coarse:h-11",
362
- onClick: onUseDefaults,
363
- "data-testid": testIdOr(defaultsTestId, { testId: "use-defaults-button" }),
364
- children: [
365
- /* @__PURE__ */ jsx(Star, { className: "size-4" }),
366
- /* @__PURE__ */ jsx("span", { className: "text-sm", children: l.useDefaults })
367
- ]
368
- }
369
- )
370
- ] }),
371
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-muted-foreground", children: legend.map(({ icon: Icon, label }) => /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
372
- /* @__PURE__ */ jsx(Icon, { className: "size-3" }),
373
- /* @__PURE__ */ jsx("span", { children: label })
374
- ] }, label)) })
375
- ] })
376
- ] });
377
- }
378
- function ThreadPreviewDialog({
379
- open,
380
- onOpenChange,
381
- crumbs,
382
- description = "Thread visualization",
383
- width,
384
- testId,
385
- children
386
- }) {
387
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
388
- DialogContent,
389
- {
390
- presentation: "sheet",
391
- className: "flex flex-col overflow-hidden p-0",
392
- style: { width: `${width}px`, maxWidth: "95vw", height: "80vh", maxHeight: "90vh" },
393
- "data-testid": testId,
394
- children: [
395
- /* @__PURE__ */ jsxs(ModalHeader, { children: [
396
- /* @__PURE__ */ jsx(DialogTitle, { children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: crumbs.map((c, i) => /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-3", children: [
397
- i > 0 && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "/" }),
398
- c.icon,
399
- /* @__PURE__ */ jsx("span", { children: c.label })
400
- ] }, i)) }) }),
401
- /* @__PURE__ */ jsx(DialogDescription, { className: "sr-only", children: description })
402
- ] }),
403
- /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-auto px-6 pb-6", children })
404
- ]
405
- }
406
- ) });
407
- }
408
- var ITEMS = { one: "item", other: "items" };
409
- function isInlineObject(data) {
410
- if (data === null || data === void 0) return true;
411
- if (typeof data !== "object") return true;
412
- if (Array.isArray(data)) return data.length === 0;
413
- const keys = Object.keys(data);
414
- if (keys.length === 0) return true;
415
- if (keys.length > 4) return false;
416
- return Object.values(data).every(
417
- (v) => v === null || v === void 0 || typeof v !== "object"
418
- );
419
- }
420
- function inlineSummary(data) {
421
- const parts = Object.entries(data).map(([k, v]) => {
422
- if (v === null) return `${k}: null`;
423
- if (typeof v === "string") return `${k}: "${v.length > 40 ? v.slice(0, 40) + "\u2026" : v}"`;
424
- return `${k}: ${String(v)}`;
425
- });
426
- return `{ ${parts.join(", ")} }`;
427
- }
428
- function arrayPreview(data) {
429
- if (data.length === 0) return "[]";
430
- const first = data[0];
431
- if (typeof first !== "object" || first === null) return `[${formatCount(data.length, ITEMS)}]`;
432
- const obj = first;
433
- const previewKeys = ["action", "step", "phase", "tool", "name", "type", "id"];
434
- const parts = previewKeys.filter((k) => obj[k] !== void 0).slice(0, 3).map((k) => `${k}: ${JSON.stringify(obj[k])}`);
435
- if (parts.length === 0) return `[${formatCount(data.length, ITEMS)}]`;
436
- return `[${formatCount(data.length, ITEMS)} \u2014 { ${parts.join(", ")} }, \u2026]`;
437
- }
438
- var NAME_CLASS = "font-medium text-primary";
439
- var COPY_CLASS = "rounded p-0.5 opacity-50 transition-opacity hover:bg-muted hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-coarse:flex pointer-coarse:size-11 pointer-coarse:items-center pointer-coarse:justify-center pointer-coarse:opacity-100";
440
- function valueColor(data) {
441
- if (data === null || data === void 0) return "text-muted-foreground";
442
- switch (typeof data) {
443
- case "string":
444
- return "text-primary";
445
- case "number":
446
- return "text-primary";
447
- case "boolean":
448
- return "text-purple-700 dark:text-purple-400";
449
- default:
450
- return "text-foreground";
451
- }
452
- }
453
- function plainValue(data) {
454
- if (data === null) return "null";
455
- if (data === void 0) return "undefined";
456
- return typeof data === "string" ? `"${data}"` : String(data);
457
- }
458
- function JsonTree({
459
- data,
460
- name,
461
- defaultExpanded = false,
462
- level = 0,
463
- path = [],
464
- resolveHint,
465
- formatValue: formatValue2
466
- }) {
467
- const [isExpanded, setIsExpanded] = useState(defaultExpanded || level < 2);
468
- const [copied, setCopied] = useState(false);
469
- const currentPath = name ? [...path, name] : path;
470
- const hint = name && resolveHint ? resolveHint(currentPath, name) : null;
471
- const displayName = hint?.displayName || name;
472
- const handleCopy = (value) => {
473
- const text = typeof value === "string" ? value : JSON.stringify(value, null, 2);
474
- void navigator.clipboard.writeText(text).then(() => {
475
- setCopied(true);
476
- setTimeout(() => setCopied(false), 2e3);
477
- });
478
- };
479
- const copyButton = (title, value) => /* @__PURE__ */ jsx(
480
- "button",
481
- {
482
- type: "button",
483
- onClick: () => handleCopy(value),
484
- className: COPY_CLASS,
485
- title,
486
- "aria-label": title,
487
- children: copied ? /* @__PURE__ */ jsx(Check, { size: 12, className: "text-green-text" }) : /* @__PURE__ */ jsx(Copy, { size: 12 })
488
- }
489
- );
490
- const isArray = Array.isArray(data);
491
- const isObject = typeof data === "object" && data !== null && !isArray;
492
- if (!isObject && !isArray) {
493
- const displayValue = hint && formatValue2 ? formatValue2(data, hint) : plainValue(data);
494
- return /* @__PURE__ */ jsxs(
495
- "div",
496
- {
497
- "data-slot": "json-tree-leaf",
498
- className: "group flex items-center gap-2 py-0.5 font-mono text-sm",
499
- children: [
500
- displayName && /* @__PURE__ */ jsxs("span", { className: NAME_CLASS, title: hint?.description, children: [
501
- displayName,
502
- ":"
503
- ] }),
504
- /* @__PURE__ */ jsx("span", { className: valueColor(data), children: displayValue }),
505
- hint?.formatType === "id" && typeof data === "string" && data.length > 20 && /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", title: data, children: [
506
- "(full: ",
507
- data,
508
- ")"
509
- ] }),
510
- copyButton("Copy value", data)
511
- ]
512
- }
513
- );
514
- }
515
- if (isObject && isInlineObject(data)) {
516
- return /* @__PURE__ */ jsxs(
517
- "div",
518
- {
519
- "data-slot": "json-tree-leaf",
520
- className: "group flex items-center gap-2 py-0.5 font-mono text-sm",
521
- children: [
522
- displayName && /* @__PURE__ */ jsxs("span", { className: NAME_CLASS, children: [
523
- displayName,
524
- ":"
525
- ] }),
526
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: inlineSummary(data) }),
527
- copyButton("Copy object", data)
528
- ]
529
- }
530
- );
531
- }
532
- const Icon = isExpanded ? ChevronDown : ChevronRight;
533
- const entries = isArray ? data.map((item, index) => [String(index), item]) : Object.entries(data);
534
- const collapsedPreview = isArray ? arrayPreview(data) : `{${Object.keys(data).length} fields}`;
535
- return /* @__PURE__ */ jsxs("div", { "data-slot": "json-tree", className: "font-mono text-sm", children: [
536
- /* @__PURE__ */ jsxs("div", { className: "group flex items-center gap-1 py-0.5", children: [
537
- /* @__PURE__ */ jsxs(
538
- "button",
539
- {
540
- type: "button",
541
- onClick: () => setIsExpanded(!isExpanded),
542
- "aria-expanded": isExpanded,
543
- className: "flex items-center gap-1 rounded p-0.5 transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-coarse:min-h-11",
544
- title: hint?.description,
545
- children: [
546
- /* @__PURE__ */ jsx(Icon, { size: 14, className: "shrink-0" }),
547
- displayName && /* @__PURE__ */ jsxs("span", { className: NAME_CLASS, children: [
548
- displayName,
549
- ":"
550
- ] }),
551
- !isExpanded && /* @__PURE__ */ jsx("span", { className: "max-w-[400px] truncate text-sm text-muted-foreground", children: collapsedPreview }),
552
- isExpanded && isArray && /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
553
- data.length,
554
- " items"
555
- ] })
556
- ]
557
- }
558
- ),
559
- copyButton("Copy object", data)
560
- ] }),
561
- isExpanded && /* @__PURE__ */ jsx("div", { className: "ml-4 border-l border-border/40 pl-2", children: entries.length === 0 ? /* @__PURE__ */ jsx("div", { className: "py-0.5 text-sm text-muted-foreground", children: isArray ? "empty array" : "empty object" }) : entries.map(([key, value]) => {
562
- if (isArray && typeof value === "object" && value !== null && isInlineObject(value)) {
563
- return /* @__PURE__ */ jsx("div", { className: "py-0.5 font-mono text-sm text-muted-foreground", children: inlineSummary(value) }, key);
564
- }
565
- return /* @__PURE__ */ jsx(
566
- JsonTree,
567
- {
568
- name: isArray ? void 0 : key,
569
- data: value,
570
- level: level + 1,
571
- path: currentPath,
572
- resolveHint,
573
- formatValue: formatValue2
574
- },
575
- key
576
- );
577
- }) })
578
- ] });
579
- }
580
-
581
- // src/settings/message-schema-hints.ts
582
- var messageFieldHints = {
583
- role: {
584
- displayName: "Role",
585
- formatType: "enum",
586
- description: "Message role (user, assistant, or system)"
587
- },
588
- content: {
589
- displayName: "Content",
590
- formatType: "long-string",
591
- description: "Message content text"
592
- },
593
- created_at: {
594
- displayName: "Created At",
595
- formatType: "timestamp",
596
- description: "Message creation timestamp"
597
- },
598
- external_id: {
599
- displayName: "Message ID",
600
- formatType: "id",
601
- description: "External message identifier"
602
- },
603
- conversation_ext_id: {
604
- displayName: "Conversation ID",
605
- formatType: "id",
606
- description: "Parent conversation identifier"
607
- },
608
- parent_message_ext_id: {
609
- displayName: "Parent Message ID",
610
- formatType: "id",
611
- description: "Parent message identifier (null for first message)"
612
- },
613
- created_by_ext_id: {
614
- displayName: "Created By",
615
- formatType: "id",
616
- description: "User who created this message"
617
- },
618
- config_ext_id: {
619
- displayName: "Config ID",
620
- formatType: "id",
621
- description: "Configuration used for this message"
622
- },
623
- shared: {
624
- displayName: "Shared",
625
- formatType: "boolean",
626
- description: "Whether message is shared"
627
- },
628
- tools: {
629
- displayName: "Tools",
630
- formatType: "json",
631
- description: "Tool outputs (citations, retrieval, trace)"
632
- },
633
- meta: {
634
- displayName: "Metadata",
635
- formatType: "json",
636
- description: "Additional metadata"
637
- }
638
- };
639
- var traceToolFieldHints = {
640
- name: {
641
- displayName: "Tool Name",
642
- formatType: "enum",
643
- description: "Execution trace tool"
644
- },
645
- description: {
646
- displayName: "Description",
647
- formatType: "string",
648
- description: "Tool description"
649
- },
650
- trace_id: {
651
- displayName: "Trace ID",
652
- formatType: "id",
653
- description: "Unique trace identifier"
654
- },
655
- start_time: {
656
- displayName: "Start Time",
657
- formatType: "timestamp",
658
- description: "When execution started"
659
- },
660
- duration_seconds: {
661
- displayName: "Duration (seconds)",
662
- formatType: "number",
663
- description: "Total execution time"
664
- },
665
- steps: {
666
- displayName: "Execution Steps",
667
- formatType: "json",
668
- description: "Full execution trace with all steps"
669
- }
670
- };
671
- var citationToolFieldHints = {
672
- name: {
673
- displayName: "Tool Name",
674
- formatType: "enum",
675
- description: "Model citation tool"
676
- },
677
- tool_responses: {
678
- displayName: "Citations",
679
- formatType: "json",
680
- description: "Citation data mapped by citation number"
681
- }
682
- };
683
- function getFieldHint(path, fieldName) {
684
- if (path.length === 1 && path[0] === "message") {
685
- return messageFieldHints[fieldName] || null;
686
- }
687
- if (path.length >= 3 && path[0] === "message" && path[1] === "tools" && path[2] === "trace") {
688
- return traceToolFieldHints[fieldName] || null;
689
- }
690
- if (path.length >= 3 && path[0] === "message" && path[1] === "tools" && path[2] === "model_citations") {
691
- return citationToolFieldHints[fieldName] || null;
692
- }
693
- if (fieldName === "external_id" || fieldName.endsWith("_ext_id")) {
694
- return { formatType: "id", displayName: fieldName.replace(/_/g, " ") };
695
- }
696
- if (fieldName === "created_at" || fieldName === "updated_at" || fieldName.endsWith("_at")) {
697
- return { formatType: "timestamp", displayName: fieldName.replace(/_/g, " ") };
698
- }
699
- if (fieldName.includes("time") && !fieldName.includes("duration")) {
700
- return { formatType: "timestamp" };
701
- }
702
- if (fieldName.includes("duration")) {
703
- return { formatType: "number", description: "Duration in seconds" };
704
- }
705
- if (path.length >= 2 && path[path.length - 2] === "tools" && fieldName === "trace") {
706
- return {
707
- displayName: "\u{1F50D} Execution Trace",
708
- formatType: "json",
709
- description: "Complete execution trace with all steps and timing"
710
- };
711
- }
712
- return null;
713
- }
714
- function formatValue(value, hint) {
715
- if (value === null) return "null";
716
- if (value === void 0) return "undefined";
717
- if (hint?.formatType === "timestamp" && typeof value === "string") {
718
- try {
719
- const date = new Date(value);
720
- if (!isNaN(date.getTime())) {
721
- return `${date.toLocaleString()} (${value})`;
722
- }
723
- } catch {
724
- }
725
- }
726
- if (hint?.formatType === "timestamp" && typeof value === "number") {
727
- const date = new Date(value * 1e3);
728
- return `${date.toLocaleString()} (${value})`;
729
- }
730
- if (hint?.formatType === "id" && typeof value === "string") {
731
- if (value.length > 20) {
732
- return `${value.substring(0, 16)}...${value.substring(value.length - 4)}`;
733
- }
734
- }
735
- if (typeof value === "string") {
736
- if (value.length > 100) {
737
- return `"${value.substring(0, 97)}..." (${value.length} chars)`;
738
- }
739
- return `"${value}"`;
740
- }
741
- return String(value);
742
- }
743
- function MessageJsonTree(props) {
744
- return /* @__PURE__ */ jsx(JsonTree, { ...props, resolveHint: getFieldHint, formatValue });
745
- }
746
- var DEFAULT_TIPS_EDITOR_LABELS = {
747
- titleList: "Edit tips",
748
- titleNew: "New tip",
749
- titleEdit: "Edit tip",
750
- descriptionList: "All tips across every page. Change a tip's placement to move it to a page, show it everywhere, or hide it. Changes save instantly.",
751
- descriptionNew: "Create a tip and choose where it shows.",
752
- descriptionEdit: "Editing the tip below \u2014 change its copy, button, or placement.",
753
- hiddenOff: "Hidden (off)",
754
- customSuffix: " \xB7 custom",
755
- editTip: "Edit tip",
756
- deleteTip: "Delete tip",
757
- addTip: "Add tip",
758
- testerTitle: "Run a sequence (test)",
759
- run: "Run",
760
- cancel: "Cancel",
761
- newTipIdentity: "New tip",
762
- customTipIdentity: "Custom tip",
763
- builtInTipIdentity: (code) => `Built-in tip \xB7 ${code}`,
764
- yourNewTip: "Your new tip",
765
- placement: "Placement",
766
- message: "Message",
767
- button: "Button",
768
- noButton: "No button",
769
- platformAction: "Platform action",
770
- actionSequence: "Action sequence",
771
- externalLink: "External link (new window)",
772
- buttonLabel: "Button label",
773
- action: "Action",
774
- sequence: "Sequence",
775
- url: "URL",
776
- back: "Back",
777
- save: "Save",
778
- saving: "Saving\u2026"
779
- };
780
- function SelectField({
781
- label,
782
- value,
783
- onValueChange,
784
- options
785
- }) {
786
- return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
787
- /* @__PURE__ */ jsx(Label, { children: label }),
788
- /* @__PURE__ */ jsxs(Select, { value, onValueChange, children: [
789
- /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
790
- /* @__PURE__ */ jsx(SelectContent, { children: options.map((o) => /* @__PURE__ */ jsx(SelectItem, { value: o.value, children: o.label }, o.value)) })
791
- ] })
792
- ] });
793
- }
794
- var ROW_BUTTON = "size-7 flex-shrink-0 pointer-coarse:size-11";
795
- function TipsEditorDialog({
796
- open,
797
- onOpenChange,
798
- form,
799
- onFormChange,
800
- onFormBack,
801
- onFormSave,
802
- rows,
803
- onEditRow,
804
- onDeleteRow,
805
- onAdd,
806
- busy,
807
- error,
808
- placementOptions,
809
- actionOptions,
810
- sequenceOptions,
811
- activeSequenceId,
812
- onRunSequence,
813
- onCancelSequence,
814
- messageMaxChars,
815
- ctaLabelMaxChars,
816
- labels
817
- }) {
818
- const tipCtaHrefId = useId();
819
- const tipCtaLabelId = useId();
820
- const tipMessageId = useId();
821
- const l = useMemo(() => ({ ...DEFAULT_TIPS_EDITOR_LABELS, ...labels }), [labels]);
822
- const isNew = form?.code === null;
823
- const title = form ? isNew ? l.titleNew : l.titleEdit : l.titleList;
824
- const description = form ? isNew ? l.descriptionNew : l.descriptionEdit : l.descriptionList;
825
- const targetOptions = [
826
- { value: "none", label: l.noButton },
827
- { value: "action", label: l.platformAction },
828
- { value: "sequence", label: l.actionSequence },
829
- { value: "url", label: l.externalLink }
830
- ];
831
- return /* @__PURE__ */ jsxs(
832
- ModalScaffold,
833
- {
834
- open,
835
- onOpenChange,
836
- title,
837
- description,
838
- showDescription: true,
839
- className: "max-w-lg",
840
- children: [
841
- error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }),
842
- form ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
843
- /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-l-4 border-l-red-500 bg-muted/40 px-3 py-2", children: [
844
- /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: isNew ? l.newTipIdentity : form.isCustom ? l.customTipIdentity : l.builtInTipIdentity(form.code ?? "") }),
845
- /* @__PURE__ */ jsx("p", { className: "mt-0.5 truncate text-sm font-medium text-foreground", children: form.original || l.yourNewTip })
846
- ] }),
847
- /* @__PURE__ */ jsx(
848
- SelectField,
849
- {
850
- label: l.placement,
851
- value: form.placement,
852
- onValueChange: (placement) => onFormChange({ placement }),
853
- options: placementOptions
854
- }
855
- ),
856
- /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
857
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
858
- /* @__PURE__ */ jsx(Label, { htmlFor: tipMessageId, children: l.message }),
859
- /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
860
- form.message.length,
861
- "/",
862
- messageMaxChars
863
- ] })
864
- ] }),
865
- /* @__PURE__ */ jsx(
866
- Textarea,
867
- {
868
- id: tipMessageId,
869
- value: form.message,
870
- onChange: (e) => onFormChange({ message: e.target.value.slice(0, messageMaxChars) }),
871
- maxLength: messageMaxChars,
872
- rows: 2
873
- }
874
- )
875
- ] }),
876
- /* @__PURE__ */ jsx(
877
- SelectField,
878
- {
879
- label: l.button,
880
- value: form.target,
881
- onValueChange: (v) => onFormChange({ target: v }),
882
- options: targetOptions
883
- }
884
- ),
885
- form.target !== "none" && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
886
- /* @__PURE__ */ jsx(Label, { htmlFor: tipCtaLabelId, children: l.buttonLabel }),
887
- /* @__PURE__ */ jsx(
888
- Input,
889
- {
890
- id: tipCtaLabelId,
891
- value: form.ctaLabel,
892
- onChange: (e) => onFormChange({ ctaLabel: e.target.value.slice(0, ctaLabelMaxChars) }),
893
- maxLength: ctaLabelMaxChars
894
- }
895
- )
896
- ] }),
897
- form.target === "action" && /* @__PURE__ */ jsx(
898
- SelectField,
899
- {
900
- label: l.action,
901
- value: form.ctaAction,
902
- onValueChange: (ctaAction) => onFormChange({ ctaAction }),
903
- options: actionOptions
904
- }
905
- ),
906
- form.target === "sequence" && /* @__PURE__ */ jsx(
907
- SelectField,
908
- {
909
- label: l.sequence,
910
- value: form.ctaSequence,
911
- onValueChange: (ctaSequence) => onFormChange({ ctaSequence }),
912
- options: sequenceOptions
913
- }
914
- ),
915
- form.target === "url" && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
916
- /* @__PURE__ */ jsx(Label, { htmlFor: tipCtaHrefId, children: l.url }),
917
- /* @__PURE__ */ jsx(
918
- Input,
919
- {
920
- id: tipCtaHrefId,
921
- placeholder: "https://\u2026",
922
- value: form.ctaHref,
923
- onChange: (e) => onFormChange({ ctaHref: e.target.value })
924
- }
925
- )
926
- ] }),
927
- /* @__PURE__ */ jsxs(DialogFooter, { children: [
928
- /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: onFormBack, disabled: busy, children: l.back }),
929
- /* @__PURE__ */ jsx(Button, { onClick: onFormSave, disabled: busy || !form.message.trim(), children: busy ? l.saving : l.save })
930
- ] })
931
- ] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
932
- /* @__PURE__ */ jsx("ul", { className: "max-h-[55dvh] divide-y overflow-y-auto rounded border", children: rows.map((row) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-2 p-2 text-sm", children: [
933
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
934
- /* @__PURE__ */ jsx("p", { className: cn(row.isHidden && "text-muted-foreground line-through"), children: row.message }),
935
- /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
936
- row.isHidden ? l.hiddenOff : row.placementLabel,
937
- row.isCustom && l.customSuffix
938
- ] })
939
- ] }),
940
- /* @__PURE__ */ jsx(
941
- Button,
942
- {
943
- size: "icon",
944
- variant: "ghost",
945
- className: ROW_BUTTON,
946
- onClick: () => onEditRow(row.code),
947
- "aria-label": l.editTip,
948
- children: /* @__PURE__ */ jsx(Pencil, { className: "size-3.5" })
949
- }
950
- ),
951
- row.isCustom && /* @__PURE__ */ jsx(
952
- Button,
953
- {
954
- size: "icon",
955
- variant: "ghost",
956
- className: ROW_BUTTON,
957
- disabled: busy,
958
- onClick: () => onDeleteRow(row.code),
959
- "aria-label": l.deleteTip,
960
- children: /* @__PURE__ */ jsx(Trash2, { className: "size-3.5" })
961
- }
962
- )
963
- ] }, row.code)) }),
964
- /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: onAdd, children: [
965
- /* @__PURE__ */ jsx(Plus, { className: "mr-1 size-4" }),
966
- " ",
967
- l.addTip
968
- ] }),
969
- /* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded border border-dashed p-2", children: [
970
- /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: l.testerTitle }),
971
- /* @__PURE__ */ jsx("ul", { className: "divide-y rounded border", children: sequenceOptions.map((s) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-2 p-2 text-sm", children: [
972
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: s.label }),
973
- activeSequenceId === s.value ? /* @__PURE__ */ jsx(
974
- Button,
975
- {
976
- size: "sm",
977
- variant: "outline",
978
- className: "h-7 flex-shrink-0 pointer-coarse:h-11",
979
- onClick: onCancelSequence,
980
- children: l.cancel
981
- }
982
- ) : /* @__PURE__ */ jsx(
983
- Button,
984
- {
985
- size: "sm",
986
- variant: "outline",
987
- className: "h-7 flex-shrink-0 pointer-coarse:h-11",
988
- onClick: () => onRunSequence(s.value),
989
- children: l.run
990
- }
991
- )
992
- ] }, s.value)) })
993
- ] })
994
- ] })
995
- ]
996
- }
997
- );
998
- }
999
- var DEFAULT_SYNC_DIALOG_FOOTER_LABELS = {
1000
- cancel: "Cancel",
1001
- save: "Save"
1002
- };
1003
- function SyncDialogFooter({
1004
- onCancel,
1005
- onSave,
1006
- saveDisabled,
1007
- isSaving,
1008
- cancelButton,
1009
- saveButton,
1010
- labels
1011
- }) {
1012
- const l = { ...DEFAULT_SYNC_DIALOG_FOOTER_LABELS, ...labels };
1013
- return /* @__PURE__ */ jsxs(DialogFooter, { "data-slot": "sync-dialog-footer", children: [
1014
- /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: onCancel, "data-testid": cancelButton?.testId, children: l.cancel }),
1015
- /* @__PURE__ */ jsxs(
1016
- Button,
1017
- {
1018
- size: "sm",
1019
- onClick: onSave,
1020
- disabled: saveDisabled || isSaving,
1021
- "data-testid": saveButton?.testId,
1022
- children: [
1023
- isSaving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 size-3.5 animate-spin" }),
1024
- l.save
1025
- ]
1026
- }
1027
- )
1028
- ] });
1029
- }
1030
- function SyncDialogBody({ children }) {
1031
- return /* @__PURE__ */ jsx("div", { "data-slot": "sync-dialog-body", className: "space-y-4 py-2", children });
1032
- }
1033
- function SyncField({ label, htmlFor, actions, hint, children }) {
1034
- return /* @__PURE__ */ jsxs("div", { "data-slot": "sync-field", className: "space-y-1.5", children: [
1035
- (label || actions) && /* @__PURE__ */ jsxs("div", { className: cn(actions && "flex items-center justify-between"), children: [
1036
- label && /* @__PURE__ */ jsx(Label, { htmlFor, children: label }),
1037
- actions
1038
- ] }),
1039
- children,
1040
- hint && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: hint })
1041
- ] });
1042
- }
1043
- function SyncLinkButton({
1044
- onClick,
1045
- testId,
1046
- children
1047
- }) {
1048
- return /* @__PURE__ */ jsx(
1049
- "button",
1050
- {
1051
- type: "button",
1052
- className: "text-xs font-medium text-primary hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-coarse:min-h-11 pointer-coarse:px-1",
1053
- onClick,
1054
- "data-testid": testId,
1055
- children
1056
- }
1057
- );
1058
- }
1059
- function SyncLinkRow({ children }) {
1060
- return /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children });
1061
- }
1062
- function SyncSelect({
1063
- id,
1064
- testId,
1065
- value,
1066
- onValueChange,
1067
- options,
1068
- placeholder
1069
- }) {
1070
- return /* @__PURE__ */ jsxs(Select, { value, onValueChange, children: [
1071
- /* @__PURE__ */ jsx(SelectTrigger, { id, "data-testid": testId, children: /* @__PURE__ */ jsx(SelectValue, { placeholder }) }),
1072
- /* @__PURE__ */ jsx(SelectContent, { children: options.map((o) => /* @__PURE__ */ jsx(SelectItem, { value: o.value, children: o.label }, o.value)) })
1073
- ] });
1074
- }
1075
- function SyncOptionToggle({
1076
- checked,
1077
- onCheckedChange,
1078
- title,
1079
- description,
1080
- testId
1081
- }) {
1082
- return /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-start gap-2 text-sm", children: [
1083
- /* @__PURE__ */ jsx(
1084
- Checkbox,
1085
- {
1086
- className: "mt-0.5",
1087
- checked,
1088
- onCheckedChange: (v) => onCheckedChange(v === true),
1089
- "data-testid": testId
1090
- }
1091
- ),
1092
- /* @__PURE__ */ jsxs("span", { children: [
1093
- title,
1094
- description && /* @__PURE__ */ jsx("span", { className: "block text-xs text-muted-foreground", children: description })
1095
- ] })
1096
- ] });
1097
- }
1098
- function SyncOptions({ children }) {
1099
- return /* @__PURE__ */ jsx("div", { "data-slot": "sync-options", className: "space-y-2.5", children });
1100
- }
1101
- function SyncLoading({ children, compact }) {
1102
- return /* @__PURE__ */ jsxs(
1103
- "div",
1104
- {
1105
- className: cn("flex items-center gap-2 text-sm text-muted-foreground", !compact && "py-2"),
1106
- children: [
1107
- /* @__PURE__ */ jsx(Loader2, { className: "size-3.5 animate-spin" }),
1108
- " ",
1109
- children
1110
- ]
1111
- }
1112
- );
1113
- }
1114
- function SyncNote({ children, testId }) {
1115
- return /* @__PURE__ */ jsx("p", { className: "py-2 text-sm text-muted-foreground", "data-testid": testId, children });
1116
- }
1117
- function SyncSmallNote({ children, testId }) {
1118
- return /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", "data-testid": testId, children });
1119
- }
1120
- function SyncReconnectNotice({
1121
- message,
1122
- onReconnect,
1123
- isPending,
1124
- testId,
1125
- label = "Reconnect"
1126
- }) {
1127
- return /* @__PURE__ */ jsxs("div", { className: "space-y-2 py-2", children: [
1128
- /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: message }),
1129
- /* @__PURE__ */ jsxs(
1130
- Button,
1131
- {
1132
- type: "button",
1133
- size: "sm",
1134
- onClick: onReconnect,
1135
- disabled: isPending,
1136
- "data-testid": testId,
1137
- children: [
1138
- isPending && /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 size-3.5 animate-spin" }),
1139
- label
1140
- ]
1141
- }
1142
- )
1143
- ] });
1144
- }
1145
- function SyncTreeFrame({ children }) {
1146
- return /* @__PURE__ */ jsx(ScrollArea, { className: "h-72 rounded-md border p-2", children });
1147
- }
1148
- function SyncCheckList({ items, selected, onToggle }) {
1149
- return /* @__PURE__ */ jsx(ScrollArea, { className: "max-h-44 rounded-md border border-border", children: /* @__PURE__ */ jsx("div", { className: "space-y-1 p-2", children: items.map((c) => /* @__PURE__ */ jsxs(
1150
- "label",
1151
- {
1152
- className: "flex cursor-pointer items-center gap-2 rounded px-1.5 py-1 text-sm hover:bg-muted/50 pointer-coarse:min-h-11",
1153
- children: [
1154
- /* @__PURE__ */ jsx(
1155
- Checkbox,
1156
- {
1157
- checked: selected.includes(c.id),
1158
- onCheckedChange: () => onToggle(c.id),
1159
- "data-testid": c.testId
1160
- }
1161
- ),
1162
- /* @__PURE__ */ jsx("span", { className: "truncate", children: c.label })
1163
- ]
1164
- },
1165
- c.id
1166
- )) }) });
1167
- }
1168
- var DEFAULT_SYNC_FOLDER_ROW_LABELS = {
1169
- collapse: "Collapse folder",
1170
- expand: "Expand folder",
1171
- loading: "Loading\u2026",
1172
- noSubfolders: "No subfolders"
1173
- };
1174
- var INDENT = 18;
1175
- function SyncFolderRow({
1176
- id,
1177
- name,
1178
- depth,
1179
- hasChildren,
1180
- isExpanded,
1181
- onToggleExpanded,
1182
- isSelectable,
1183
- checked,
1184
- disabled,
1185
- onCheckedChange,
1186
- count,
1187
- isCountLoading,
1188
- children,
1189
- labels
1190
- }) {
1191
- const l = { ...DEFAULT_SYNC_FOLDER_ROW_LABELS, ...labels };
1192
- return /* @__PURE__ */ jsxs("div", { children: [
1193
- /* @__PURE__ */ jsxs(
1194
- "div",
1195
- {
1196
- className: "flex items-center gap-1.5 rounded px-1 py-1 text-sm hover:bg-muted/50 pointer-coarse:min-h-11",
1197
- style: { paddingLeft: depth * INDENT + 4 },
1198
- children: [
1199
- hasChildren ? /* @__PURE__ */ jsx(
1200
- "button",
1201
- {
1202
- type: "button",
1203
- className: "flex size-4 shrink-0 items-center justify-center text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-coarse:size-8",
1204
- onClick: onToggleExpanded,
1205
- "aria-label": isExpanded ? l.collapse : l.expand,
1206
- "aria-expanded": isExpanded,
1207
- children: isExpanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "size-3.5" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5" })
1208
- }
1209
- ) : /* @__PURE__ */ jsx("span", { className: "size-4 shrink-0" }),
1210
- isSelectable ? /* @__PURE__ */ jsxs(
1211
- "label",
1212
- {
1213
- className: "flex flex-1 cursor-pointer items-center gap-2 truncate",
1214
- "data-testid": `drive-folder-row-${id}`,
1215
- children: [
1216
- /* @__PURE__ */ jsx(
1217
- Checkbox,
1218
- {
1219
- checked,
1220
- disabled,
1221
- onCheckedChange,
1222
- "data-testid": `drive-folder-checkbox-${id}`
1223
- }
1224
- ),
1225
- /* @__PURE__ */ jsx("span", { className: "truncate", "data-testid": `drive-folder-name-${id}`, children: name }),
1226
- isCountLoading ? /* @__PURE__ */ jsx(Loader2, { className: "size-3 shrink-0 animate-spin text-muted-foreground" }) : typeof count === "number" ? /* @__PURE__ */ jsxs("span", { className: "shrink-0 text-xs text-muted-foreground", children: [
1227
- "(",
1228
- count.toLocaleString(),
1229
- ")"
1230
- ] }) : null
1231
- ]
1232
- }
1233
- ) : /* @__PURE__ */ jsx(
1234
- "span",
1235
- {
1236
- className: "flex flex-1 items-center truncate font-medium text-muted-foreground",
1237
- "data-testid": `drive-folder-row-${id}`,
1238
- children: /* @__PURE__ */ jsx("span", { className: "truncate", "data-testid": `drive-folder-name-${id}`, children: name })
1239
- }
1240
- )
1241
- ]
1242
- }
1243
- ),
1244
- isExpanded && /* @__PURE__ */ jsx("div", { children })
1245
- ] });
1246
- }
1247
- function SyncFolderStatus({
1248
- depth,
1249
- kind,
1250
- labels
1251
- }) {
1252
- const l = { ...DEFAULT_SYNC_FOLDER_ROW_LABELS, ...labels };
1253
- return /* @__PURE__ */ jsx(
1254
- "div",
1255
- {
1256
- className: "flex items-center gap-2 py-1 text-xs text-muted-foreground",
1257
- style: { paddingLeft: (depth + 1) * INDENT + 4 },
1258
- children: kind === "loading" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1259
- /* @__PURE__ */ jsx(Loader2, { className: "size-3 animate-spin" }),
1260
- " ",
1261
- l.loading
1262
- ] }) : l.noSubfolders
1263
- }
1264
- );
1265
- }
1266
- function FilterChip({
1267
- label,
1268
- state,
1269
- icon,
1270
- color,
1271
- onDismiss,
1272
- dismissLabel,
1273
- className,
1274
- testId
1275
- }) {
1276
- const isExclude = state === "exclude";
1277
- return /* @__PURE__ */ jsxs(
1278
- "span",
1279
- {
1280
- className: cn(
1281
- "inline-flex items-center gap-1 text-xs font-medium rounded px-1.5 py-0.5 flex-shrink-0 border",
1282
- isExclude ? "bg-background text-red-text border-red-text" : "bg-primary text-white border-primary",
1283
- className
1284
- ),
1285
- "data-state": state,
1286
- "data-testid": testId,
1287
- children: [
1288
- color ? (
1289
- /* Filled square for include; empty square for exclude */
1290
- isExclude ? /* @__PURE__ */ jsx(
1291
- "span",
1292
- {
1293
- className: "w-3.5 h-3.5 rounded flex-shrink-0 border-2",
1294
- style: { borderColor: color }
1295
- }
1296
- ) : /* @__PURE__ */ jsx(
1297
- "span",
1298
- {
1299
- className: "w-3.5 h-3.5 rounded flex items-center justify-center flex-shrink-0",
1300
- style: { backgroundColor: color },
1301
- children: /* @__PURE__ */ jsx(Check, { className: "w-2.5 h-2.5 text-white", strokeWidth: 3 })
1302
- }
1303
- )
1304
- ) : icon && /* @__PURE__ */ jsx("span", { className: "flex-none", children: icon }),
1305
- /* @__PURE__ */ jsxs("span", { className: "truncate max-w-[80px]", title: label, children: [
1306
- isExclude && /* @__PURE__ */ jsx("span", { className: "opacity-70 mr-0.5", children: "Not" }),
1307
- label
1308
- ] }),
1309
- onDismiss && /* @__PURE__ */ jsx(
1310
- "button",
1311
- {
1312
- type: "button",
1313
- onClick: onDismiss,
1314
- "aria-label": dismissLabel ?? `Remove ${label}`,
1315
- className: "flex-none ml-0.5 opacity-70 hover:opacity-100",
1316
- "data-testid": testId ? `${testId}-remove` : void 0,
1317
- children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3", strokeWidth: 2.5 })
1318
- }
1319
- )
1320
- ]
1321
- }
1322
- );
1323
- }
1324
- var planBadgeVariants = cva(
1325
- "inline-flex flex-shrink-0 items-center rounded-full border font-semibold leading-none",
1326
- {
1327
- variants: {
1328
- /** free/dev = neutral, paid = accent tint, pending = payment trouble or cancelling. */
1329
- tone: {
1330
- free: `border-transparent ${TONE_SURFACE.neutral}`,
1331
- dev: `border-transparent ${TONE_SURFACE.neutral}`,
1332
- paid: "border-transparent bg-accent text-accent-foreground",
1333
- pending: `${TONE_BORDER.warning} ${TONE_SURFACE.warning}`
1334
- },
1335
- /** `sm` fits dense rows and tables; `md` holds its own beside body text. */
1336
- size: {
1337
- sm: "h-5 px-2 text-xs",
1338
- md: "h-6 px-2.5 text-sm"
1339
- }
1340
- },
1341
- defaultVariants: { tone: "free", size: "sm" }
1342
- }
1343
- );
1344
- function PlanBadge({
1345
- label,
1346
- tone,
1347
- size,
1348
- className,
1349
- testId = "project-plan-chip"
1350
- }) {
1351
- return /* @__PURE__ */ jsx(
1352
- "span",
1353
- {
1354
- "data-slot": "plan-badge",
1355
- className: cn(planBadgeVariants({ tone, size }), className),
1356
- "data-testid": testId,
1357
- children: label
1358
- }
1359
- );
1360
- }
1361
- function MoreInfo({
1362
- title,
1363
- children,
1364
- label = "More info",
1365
- testId = "more-info",
1366
- className
1367
- }) {
1368
- const [open, setOpen] = useState(false);
1369
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1370
- /* @__PURE__ */ jsxs(
1371
- "button",
1372
- {
1373
- type: "button",
1374
- onClick: () => setOpen(true),
1375
- className: cn(
1376
- "inline-flex shrink-0 items-center gap-1 whitespace-nowrap rounded-sm text-xs font-medium text-muted-foreground underline-offset-2 hover:text-foreground hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-coarse:min-h-11",
1377
- className
1378
- ),
1379
- "data-slot": "more-info",
1380
- "data-testid": testId,
1381
- children: [
1382
- /* @__PURE__ */ jsx(Info, { className: "size-3.5", "aria-hidden": true }),
1383
- label
1384
- ]
1385
- }
1386
- ),
1387
- /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", "data-testid": `${testId}-dialog`, children: [
1388
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
1389
- /* @__PURE__ */ jsx(DialogTitle, { children: title }),
1390
- /* @__PURE__ */ jsx(DialogDescription, { className: "sr-only", children: title })
1391
- ] }),
1392
- /* @__PURE__ */ jsx("div", { className: "space-y-3 text-sm leading-relaxed text-muted-foreground", children })
1393
- ] }) })
1394
- ] });
1395
- }
1396
- function MoreInfoList({ items }) {
1397
- return /* @__PURE__ */ jsx("ul", { className: "space-y-2", "data-slot": "more-info-list", children: items.map((item, i) => /* @__PURE__ */ jsxs("li", { children: [
1398
- item.term && /* @__PURE__ */ jsxs("span", { className: "font-medium text-foreground", children: [
1399
- item.term,
1400
- ". "
1401
- ] }),
1402
- item.text
1403
- ] }, item.term ?? i)) });
1404
- }
1405
- var barColor = (pct) => pct >= 100 ? "bg-red-500" : pct >= 50 ? "bg-yellow-500" : "bg-primary";
1406
- var valueColor2 = (pct) => pct >= 100 ? "text-red-text" : pct >= 80 ? "text-yellow-text" : "text-muted-foreground";
1407
- function QuotaMeter({
1408
- items,
1409
- size = "sm",
1410
- showTooltips = true,
1411
- showBars = true,
1412
- showPercent = true,
1413
- className,
1414
- testIds
1415
- }) {
1416
- if (size === "lg") {
1417
- return /* @__PURE__ */ jsx(
1418
- "div",
1419
- {
1420
- "data-slot": "quota-meter",
1421
- className: cn("flex flex-wrap gap-3", className),
1422
- "data-testid": testIdOr(testIds?.root, { testId: "quota-badges-lg" }),
1423
- children: items.map(({ key, icon: Icon, label, usedOfLimit, pct }) => /* @__PURE__ */ jsxs(
1424
- "div",
1425
- {
1426
- className: "min-w-[8rem] flex-1 rounded-lg bg-muted p-3",
1427
- "data-testid": prefixedTestId(testIds?.badge, key, { testId: `quota-badge-${key}` }),
1428
- children: [
1429
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
1430
- /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5 flex-shrink-0" }),
1431
- /* @__PURE__ */ jsx("span", { className: "truncate", children: label })
1432
- ] }),
1433
- /* @__PURE__ */ jsx("div", { className: "mt-1 text-lg font-semibold tabular-nums text-foreground", children: usedOfLimit }),
1434
- /* @__PURE__ */ jsx("div", { className: "mt-2 h-1.5 overflow-hidden rounded-full bg-background", children: /* @__PURE__ */ jsx(
1435
- "div",
1436
- {
1437
- className: cn("h-full rounded-full", barColor(pct)),
1438
- style: { width: `${Math.min(pct, 100)}%` },
1439
- "data-testid": prefixedTestId(
1440
- testIds?.badge,
1441
- key,
1442
- { testId: `quota-badge-${key}-fill` },
1443
- "-fill"
1444
- )
1445
- }
1446
- ) })
1447
- ]
1448
- },
1449
- key
1450
- ))
1451
- }
1452
- );
1453
- }
1454
- return /* @__PURE__ */ jsx(
1455
- "div",
1456
- {
1457
- "data-slot": "quota-meter",
1458
- className: cn("flex flex-shrink-0 items-center gap-3 text-sm tabular-nums", className),
1459
- "data-testid": testIdOr(testIds?.root, { testId: "quota-badges-sm" }),
1460
- children: items.map(({ key, icon: Icon, label, used, usedOfLimit, pct }) => {
1461
- const badge = /* @__PURE__ */ jsxs(
1462
- "span",
1463
- {
1464
- className: "inline-flex items-center gap-1",
1465
- "data-testid": prefixedTestId(testIds?.badge, key, { testId: `quota-badge-${key}` }),
1466
- children: [
1467
- /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 flex-shrink-0 text-muted-foreground" }),
1468
- /* @__PURE__ */ jsx("span", { className: showBars ? valueColor2(pct) : "text-muted-foreground", children: showBars && showPercent ? `${used} (${Math.round(pct)}%)` : used })
1469
- ]
1470
- }
1471
- );
1472
- return showTooltips ? /* @__PURE__ */ jsxs(Tooltip, { children: [
1473
- /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: badge }),
1474
- /* @__PURE__ */ jsx(TooltipContent, { children: `${usedOfLimit} ${label}` })
1475
- ] }, key) : /* @__PURE__ */ jsx(Fragment$1, { children: badge }, key);
1476
- })
1477
- }
1478
- );
1479
- }
1480
- var DEFAULT_PLAN_CARD_LABELS = {
1481
- currentPlan: "Current plan",
1482
- popular: "Popular",
1483
- noMonthlyCost: "\xA30",
1484
- perMonth: "/mo",
1485
- current: "Current",
1486
- monthly: "Monthly",
1487
- annual: "Annual",
1488
- annualBilling: "Annual billing",
1489
- save: (percent) => `Save ${percent}%`,
1490
- havePromoCode: "Have a promo code?",
1491
- promoCode: "Promo Code",
1492
- enterCode: "Enter code",
1493
- pricingExplainer: "How pricing works",
1494
- explainerTiers: [
1495
- "AI usage and storage are shared by everyone in the project. AI usage resets monthly.",
1496
- "Need more? Add \xA325 of AI usage or 25 GB at a time, at checkout or later. Add-ons renew with the plan until you reduce them.",
1497
- "Automatic top-ups add one more step when you run low, up to your monthly usage cap (\xA3100 by default; \xA30 = none; or no limit). Adding by hand is never capped.",
1498
- "Add-ons start now: AI usage is charged in full, storage only for the rest of the period. Downgrades, billing switches and reductions start at the end of the period. No refunds.",
1499
- "Annual plans are 25% off, add-ons included.",
1500
- "Prices are shown + VAT; the total for your country is shown before you pay."
1501
- ],
1502
- explainerAiUsage: "About 1p of AI usage buys one moderately complex request, or reading ~25 documents, on a Standard model. Premium models can cost 10\u2013100\xD7 more.",
1503
- seeModelList: "AI model prices \u2192"
1504
- };
1505
- function PlanCard({ plan, onSelect, labels }) {
1506
- const l = useMemo(() => ({ ...DEFAULT_PLAN_CARD_LABELS, ...labels }), [labels]);
1507
- const { id, name, isFree, isCurrent, isSelected, isPopular, price, features } = plan;
1508
- return /* @__PURE__ */ jsxs(
1509
- "button",
1510
- {
1511
- type: "button",
1512
- disabled: isCurrent,
1513
- "aria-pressed": !!isSelected,
1514
- onClick: () => !isCurrent && onSelect(id),
1515
- "data-slot": "plan-card",
1516
- "data-testid": `tier-card-${id}`,
1517
- className: cn(
1518
- "relative flex flex-col justify-start rounded-lg p-4 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1519
- isCurrent ? "cursor-default bg-blue-bg" : isSelected ? "bg-accent ring-1 ring-primary/50" : "bg-muted hover:bg-muted/70"
1520
- ),
1521
- children: [
1522
- isCurrent && /* @__PURE__ */ jsx("span", { className: "absolute -top-3 left-1/2 -translate-x-1/2 whitespace-nowrap rounded-full bg-blue-bg px-3 py-1 text-xs font-semibold leading-none text-blue-text", children: l.currentPlan }),
1523
- isPopular && !isCurrent && /* @__PURE__ */ jsx("span", { className: "absolute -top-3 left-1/2 -translate-x-1/2 whitespace-nowrap rounded-full bg-primary px-3 py-1 text-xs font-semibold leading-none text-primary-foreground", children: l.popular }),
1524
- /* @__PURE__ */ jsx("div", { className: "mb-3 flex items-baseline gap-2", children: /* @__PURE__ */ jsx(
1525
- "span",
1526
- {
1527
- className: cn(
1528
- "text-lg font-semibold leading-none tracking-tight",
1529
- isCurrent ? "text-blue-text" : "text-foreground"
1530
- ),
1531
- children: name
1532
- }
1533
- ) }),
1534
- isFree ? /* @__PURE__ */ jsxs("div", { className: "text-2xl font-semibold leading-none tabular-nums text-foreground", children: [
1535
- l.noMonthlyCost,
1536
- /* @__PURE__ */ jsx("span", { className: "ml-0.5 text-sm font-medium text-muted-foreground", children: l.perMonth })
1537
- ] }) : price && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
1538
- price.annual && price.yearlyTotal && /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-x-2 whitespace-nowrap text-base tabular-nums", children: [
1539
- price.yearlyStrike && /* @__PURE__ */ jsx("span", { className: "font-medium text-muted-foreground line-through decoration-muted-foreground/70 decoration-1", children: price.yearlyStrike }),
1540
- /* @__PURE__ */ jsxs("span", { className: "text-lg font-semibold text-foreground", children: [
1541
- price.yearlyTotal,
1542
- /* @__PURE__ */ jsx("span", { className: "ml-0.5 text-xs font-semibold text-muted-foreground", children: price.intervalLabel })
1543
- ] })
1544
- ] }),
1545
- /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-x-1.5 whitespace-nowrap", children: [
1546
- price.annual && /* @__PURE__ */ jsx("span", { className: "text-xl font-semibold leading-none text-muted-foreground", children: "=" }),
1547
- price.monthlyStrike && /* @__PURE__ */ jsx("span", { className: "text-base font-medium tabular-nums text-muted-foreground line-through decoration-muted-foreground/70 decoration-1", children: price.monthlyStrike }),
1548
- /* @__PURE__ */ jsxs("span", { className: "text-2xl font-semibold leading-none tabular-nums text-foreground", children: [
1549
- price.monthly,
1550
- /* @__PURE__ */ jsxs("span", { className: "ml-0.5 text-sm font-medium text-muted-foreground", children: [
1551
- l.perMonth,
1552
- price.taxLabel && ` ${price.taxLabel}`
1553
- ] })
1554
- ] })
1555
- ] }),
1556
- price.note && /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", "data-slot": "plan-card-price-note", children: price.note })
1557
- ] }),
1558
- /* @__PURE__ */ jsx("ul", { className: "mt-4 space-y-1 border-t border-border pt-4 text-sm text-foreground", children: features.map((feature) => /* @__PURE__ */ jsx("li", { children: feature }, feature)) })
1559
- ]
1560
- }
1561
- );
1562
- }
1563
- function PlanCardGrid({
1564
- plans,
1565
- onSelect,
1566
- labels
1567
- }) {
1568
- return /* @__PURE__ */ jsx("div", { className: "@container space-y-2", "data-slot": "plan-card-grid", children: /* @__PURE__ */ jsx(
1569
- "div",
1570
- {
1571
- className: cn(
1572
- "grid gap-2",
1573
- plans.length === 4 ? "grid-cols-1 @xl:grid-cols-2 @4xl:grid-cols-4" : plans.length === 2 ? "grid-cols-1 @xl:grid-cols-2" : "grid-cols-1 @2xl:grid-cols-3"
1574
- ),
1575
- children: plans.map((plan) => /* @__PURE__ */ jsx(PlanCard, { plan, onSelect, labels }, plan.id))
1576
- }
1577
- ) });
1578
- }
1579
- function PlanTierOption({
1580
- option,
1581
- onSelect,
1582
- labels
1583
- }) {
1584
- const l = useMemo(() => ({ ...DEFAULT_PLAN_CARD_LABELS, ...labels }), [labels]);
1585
- return /* @__PURE__ */ jsxs(
1586
- "button",
1587
- {
1588
- type: "button",
1589
- onClick: () => onSelect(option.id),
1590
- "aria-pressed": option.isSelected,
1591
- "data-slot": "plan-tier-option",
1592
- "data-testid": `tier-option-${option.id}`,
1593
- className: cn(
1594
- "flex w-full items-center justify-between rounded-lg p-3 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-coarse:min-h-11",
1595
- option.isSelected ? "bg-accent ring-1 ring-primary/50" : "bg-muted hover:bg-muted/70"
1596
- ),
1597
- children: [
1598
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1599
- /* @__PURE__ */ jsx("span", { className: "text-base font-semibold text-foreground", children: option.name }),
1600
- option.isCurrent && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-background px-1.5 py-0.5 text-xs font-medium text-muted-foreground", children: l.current }),
1601
- option.isSelected && /* @__PURE__ */ jsx(Check, { className: "h-4 w-4 text-primary" })
1602
- ] }),
1603
- /* @__PURE__ */ jsxs("div", { className: "text-right", children: [
1604
- option.total && /* @__PURE__ */ jsxs("div", { className: "font-semibold tabular-nums", children: [
1605
- option.total,
1606
- /* @__PURE__ */ jsx("span", { className: "text-xs font-normal text-muted-foreground", children: option.intervalLabel })
1607
- ] }),
1608
- /* @__PURE__ */ jsx("div", { className: "text-xs tabular-nums text-muted-foreground", children: option.summary })
1609
- ] })
1610
- ]
1611
- }
1612
- );
1613
- }
1614
- function PricingToggle({
1615
- isAnnual,
1616
- onToggle,
1617
- savingsPercent,
1618
- labels
1619
- }) {
1620
- const l = useMemo(() => ({ ...DEFAULT_PLAN_CARD_LABELS, ...labels }), [labels]);
1621
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm", children: [
1622
- /* @__PURE__ */ jsx("span", { className: isAnnual ? "text-muted-foreground" : "font-medium", children: l.monthly }),
1623
- /* @__PURE__ */ jsx(
1624
- "button",
1625
- {
1626
- type: "button",
1627
- role: "switch",
1628
- "aria-checked": isAnnual,
1629
- "aria-label": l.annualBilling,
1630
- onClick: onToggle,
1631
- className: cn(
1632
- "relative inline-flex h-5 w-9 items-center rounded-full transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1633
- isAnnual ? "bg-primary" : "bg-muted"
1634
- ),
1635
- children: /* @__PURE__ */ jsx(
1636
- "span",
1637
- {
1638
- className: cn(
1639
- "inline-block h-3 w-3 transform rounded-full bg-card transition-transform",
1640
- isAnnual ? "translate-x-5" : "translate-x-1"
1641
- )
1642
- }
1643
- )
1644
- }
1645
- ),
1646
- /* @__PURE__ */ jsx("span", { className: isAnnual ? "font-medium" : "text-muted-foreground", children: l.annual }),
1647
- savingsPercent != null && savingsPercent > 0 && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-green-bg px-1.5 py-0.5 text-sm font-semibold leading-none text-green-text", children: l.save(savingsPercent) })
1648
- ] });
1649
- }
1650
- function PromoCodeInput({
1651
- value,
1652
- onChange,
1653
- labels
1654
- }) {
1655
- const l = useMemo(() => ({ ...DEFAULT_PLAN_CARD_LABELS, ...labels }), [labels]);
1656
- const [expanded, setExpanded] = useState(false);
1657
- if (!expanded) {
1658
- return /* @__PURE__ */ jsxs(
1659
- "button",
1660
- {
1661
- type: "button",
1662
- onClick: () => setExpanded(true),
1663
- className: "flex items-center gap-1.5 text-sm text-muted-foreground transition-colors hover:text-foreground pointer-coarse:min-h-11",
1664
- children: [
1665
- /* @__PURE__ */ jsx(Tag, { className: "h-3 w-3" }),
1666
- /* @__PURE__ */ jsx("span", { children: l.havePromoCode })
1667
- ]
1668
- }
1669
- );
1670
- }
1671
- return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
1672
- /* @__PURE__ */ jsx(Label, { className: "text-sm text-muted-foreground", children: l.promoCode }),
1673
- /* @__PURE__ */ jsx(
1674
- Input,
1675
- {
1676
- value,
1677
- onChange: (e) => onChange(e.target.value),
1678
- placeholder: l.enterCode,
1679
- className: "h-8 text-sm",
1680
- autoFocus: true
1681
- }
1682
- )
1683
- ] });
1684
- }
1685
- function PlanInfoPopover({
1686
- onOpenModelList,
1687
- labels
1688
- }) {
1689
- const l = useMemo(() => ({ ...DEFAULT_PLAN_CARD_LABELS, ...labels }), [labels]);
1690
- return /* @__PURE__ */ jsxs(MoreInfo, { title: l.pricingExplainer, label: l.pricingExplainer, testId: "pricing-explainer", children: [
1691
- /* @__PURE__ */ jsx(MoreInfoList, { items: l.explainerTiers.map((text) => ({ text })) }),
1692
- /* @__PURE__ */ jsxs("p", { children: [
1693
- l.explainerAiUsage,
1694
- " ",
1695
- /* @__PURE__ */ jsx(
1696
- "button",
1697
- {
1698
- type: "button",
1699
- onClick: onOpenModelList,
1700
- className: "font-medium text-foreground underline underline-offset-2 hover:text-primary/80",
1701
- children: l.seeModelList
1702
- }
1703
- )
1704
- ] })
1705
- ] });
1706
- }
1707
- var DEFAULT_REFERRAL_REWARD_LABELS = {
1708
- badgeTitle: "Reward \u2014 automatically applied to your invoice period",
1709
- badge: (amount) => `${amount} reward`,
1710
- subtotal: "Subtotal",
1711
- reward: "Reward",
1712
- youPayNow: "You pay now",
1713
- rollover: (amount) => `${amount} reward rolls over to your next invoice.`,
1714
- applied: (amount) => `${amount} reward applied automatically to your invoice period.`,
1715
- note: (amount) => `Your ${amount} reward applies to your invoice period.`
1716
- };
1717
- function ReferralRewardBadge({
1718
- amountLabel,
1719
- labels,
1720
- testId
1721
- }) {
1722
- const l = useMemo(() => ({ ...DEFAULT_REFERRAL_REWARD_LABELS, ...labels }), [labels]);
1723
- return /* @__PURE__ */ jsxs(
1724
- "span",
1725
- {
1726
- className: "flex h-8 items-center gap-1.5 rounded-full bg-green-bg px-2.5 text-sm font-medium text-green-text",
1727
- title: l.badgeTitle,
1728
- "data-testid": testIdOr(testId, { testId: "referral-reward-badge" }),
1729
- children: [
1730
- /* @__PURE__ */ jsx(Gift, { className: "h-3.5 w-3.5" }),
1731
- l.badge(amountLabel)
1732
- ]
1733
- }
1734
- );
1735
- }
1736
- function ReferralRewardSummary({
1737
- amountLabel,
1738
- breakdown,
1739
- labels,
1740
- testId,
1741
- testIds
1742
- }) {
1743
- const l = useMemo(() => ({ ...DEFAULT_REFERRAL_REWARD_LABELS, ...labels }), [labels]);
1744
- return /* @__PURE__ */ jsx(
1745
- "div",
1746
- {
1747
- className: "rounded-lg bg-green-bg/60 p-3 text-sm",
1748
- "data-testid": testIdOr(testId, { testId: "referral-reward-summary" }),
1749
- children: breakdown ? /* @__PURE__ */ jsxs("dl", { className: "space-y-1 tabular-nums", children: [
1750
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-muted-foreground", children: [
1751
- /* @__PURE__ */ jsx("dt", { children: l.subtotal }),
1752
- /* @__PURE__ */ jsx("dd", { children: breakdown.subtotal })
1753
- ] }),
1754
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-green-text", children: [
1755
- /* @__PURE__ */ jsxs("dt", { className: "flex items-center gap-1.5", children: [
1756
- /* @__PURE__ */ jsx(Gift, { className: "h-3.5 w-3.5" }),
1757
- l.reward
1758
- ] }),
1759
- /* @__PURE__ */ jsxs("dd", { "data-testid": testIdOr(testIds?.applied, { testId: "referral-reward-applied" }), children: [
1760
- "\u2212",
1761
- breakdown.reward
1762
- ] })
1763
- ] }),
1764
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-t border-green-border pt-1 font-semibold text-foreground", children: [
1765
- /* @__PURE__ */ jsx("dt", { children: l.youPayNow }),
1766
- /* @__PURE__ */ jsx("dd", { "data-testid": testIdOr(testIds?.youPay, { testId: "referral-reward-you-pay" }), children: breakdown.youPay })
1767
- ] }),
1768
- breakdown.rollover && /* @__PURE__ */ jsx("p", { className: "pt-0.5 text-xs font-normal text-muted-foreground", children: l.rollover(breakdown.rollover) })
1769
- ] }) : /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-1.5 font-medium text-green-text", children: [
1770
- /* @__PURE__ */ jsx(Gift, { className: "h-3.5 w-3.5 shrink-0" }),
1771
- l.applied(amountLabel)
1772
- ] })
1773
- }
1774
- );
1775
- }
1776
- function ReferralRewardNote({
1777
- amountLabel,
1778
- labels,
1779
- testId
1780
- }) {
1781
- const l = useMemo(() => ({ ...DEFAULT_REFERRAL_REWARD_LABELS, ...labels }), [labels]);
1782
- return /* @__PURE__ */ jsxs(
1783
- "p",
1784
- {
1785
- className: "flex items-center gap-1.5 text-sm text-green-text",
1786
- "data-testid": testIdOr(testId, { testId: "referral-reward-note" }),
1787
- children: [
1788
- /* @__PURE__ */ jsx(Gift, { className: "h-3.5 w-3.5 shrink-0" }),
1789
- l.note(amountLabel)
1790
- ]
1791
- }
1792
- );
1793
- }
1794
- var DEFAULT_CONNECTION_CARD_LABELS = {
1795
- connectedAs: (identity) => `Connected as ${identity}`,
1796
- justConnected: "Just connected \u2713",
1797
- connected: "Connected",
1798
- disconnect: "Disconnect",
1799
- connect: "Connect",
1800
- organisationAccount: "Organisation account",
1801
- readOnlyLead: "Read-only.",
1802
- readOnlyBody: " ARBI only imports a copy of your email and files. It can\u2019t modify or delete anything in your account, and deleting an imported document here never affects the original."
1803
- };
1804
- function ConnectionCard({
1805
- connection,
1806
- onConnect,
1807
- onDisconnect,
1808
- labels
1809
- }) {
1810
- const l = useMemo(() => ({ ...DEFAULT_CONNECTION_CARD_LABELS, ...labels }), [labels]);
1811
- const { id, status } = connection;
1812
- const isConnected = status === "connected";
1813
- return /* @__PURE__ */ jsxs(
1814
- "div",
1815
- {
1816
- "data-slot": "connection-card",
1817
- className: "rounded-lg bg-background px-5 py-4",
1818
- "data-testid": `connector-card-${id}`,
1819
- children: [
1820
- /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-4", children: [
1821
- /* @__PURE__ */ jsx("div", { className: "mt-0.5 flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground", children: connection.iconUrl ? /* @__PURE__ */ jsx(
1822
- "img",
1823
- {
1824
- src: connection.iconUrl,
1825
- alt: connection.iconAlt ?? connection.title,
1826
- className: "h-6 w-6 rounded object-contain"
1827
- }
1828
- ) : /* @__PURE__ */ jsx(PlugIcon, { className: "h-5 w-5" }) }),
1829
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
1830
- /* @__PURE__ */ jsx("span", { className: "font-heading text-sm font-semibold text-foreground", children: connection.title }),
1831
- connection.description && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-sm text-muted-foreground", children: connection.description }),
1832
- isConnected && connection.identity && /* @__PURE__ */ jsx(
1833
- "p",
1834
- {
1835
- className: "mt-1 text-xs text-muted-foreground",
1836
- "data-testid": `connector-identity-${id}`,
1837
- children: l.connectedAs(connection.identity)
1838
- }
1839
- ),
1840
- connection.warning && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-yellow-text", children: connection.warning }),
1841
- connection.note && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: connection.note })
1842
- ] }),
1843
- /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 flex-col items-end gap-2", children: [
1844
- (isConnected || connection.justConnected) && /* @__PURE__ */ jsx(
1845
- ToneBadge,
1846
- {
1847
- tone: "success",
1848
- icon: /* @__PURE__ */ jsx(CheckCircle2, { className: "h-3 w-3", "aria-hidden": "true" }),
1849
- children: connection.justConnected ? l.justConnected : l.connected
1850
- }
1851
- ),
1852
- isConnected ? /* @__PURE__ */ jsxs(
1853
- Button,
1854
- {
1855
- variant: "outline",
1856
- size: "sm",
1857
- onClick: () => onDisconnect(id),
1858
- disabled: connection.isDisconnecting,
1859
- className: "text-destructive hover:bg-destructive/10 hover:text-destructive",
1860
- "data-testid": `connector-disconnect-${id}`,
1861
- children: [
1862
- connection.isDisconnecting && /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-3.5 w-3.5 animate-spin" }),
1863
- l.disconnect
1864
- ]
1865
- }
1866
- ) : status === "available" ? /* @__PURE__ */ jsxs(
1867
- Button,
1868
- {
1869
- variant: "outline",
1870
- size: "sm",
1871
- onClick: () => onConnect(id),
1872
- className: "gap-1.5",
1873
- "data-testid": `connector-connect-${id}`,
1874
- children: [
1875
- /* @__PURE__ */ jsx(ExternalLink, { className: "h-3.5 w-3.5" }),
1876
- l.connect
1877
- ]
1878
- }
1879
- ) : /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-muted-foreground", children: l.organisationAccount })
1880
- ] })
1881
- ] }),
1882
- connection.showReadOnlyNotice && isConnected && /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-start gap-2 rounded-md bg-blue-bg px-3 py-2 text-xs text-blue-text", children: [
1883
- /* @__PURE__ */ jsx(ShieldCheck, { className: "mt-0.5 h-4 w-4 shrink-0" }),
1884
- /* @__PURE__ */ jsxs("span", { children: [
1885
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: l.readOnlyLead }),
1886
- l.readOnlyBody
1887
- ] })
1888
- ] })
1889
- ]
1890
- }
1891
- );
1892
- }
1893
- function ConnectionsEmpty({ message }) {
1894
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 py-8 text-center", children: [
1895
- /* @__PURE__ */ jsx(Plug, { className: "h-8 w-8 text-muted-foreground/40" }),
1896
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: message })
1897
- ] });
1898
- }
1899
- function ConnectionsLoading({ message }) {
1900
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 py-12 text-muted-foreground", children: [
1901
- /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }),
1902
- /* @__PURE__ */ jsx("span", { className: "text-sm", children: message })
1903
- ] });
1904
- }
1905
- function ConnectionsError({ message }) {
1906
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-lg bg-destructive/10 px-4 py-3 text-sm text-destructive", children: [
1907
- /* @__PURE__ */ jsx(AlertCircle, { className: "h-4 w-4 shrink-0" }),
1908
- /* @__PURE__ */ jsx("span", { children: message })
1909
- ] });
1910
- }
1911
- var DEFAULT_PROJECT_LIST_LABELS = {
1912
- loading: "Loading projects...",
1913
- empty: "No projects yet. Create one to get started.",
1914
- filteredOut: "No projects match the filter."
1915
- };
1916
- var DEFAULT_SHARED_PROJECTS_SECTION_LABELS = {
1917
- title: "Shared with you"
1918
- };
1919
- var DEFAULT_PROJECT_DETAIL_HEADER_LABELS = {
1920
- renameProject: "Rename project"
1921
- };
1922
- var DEFAULT_RENAME_DIALOG_LABELS = {
1923
- title: "Rename project",
1924
- description: "Enter a new name.",
1925
- cancel: "Cancel",
1926
- save: "Save"
1927
- };
1928
- function ProjectRow({ testId, onOpen, identity, summary, usage, actions }) {
1929
- const handleKeyDown = (e) => {
1930
- if (e.key === "Enter" || e.key === " ") {
1931
- e.preventDefault();
1932
- onOpen();
1933
- }
1934
- };
1935
- return /* @__PURE__ */ jsxs(
1936
- "div",
1937
- {
1938
- role: "button",
1939
- tabIndex: 0,
1940
- onClick: onOpen,
1941
- onKeyDown: handleKeyDown,
1942
- "data-slot": "project-row",
1943
- "data-testid": testId,
1944
- className: "group flex cursor-pointer flex-wrap items-center gap-x-3 gap-y-3 px-3 py-3 transition-colors hover:bg-muted/50 focus:bg-muted/50 focus:outline-none pointer-coarse:min-h-11",
1945
- children: [
1946
- /* @__PURE__ */ jsx("div", { className: "flex min-w-0 items-center", children: identity }),
1947
- /* @__PURE__ */ jsx("div", { className: "flex-1" }),
1948
- summary && /* @__PURE__ */ jsx("div", { className: "hidden items-center gap-3 md:flex", children: summary }),
1949
- actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5", onClick: (e) => e.stopPropagation(), children: actions }),
1950
- usage && /* @__PURE__ */ jsx("div", { className: "basis-full", children: usage })
1951
- ]
1952
- }
1953
- );
1954
- }
1955
- function ProjectList({
1956
- isLoading,
1957
- isEmpty,
1958
- isFilteredOut,
1959
- children,
1960
- testId = "projects-list",
1961
- labels
1962
- }) {
1963
- const l = useMemo(() => ({ ...DEFAULT_PROJECT_LIST_LABELS, ...labels }), [labels]);
1964
- const message = isLoading ? l.loading : isEmpty ? l.empty : isFilteredOut ? l.filteredOut : null;
1965
- return /* @__PURE__ */ jsx("div", { "data-testid": testId, children: isLoading ? /* @__PURE__ */ jsxs("div", { className: "space-y-3 px-3 py-4", "aria-busy": "true", children: [
1966
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: message }),
1967
- [0, 1].map((i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", "aria-hidden": "true", children: [
1968
- /* @__PURE__ */ jsx("div", { className: "h-5 w-40 animate-pulse rounded-md bg-muted" }),
1969
- /* @__PURE__ */ jsx("div", { className: "flex-1" }),
1970
- /* @__PURE__ */ jsx("div", { className: "h-5 w-48 animate-pulse rounded-md bg-muted" })
1971
- ] }, i))
1972
- ] }) : message ? /* @__PURE__ */ jsx("p", { className: "px-3 py-6 text-sm text-muted-foreground", children: message }) : /* @__PURE__ */ jsx("div", { className: "divide-y divide-border", children }) });
1973
- }
1974
- function SharedProjectsSection({
1975
- title,
1976
- footnote,
1977
- children,
1978
- testId = "shared-projects",
1979
- labels
1980
- }) {
1981
- const l = useMemo(() => ({ ...DEFAULT_SHARED_PROJECTS_SECTION_LABELS, ...labels }), [labels]);
1982
- return /* @__PURE__ */ jsxs("section", { className: "space-y-2", "data-testid": testId, children: [
1983
- /* @__PURE__ */ jsx("h3", { className: "px-1 text-sm font-semibold text-foreground", children: title ?? l.title }),
1984
- /* @__PURE__ */ jsx("div", { className: "divide-y divide-border", children }),
1985
- footnote && /* @__PURE__ */ jsx("p", { className: "px-1 text-xs text-muted-foreground", children: footnote })
1986
- ] });
1987
- }
1988
- function SharedProjectRow({
1989
- testId,
1990
- identity,
1991
- allowance
1992
- }) {
1993
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 px-3 py-2.5", "data-testid": testId, children: [
1994
- identity,
1995
- /* @__PURE__ */ jsx("div", { className: "ml-auto w-44", children: allowance })
1996
- ] });
1997
- }
1998
- function ProjectDetailHeader({
1999
- icon,
2000
- name,
2001
- badge,
2002
- onRename,
2003
- action,
2004
- labels
2005
- }) {
2006
- const l = useMemo(() => ({ ...DEFAULT_PROJECT_DETAIL_HEADER_LABELS, ...labels }), [labels]);
2007
- return /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
2008
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [
2009
- /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 text-primary [&>svg]:h-8 [&>svg]:w-8", children: icon }),
2010
- /* @__PURE__ */ jsx("div", { className: "min-w-0", children: /* @__PURE__ */ jsxs("div", { className: "group flex items-center gap-2", children: [
2011
- /* @__PURE__ */ jsx("h2", { className: "truncate text-xl font-semibold text-foreground", title: name, children: name }),
2012
- /* @__PURE__ */ jsx(
2013
- Button,
2014
- {
2015
- variant: "ghost",
2016
- size: "icon",
2017
- className: "h-6 w-6 shrink-0 text-muted-foreground opacity-0 transition-opacity hover:text-primary focus-visible:opacity-100 [.group:hover_&]:opacity-100 [@media(hover:none)]:opacity-100",
2018
- title: l.renameProject,
2019
- "aria-label": l.renameProject,
2020
- "data-testid": "project-detail-rename",
2021
- onClick: onRename,
2022
- children: /* @__PURE__ */ jsx(Pencil, { className: "h-3.5 w-3.5" })
2023
- }
2024
- ),
2025
- badge
2026
- ] }) })
2027
- ] }),
2028
- action && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: action })
2029
- ] });
2030
- }
2031
- function ProjectDetailSection({
2032
- title,
2033
- className,
2034
- children
2035
- }) {
2036
- return /* @__PURE__ */ jsxs("section", { className: cn("rounded-lg border border-border bg-card shadow-xs p-5", className), children: [
2037
- /* @__PURE__ */ jsx("h3", { className: "mb-4 text-base font-semibold text-foreground", children: title }),
2038
- children
2039
- ] });
2040
- }
2041
- function RenameDialog({
2042
- open,
2043
- onClose,
2044
- title,
2045
- value,
2046
- onChange,
2047
- onSave,
2048
- isSaving,
2049
- testId,
2050
- dialogTestId,
2051
- fieldLabel,
2052
- placeholder,
2053
- canSave = true,
2054
- labels
2055
- }) {
2056
- const renameInputId = useId();
2057
- const l = useMemo(() => ({ ...DEFAULT_RENAME_DIALOG_LABELS, ...labels }), [labels]);
2058
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (next) => !next && onClose(), children: /* @__PURE__ */ jsxs(DialogContent, { presentation: "sheet", "data-testid": dialogTestId, children: [
2059
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
2060
- /* @__PURE__ */ jsx(DialogTitle, { children: title ?? l.title }),
2061
- /* @__PURE__ */ jsx(DialogDescription, { className: "sr-only", children: l.description })
2062
- ] }),
2063
- fieldLabel && /* @__PURE__ */ jsx("label", { htmlFor: renameInputId, className: "block text-sm font-medium", children: fieldLabel }),
2064
- /* @__PURE__ */ jsx(
2065
- Input,
2066
- {
2067
- id: renameInputId,
2068
- value,
2069
- onChange: (e) => onChange(e.target.value),
2070
- placeholder,
2071
- autoFocus: true,
2072
- onKeyDown: (e) => {
2073
- if (e.key === "Enter") onSave();
2074
- if (e.key === "Escape") onClose();
2075
- },
2076
- "data-testid": testId
2077
- }
2078
- ),
2079
- /* @__PURE__ */ jsxs(DialogFooter, { children: [
2080
- /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onClose, children: l.cancel }),
2081
- /* @__PURE__ */ jsx(Button, { onClick: onSave, disabled: isSaving || !canSave, children: l.save })
2082
- ] })
2083
- ] }) });
2084
- }
2085
- var DEFAULT_SUPPORT_PANEL_LABELS = {
2086
- support: "Support",
2087
- userGuide: "User Guide"
2088
- };
2089
- function SupportPanel({
2090
- activeTab,
2091
- onTabChange,
2092
- buildInfo = [],
2093
- modeSwitcher,
2094
- feedback,
2095
- isFeedbackLoading,
2096
- loadingIndicator,
2097
- guide,
2098
- testId = "feedback-modal",
2099
- labels
2100
- }) {
2101
- const l = useMemo(() => ({ ...DEFAULT_SUPPORT_PANEL_LABELS, ...labels }), [labels]);
2102
- const tabs = useMemo(
2103
- () => [
2104
- {
2105
- id: "feedback",
2106
- label: l.support,
2107
- icon: /* @__PURE__ */ jsx(MessageCircleQuestionMark, { className: "h-4 w-4" }),
2108
- tooltip: l.support
2109
- },
2110
- {
2111
- id: "guide",
2112
- label: l.userGuide,
2113
- icon: /* @__PURE__ */ jsx(BookOpen, { className: "h-4 w-4" }),
2114
- tooltip: l.userGuide
2115
- }
2116
- ],
2117
- [l]
2118
- );
2119
- return /* @__PURE__ */ jsxs(
2120
- "div",
2121
- {
2122
- className: "@container flex h-full flex-col bg-background",
2123
- "data-testid": testId,
2124
- "data-slot": "support-panel",
2125
- children: [
2126
- /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(
2127
- TabHeader,
2128
- {
2129
- tabs,
2130
- activeTabId: activeTab,
2131
- onTabChange: (id) => onTabChange(id),
2132
- rightContent: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2133
- buildInfo.length > 0 && /* @__PURE__ */ jsx("div", { className: "hidden text-right font-mono text-xs leading-tight text-muted-foreground/70 @2xl:block", children: buildInfo.map((line) => /* @__PURE__ */ jsx("div", { children: line }, line)) }),
2134
- modeSwitcher
2135
- ] }),
2136
- compactBreakpoint: 360
2137
- }
2138
- ) }),
2139
- activeTab === "feedback" && /* @__PURE__ */ jsxs("div", { className: "relative min-h-0 flex-1", children: [
2140
- isFeedbackLoading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-10 flex items-center justify-center bg-background", children: loadingIndicator }),
2141
- feedback
2142
- ] }),
2143
- activeTab === "guide" && /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-auto p-4", children: guide })
2144
- ]
2145
- }
2146
- );
2147
- }
2148
- function SupportUnavailable({ title, message }) {
2149
- return /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center p-8 text-center", children: /* @__PURE__ */ jsxs("div", { children: [
2150
- /* @__PURE__ */ jsx("h3", { className: "mb-2 text-lg font-semibold", children: title }),
2151
- /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: message })
2152
- ] }) });
2153
- }
2154
- var DEFAULT_WELCOME_DIALOG_LABELS = {
2155
- welcome: (firstName) => `Welcome${firstName ? `, ${firstName}` : ""}!`,
2156
- description: "Your plan, usage and rewards at a glance.",
2157
- currentPlan: "Current plan",
2158
- seeUpgradeOptions: "See upgrade options",
2159
- referralBefore: "Get ",
2160
- referralAfter: " for every invited contact who goes Pro.",
2161
- inviteContact: "Invite contact",
2162
- currentRewards: "Current rewards: ",
2163
- continue: "Continue"
2164
- };
2165
- var DEFAULT_INVITE_CONTACTS_DIALOG_LABELS = {
2166
- title: "Invite contacts",
2167
- description: "Invite contacts by email and earn the referral reward."
2168
- };
2169
- function WelcomeDialog({
2170
- open,
2171
- onClose,
2172
- firstName,
2173
- hasReward,
2174
- projectPicker,
2175
- planBadge,
2176
- quotas,
2177
- onSeePlans,
2178
- referralOfferLabel,
2179
- onInvite,
2180
- rewardBalanceLabel,
2181
- labels,
2182
- testId,
2183
- testIds
2184
- }) {
2185
- const l = useMemo(() => ({ ...DEFAULT_WELCOME_DIALOG_LABELS, ...labels }), [labels]);
2186
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (next) => !next && onClose(), children: /* @__PURE__ */ jsxs(
2187
- DialogContent,
2188
- {
2189
- className: "sm:max-w-md",
2190
- "data-testid": testIdOr(testId, { testId: "welcome-modal" }),
2191
- hideCloseButton: true,
2192
- children: [
2193
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
2194
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-wrap items-start justify-between gap-2", children: [
2195
- /* @__PURE__ */ jsxs(DialogTitle, { className: "flex items-center gap-2", children: [
2196
- hasReward ? /* @__PURE__ */ jsx(Gift, { className: "h-5 w-5 text-green-text" }) : /* @__PURE__ */ jsx(Sparkles, { className: "h-5 w-5 text-yellow-text" }),
2197
- l.welcome(firstName)
2198
- ] }),
2199
- projectPicker
2200
- ] }),
2201
- /* @__PURE__ */ jsx(DialogDescription, { className: "sr-only", children: l.description })
2202
- ] }),
2203
- /* @__PURE__ */ jsxs(
2204
- ModalSection,
2205
- {
2206
- className: "space-y-4",
2207
- "data-testid": testIdOr(testIds?.copy, { testId: "welcome-modal-copy" }),
2208
- children: [
2209
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
2210
- /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: l.currentPlan }),
2211
- planBadge
2212
- ] }),
2213
- /* @__PURE__ */ jsx("div", { className: "space-y-3", children: quotas.map((quota) => /* @__PURE__ */ jsx(
2214
- UsageMeter,
2215
- {
2216
- icon: quota.icon,
2217
- label: quota.label,
2218
- value: quota.value,
2219
- pct: quota.pct,
2220
- hint: quota.hint,
2221
- testId: prefixedTestId(testIds?.quota, quota.key, {
2222
- testId: `welcome-modal-quota-${quota.key}`
2223
- })
2224
- },
2225
- quota.key
2226
- )) }),
2227
- /* @__PURE__ */ jsx(
2228
- Button,
2229
- {
2230
- variant: "outline",
2231
- className: "w-full",
2232
- onClick: onSeePlans,
2233
- "data-testid": testIdOr(testIds?.showPlans, { testId: "welcome-modal-show-plans" }),
2234
- children: l.seeUpgradeOptions
2235
- }
2236
- )
2237
- ]
2238
- }
2239
- ),
2240
- /* @__PURE__ */ jsxs(ModalSection, { className: "space-y-2.5", children: [
2241
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
2242
- /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
2243
- l.referralBefore,
2244
- /* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: referralOfferLabel }),
2245
- l.referralAfter
2246
- ] }),
2247
- /* @__PURE__ */ jsxs(
2248
- Button,
2249
- {
2250
- variant: "outline",
2251
- size: "sm",
2252
- className: "shrink-0 gap-1.5",
2253
- onClick: onInvite,
2254
- "data-testid": testIdOr(testIds?.earn, { testId: "welcome-modal-earn" }),
2255
- children: [
2256
- /* @__PURE__ */ jsx(UserPlus, { className: "h-3.5 w-3.5" }),
2257
- l.inviteContact
2258
- ]
2259
- }
2260
- )
2261
- ] }),
2262
- rewardBalanceLabel && // A statement, not a door: the balance applies itself to the next invoice.
2263
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 rounded-md bg-green-bg px-3 py-2 text-sm text-green-text", children: [
2264
- /* @__PURE__ */ jsx(Gift, { className: "h-4 w-4 shrink-0" }),
2265
- /* @__PURE__ */ jsxs("span", { className: "truncate", children: [
2266
- l.currentRewards,
2267
- /* @__PURE__ */ jsx("span", { className: "font-semibold", children: rewardBalanceLabel })
2268
- ] })
2269
- ] })
2270
- ] }),
2271
- /* @__PURE__ */ jsx(DialogFooter, { children: /* @__PURE__ */ jsx(
2272
- Button,
2273
- {
2274
- className: "w-full",
2275
- onClick: onClose,
2276
- "data-testid": testIdOr(testIds?.dismiss, { testId: "welcome-modal-dismiss" }),
2277
- children: l.continue
2278
- }
2279
- ) })
2280
- ]
2281
- }
2282
- ) });
2283
- }
2284
- function InviteContactsDialog({
2285
- open,
2286
- onClose,
2287
- children,
2288
- testId = "welcome-modal-invite",
2289
- labels
2290
- }) {
2291
- const l = useMemo(() => ({ ...DEFAULT_INVITE_CONTACTS_DIALOG_LABELS, ...labels }), [labels]);
2292
- return /* @__PURE__ */ jsx(
2293
- ModalScaffold,
2294
- {
2295
- open,
2296
- onOpenChange: (next) => !next && onClose(),
2297
- title: l.title,
2298
- description: l.description,
2299
- icon: /* @__PURE__ */ jsx(UserPlus, { className: "h-5 w-5 text-primary" }),
2300
- size: "lg",
2301
- className: "sm:max-w-lg",
2302
- testId,
2303
- children: /* @__PURE__ */ jsx(ModalSection, { children })
2304
- }
2305
- );
2306
- }
2307
- var SERIES_STYLES = [
2308
- { bg: "bg-blue-500", stroke: "stroke-blue-500" },
2309
- { bg: "bg-purple-500", stroke: "stroke-purple-500" },
2310
- { bg: "bg-emerald-500", stroke: "stroke-emerald-500" },
2311
- { bg: "bg-amber-500", stroke: "stroke-amber-500" },
2312
- { bg: "bg-rose-500", stroke: "stroke-rose-500" },
2313
- { bg: "bg-cyan-500", stroke: "stroke-cyan-500" },
2314
- { bg: "bg-indigo-500", stroke: "stroke-indigo-500" },
2315
- { bg: "bg-orange-500", stroke: "stroke-orange-500" }
2316
- ];
2317
- var styleAt = (index) => SERIES_STYLES[index % SERIES_STYLES.length];
2318
- var DEFAULT_USAGE_LINE_CHART_LABELS = {
2319
- noDailyData: "No daily data"
2320
- };
2321
- var DEFAULT_USAGE_PROPORTION_BAR_LABELS = {
2322
- tokens: (formatted) => `${formatted} tokens`,
2323
- gbp: (formatted) => formatted,
2324
- tokensShort: (formatted) => `${formatted} tok`
2325
- };
2326
- var DEFAULT_USAGE_PANEL_LABELS = {
2327
- empty: "No usage yet.",
2328
- error: "Failed to load usage data"
2329
- };
2330
- var CHART_W = 440;
2331
- var CHART_H = 120;
2332
- var PAD = { top: 8, right: 8, bottom: 20, left: 40 };
2333
- function UsageLineChart({
2334
- dates,
2335
- series,
2336
- formatDate,
2337
- formatValue: formatValue2,
2338
- emptyLabel,
2339
- labels
2340
- }) {
2341
- const l = useMemo(() => ({ ...DEFAULT_USAGE_LINE_CHART_LABELS, ...labels }), [labels]);
2342
- const [hoverIdx, setHoverIdx] = useState(null);
2343
- if (!dates.length || !series.length) {
2344
- return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: emptyLabel ?? l.noDailyData });
2345
- }
2346
- const maxVal = Math.max(...series.flatMap((s) => s.values), 1);
2347
- const plotW = CHART_W - PAD.left - PAD.right;
2348
- const plotH = CHART_H - PAD.top - PAD.bottom;
2349
- const xStep = dates.length > 1 ? plotW / (dates.length - 1) : plotW;
2350
- const yScale = (v) => PAD.top + plotH - v / maxVal * plotH;
2351
- const yTicks = [0, maxVal / 2, maxVal].map((v) => ({
2352
- v,
2353
- y: yScale(v),
2354
- label: v >= 1e3 ? `${Math.round(v / 1e3)}k` : String(Math.round(v * 100) / 100)
2355
- }));
2356
- const labelCount = Math.min(dates.length, 5);
2357
- const labelStep = Math.max(1, Math.floor((dates.length - 1) / (labelCount - 1)));
2358
- return /* @__PURE__ */ jsxs("div", { className: "relative", "data-slot": "usage-line-chart", children: [
2359
- /* @__PURE__ */ jsxs(
2360
- "svg",
2361
- {
2362
- viewBox: `0 0 ${CHART_W} ${CHART_H}`,
2363
- className: "h-auto w-full",
2364
- preserveAspectRatio: "xMidYMid meet",
2365
- onMouseLeave: () => setHoverIdx(null),
2366
- children: [
2367
- yTicks.map((t) => /* @__PURE__ */ jsxs("g", { children: [
2368
- /* @__PURE__ */ jsx(
2369
- "line",
2370
- {
2371
- x1: PAD.left,
2372
- x2: CHART_W - PAD.right,
2373
- y1: t.y,
2374
- y2: t.y,
2375
- className: "stroke-border",
2376
- strokeDasharray: "2,2"
2377
- }
2378
- ),
2379
- /* @__PURE__ */ jsx(
2380
- "text",
2381
- {
2382
- x: PAD.left - 4,
2383
- y: t.y + 3,
2384
- textAnchor: "end",
2385
- className: "fill-muted-foreground text-xs",
2386
- children: t.label
2387
- }
2388
- )
2389
- ] }, t.v)),
2390
- series.map((s, si) => /* @__PURE__ */ jsx(
2391
- "polyline",
2392
- {
2393
- points: s.values.map((v, i) => `${PAD.left + i * xStep},${yScale(v)}`).join(" "),
2394
- fill: "none",
2395
- className: styleAt(si).stroke,
2396
- strokeWidth: 1.5,
2397
- strokeLinejoin: "round"
2398
- },
2399
- s.key
2400
- )),
2401
- dates.map((date, i) => /* @__PURE__ */ jsx(
2402
- "rect",
2403
- {
2404
- x: PAD.left + i * xStep - xStep / 2,
2405
- y: PAD.top,
2406
- width: xStep,
2407
- height: plotH,
2408
- fill: "transparent",
2409
- onMouseEnter: () => setHoverIdx(i),
2410
- onClick: () => setHoverIdx(i)
2411
- },
2412
- date
2413
- )),
2414
- hoverIdx !== null && /* @__PURE__ */ jsx(
2415
- "line",
2416
- {
2417
- x1: PAD.left + hoverIdx * xStep,
2418
- x2: PAD.left + hoverIdx * xStep,
2419
- y1: PAD.top,
2420
- y2: PAD.top + plotH,
2421
- className: "stroke-muted-foreground",
2422
- strokeDasharray: "3,3"
2423
- }
2424
- ),
2425
- dates.map((d, i) => {
2426
- const isLast = i === dates.length - 1;
2427
- if (!isLast && (i % labelStep !== 0 || dates.length - 1 - i < labelStep / 2)) return null;
2428
- return /* @__PURE__ */ jsx(
2429
- "text",
2430
- {
2431
- x: PAD.left + i * xStep,
2432
- y: CHART_H - 2,
2433
- textAnchor: i === 0 ? "start" : isLast ? "end" : "middle",
2434
- className: "fill-muted-foreground text-xs",
2435
- children: formatDate(d)
2436
- },
2437
- d
2438
- );
2439
- })
2440
- ]
2441
- }
2442
- ),
2443
- hoverIdx !== null && /* @__PURE__ */ jsxs("div", { className: "pointer-events-none absolute right-0 top-0 rounded-md border bg-card/90 px-2 py-1 text-sm shadow-lg", children: [
2444
- /* @__PURE__ */ jsx("div", { className: "font-medium", children: dates[hoverIdx] }),
2445
- series.map((s, si) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
2446
- /* @__PURE__ */ jsx("span", { className: cn("inline-block h-2 w-2 rounded-full", styleAt(si).bg) }),
2447
- /* @__PURE__ */ jsxs("span", { children: [
2448
- s.key,
2449
- ":"
2450
- ] }),
2451
- /* @__PURE__ */ jsx("span", { className: "font-medium tabular-nums", children: formatValue2(s.values[hoverIdx]) })
2452
- ] }, s.key))
2453
- ] })
2454
- ] });
2455
- }
2456
- function UsageLegend({ series }) {
2457
- if (series.length <= 1 && series[0]?.key === "Total") return null;
2458
- return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-x-3 gap-y-1", children: series.map((s, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-sm", children: [
2459
- /* @__PURE__ */ jsx("span", { className: cn("inline-block h-2.5 w-2.5 rounded-full", styleAt(i).bg) }),
2460
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: s.key })
2461
- ] }, s.key)) });
2462
- }
2463
- function UsageProportionBar({
2464
- items,
2465
- metric,
2466
- labels
2467
- }) {
2468
- const l = useMemo(() => ({ ...DEFAULT_USAGE_PROPORTION_BAR_LABELS, ...labels }), [labels]);
2469
- const total = items.reduce((sum, item) => sum + item.amount, 0);
2470
- if (!items.length || total === 0) return null;
2471
- return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2472
- /* @__PURE__ */ jsx("div", { className: "flex h-3 w-full overflow-hidden rounded-full bg-muted", children: items.map((item, i) => {
2473
- const pct = item.amount / total * 100;
2474
- if (pct < 0.5) return null;
2475
- return /* @__PURE__ */ jsx(
2476
- "div",
2477
- {
2478
- className: cn("h-full first:rounded-l-full last:rounded-r-full", styleAt(i).bg),
2479
- style: { width: `${pct}%` },
2480
- title: `${item.label}: ${metric === "tokens" ? l.tokens(item.tokens) : l.gbp(item.gbp)}`
2481
- },
2482
- item.label
2483
- );
2484
- }) }),
2485
- /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: items.map((item, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm", children: [
2486
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1.5", children: [
2487
- /* @__PURE__ */ jsx(
2488
- "span",
2489
- {
2490
- className: cn("inline-block h-2.5 w-2.5 flex-shrink-0 rounded-full", styleAt(i).bg)
2491
- }
2492
- ),
2493
- /* @__PURE__ */ jsx("span", { className: "truncate", children: item.label })
2494
- ] }),
2495
- /* @__PURE__ */ jsxs("div", { className: "ml-2 flex flex-shrink-0 items-center gap-2 tabular-nums", children: [
2496
- /* @__PURE__ */ jsx(
2497
- "span",
2498
- {
2499
- className: cn(
2500
- "flex items-center gap-0.5",
2501
- metric === "gbp" ? "font-medium text-muted-foreground" : "text-muted-foreground/60"
2502
- ),
2503
- children: item.gbp
2504
- }
2505
- ),
2506
- /* @__PURE__ */ jsx(
2507
- "span",
2508
- {
2509
- className: metric === "tokens" ? "font-medium text-muted-foreground" : "text-muted-foreground/60",
2510
- children: l.tokensShort(item.tokens)
2511
- }
2512
- ),
2513
- /* @__PURE__ */ jsxs("span", { className: "w-8 text-right text-muted-foreground/40", children: [
2514
- (item.amount / total * 100).toFixed(0),
2515
- "%"
2516
- ] })
2517
- ] })
2518
- ] }, item.label)) })
2519
- ] });
2520
- }
2521
- function UsageSummary({ stats }) {
2522
- return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2 text-center", children: stats.map((stat) => /* @__PURE__ */ jsxs("div", { className: "rounded-lg bg-muted p-3", children: [
2523
- /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center gap-1 text-base font-semibold tabular-nums", children: stat.value }),
2524
- /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: stat.label })
2525
- ] }, stat.key)) });
2526
- }
2527
- function UsageToggle({
2528
- value,
2529
- onChange,
2530
- options,
2531
- ariaLabel,
2532
- size = "compact"
2533
- }) {
2534
- return /* @__PURE__ */ jsx(
2535
- RadioGroupPrimitive.Root,
2536
- {
2537
- "aria-label": ariaLabel,
2538
- value,
2539
- onValueChange: (next) => onChange(next),
2540
- orientation: "horizontal",
2541
- className: "flex gap-1 rounded-md border border-border bg-background p-0.5 print:hidden",
2542
- children: options.map((option) => /* @__PURE__ */ jsx(
2543
- RadioGroupPrimitive.Item,
2544
- {
2545
- value: option.value,
2546
- className: cn(
2547
- "rounded-md px-3 py-1 text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-coarse:py-2.5",
2548
- size === "fill" && "flex-1 px-2 py-1.5",
2549
- option.value === value ? "bg-accent font-medium text-accent-foreground" : "text-muted-foreground hover:text-foreground"
2550
- ),
2551
- children: option.label
2552
- },
2553
- option.value
2554
- ))
2555
- }
2556
- );
2557
- }
2558
- function UsagePanel({
2559
- state,
2560
- period,
2561
- actions,
2562
- dateRange,
2563
- emptyMessage,
2564
- errorMessage,
2565
- labels,
2566
- children
2567
- }) {
2568
- const l = useMemo(() => ({ ...DEFAULT_USAGE_PANEL_LABELS, ...labels }), [labels]);
2569
- return /* @__PURE__ */ jsxs("div", { className: "space-y-4", "data-slot": "usage-panel", children: [
2570
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
2571
- period,
2572
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: actions })
2573
- ] }),
2574
- dateRange && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: dateRange }),
2575
- state === "loading" && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx(Loader2, { className: "h-5 w-5 animate-spin text-muted-foreground" }) }),
2576
- state === "error" && /* @__PURE__ */ jsx("p", { className: "py-4 text-sm text-destructive", children: errorMessage ?? l.error }),
2577
- state === "empty" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center gap-2 py-10 text-center", children: [
2578
- /* @__PURE__ */ jsx(ChartNoAxesColumn, { className: "h-8 w-8 text-muted-foreground/40" }),
2579
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: emptyMessage ?? l.empty })
2580
- ] }),
2581
- state === "ready" && /* @__PURE__ */ jsx("div", { className: "space-y-4", children })
2582
- ] });
2583
- }
2584
- var DEFAULT_INVOICES_DIALOG_LABELS = {
2585
- title: (projectName) => `Receipts \xB7 ${projectName}`,
2586
- description: (projectName) => `Receipts for ${projectName}`,
2587
- noReceipts: "No receipts yet.",
2588
- paid: "Paid"
2589
- };
2590
- var DEFAULT_DAILY_CAP_FIELD_LABELS = {
2591
- noLimit: "No limit",
2592
- ariaLabel: "Daily AI usage limit per collaborator (\xA3)",
2593
- unit: "\xA3 / user / day",
2594
- save: "Save"
2595
- };
2596
- var DEFAULT_MEMBER_PROJECT_ROW_LABELS = {
2597
- noDailyLimit: "No daily limit"
2598
- };
2599
- var DEFAULT_INLINE_NAME_EDITOR_LABELS = {
2600
- saveName: "Save name",
2601
- cancelRename: "Cancel rename"
2602
- };
2603
- var DEFAULT_PROJECT_CARD_VIEW_LABELS = {
2604
- collapseProject: "Collapse project",
2605
- expandProject: "Expand project"
2606
- };
2607
- function InvoicesDialog({
2608
- open,
2609
- onOpenChange,
2610
- projectName,
2611
- isLoading,
2612
- invoices,
2613
- labels
2614
- }) {
2615
- const l = useMemo(() => ({ ...DEFAULT_INVOICES_DIALOG_LABELS, ...labels }), [labels]);
2616
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { presentation: "sheet", className: "max-w-lg", children: [
2617
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
2618
- /* @__PURE__ */ jsx(DialogTitle, { children: l.title(projectName) }),
2619
- /* @__PURE__ */ jsx(DialogDescription, { className: "sr-only", children: l.description(projectName) })
2620
- ] }),
2621
- /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
2622
- isLoading && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx(Loader2, { className: "h-5 w-5 animate-spin text-muted-foreground" }) }),
2623
- !isLoading && invoices.length === 0 && /* @__PURE__ */ jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: l.noReceipts }),
2624
- !isLoading && invoices.map((invoice) => /* @__PURE__ */ jsxs(
2625
- "div",
2626
- {
2627
- className: "flex items-center justify-between border-b py-2 text-sm last:border-0",
2628
- children: [
2629
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-0.5", children: [
2630
- /* @__PURE__ */ jsx("span", { className: "tabular-nums text-muted-foreground", children: invoice.date }),
2631
- invoice.kind && /* @__PURE__ */ jsx("span", { className: "text-foreground", children: invoice.kind }),
2632
- /* @__PURE__ */ jsx("span", { className: "font-medium tabular-nums", children: invoice.amount }),
2633
- invoice.amountNote && /* @__PURE__ */ jsx("span", { className: "text-xs tabular-nums text-muted-foreground", children: invoice.amountNote }),
2634
- /* @__PURE__ */ jsx(
2635
- Badge,
2636
- {
2637
- variant: "outline",
2638
- className: cn(
2639
- "h-4 px-1.5 py-0 text-sm",
2640
- invoice.isPaid && "border-green-border text-green-text"
2641
- ),
2642
- children: invoice.isPaid ? l.paid : invoice.status
2643
- }
2644
- )
2645
- ] }),
2646
- invoice.href && /* @__PURE__ */ jsxs(
2647
- "a",
2648
- {
2649
- href: invoice.href,
2650
- target: "_blank",
2651
- rel: "noopener noreferrer",
2652
- className: "flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground",
2653
- children: [
2654
- invoice.linkLabel,
2655
- " ",
2656
- /* @__PURE__ */ jsx(FileText, { className: "h-3 w-3" })
2657
- ]
2658
- }
2659
- )
2660
- ]
2661
- },
2662
- invoice.key
2663
- ))
2664
- ] })
2665
- ] }) });
2666
- }
2667
- function DailyCapField({
2668
- value,
2669
- onChange,
2670
- onSave,
2671
- canSave,
2672
- isSaving,
2673
- labels
2674
- }) {
2675
- const l = useMemo(() => ({ ...DEFAULT_DAILY_CAP_FIELD_LABELS, ...labels }), [labels]);
2676
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
2677
- /* @__PURE__ */ jsx(
2678
- Input,
2679
- {
2680
- type: "number",
2681
- min: 0,
2682
- step: 0.01,
2683
- value,
2684
- onChange: (e) => onChange(e.target.value),
2685
- onKeyDown: (e) => {
2686
- if (e.key === "Enter") onSave();
2687
- },
2688
- placeholder: l.noLimit,
2689
- className: "h-9 w-40",
2690
- "aria-label": l.ariaLabel
2691
- }
2692
- ),
2693
- /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: l.unit }),
2694
- /* @__PURE__ */ jsx(Button, { size: "sm", className: "h-9 px-4", disabled: !canSave || isSaving, onClick: onSave, children: l.save })
2695
- ] });
2696
- }
2697
- function MemberProjectRow({ icon, name, owner, allowance, labels }) {
2698
- const l = useMemo(() => ({ ...DEFAULT_MEMBER_PROJECT_ROW_LABELS, ...labels }), [labels]);
2699
- const barColor2 = allowance && allowance.pct >= 100 ? "bg-red-500" : allowance && allowance.pct >= 90 ? "bg-yellow-500" : "bg-primary";
2700
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 rounded-lg bg-muted p-3", children: [
2701
- /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 text-muted-foreground", children: icon }),
2702
- /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2703
- /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium", children: name }),
2704
- owner
2705
- ] }) }),
2706
- allowance === null ? /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 text-sm text-muted-foreground", children: l.noDailyLimit }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-shrink-0 flex-col items-end gap-0.5 text-sm tabular-nums", children: [
2707
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: allowance.text }),
2708
- /* @__PURE__ */ jsx("span", { className: "h-1 w-24 overflow-hidden rounded-full bg-background", children: /* @__PURE__ */ jsx(
2709
- "span",
2710
- {
2711
- className: cn("block h-full rounded-full", barColor2),
2712
- style: { width: `${allowance.pct}%` }
2713
- }
2714
- ) })
2715
- ] })
2716
- ] });
2717
- }
2718
- function InlineNameEditor({
2719
- value,
2720
- onChange,
2721
- onSave,
2722
- onCancel,
2723
- labels
2724
- }) {
2725
- const l = useMemo(() => ({ ...DEFAULT_INLINE_NAME_EDITOR_LABELS, ...labels }), [labels]);
2726
- return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-1", children: [
2727
- /* @__PURE__ */ jsx(
2728
- Input,
2729
- {
2730
- value,
2731
- onChange: (e) => onChange(e.target.value),
2732
- className: "h-7 text-sm",
2733
- autoFocus: true,
2734
- onKeyDown: (e) => {
2735
- if (e.key === "Enter") onSave();
2736
- if (e.key === "Escape") onCancel();
2737
- }
2738
- }
2739
- ),
2740
- /* @__PURE__ */ jsx(
2741
- Button,
2742
- {
2743
- variant: "ghost",
2744
- size: "icon",
2745
- className: "h-6 w-6",
2746
- "aria-label": l.saveName,
2747
- onClick: onSave,
2748
- children: /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" })
2749
- }
2750
- ),
2751
- /* @__PURE__ */ jsx(
2752
- Button,
2753
- {
2754
- variant: "ghost",
2755
- size: "icon",
2756
- className: "h-6 w-6",
2757
- "aria-label": l.cancelRename,
2758
- onClick: onCancel,
2759
- children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
2760
- }
2761
- )
2762
- ] });
2763
- }
2764
- function ProjectCardView({
2765
- expanded,
2766
- onToggleExpanded,
2767
- rowRef,
2768
- icon,
2769
- identity,
2770
- actions,
2771
- badge,
2772
- buttons,
2773
- summary,
2774
- children,
2775
- overlays,
2776
- labels
2777
- }) {
2778
- const l = useMemo(() => ({ ...DEFAULT_PROJECT_CARD_VIEW_LABELS, ...labels }), [labels]);
2779
- return /* @__PURE__ */ jsxs(
2780
- "div",
2781
- {
2782
- "data-slot": "project-card",
2783
- className: cn("rounded-lg bg-muted transition-colors", expanded && "ring-1 ring-primary/40"),
2784
- children: [
2785
- /* @__PURE__ */ jsxs(
2786
- "div",
2787
- {
2788
- ref: rowRef,
2789
- className: cn(
2790
- "flex items-center gap-2 p-3 transition-colors",
2791
- expanded ? "bg-blue-bg/60" : "hover:bg-muted/50"
2792
- ),
2793
- children: [
2794
- icon,
2795
- identity,
2796
- actions,
2797
- badge,
2798
- buttons,
2799
- /* @__PURE__ */ jsx("div", { className: "flex-1" }),
2800
- summary,
2801
- /* @__PURE__ */ jsx(
2802
- "button",
2803
- {
2804
- type: "button",
2805
- onClick: onToggleExpanded,
2806
- "aria-expanded": expanded,
2807
- "aria-label": expanded ? l.collapseProject : l.expandProject,
2808
- className: "flex-shrink-0 rounded p-1 hover:bg-muted pointer-coarse:p-3",
2809
- children: expanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4 text-muted-foreground" })
2810
- }
2811
- )
2812
- ]
2813
- }
2814
- ),
2815
- expanded && /* @__PURE__ */ jsx("div", { className: "space-y-4 border-t px-4 py-3", children }),
2816
- overlays
2817
- ]
2818
- }
2819
- );
2820
- }
2821
- var DEFAULT_EMAIL_WORKSPACE_DIALOG_LABELS = {
2822
- title: "Email this workspace",
2823
- description: "Send documents and questions here from your registered email address \u2014 your Assistant reads the email and replies.",
2824
- compose: "Compose email",
2825
- paHintBefore: "Don\u2019t need this specific workspace? Send general questions to ",
2826
- paHintAfter: " instead.",
2827
- checking: "Checking your Assistant\u2019s access\u2026",
2828
- noAssistant: "You don\u2019t have a default Assistant yet \u2014 one is created automatically the first time you email a workspace.",
2829
- authorizedExpires: (ttl) => `Authorized \u2014 expires in ${ttl}`,
2830
- end: "End",
2831
- authorizePrompt: (ttl) => `Authorize your Assistant to act in this workspace${ttl ? ` for ${ttl}` : ""}, so it\u2019s ready as soon as it wakes.`,
2832
- authorize: "Authorize",
2833
- authorizing: "Authorizing\u2026",
2834
- advanced: "Advanced",
2835
- alreadyGranted: "Your Assistant already has standing access to this workspace.",
2836
- standingWarning: "Standing access never expires and doesn\u2019t need re-authorizing \u2014 only use it if you trust the Assistant\u2019s environment long-term.",
2837
- grant: "Grant standing access",
2838
- granting: "Granting\u2026"
2839
- };
2840
- function EmailWorkspaceDialogView({
2841
- open,
2842
- onOpenChange,
2843
- email,
2844
- mailtoHref,
2845
- paDomain,
2846
- copyButton,
2847
- access,
2848
- standing,
2849
- labels
2850
- }) {
2851
- const l = useMemo(() => ({ ...DEFAULT_EMAIL_WORKSPACE_DIALOG_LABELS, ...labels }), [labels]);
2852
- const [advancedOpen, setAdvancedOpen] = useState(false);
2853
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", "data-testid": "email-workspace-dialog", children: [
2854
- /* @__PURE__ */ jsxs(DialogHeader, { className: "-m-6 mb-4 bg-muted p-6 pb-4", children: [
2855
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
2856
- /* @__PURE__ */ jsx(Mail, { className: "size-6" }),
2857
- /* @__PURE__ */ jsx(DialogTitle, { children: l.title })
2858
- ] }),
2859
- /* @__PURE__ */ jsx(DialogDescription, { children: l.description })
2860
- ] }),
2861
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-border bg-muted/30 px-3 py-2", children: [
2862
- /* @__PURE__ */ jsx("code", { className: "flex-1 truncate font-mono text-sm", "data-testid": "email-workspace-address", children: email }),
2863
- copyButton
2864
- ] }),
2865
- /* @__PURE__ */ jsx(Button, { asChild: true, "data-testid": "email-workspace-compose-button", children: /* @__PURE__ */ jsxs("a", { href: mailtoHref, children: [
2866
- /* @__PURE__ */ jsx(Send, { className: "mr-1.5 size-4" }),
2867
- l.compose
2868
- ] }) }),
2869
- /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", "data-testid": "email-workspace-pa-hint", children: [
2870
- l.paHintBefore,
2871
- /* @__PURE__ */ jsxs("code", { className: "font-mono", children: [
2872
- "pa@",
2873
- paDomain
2874
- ] }),
2875
- l.paHintAfter
2876
- ] }),
2877
- access && /* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded-md border border-border bg-muted/30 p-3", children: [
2878
- access.kind === "loading" ? /* @__PURE__ */ jsx(
2879
- "p",
2880
- {
2881
- className: "text-xs text-muted-foreground",
2882
- "data-testid": "email-workspace-authorize-loading",
2883
- children: l.checking
2884
- }
2885
- ) : access.kind === "none" ? /* @__PURE__ */ jsx(
2886
- "p",
2887
- {
2888
- className: "text-xs text-muted-foreground",
2889
- "data-testid": "email-workspace-no-assistant",
2890
- children: l.noAssistant
2891
- }
2892
- ) : access.kind === "session" ? /* @__PURE__ */ jsxs(
2893
- "div",
2894
- {
2895
- className: "flex items-center justify-between gap-3",
2896
- "data-testid": "email-workspace-active-session",
2897
- children: [
2898
- /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
2899
- /* @__PURE__ */ jsx(Clock, { className: "size-3.5" }),
2900
- l.authorizedExpires(access.expiresIn)
2901
- ] }),
2902
- /* @__PURE__ */ jsxs(
2903
- Button,
2904
- {
2905
- variant: "ghost",
2906
- size: "sm",
2907
- onClick: access.onEnd,
2908
- disabled: access.isEnding,
2909
- className: "h-7 shrink-0 gap-1 px-2 text-xs text-destructive hover:bg-destructive/10 hover:text-destructive pointer-coarse:h-11",
2910
- "data-testid": "email-workspace-end-session-button",
2911
- children: [
2912
- /* @__PURE__ */ jsx(X, { className: "size-3" }),
2913
- l.end
2914
- ]
2915
- }
2916
- )
2917
- ]
2918
- }
2919
- ) : /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
2920
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: l.authorizePrompt(access.defaultTtl) }),
2921
- /* @__PURE__ */ jsxs(
2922
- Button,
2923
- {
2924
- variant: "outline",
2925
- size: "sm",
2926
- onClick: access.onAuthorize,
2927
- disabled: access.isAuthorizing,
2928
- className: "shrink-0 gap-1.5",
2929
- "data-testid": "email-workspace-authorize-button",
2930
- children: [
2931
- /* @__PURE__ */ jsx(Clock, { className: "size-3.5" }),
2932
- access.isAuthorizing ? l.authorizing : l.authorize
2933
- ]
2934
- }
2935
- )
2936
- ] }),
2937
- /* @__PURE__ */ jsx(
2938
- "button",
2939
- {
2940
- type: "button",
2941
- onClick: () => setAdvancedOpen((o) => !o),
2942
- "aria-expanded": advancedOpen,
2943
- className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-coarse:min-h-11",
2944
- "data-testid": "email-workspace-advanced-toggle",
2945
- children: l.advanced
2946
- }
2947
- ),
2948
- advancedOpen && standing && (standing.alreadyGranted ? /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
2949
- /* @__PURE__ */ jsx(Check, { className: "size-3.5 text-green-solid" }),
2950
- l.alreadyGranted
2951
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2952
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: l.standingWarning }),
2953
- /* @__PURE__ */ jsxs(
2954
- Button,
2955
- {
2956
- variant: "outline",
2957
- size: "sm",
2958
- onClick: standing.onGrant,
2959
- disabled: standing.isGranting,
2960
- className: "gap-1.5",
2961
- "data-testid": "email-workspace-grant-access-button",
2962
- children: [
2963
- /* @__PURE__ */ jsx(Infinity, { className: "size-3.5" }),
2964
- standing.isGranting ? l.granting : l.grant
2965
- ]
2966
- }
2967
- )
2968
- ] }))
2969
- ] })
2970
- ] }) });
2971
- }
2972
- function PlanNotice({
2973
- tone = "muted",
2974
- className,
2975
- testId,
2976
- children
2977
- }) {
2978
- return /* @__PURE__ */ jsx(
2979
- "p",
2980
- {
2981
- "data-slot": "plan-notice",
2982
- "data-testid": testId,
2983
- className: cn(
2984
- "text-sm",
2985
- tone === "error" ? "text-destructive" : "text-muted-foreground",
2986
- className
2987
- ),
2988
- children
2989
- }
2990
- );
2991
- }
2992
- function PlanStack({
2993
- gap = 3,
2994
- testId,
2995
- className,
2996
- children
2997
- }) {
2998
- const GAP = { 2: "space-y-2", 3: "space-y-3", 4: "space-y-4", 5: "space-y-5 pt-2" };
2999
- return /* @__PURE__ */ jsx("div", { "data-slot": "plan-stack", "data-testid": testId, className: cn(GAP[gap], className), children });
3000
- }
3001
- function PlanTransition({ from, to }) {
3002
- return /* @__PURE__ */ jsxs("div", { "data-slot": "plan-transition", className: "flex flex-wrap items-center gap-2 text-sm", children: [
3003
- /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-full bg-muted px-2 py-0.5 text-xs font-semibold text-muted-foreground", children: from }),
3004
- /* @__PURE__ */ jsx(ArrowRight, { className: "size-4 text-muted-foreground", "aria-hidden": true }),
3005
- /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-full bg-primary px-2 py-0.5 text-xs font-semibold text-white", children: to })
3006
- ] });
3007
- }
3008
- function PlanPickerHeader({
3009
- title,
3010
- info,
3011
- infoPlacement = "title",
3012
- toggle,
3013
- className
3014
- }) {
3015
- return /* @__PURE__ */ jsxs(
3016
- "div",
3017
- {
3018
- "data-slot": "plan-picker-header",
3019
- className: cn("flex flex-wrap items-center justify-between gap-x-3 gap-y-2", className),
3020
- children: [
3021
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3022
- /* @__PURE__ */ jsx("span", { className: "text-lg font-semibold", children: title }),
3023
- infoPlacement === "title" && info
3024
- ] }),
3025
- infoPlacement === "toggle" ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-4 gap-y-2", children: [
3026
- info,
3027
- toggle
3028
- ] }) : toggle
3029
- ]
3030
- }
3031
- );
3032
- }
3033
- function PlanPickerCard({ children }) {
3034
- return /* @__PURE__ */ jsx(
3035
- "div",
3036
- {
3037
- "data-slot": "plan-picker-card",
3038
- className: "space-y-4 rounded-lg border border-border bg-card shadow-xs p-4",
3039
- children
3040
- }
3041
- );
3042
- }
3043
- function PlanActionRow({ children }) {
3044
- return /* @__PURE__ */ jsx("div", { "data-slot": "plan-action-row", className: "flex flex-col-reverse gap-2 @xl:flex-row", children });
3045
- }
3046
- function PlanActionButton({
3047
- tone = "primary",
3048
- fill,
3049
- ...props
3050
- }) {
3051
- return /* @__PURE__ */ jsx(
3052
- Button,
3053
- {
3054
- "data-slot": "plan-action-button",
3055
- variant: tone === "muted" ? "ghost" : "default",
3056
- ...props,
3057
- className: cn(
3058
- "w-full",
3059
- fill && "sm:flex-1",
3060
- tone === "muted" && "text-muted-foreground",
3061
- props.className
3062
- )
3063
- }
3064
- );
3065
- }
3066
- var DEFAULT_SUBSCRIPTION_STATUS_LABELS = {
3067
- aiUsageResets: (date) => `AI usage resets ${date}`,
3068
- cancelsAtPeriodEnd: "\xB7 Cancels at period end",
3069
- cancelsAtEndOfBillingPeriod: "Cancels at end of billing period"
3070
- };
3071
- function SubscriptionStatusView({
3072
- resetDate,
3073
- isCanceling,
3074
- meters,
3075
- testIdPrefix,
3076
- labels
3077
- }) {
3078
- const l = { ...DEFAULT_SUBSCRIPTION_STATUS_LABELS, ...labels };
3079
- return /* @__PURE__ */ jsxs("div", { "data-slot": "subscription-status", className: "space-y-3", children: [
3080
- resetDate && /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
3081
- l.aiUsageResets(resetDate),
3082
- isCanceling && /* @__PURE__ */ jsx("span", { className: "ml-2 text-yellow-text", children: l.cancelsAtPeriodEnd })
3083
- ] }),
3084
- !resetDate && isCanceling && /* @__PURE__ */ jsx("p", { className: "text-sm text-yellow-text", children: l.cancelsAtEndOfBillingPeriod }),
3085
- /* @__PURE__ */ jsx("div", { className: "space-y-3", children: meters.map((m) => /* @__PURE__ */ jsx(
3086
- UsageMeter,
3087
- {
3088
- icon: m.icon,
3089
- label: m.label,
3090
- value: m.value,
3091
- pct: m.pct,
3092
- testId: prefixedTestId(testIdPrefix, m.key, { testId: `project-quota-${m.key}` })
3093
- },
3094
- m.key
3095
- )) })
3096
- ] });
3097
- }
3098
- function PlanDialogFrame({
3099
- open,
3100
- onOpenChange,
3101
- title,
3102
- description,
3103
- isDescriptionHidden,
3104
- projectName,
3105
- projectTestId,
3106
- children,
3107
- labels
3108
- }) {
3109
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-5xl", children: [
3110
- /* @__PURE__ */ jsxs(DialogHeader, { className: "flex-row items-start justify-between gap-3 space-y-0 pr-8", children: [
3111
- /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
3112
- /* @__PURE__ */ jsx(DialogTitle, { children: title }),
3113
- /* @__PURE__ */ jsx(DialogDescription, { className: isDescriptionHidden ? "sr-only" : void 0, children: description })
3114
- ] }),
3115
- projectName !== void 0 && /* @__PURE__ */ jsxs(
3116
- "span",
3117
- {
3118
- className: "shrink-0 text-sm text-muted-foreground",
3119
- "data-testid": testIdOr(projectTestId, { testId: "change-plan-project" }),
3120
- children: [
3121
- labels?.project ?? "Project:",
3122
- " ",
3123
- /* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: projectName })
3124
- ]
3125
- }
3126
- )
3127
- ] }),
3128
- children
3129
- ] }) });
3130
- }
3131
- var DEFAULT_CHECKOUT_DIALOG_LABELS = {
3132
- unavailableTitle: "Stripe Not Configured",
3133
- unavailableDescription: "Stripe checkout is not available. Please contact support.",
3134
- title: "Payment",
3135
- description: "Enter your card to start the plan",
3136
- loading: "Loading\u2026",
3137
- error: "Checkout didn't load. Try again."
3138
- };
3139
- function CheckoutDialogFrame({
3140
- open,
3141
- onOpenChange,
3142
- isUnavailable,
3143
- isLoading,
3144
- hasError,
3145
- children,
3146
- labels
3147
- }) {
3148
- const l = { ...DEFAULT_CHECKOUT_DIALOG_LABELS, ...labels };
3149
- if (isUnavailable) {
3150
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsxs(DialogHeader, { children: [
3151
- /* @__PURE__ */ jsx(DialogTitle, { children: l.unavailableTitle }),
3152
- /* @__PURE__ */ jsx(DialogDescription, { children: l.unavailableDescription })
3153
- ] }) }) });
3154
- }
3155
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
3156
- DialogContent,
3157
- {
3158
- presentation: "sheet",
3159
- className: "flex max-h-[calc(100dvh-2rem)] max-w-2xl flex-col gap-0 overflow-hidden p-0",
3160
- onInteractOutside: (e) => e.preventDefault(),
3161
- onEscapeKeyDown: (e) => e.preventDefault(),
3162
- children: [
3163
- /* @__PURE__ */ jsxs(DialogHeader, { className: "flex-shrink-0 px-6 pb-3 pt-6", children: [
3164
- /* @__PURE__ */ jsx(DialogTitle, { children: l.title }),
3165
- /* @__PURE__ */ jsx(DialogDescription, { className: "sr-only", children: l.description })
3166
- ] }),
3167
- /* @__PURE__ */ jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto px-6 pb-6", children: [
3168
- isLoading && /* @__PURE__ */ jsx("div", { className: "py-8 text-center text-muted-foreground", children: l.loading }),
3169
- hasError && /* @__PURE__ */ jsx("div", { className: "py-8 text-center text-destructive", children: l.error }),
3170
- children
3171
- ] })
3172
- ]
3173
- }
3174
- ) });
3175
- }
3176
- var DEFAULT_QUOTA_EXCEEDED_DIALOG_LABELS = {
3177
- title: "AI usage used up",
3178
- projectUsage: "Project usage",
3179
- progress: "AI usage this month",
3180
- project: "Project:",
3181
- close: "Not now",
3182
- upgrade: "Upgrade"
3183
- };
3184
- function QuotaExceededDialogView({
3185
- open,
3186
- onClose,
3187
- onUpgrade,
3188
- message,
3189
- usage,
3190
- actions,
3191
- extra,
3192
- labels
3193
- }) {
3194
- const l = { ...DEFAULT_QUOTA_EXCEEDED_DIALOG_LABELS, ...labels };
3195
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (o) => !o && onClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-md", "data-testid": "quota-exceeded-modal", children: [
3196
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
3197
- /* @__PURE__ */ jsx(DialogTitle, { children: l.title }),
3198
- /* @__PURE__ */ jsx(DialogDescription, { children: message })
3199
- ] }),
3200
- usage && /* @__PURE__ */ jsxs(ModalSection, { className: "space-y-2", children: [
3201
- /* @__PURE__ */ jsx(Progress, { value: usage.percent, "aria-label": l.progress }),
3202
- /* @__PURE__ */ jsxs(
3203
- "div",
3204
- {
3205
- className: "flex flex-wrap items-center justify-between gap-x-3 text-sm text-muted-foreground",
3206
- "data-testid": "quota-usage-readout",
3207
- children: [
3208
- /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: usage.readout }),
3209
- usage.projectId && /* @__PURE__ */ jsx("span", { className: "truncate", children: usage.projectId })
3210
- ]
3211
- }
3212
- )
3213
- ] }),
3214
- extra,
3215
- /* @__PURE__ */ jsxs(DialogFooter, { children: [
3216
- /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onClose, "data-testid": "quota-close-button", children: l.close }),
3217
- actions ?? /* @__PURE__ */ jsx(Button, { onClick: onUpgrade, "data-testid": "quota-upgrade-button", children: l.upgrade })
3218
- ] })
3219
- ] }) });
3220
- }
3221
- function WelcomeProjectPicker({
3222
- value,
3223
- options,
3224
- onChange,
3225
- testId = "welcome-modal-project-select"
3226
- }) {
3227
- return /* @__PURE__ */ jsxs(Select, { value, onValueChange: onChange, children: [
3228
- /* @__PURE__ */ jsx(
3229
- SelectTrigger,
3230
- {
3231
- className: "h-7 w-auto max-w-[9.5rem] shrink-0 gap-1 border-none bg-muted px-2 text-xs font-medium pointer-coarse:h-11",
3232
- "data-testid": testId,
3233
- children: /* @__PURE__ */ jsx(SelectValue, {})
3234
- }
3235
- ),
3236
- /* @__PURE__ */ jsx(SelectContent, { align: "end", children: options.map((o) => /* @__PURE__ */ jsx(SelectItem, { value: o.id, children: o.name }, o.id)) })
3237
- ] });
3238
- }
3239
- var DEFAULT_SUBSCRIPTION_SECTION_LABELS = {
3240
- loading: "Loading projects...",
3241
- empty: "No projects yet \u2014 create one above."
3242
- };
3243
- function SubscriptionSectionView({
3244
- intro,
3245
- isLoading,
3246
- isEmpty,
3247
- children,
3248
- labels
3249
- }) {
3250
- const l = { ...DEFAULT_SUBSCRIPTION_SECTION_LABELS, ...labels };
3251
- if (isLoading) {
3252
- return /* @__PURE__ */ jsx("div", { className: "py-4 text-sm text-muted-foreground", children: l.loading });
3253
- }
3254
- return /* @__PURE__ */ jsxs("div", { "data-slot": "subscription-section", className: "space-y-4", children: [
3255
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: intro }),
3256
- isEmpty ? /* @__PURE__ */ jsx("div", { className: "rounded-lg bg-muted py-8 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: l.empty }) }) : children
3257
- ] });
3258
- }
3259
- function ProjectGroup({ children }) {
3260
- return /* @__PURE__ */ jsx("div", { "data-slot": "project-group", className: "space-y-2", children });
3261
- }
3262
- function ProjectPlanPicker({ children }) {
3263
- return /* @__PURE__ */ jsx("div", { "data-slot": "project-plan-picker", className: "border-b pb-3", children });
3264
- }
3265
- function ProjectNameLabel({ children }) {
3266
- return /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium", children });
3267
- }
3268
- function ProjectRowActions({ children }) {
3269
- return /* @__PURE__ */ jsx("div", { "data-slot": "project-row-actions", className: "flex flex-shrink-0 items-center gap-0.5", children });
3270
- }
3271
- function RowIconAction({
3272
- label,
3273
- tooltip,
3274
- icon,
3275
- tone = "primary",
3276
- disabled,
3277
- onClick
3278
- }) {
3279
- return /* @__PURE__ */ jsxs(Tooltip, { children: [
3280
- /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
3281
- Button,
3282
- {
3283
- variant: "ghost",
3284
- size: "icon",
3285
- className: cn(
3286
- "size-6 text-muted-foreground/50 pointer-coarse:size-11",
3287
- tone === "destructive" ? "hover:text-destructive" : "hover:text-primary"
3288
- ),
3289
- "aria-label": label,
3290
- disabled,
3291
- onClick,
3292
- children: icon
3293
- }
3294
- ) }) }),
3295
- /* @__PURE__ */ jsx(TooltipContent, { children: tooltip ?? label })
3296
- ] });
3297
- }
3298
- function RowButton({
3299
- icon,
3300
- children,
3301
- onClick
3302
- }) {
3303
- return /* @__PURE__ */ jsxs(
3304
- Button,
3305
- {
3306
- variant: "outline",
3307
- size: "sm",
3308
- className: "h-7 gap-1 px-2.5 text-sm pointer-coarse:h-11",
3309
- onClick,
3310
- children: [
3311
- icon,
3312
- children
3313
- ]
3314
- }
3315
- );
3316
- }
3317
- function UsageActionButton({
3318
- label,
3319
- icon,
3320
- onClick
3321
- }) {
3322
- return /* @__PURE__ */ jsxs(Tooltip, { children: [
3323
- /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
3324
- Button,
3325
- {
3326
- variant: "ghost",
3327
- size: "icon",
3328
- className: "size-6 text-muted-foreground hover:text-foreground pointer-coarse:size-11",
3329
- "aria-label": label,
3330
- onClick,
3331
- children: icon
3332
- }
3333
- ) }),
3334
- /* @__PURE__ */ jsx(TooltipContent, { children: label })
3335
- ] });
3336
- }
3337
- function UsageQuotaHint({ children }) {
3338
- return /* @__PURE__ */ jsx("span", { className: "ml-1 tabular-nums", children });
3339
- }
3340
- function UsageSection({
3341
- title,
3342
- action,
3343
- children,
3344
- footer,
3345
- gap = "sm"
3346
- }) {
3347
- return /* @__PURE__ */ jsxs("div", { "data-slot": "usage-section", className: gap === "md" ? "space-y-3" : void 0, children: [
3348
- /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between", gap === "sm" && "mb-2"), children: [
3349
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-muted-foreground", children: title }),
3350
- action
3351
- ] }),
3352
- children,
3353
- footer && /* @__PURE__ */ jsx("div", { className: "mt-2", children: footer })
3354
- ] });
3355
- }
3356
- var DEFAULT_ERROR_DIALOG_LABELS = {
3357
- title: "Something Went Wrong",
3358
- srDescription: "Error dialog",
3359
- reportHint: "Please click below to report the error to our support team if you have not done so already.",
3360
- refreshHint: "Clicking Refresh Session will attempt to refresh your session, which may help resolve the error.",
3361
- hideDetails: "Hide Details",
3362
- viewDetails: "View Details",
3363
- toggleDetailsAria: "Toggle error details",
3364
- copyAria: "Copy error details",
3365
- copy: "Copy error",
3366
- copied: "Copied",
3367
- refreshSession: "Refresh Session",
3368
- logout: "Logout"
3369
- };
3370
- function ErrorDialogView({
3371
- details,
3372
- report,
3373
- onRefresh,
3374
- onLogout,
3375
- labels
3376
- }) {
3377
- const l = { ...DEFAULT_ERROR_DIALOG_LABELS, ...labels };
3378
- return /* @__PURE__ */ jsx(Dialog, { open: true, onOpenChange: () => {
3379
- }, children: /* @__PURE__ */ jsxs(
3380
- DialogContent,
3381
- {
3382
- presentation: "sheet",
3383
- className: "flex max-h-panel w-modal max-w-modal-vw flex-col overflow-hidden p-0",
3384
- "data-testid": "error-boundary-modal",
3385
- hideCloseButton: true,
3386
- children: [
3387
- /* @__PURE__ */ jsxs(ModalHeader, { children: [
3388
- /* @__PURE__ */ jsx(DialogTitle, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3389
- /* @__PURE__ */ jsx(AlertCircle, { className: "text-red-text", size: 24 }),
3390
- /* @__PURE__ */ jsx("span", { children: l.title })
3391
- ] }) }),
3392
- /* @__PURE__ */ jsx(DialogDescription, { className: "sr-only", children: l.srDescription })
3393
- ] }),
3394
- /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-auto px-6 pb-6", children: /* @__PURE__ */ jsxs(ModalSection, { "data-testid": "error-boundary", children: [
3395
- /* @__PURE__ */ jsx("p", { className: "mb-4 text-sm text-muted-foreground", children: l.reportHint }),
3396
- /* @__PURE__ */ jsx("p", { className: "mb-4 text-sm text-muted-foreground", children: l.refreshHint }),
3397
- details?.isShown && /* @__PURE__ */ jsx("div", { className: "mb-4 rounded border border-border bg-background p-3", children: /* @__PURE__ */ jsx("pre", { className: "max-h-96 overflow-auto whitespace-pre-wrap font-mono text-sm", children: details.text }) }),
3398
- /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-col gap-2", children: [
3399
- details && /* @__PURE__ */ jsxs("div", { className: "flex w-full gap-2", children: [
3400
- /* @__PURE__ */ jsxs(
3401
- Button,
3402
- {
3403
- variant: "outline",
3404
- className: "flex-1",
3405
- onClick: details.onToggle,
3406
- "aria-label": l.toggleDetailsAria,
3407
- "data-testid": "error-boundary-view-details",
3408
- children: [
3409
- /* @__PURE__ */ jsx(FileText, { className: "mr-2 size-4" }),
3410
- details.isShown ? l.hideDetails : l.viewDetails
3411
- ]
3412
- }
3413
- ),
3414
- /* @__PURE__ */ jsxs(
3415
- Button,
3416
- {
3417
- variant: "outline",
3418
- className: "flex-1",
3419
- onClick: details.onCopy,
3420
- "aria-label": l.copyAria,
3421
- "data-testid": "error-boundary-copy-details",
3422
- children: [
3423
- details.isCopied ? /* @__PURE__ */ jsx(Check, { className: "mr-2 size-4 text-green-text" }) : /* @__PURE__ */ jsx(Copy, { className: "mr-2 size-4" }),
3424
- details.isCopied ? l.copied : l.copy
3425
- ]
3426
- }
3427
- )
3428
- ] }),
3429
- report,
3430
- /* @__PURE__ */ jsxs(
3431
- Button,
3432
- {
3433
- variant: "outline",
3434
- className: "w-full",
3435
- onClick: onRefresh,
3436
- "aria-label": "Refresh session",
3437
- children: [
3438
- /* @__PURE__ */ jsx(RefreshCw, { className: "mr-2 size-4" }),
3439
- l.refreshSession
3440
- ]
3441
- }
3442
- ),
3443
- /* @__PURE__ */ jsxs(Button, { variant: "outline", className: "w-full", onClick: onLogout, "aria-label": "Logout", children: [
3444
- /* @__PURE__ */ jsx(LogOut, { className: "mr-2 size-4" }),
3445
- l.logout
3446
- ] })
3447
- ] })
3448
- ] }) })
3449
- ]
3450
- }
3451
- ) });
3452
- }
3453
- var SCHEME_OPTIONS = [
3454
- { value: "light", label: "Light", icon: /* @__PURE__ */ jsx(Sun, { className: "h-4 w-4" }) },
3455
- { value: "dark", label: "Dark", icon: /* @__PURE__ */ jsx(Moon, { className: "h-4 w-4" }) }
3456
- ].map((option) => ({ ...option, testId: `appearance-scheme-${option.value}` }));
3457
- var TEXT_SIZE_OPTIONS = [
3458
- { value: "default", label: "Default" },
3459
- { value: "large", label: "Large" },
3460
- { value: "larger", label: "Larger" }
3461
- ].map((option) => ({ ...option, testId: `appearance-text-size-${option.value}` }));
3462
- var DEFAULT_APPEARANCE_PAIRING_IDS = [
3463
- "office",
3464
- "inter",
3465
- "fraunces-inter",
3466
- "montserrat-nunito"
3467
- ];
3468
- function AppearanceSettings({
3469
- colorScheme,
3470
- onColorSchemeChange,
3471
- textSize,
3472
- onTextSizeChange,
3473
- pairingId,
3474
- onPairingChange,
3475
- pairingIds = DEFAULT_APPEARANCE_PAIRING_IDS,
3476
- onReset
3477
- }) {
3478
- const activePairing = getFontPairing(pairingId) ?? getFontPairing(pairingIds[0]);
3479
- return /* @__PURE__ */ jsxs(SettingsPanel, { testId: "settings-appearance", children: [
3480
- /* @__PURE__ */ jsx(
3481
- SettingsSection,
3482
- {
3483
- title: "Colour scheme",
3484
- action: onReset && /* @__PURE__ */ jsxs(
3485
- "button",
3486
- {
3487
- type: "button",
3488
- onClick: onReset,
3489
- className: "flex items-center gap-1.5 py-1 text-sm text-muted-foreground hover:text-foreground pointer-coarse:min-h-11",
3490
- "data-testid": "appearance-reset",
3491
- children: [
3492
- /* @__PURE__ */ jsx(RotateCcw, { className: "h-3 w-3" }),
3493
- "Reset"
3494
- ]
3495
- }
3496
- ),
3497
- children: /* @__PURE__ */ jsx(
3498
- SegmentedControl,
3499
- {
3500
- value: colorScheme,
3501
- onChange: onColorSchemeChange,
3502
- options: SCHEME_OPTIONS,
3503
- ariaLabel: "Colour scheme"
3504
- }
3505
- )
3506
- }
3507
- ),
3508
- /* @__PURE__ */ jsxs(SettingsSection, { title: "Text size", children: [
3509
- /* @__PURE__ */ jsx(
3510
- SegmentedControl,
3511
- {
3512
- value: textSize,
3513
- onChange: onTextSizeChange,
3514
- options: TEXT_SIZE_OPTIONS,
3515
- ariaLabel: "Text size"
3516
- }
3517
- ),
3518
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Makes all text larger, including documents in the viewer and citations. Applied straight away and remembered on this device." })
3519
- ] }),
3520
- /* @__PURE__ */ jsxs(SettingsSection, { title: "Typeface", children: [
3521
- /* @__PURE__ */ jsxs(Select, { value: pairingId, onValueChange: onPairingChange, children: [
3522
- /* @__PURE__ */ jsx(SelectTrigger, { className: "w-full", "data-testid": "appearance-typeface-select", children: /* @__PURE__ */ jsx(SelectValue, { children: activePairing && /* @__PURE__ */ jsx("span", { style: { fontFamily: activePairing.display }, children: activePairing.label }) }) }),
3523
- /* @__PURE__ */ jsx(SelectContent, { children: pairingIds.map((id) => {
3524
- const pairing = getFontPairing(id);
3525
- if (!pairing) return null;
3526
- return /* @__PURE__ */ jsx(SelectItem, { value: id, "data-testid": `appearance-typeface-${id}`, children: /* @__PURE__ */ jsx("span", { style: { fontFamily: pairing.display }, children: pairing.label }) }, id);
3527
- }) })
3528
- ] }),
3529
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Sets the headings and body text across the app. Applied straight away and remembered on this device." })
3530
- ] })
3531
- ] });
3532
- }
3533
-
3534
- export { AppearanceSettings, CONVERSATION_TITLE_MAX, CheckoutDialogFrame, ConfigSwitcherBar, ConnectionCard, ConnectionsEmpty, ConnectionsError, ConnectionsLoading, ConversationTitleSection, DEFAULT_APPEARANCE_PAIRING_IDS, DEFAULT_CHECKOUT_DIALOG_LABELS, DEFAULT_CONFIG_SWITCHER_BAR_LABELS, DEFAULT_CONNECTION_CARD_LABELS, DEFAULT_CONVERSATION_TITLE_SECTION_LABELS, DEFAULT_DAILY_CAP_FIELD_LABELS, DEFAULT_EMAIL_WORKSPACE_DIALOG_LABELS, DEFAULT_ERROR_DIALOG_LABELS, DEFAULT_INLINE_NAME_EDITOR_LABELS, DEFAULT_INVITE_CONTACTS_DIALOG_LABELS, DEFAULT_INVOICES_DIALOG_LABELS, DEFAULT_MEMBER_PROJECT_ROW_LABELS, DEFAULT_MODEL_SELECTION_SECTION_LABELS, DEFAULT_PLAN_CARD_LABELS, DEFAULT_PROJECT_CARD_VIEW_LABELS, DEFAULT_PROJECT_DETAIL_HEADER_LABELS, DEFAULT_PROJECT_LIST_LABELS, DEFAULT_QUOTA_EXCEEDED_DIALOG_LABELS, DEFAULT_REFERRAL_REWARD_LABELS, DEFAULT_RENAME_DIALOG_LABELS, DEFAULT_SHARED_PROJECTS_SECTION_LABELS, DEFAULT_SUBSCRIPTION_SECTION_LABELS, DEFAULT_SUBSCRIPTION_STATUS_LABELS, DEFAULT_SUPPORT_PANEL_LABELS, DEFAULT_SYNC_DIALOG_FOOTER_LABELS, DEFAULT_SYNC_FOLDER_ROW_LABELS, DEFAULT_TIPS_EDITOR_LABELS, DEFAULT_USAGE_LINE_CHART_LABELS, DEFAULT_USAGE_PANEL_LABELS, DEFAULT_USAGE_PROPORTION_BAR_LABELS, DEFAULT_WELCOME_DIALOG_LABELS, DailyCapField, EmailWorkspaceDialogView, ErrorDialogView, FilterChip, FloatingPanel, InlineNameEditor, InviteContactsDialog, InvoicesDialog, JsonTree, MemberProjectRow, MessageJsonTree, ModelSelectionSection, MoreInfo, MoreInfoList, PlanActionButton, PlanActionRow, PlanBadge, PlanCard, PlanCardGrid, PlanDialogFrame, PlanInfoPopover, PlanNotice, PlanPickerCard, PlanPickerHeader, PlanStack, PlanTierOption, PlanTransition, PricingToggle, ProjectCardView, ProjectDetailHeader, ProjectDetailSection, ProjectGroup, ProjectList, ProjectNameLabel, ProjectPlanPicker, ProjectRow, ProjectRowActions, PromoCodeInput, QuotaExceededDialogView, QuotaMeter, ReferralRewardBadge, ReferralRewardNote, ReferralRewardSummary, RenameDialog, RowButton, RowIconAction, SegmentedControl, SharedProjectRow, SharedProjectsSection, SubscriptionSectionView, SubscriptionStatusView, SupportPanel, SupportUnavailable, SyncCheckList, SyncDialogBody, SyncDialogFooter, SyncField, SyncFolderRow, SyncFolderStatus, SyncLinkButton, SyncLinkRow, SyncLoading, SyncNote, SyncOptionToggle, SyncOptions, SyncReconnectNotice, SyncSelect, SyncSmallNote, SyncTreeFrame, ThreadPreviewDialog, TipsEditorDialog, UsageActionButton, UsageLegend, UsageLineChart, UsagePanel, UsageProportionBar, UsageQuotaHint, UsageSection, UsageSummary, UsageToggle, WelcomeDialog, WelcomeProjectPicker };
3535
18
  //# sourceMappingURL=index.js.map
3536
19
  //# sourceMappingURL=index.js.map