@cosmoops/connect-sdk 0.45.0 → 0.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/dist/cjs/client/components/access-gate.js +1 -1
  3. package/dist/cjs/client/components/account-sessions.js +1 -1
  4. package/dist/cjs/client/components/ai-format.js +3 -3
  5. package/dist/cjs/client/components/ai-widgets.js +5 -5
  6. package/dist/cjs/client/components/apps-grid.js +2 -2
  7. package/dist/cjs/client/components/ask-ai-history.js +2 -2
  8. package/dist/cjs/client/components/ask-ai.js +1 -1
  9. package/dist/cjs/client/components/availability-form.js +1 -1
  10. package/dist/cjs/client/components/booking-calendar.js +4 -4
  11. package/dist/cjs/client/components/booking-dialogs.js +2 -2
  12. package/dist/cjs/client/components/call-button.js +6 -6
  13. package/dist/cjs/client/components/create-workspace-form.js +1 -1
  14. package/dist/cjs/client/components/event-type-form.js +1 -1
  15. package/dist/cjs/client/components/filter-tabs.js +1 -1
  16. package/dist/cjs/client/components/grievance-notice.js +1 -1
  17. package/dist/cjs/client/components/integrations-modal.js +2 -2
  18. package/dist/cjs/client/components/plans.js +5 -5
  19. package/dist/cjs/client/components/readiness.js +1 -1
  20. package/dist/cjs/client/components/subscribe-dialog.js +1 -1
  21. package/dist/cjs/client/components/subscription.js +5 -5
  22. package/dist/cjs/client/components/usage-meter.js +2 -2
  23. package/dist/cjs/client/components/user-menu.js +1 -1
  24. package/dist/cjs/client/components/workspace-switcher.js +1 -1
  25. package/dist/cjs/client/portal-links.js +1 -1
  26. package/dist/cjs/errors.js +9 -0
  27. package/dist/cjs/server/client.js +1 -1
  28. package/dist/cjs/server/http.js +4 -1
  29. package/dist/cjs/server/resources/drive.js +11 -0
  30. package/dist/cjs/server/resources/integrations.js +16 -0
  31. package/dist/cjs/server/resources/workspaces.js +9 -0
  32. package/dist/cjs/ui/page-shell.js +64 -2
  33. package/dist/esm/client/components/access-gate.js +1 -1
  34. package/dist/esm/client/components/account-sessions.js +1 -1
  35. package/dist/esm/client/components/ai-format.js +3 -3
  36. package/dist/esm/client/components/ai-widgets.js +5 -5
  37. package/dist/esm/client/components/apps-grid.js +2 -2
  38. package/dist/esm/client/components/ask-ai-history.js +2 -2
  39. package/dist/esm/client/components/ask-ai.js +1 -1
  40. package/dist/esm/client/components/availability-form.js +1 -1
  41. package/dist/esm/client/components/booking-calendar.js +4 -4
  42. package/dist/esm/client/components/booking-dialogs.js +2 -2
  43. package/dist/esm/client/components/call-button.js +6 -6
  44. package/dist/esm/client/components/create-workspace-form.js +1 -1
  45. package/dist/esm/client/components/event-type-form.js +1 -1
  46. package/dist/esm/client/components/filter-tabs.js +1 -1
  47. package/dist/esm/client/components/grievance-notice.js +1 -1
  48. package/dist/esm/client/components/integrations-modal.js +2 -2
  49. package/dist/esm/client/components/plans.js +5 -5
  50. package/dist/esm/client/components/readiness.js +1 -1
  51. package/dist/esm/client/components/subscribe-dialog.js +1 -1
  52. package/dist/esm/client/components/subscription.js +5 -5
  53. package/dist/esm/client/components/usage-meter.js +2 -2
  54. package/dist/esm/client/components/user-menu.js +1 -1
  55. package/dist/esm/client/components/workspace-switcher.js +1 -1
  56. package/dist/esm/client/portal-links.js +1 -1
  57. package/dist/esm/errors.js +9 -0
  58. package/dist/esm/server/client.js +1 -1
  59. package/dist/esm/server/http.js +4 -1
  60. package/dist/esm/server/resources/drive.js +11 -0
  61. package/dist/esm/server/resources/integrations.js +16 -0
  62. package/dist/esm/server/resources/workspaces.js +9 -0
  63. package/dist/esm/ui/page-shell.js +31 -2
  64. package/dist/theme.css +78 -2
  65. package/dist/types/client/components/call-button.d.ts +3 -1
  66. package/dist/types/client/portal-links.d.ts +1 -1
  67. package/dist/types/errors.d.ts +7 -0
  68. package/dist/types/server/client.d.ts +1 -1
  69. package/dist/types/server/index.d.ts +1 -1
  70. package/dist/types/server/resources/drive.d.ts +8 -1
  71. package/dist/types/server/resources/integrations.d.ts +33 -1
  72. package/dist/types/server/resources/workspaces.d.ts +9 -0
  73. package/dist/types/types.d.ts +21 -0
  74. package/dist/types/ui/page-shell.d.ts +20 -2
  75. package/package.json +1 -1
@@ -112,9 +112,9 @@ function ConnectPlans({ planIds, hideCoupon = false, className }) {
112
112
  return (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-muted", children: "Sign in to see your plan options." });
113
113
  }
114
114
  if (error) {
115
- return (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-danger", children: "Could not load plans." });
115
+ return (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-danger-soft-foreground", children: "Could not load plans." });
116
116
  }
117
- return ((0, jsx_runtime_1.jsx)(motion_js_1.MotionProvider, { children: (0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)("flex flex-col gap-6", className), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-4", children: [hasAnnual && hasMonthly && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("span", { className: showAnnual ? "text-sm text-muted" : "text-sm font-medium text-foreground", children: "Monthly" }), (0, jsx_runtime_1.jsx)(react_1.Switch, { "aria-label": "Bill annually", isSelected: showAnnual, onChange: setAnnual, children: (0, jsx_runtime_1.jsx)(react_1.Switch.Content, { children: (0, jsx_runtime_1.jsx)(react_1.Switch.Control, { children: (0, jsx_runtime_1.jsx)(react_1.Switch.Thumb, {}) }) }) }), (0, jsx_runtime_1.jsx)("span", { className: showAnnual ? "text-sm font-medium text-foreground" : "text-sm text-muted", children: "Annual" }), bestSaving !== null && ((0, jsx_runtime_1.jsxs)(react_1.Chip, { className: "bg-accent-soft text-accent", children: ["Save up to ", bestSaving, "%"] }))] })), !hideCoupon && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Tag, { className: "size-4 text-muted", "aria-hidden": "true" }), (0, jsx_runtime_1.jsx)(react_1.Input, { "aria-label": "Coupon code", placeholder: "Coupon code", value: coupon, onChange: (event) => setCoupon(event.target.value.toUpperCase()), className: "w-40" })] }))] }), subscription?.subscription?.appliedDiscount && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 rounded-lg bg-accent-soft px-3 py-2 text-sm text-accent", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Sparkles, { className: "size-4 shrink-0", "aria-hidden": "true" }), (0, jsx_runtime_1.jsxs)("span", { children: [(0, plan_pricing_js_1.discountLabel)(subscription.subscription.appliedDiscount), subscription.subscription.appliedDiscount.code
117
+ return ((0, jsx_runtime_1.jsx)(motion_js_1.MotionProvider, { children: (0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)("flex flex-col gap-6", className), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-4", children: [hasAnnual && hasMonthly && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("span", { className: showAnnual ? "text-sm text-muted" : "text-sm font-medium text-foreground", children: "Monthly" }), (0, jsx_runtime_1.jsx)(react_1.Switch, { "aria-label": "Bill annually", isSelected: showAnnual, onChange: setAnnual, children: (0, jsx_runtime_1.jsx)(react_1.Switch.Content, { children: (0, jsx_runtime_1.jsx)(react_1.Switch.Control, { children: (0, jsx_runtime_1.jsx)(react_1.Switch.Thumb, {}) }) }) }), (0, jsx_runtime_1.jsx)("span", { className: showAnnual ? "text-sm font-medium text-foreground" : "text-sm text-muted", children: "Annual" }), bestSaving !== null && ((0, jsx_runtime_1.jsxs)(react_1.Chip, { color: "accent", variant: "soft", children: ["Save up to ", bestSaving, "%"] }))] })), !hideCoupon && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Tag, { className: "size-4 text-muted", "aria-hidden": "true" }), (0, jsx_runtime_1.jsx)(react_1.Input, { "aria-label": "Coupon code", placeholder: "Coupon code", value: coupon, onChange: (event) => setCoupon(event.target.value.toUpperCase()), className: "w-40" })] }))] }), subscription?.subscription?.appliedDiscount && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 rounded-lg bg-accent-soft px-3 py-2 text-sm text-accent-soft-foreground", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Sparkles, { className: "size-4 shrink-0", "aria-hidden": "true" }), (0, jsx_runtime_1.jsxs)("span", { children: [(0, plan_pricing_js_1.discountLabel)(subscription.subscription.appliedDiscount), subscription.subscription.appliedDiscount.code
118
118
  ? ` - code ${subscription.subscription.appliedDiscount.code}`
119
119
  : ""] })] })), (0, jsx_runtime_1.jsx)(ConnectCheckoutStatusRow, { checkout: checkout }), (0, jsx_runtime_1.jsx)(motion_js_1.m.div, { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3", variants: { show: { transition: { staggerChildren: 0.06 } } }, initial: "hidden", animate: "show", children: tiers.map((tier) => {
120
120
  // The price point this card is showing. A tier missing the toggled
@@ -134,7 +134,7 @@ function ConnectPlans({ planIds, hideCoupon = false, className }) {
134
134
  tier.oneTime?.id === subscription.plan.id);
135
135
  const isCurrent = action === "current";
136
136
  const cadenceUnavailable = showAnnual ? tier.annual === null : tier.monthly === null;
137
- return ((0, jsx_runtime_1.jsx)(motion_js_1.m.div, { variants: { hidden: { opacity: 0, y: 12 }, show: { opacity: 1, y: 0, transition: motion_js_1.ENTER } }, children: (0, jsx_runtime_1.jsxs)(react_1.Card, { className: isCurrentTier ? "h-full border-accent" : "h-full", children: [(0, jsx_runtime_1.jsxs)(react_1.Card.Header, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-start justify-between gap-2", children: [(0, jsx_runtime_1.jsx)(react_1.Card.Title, { children: tier.name }), (0, jsx_runtime_1.jsxs)("div", { className: "flex shrink-0 items-center gap-1.5", children: [tier.annualSavingPercent !== null && showAnnual && ((0, jsx_runtime_1.jsxs)(react_1.Chip, { className: "bg-accent-soft text-accent", children: ["Save ", tier.annualSavingPercent, "%"] })), isCurrentTier && (0, jsx_runtime_1.jsx)(react_1.Chip, { className: "bg-accent-soft text-accent", children: "Current" })] })] }), tier.description && (0, jsx_runtime_1.jsx)(react_1.Card.Description, { children: tier.description })] }), (0, jsx_runtime_1.jsxs)(react_1.Card.Content, { className: "flex flex-col gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(motion_js_1.AnimatePresence, { mode: "popLayout", initial: false, children: (0, jsx_runtime_1.jsx)(motion_js_1.m.div, { initial: { opacity: 0, y: 6 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -6 }, transition: { duration: 0.15 }, children: (0, plan_pricing_js_1.isFreePlan)(plan.price) ? ((0, jsx_runtime_1.jsx)("span", { className: "text-2xl font-semibold text-foreground", children: "Free" })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "text-2xl font-semibold text-foreground", children: (0, plan_pricing_js_1.formatPrice)(plan.price) }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm text-muted", children: (0, plan_pricing_js_1.pricePerLabel)(plan.billingInterval) })] })) }, plan.billingInterval) }), !(0, plan_pricing_js_1.isFreePlan)(plan.price) && ((0, jsx_runtime_1.jsxs)("p", { className: "mt-1 text-xs text-muted", children: ["+", plan.gstRate, "% GST"] })), cadenceUnavailable && ((0, jsx_runtime_1.jsxs)("p", { className: "mt-1 text-xs text-muted", children: ["Only sold ", (0, plan_pricing_js_1.intervalLabel)(plan.billingInterval).adverb, "."] }))] }), plan.trialDays && !(0, plan_pricing_js_1.isFreePlan)(plan.price) ? ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs font-medium text-accent", children: [plan.trialDays, "-day free trial"] })) : null, plan.features.length > 0 && ((0, jsx_runtime_1.jsx)("ul", { className: "flex flex-col gap-1.5", children: plan.features.map((feature) => (
137
+ return ((0, jsx_runtime_1.jsx)(motion_js_1.m.div, { variants: { hidden: { opacity: 0, y: 12 }, show: { opacity: 1, y: 0, transition: motion_js_1.ENTER } }, children: (0, jsx_runtime_1.jsxs)(react_1.Card, { className: isCurrentTier ? "h-full border-accent" : "h-full", children: [(0, jsx_runtime_1.jsxs)(react_1.Card.Header, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-start justify-between gap-2", children: [(0, jsx_runtime_1.jsx)(react_1.Card.Title, { children: tier.name }), (0, jsx_runtime_1.jsxs)("div", { className: "flex shrink-0 items-center gap-1.5", children: [tier.annualSavingPercent !== null && showAnnual && ((0, jsx_runtime_1.jsxs)(react_1.Chip, { color: "accent", variant: "soft", children: ["Save ", tier.annualSavingPercent, "%"] })), isCurrentTier && (0, jsx_runtime_1.jsx)(react_1.Chip, { color: "accent", variant: "soft", children: "Current" })] })] }), tier.description && (0, jsx_runtime_1.jsx)(react_1.Card.Description, { children: tier.description })] }), (0, jsx_runtime_1.jsxs)(react_1.Card.Content, { className: "flex flex-col gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(motion_js_1.AnimatePresence, { mode: "popLayout", initial: false, children: (0, jsx_runtime_1.jsx)(motion_js_1.m.div, { initial: { opacity: 0, y: 6 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -6 }, transition: { duration: 0.15 }, children: (0, plan_pricing_js_1.isFreePlan)(plan.price) ? ((0, jsx_runtime_1.jsx)("span", { className: "text-2xl font-semibold text-foreground", children: "Free" })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "text-2xl font-semibold text-foreground", children: (0, plan_pricing_js_1.formatPrice)(plan.price) }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm text-muted", children: (0, plan_pricing_js_1.pricePerLabel)(plan.billingInterval) })] })) }, plan.billingInterval) }), !(0, plan_pricing_js_1.isFreePlan)(plan.price) && ((0, jsx_runtime_1.jsxs)("p", { className: "mt-1 text-xs text-muted", children: ["+", plan.gstRate, "% GST"] })), cadenceUnavailable && ((0, jsx_runtime_1.jsxs)("p", { className: "mt-1 text-xs text-muted", children: ["Only sold ", (0, plan_pricing_js_1.intervalLabel)(plan.billingInterval).adverb, "."] }))] }), plan.trialDays && !(0, plan_pricing_js_1.isFreePlan)(plan.price) ? ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs font-medium text-accent-soft-foreground", children: [plan.trialDays, "-day free trial"] })) : null, plan.features.length > 0 && ((0, jsx_runtime_1.jsx)("ul", { className: "flex flex-col gap-1.5", children: plan.features.map((feature) => (
138
138
  // `name`, not `key` - the key is an identifier
139
139
  // ("campaign-module"), and printing it here is what this
140
140
  // card used to do. The description rides along as a
@@ -170,7 +170,7 @@ function CheckoutStatusRowContent({ checkout, }) {
170
170
  return ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 rounded-lg border border-separator px-3 py-2 text-sm text-muted", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "size-4 shrink-0 animate-spin", "aria-hidden": "true" }), (0, jsx_runtime_1.jsx)("span", { children: "Finish up in the Connect window. This page updates on its own." })] }));
171
171
  }
172
172
  if (checkout.status === "completed") {
173
- return ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between gap-3 rounded-lg bg-accent-soft px-3 py-2 text-sm text-accent", children: [(0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.CheckCircle2, { className: "size-4 shrink-0", "aria-hidden": "true" }), "Your plan is updated."] }), (0, jsx_runtime_1.jsx)(react_1.Button, { variant: "tertiary", onPress: checkout.reset, children: "Dismiss" })] }));
173
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between gap-3 rounded-lg bg-accent-soft px-3 py-2 text-sm text-accent-soft-foreground", children: [(0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.CheckCircle2, { className: "size-4 shrink-0", "aria-hidden": "true" }), "Your plan is updated."] }), (0, jsx_runtime_1.jsx)(react_1.Button, { variant: "tertiary", onPress: checkout.reset, children: "Dismiss" })] }));
174
174
  }
175
175
  // `blocked` and `abandoned` share a row: both mean nothing changed, and both
176
176
  // are fixed by going to the same URL - so offer it rather than explaining.
@@ -205,7 +205,7 @@ function ConnectUnsubscribeRow({ subscription, appUrl, checkout, }) {
205
205
  if (subscription.cancelAtPeriodEnd) {
206
206
  return ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-3 border-t border-separator pt-4", children: [(0, jsx_runtime_1.jsxs)("p", { className: "text-sm text-muted", children: ["Cancels", endsOn ? ` on ${new Date(endsOn).toLocaleDateString()}` : " at the end of this cycle", ". You keep access until then."] }), (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "tertiary", onPress: () => openConnect(`/billing/subscriptions/${encodeURIComponent(subscription.id)}`), children: ["View on Connect", (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "size-3.5" })] })] }));
207
207
  }
208
- return ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-3 border-t border-separator pt-4", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm text-muted", children: "Cancel any time. Access continues to the end of the paid cycle." }), (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "tertiary", className: "text-danger", isDisabled: checkout.status === "waiting", onPress: () => checkout.start({
208
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-3 border-t border-separator pt-4", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm text-muted", children: "Cancel any time. Access continues to the end of the paid cycle." }), (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "danger-soft", isDisabled: checkout.status === "waiting", onPress: () => checkout.start({
209
209
  url: (0, use_checkout_js_1.buildCancelUrl)({ appUrl, subscriptionId: subscription.id }),
210
210
  // A paused subscription cancels outright (`status` goes to
211
211
  // `"cancelled"`); an active one is deferred to the cycle end
@@ -75,7 +75,7 @@ function BlockerRow({ blocker }) {
75
75
  const { openConnect } = (0, provider_js_1.useConnect)();
76
76
  const inProgress = IN_PROGRESS_CODES.has(blocker.code);
77
77
  const isBlocking = blocker.severity === "blocking";
78
- return ((0, jsx_runtime_1.jsxs)("div", { className: `flex flex-wrap items-center justify-between gap-3 rounded-lg px-3 py-2 text-sm ${inProgress || !isBlocking ? "bg-surface text-muted" : "bg-danger-soft text-danger"}`, children: [(0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [inProgress || !isBlocking ? ((0, jsx_runtime_1.jsx)(lucide_react_1.Info, { className: "size-4 shrink-0", "aria-hidden": "true" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "size-4 shrink-0", "aria-hidden": "true" })), blocker.message] }), blocker.resolveUrl && !inProgress && ((0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "secondary", onPress: () => openConnect(blocker.resolveUrl), children: [actionLabel(blocker), (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "size-3.5" })] }))] }));
78
+ return ((0, jsx_runtime_1.jsxs)("div", { className: `flex flex-wrap items-center justify-between gap-3 rounded-lg px-3 py-2 text-sm ${inProgress || !isBlocking ? "bg-surface text-muted" : "bg-danger-soft text-danger-soft-foreground"}`, children: [(0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [inProgress || !isBlocking ? ((0, jsx_runtime_1.jsx)(lucide_react_1.Info, { className: "size-4 shrink-0", "aria-hidden": "true" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "size-4 shrink-0", "aria-hidden": "true" })), blocker.message] }), blocker.resolveUrl && !inProgress && ((0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "secondary", onPress: () => openConnect(blocker.resolveUrl), children: [actionLabel(blocker), (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "size-3.5" })] }))] }));
79
79
  }
80
80
  function ReadyRow({ className }) {
81
81
  return ((0, jsx_runtime_1.jsx)("p", { className: (0, cn_js_1.cn)("text-sm text-muted", className), children: "This workspace is cleared to use every part of the application." }));
@@ -50,7 +50,7 @@ function ConnectSubscribeDialog({ plan, action, current, coupon, onClose, onConf
50
50
  // member and the one most in need of it: the two prices differ by an order
51
51
  // of magnitude, and showing only the new one reads as a price rise.
52
52
  const isChange = action === "upgrade" || action === "downgrade" || isIntervalSwitch;
53
- return ((0, jsx_runtime_1.jsx)(react_1.Modal.Backdrop, { isOpen: plan !== null, onOpenChange: (open) => !open && onClose(), children: (0, jsx_runtime_1.jsx)(react_1.Modal.Container, { size: "sm", children: (0, jsx_runtime_1.jsxs)(react_1.Modal.Dialog, { children: [(0, jsx_runtime_1.jsx)(react_1.Modal.CloseTrigger, { onPress: onClose }), (0, jsx_runtime_1.jsx)(react_1.Modal.Header, { children: (0, jsx_runtime_1.jsx)(react_1.Modal.Heading, { children: HEADING[action] }) }), plan && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(react_1.Modal.Body, { className: "flex flex-col gap-4", children: [isChange && currentPlan ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3 text-sm", children: [(0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate text-muted", children: currentPlan.name }), (0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: [(0, plan_pricing_js_1.formatPrice)(currentPlan.price), "/", (0, plan_pricing_js_1.intervalLabel)(currentPlan.billingInterval).short] })] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ArrowRight, { className: "size-4 shrink-0 text-muted", "aria-hidden": "true" }), (0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate font-medium text-foreground", children: plan.name }), (0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: [(0, plan_pricing_js_1.formatPrice)(plan.price), "/", (0, plan_pricing_js_1.intervalLabel)(plan.billingInterval).short] })] })] })) : ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: "font-medium text-foreground", children: plan.name }), plan.description && (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-muted", children: plan.description })] })), (0, plan_pricing_js_1.isFreePlan)(plan.price) ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between border-t border-separator pt-3 text-sm font-semibold text-foreground", children: [(0, jsx_runtime_1.jsx)("span", { children: plan.name }), (0, jsx_runtime_1.jsx)("span", { children: "Free" })] })) : ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-1 border-t border-separator pt-3 text-sm", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between text-muted", children: [(0, jsx_runtime_1.jsx)("span", { children: plan.name }), (0, jsx_runtime_1.jsx)("span", { children: (0, plan_pricing_js_1.formatPrice)(plan.price) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between text-muted", children: [(0, jsx_runtime_1.jsxs)("span", { children: ["GST (", plan.gstRate, "%)"] }), (0, jsx_runtime_1.jsx)("span", { children: (0, plan_pricing_js_1.formatPrice)(withGst(plan.price, plan.gstRate) - plan.price) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between border-t border-separator pt-1 font-semibold text-foreground", children: [(0, jsx_runtime_1.jsxs)("span", { children: ["Per ", cadenceOf(plan)] }), (0, jsx_runtime_1.jsx)("span", { children: (0, plan_pricing_js_1.formatPrice)(withGst(plan.price, plan.gstRate)) })] }), plan.billingInterval === "annual" && ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: ["Works out to ", (0, plan_pricing_js_1.formatPrice)((0, plan_pricing_js_1.monthlyOf)(withGst(plan.price, plan.gstRate), "annual")), " a month, billed yearly."] }))] })), plan.trialDays && !(0, plan_pricing_js_1.isFreePlan)(plan.price) ? ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-accent", children: [plan.trialDays, "-day free trial, if this workspace has not subscribed to this application before."] })) : null, coupon?.trim() ? ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: ["Coupon ", (0, jsx_runtime_1.jsx)("span", { className: "font-mono text-foreground", children: coupon.trim() }), " is carried over and validated on Connect."] })) : null, (0, jsx_runtime_1.jsxs)("div", { className: "flex gap-2 rounded-lg bg-surface p-3 text-xs text-muted", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Info, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": "true" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-1", children: [action === "subscribe" &&
53
+ return ((0, jsx_runtime_1.jsx)(react_1.Modal.Backdrop, { isOpen: plan !== null, onOpenChange: (open) => !open && onClose(), children: (0, jsx_runtime_1.jsx)(react_1.Modal.Container, { size: "sm", children: (0, jsx_runtime_1.jsxs)(react_1.Modal.Dialog, { children: [(0, jsx_runtime_1.jsx)(react_1.Modal.CloseTrigger, { onPress: onClose }), (0, jsx_runtime_1.jsx)(react_1.Modal.Header, { children: (0, jsx_runtime_1.jsx)(react_1.Modal.Heading, { children: HEADING[action] }) }), plan && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(react_1.Modal.Body, { className: "flex flex-col gap-4", children: [isChange && currentPlan ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3 text-sm", children: [(0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate text-muted", children: currentPlan.name }), (0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: [(0, plan_pricing_js_1.formatPrice)(currentPlan.price), "/", (0, plan_pricing_js_1.intervalLabel)(currentPlan.billingInterval).short] })] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ArrowRight, { className: "size-4 shrink-0 text-muted", "aria-hidden": "true" }), (0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate font-medium text-foreground", children: plan.name }), (0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: [(0, plan_pricing_js_1.formatPrice)(plan.price), "/", (0, plan_pricing_js_1.intervalLabel)(plan.billingInterval).short] })] })] })) : ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: "font-medium text-foreground", children: plan.name }), plan.description && (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-muted", children: plan.description })] })), (0, plan_pricing_js_1.isFreePlan)(plan.price) ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between border-t border-separator pt-3 text-sm font-semibold text-foreground", children: [(0, jsx_runtime_1.jsx)("span", { children: plan.name }), (0, jsx_runtime_1.jsx)("span", { children: "Free" })] })) : ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-1 border-t border-separator pt-3 text-sm", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between text-muted", children: [(0, jsx_runtime_1.jsx)("span", { children: plan.name }), (0, jsx_runtime_1.jsx)("span", { children: (0, plan_pricing_js_1.formatPrice)(plan.price) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between text-muted", children: [(0, jsx_runtime_1.jsxs)("span", { children: ["GST (", plan.gstRate, "%)"] }), (0, jsx_runtime_1.jsx)("span", { children: (0, plan_pricing_js_1.formatPrice)(withGst(plan.price, plan.gstRate) - plan.price) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between border-t border-separator pt-1 font-semibold text-foreground", children: [(0, jsx_runtime_1.jsxs)("span", { children: ["Per ", cadenceOf(plan)] }), (0, jsx_runtime_1.jsx)("span", { children: (0, plan_pricing_js_1.formatPrice)(withGst(plan.price, plan.gstRate)) })] }), plan.billingInterval === "annual" && ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: ["Works out to ", (0, plan_pricing_js_1.formatPrice)((0, plan_pricing_js_1.monthlyOf)(withGst(plan.price, plan.gstRate), "annual")), " a month, billed yearly."] }))] })), plan.trialDays && !(0, plan_pricing_js_1.isFreePlan)(plan.price) ? ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-accent-soft-foreground", children: [plan.trialDays, "-day free trial, if this workspace has not subscribed to this application before."] })) : null, coupon?.trim() ? ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: ["Coupon ", (0, jsx_runtime_1.jsx)("span", { className: "font-mono text-foreground", children: coupon.trim() }), " is carried over and validated on Connect."] })) : null, (0, jsx_runtime_1.jsxs)("div", { className: "flex gap-2 rounded-lg bg-surface p-3 text-xs text-muted", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Info, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": "true" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-1", children: [action === "subscribe" &&
54
54
  ((0, plan_pricing_js_1.isFreePlan)(plan.price) ? ((0, jsx_runtime_1.jsx)("p", { children: "This plan is free. Connect activates it straight away - there is no payment to make, no card or UPI mandate to set up, and no invoice to expect. This page updates by itself once it is done." })) : ((0, jsx_runtime_1.jsx)("p", { children: "Payment is completed on Connect, where your card or UPI mandate is set up and the tax invoice is issued. This page updates by itself once it is done." }))), action === "upgrade" && ((0, jsx_runtime_1.jsxs)("p", { children: ["Connect prices the difference for the days left in your current cycle and collects just that amount now. From the next cycle you are billed the ", plan.name, " rate."] })), action === "downgrade" && ((0, jsx_runtime_1.jsx)("p", { children: "You keep what you have paid for. Connect works out the unused part of this cycle and applies it as credit or a refund, depending on how this application is configured." })), isIntervalSwitch && (
55
55
  // Not a proration. The cycle you are on ends, so this is
56
56
  // the one action where the amount collected can exceed a
@@ -91,15 +91,15 @@ function ConnectSubscriptionCard({ plansHref, className }) {
91
91
  function SubscriptionChip({ state }) {
92
92
  switch (state) {
93
93
  case "trialing":
94
- return (0, jsx_runtime_1.jsx)(react_1.Chip, { className: "bg-accent-soft text-accent", children: "Free trial" });
94
+ return (0, jsx_runtime_1.jsx)(react_1.Chip, { color: "accent", variant: "soft", children: "Free trial" });
95
95
  case "cancelling":
96
96
  return (0, jsx_runtime_1.jsx)(react_1.Chip, { className: "bg-surface text-muted", children: "Ending soon" });
97
97
  case "pending":
98
98
  return (0, jsx_runtime_1.jsx)(react_1.Chip, { className: "bg-surface text-muted", children: "Confirming payment" });
99
99
  case "problem":
100
- return (0, jsx_runtime_1.jsx)(react_1.Chip, { className: "bg-danger-soft text-danger", children: "Needs attention" });
100
+ return (0, jsx_runtime_1.jsx)(react_1.Chip, { color: "danger", variant: "soft", children: "Needs attention" });
101
101
  default:
102
- return (0, jsx_runtime_1.jsx)(react_1.Chip, { className: "bg-accent-soft text-accent", children: "Active" });
102
+ return (0, jsx_runtime_1.jsx)(react_1.Chip, { color: "accent", variant: "soft", children: "Active" });
103
103
  }
104
104
  }
105
105
  /** The one sentence that matters most: what happens next, and when. */
@@ -112,7 +112,7 @@ function NextEvent({ detail, state, }) {
112
112
  return ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2 text-muted", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.CreditCard, { className: "size-4 shrink-0", "aria-hidden": "true" }), "Waiting for your first payment to confirm."] }));
113
113
  }
114
114
  if (state === "problem") {
115
- return ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2 text-danger", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "size-4 shrink-0", "aria-hidden": "true" }), "This subscription isn't active. Settle it on Connect to restore access."] }));
115
+ return ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2 text-danger-soft-foreground", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "size-4 shrink-0", "aria-hidden": "true" }), "This subscription isn't active. Settle it on Connect to restore access."] }));
116
116
  }
117
117
  if (!detail.nextBillingDate)
118
118
  return null;
@@ -161,7 +161,7 @@ function ConnectTrialBanner({ plansHref, className }) {
161
161
  const { detail, state } = useSubscriptionState();
162
162
  if (state !== "trialing" || !detail)
163
163
  return null;
164
- return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)("flex flex-wrap items-center justify-between gap-3 rounded-lg bg-accent-soft px-3 py-2 text-sm text-accent", className), children: [(0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Clock, { className: "size-4 shrink-0", "aria-hidden": "true" }), "You're on a free trial", detail.nextBillingDate ? `. Your first charge is on ${formatDate(detail.nextBillingDate)}.` : "."] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(link_button_js_1.ConnectLinkButton, { href: plansHref ?? DEFAULT_PLANS_HREF, variant: "tertiary", children: "Compare plans" }), (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "secondary", onPress: () => openConnect(`/billing/subscriptions/${encodeURIComponent(detail.id)}`), children: ["Manage", (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "size-3.5" })] })] })] }));
164
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)("flex flex-wrap items-center justify-between gap-3 rounded-lg bg-accent-soft px-3 py-2 text-sm text-accent-soft-foreground", className), children: [(0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Clock, { className: "size-4 shrink-0", "aria-hidden": "true" }), "You're on a free trial", detail.nextBillingDate ? `. Your first charge is on ${formatDate(detail.nextBillingDate)}.` : "."] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(link_button_js_1.ConnectLinkButton, { href: plansHref ?? DEFAULT_PLANS_HREF, variant: "tertiary", children: "Compare plans" }), (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "secondary", onPress: () => openConnect(`/billing/subscriptions/${encodeURIComponent(detail.id)}`), children: ["Manage", (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "size-3.5" })] })] })] }));
165
165
  }
166
166
  /**
167
167
  * Renders `children` for a subscribed workspace, and the "pick a plan" card for
@@ -64,7 +64,7 @@ function AddonRow({ addon }) {
64
64
  // unclamped width renders as a bar overflowing its own track.
65
65
  const fraction = addon.includedPerCycle > 0 ? Math.min(1, Math.max(0, addon.balance / addon.includedPerCycle)) : 0;
66
66
  const carried = addon.balance > addon.includedPerCycle ? addon.balance - addon.includedPerCycle : 0;
67
- return ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-1.5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-baseline justify-between gap-2 text-sm", children: [(0, jsx_runtime_1.jsx)("span", { className: "font-medium text-foreground", children: addon.name }), (0, jsx_runtime_1.jsxs)("span", { className: overdrawn ? "text-danger" : "text-muted", children: [overdrawn
67
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-1.5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-baseline justify-between gap-2 text-sm", children: [(0, jsx_runtime_1.jsx)("span", { className: "font-medium text-foreground", children: addon.name }), (0, jsx_runtime_1.jsxs)("span", { className: overdrawn ? "text-danger-soft-foreground" : "text-muted", children: [overdrawn
68
68
  ? `${formatUnits(Math.abs(addon.balance), addon.unit)} over`
69
- : `${formatUnits(addon.balance, addon.unit)} left`, (0, jsx_runtime_1.jsxs)("span", { className: "text-muted", children: [" of ", formatUnits(addon.includedPerCycle, addon.unit)] })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-surface", role: "meter", "aria-label": `${addon.name} remaining`, "aria-valuenow": addon.balance, "aria-valuemin": 0, "aria-valuemax": addon.includedPerCycle, children: (0, jsx_runtime_1.jsx)(motion_js_1.m.div, { className: `h-full rounded-full ${overdrawn ? "bg-danger" : "bg-accent"}`, initial: { width: 0 }, animate: { width: `${overdrawn ? 100 : fraction * 100}%` }, transition: motion_js_1.METER_SPRING }) }), carried > 0 && ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: ["Includes ", formatUnits(carried, addon.unit), " carried over from last cycle."] })), overdrawn && addon.overagePricePerUnit !== null && ((0, jsx_runtime_1.jsxs)("p", { className: "flex items-center gap-1.5 text-xs text-danger", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "size-3.5 shrink-0", "aria-hidden": "true" }), "Billed at ", (0, plan_pricing_js_1.formatPrice)(addon.overagePricePerUnit), " per ", addon.unit, " beyond your allowance."] })), !overdrawn && addon.overagePolicy === "reject" && addon.balance === 0 && ((0, jsx_runtime_1.jsxs)("p", { className: "flex items-center gap-1.5 text-xs text-danger", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "size-3.5 shrink-0", "aria-hidden": "true" }), "Nothing left this cycle - further use is blocked until it resets."] }))] }));
69
+ : `${formatUnits(addon.balance, addon.unit)} left`, (0, jsx_runtime_1.jsxs)("span", { className: "text-muted", children: [" of ", formatUnits(addon.includedPerCycle, addon.unit)] })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-surface", role: "meter", "aria-label": `${addon.name} remaining`, "aria-valuenow": addon.balance, "aria-valuemin": 0, "aria-valuemax": addon.includedPerCycle, children: (0, jsx_runtime_1.jsx)(motion_js_1.m.div, { className: `h-full rounded-full ${overdrawn ? "bg-danger" : "bg-accent"}`, initial: { width: 0 }, animate: { width: `${overdrawn ? 100 : fraction * 100}%` }, transition: motion_js_1.METER_SPRING }) }), carried > 0 && ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: ["Includes ", formatUnits(carried, addon.unit), " carried over from last cycle."] })), overdrawn && addon.overagePricePerUnit !== null && ((0, jsx_runtime_1.jsxs)("p", { className: "flex items-center gap-1.5 text-xs text-danger-soft-foreground", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "size-3.5 shrink-0", "aria-hidden": "true" }), "Billed at ", (0, plan_pricing_js_1.formatPrice)(addon.overagePricePerUnit), " per ", addon.unit, " beyond your allowance."] })), !overdrawn && addon.overagePolicy === "reject" && addon.balance === 0 && ((0, jsx_runtime_1.jsxs)("p", { className: "flex items-center gap-1.5 text-xs text-danger-soft-foreground", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "size-3.5 shrink-0", "aria-hidden": "true" }), "Nothing left this cycle - further use is blocked until it resets."] }))] }));
70
70
  }
@@ -138,7 +138,7 @@ function ProfileSurface({ onSignOut, theme, onThemeChange, }) {
138
138
  const [next] = [...keys];
139
139
  if (typeof next === "string")
140
140
  onThemeChange(next);
141
- }, children: THEME_OPTIONS.map(({ value, label: optionLabel, icon: Icon }, index) => ((0, jsx_runtime_1.jsxs)(react_1.ToggleButton, { id: value, children: [index > 0 && (0, jsx_runtime_1.jsx)(react_1.ToggleButtonGroup.Separator, {}), (0, jsx_runtime_1.jsx)(Icon, { className: "size-4", "aria-hidden": "true" }), optionLabel] }, value))) })] })), (0, jsx_runtime_1.jsx)(account_sessions_js_1.ConnectAccountSessions, {})] }), (0, jsx_runtime_1.jsx)(react_1.Modal.Footer, { children: (0, jsx_runtime_1.jsxs)(react_1.Button, { slot: "close", fullWidth: true, variant: "tertiary", className: "text-danger", onPress: () => {
141
+ }, children: THEME_OPTIONS.map(({ value, label: optionLabel, icon: Icon }, index) => ((0, jsx_runtime_1.jsxs)(react_1.ToggleButton, { id: value, children: [index > 0 && (0, jsx_runtime_1.jsx)(react_1.ToggleButtonGroup.Separator, {}), (0, jsx_runtime_1.jsx)(Icon, { className: "size-4", "aria-hidden": "true" }), optionLabel] }, value))) })] })), (0, jsx_runtime_1.jsx)(account_sessions_js_1.ConnectAccountSessions, {})] }), (0, jsx_runtime_1.jsx)(react_1.Modal.Footer, { children: (0, jsx_runtime_1.jsxs)(react_1.Button, { slot: "close", fullWidth: true, variant: "danger-soft", onPress: () => {
142
142
  void signOut().then(onSignOut);
143
143
  }, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.LogOut, { className: "size-4 shrink-0" }), "Sign out"] }) })] }) }) }));
144
144
  }
@@ -130,7 +130,7 @@ function ConnectWorkspaceSwitcher({ onSwitched, row = true, className, alwaysSho
130
130
  // A 32px tile rather than a 20px glyph, so the initials land on the
131
131
  // same optical centre the avatar below them uses - see
132
132
  // `SIDEBAR_ROW_TILE`. Collapsed, this tile *is* the switcher.
133
- (0, jsx_runtime_1.jsxs)(react_2.Button, { variant: "tertiary", "aria-label": `Workspace: ${label}. Switch workspace`, className: (0, shell_context_js_1.sidebarRowClass)({ tile: true, className }), children: [(0, jsx_runtime_1.jsx)("span", { className: "flex size-8 shrink-0 items-center justify-center rounded-lg bg-accent-soft text-xs font-semibold text-accent", children: workspaceInitials(label) }), (0, jsx_runtime_1.jsx)(shell_context_js_1.SidebarLabel, { className: "text-sm font-medium text-foreground", children: label }), (0, jsx_runtime_1.jsx)(motion_js_1.m.span, { className: "shrink-0 text-muted", animate: { opacity: collapsed ? 0 : 1 }, transition: collapsed ? { duration: 0.1 } : { duration: 0.18, delay: 0.1 }, "aria-hidden": true, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "size-4" }) })] })) : ((0, jsx_runtime_1.jsxs)(react_2.Button, { variant: "secondary", size: "sm", className: "w-full justify-between", children: [(0, jsx_runtime_1.jsx)("span", { className: "truncate", children: label }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "size-4 shrink-0 text-muted", "aria-hidden": "true" })] })), (0, jsx_runtime_1.jsx)(react_2.Dropdown.Popover, { children: (0, jsx_runtime_1.jsxs)(react_2.ListBox, { children: [workspaces.map((workspace) => ((0, jsx_runtime_1.jsxs)(react_2.ListBox.Item, { id: workspace.workspaceId, textValue: workspace.name, isDisabled: !selectable(workspace) || switching !== null, onAction: () => void handleSwitch(workspace), children: [(0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate text-sm", children: workspace.name }), selectable(workspace) ? null : ((0, jsx_runtime_1.jsx)("p", { className: "truncate text-xs text-muted", children: reasonUnavailable(workspace) }))] }), (0, jsx_runtime_1.jsx)(react_2.ListBox.ItemIndicator, {})] }, workspace.workspaceId))), alwaysShow ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [workspaces.length > 0 && (0, jsx_runtime_1.jsx)(react_2.Separator, {}), (0, jsx_runtime_1.jsxs)(react_2.ListBox.Item, { id: "__create_workspace", textValue: "Create workspace", onAction: () => setIsCreateOpen(true), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { className: "size-4 shrink-0 text-muted", "aria-hidden": true }), (0, jsx_runtime_1.jsx)("p", { className: "truncate text-sm", children: "Create workspace" })] })] })) : null] }) })] }), error && !collapsed ? (0, jsx_runtime_1.jsx)("p", { className: "mt-1 px-2 text-xs text-danger", children: error }) : null, alwaysShow ? ((0, jsx_runtime_1.jsx)(create_workspace_form_js_1.ConnectCreateWorkspaceModal, { isOpen: isCreateOpen, onOpenChange: setIsCreateOpen, onCreated: (workspace) => void handleCreated(workspace) })) : null] }));
133
+ (0, jsx_runtime_1.jsxs)(react_2.Button, { variant: "tertiary", "aria-label": `Workspace: ${label}. Switch workspace`, className: (0, shell_context_js_1.sidebarRowClass)({ tile: true, className }), children: [(0, jsx_runtime_1.jsx)("span", { className: "flex size-8 shrink-0 items-center justify-center rounded-lg bg-accent-soft text-xs font-semibold text-accent-soft-foreground", children: workspaceInitials(label) }), (0, jsx_runtime_1.jsx)(shell_context_js_1.SidebarLabel, { className: "text-sm font-medium text-foreground", children: label }), (0, jsx_runtime_1.jsx)(motion_js_1.m.span, { className: "shrink-0 text-muted", animate: { opacity: collapsed ? 0 : 1 }, transition: collapsed ? { duration: 0.1 } : { duration: 0.18, delay: 0.1 }, "aria-hidden": true, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "size-4" }) })] })) : ((0, jsx_runtime_1.jsxs)(react_2.Button, { variant: "secondary", size: "sm", className: "w-full justify-between", children: [(0, jsx_runtime_1.jsx)("span", { className: "truncate", children: label }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "size-4 shrink-0 text-muted", "aria-hidden": "true" })] })), (0, jsx_runtime_1.jsx)(react_2.Dropdown.Popover, { children: (0, jsx_runtime_1.jsxs)(react_2.ListBox, { children: [workspaces.map((workspace) => ((0, jsx_runtime_1.jsxs)(react_2.ListBox.Item, { id: workspace.workspaceId, textValue: workspace.name, isDisabled: !selectable(workspace) || switching !== null, onAction: () => void handleSwitch(workspace), children: [(0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate text-sm", children: workspace.name }), selectable(workspace) ? null : ((0, jsx_runtime_1.jsx)("p", { className: "truncate text-xs text-muted", children: reasonUnavailable(workspace) }))] }), (0, jsx_runtime_1.jsx)(react_2.ListBox.ItemIndicator, {})] }, workspace.workspaceId))), alwaysShow ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [workspaces.length > 0 && (0, jsx_runtime_1.jsx)(react_2.Separator, {}), (0, jsx_runtime_1.jsxs)(react_2.ListBox.Item, { id: "__create_workspace", textValue: "Create workspace", onAction: () => setIsCreateOpen(true), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { className: "size-4 shrink-0 text-muted", "aria-hidden": true }), (0, jsx_runtime_1.jsx)("p", { className: "truncate text-sm", children: "Create workspace" })] })] })) : null] }) })] }), error && !collapsed ? (0, jsx_runtime_1.jsx)("p", { className: "mt-1 px-2 text-xs text-danger-soft-foreground", children: error }) : null, alwaysShow ? ((0, jsx_runtime_1.jsx)(create_workspace_form_js_1.ConnectCreateWorkspaceModal, { isOpen: isCreateOpen, onOpenChange: setIsCreateOpen, onCreated: (workspace) => void handleCreated(workspace) })) : null] }));
134
134
  }
135
135
  /** Belongs to the user, and usable here. The same two facts `switch` enforces. */
136
136
  function selectable(workspace) {
@@ -11,7 +11,7 @@ const provider_js_1 = require("./provider.js");
11
11
  *
12
12
  * Connect answers any of these paths for a signed-in user on the bare URL -
13
13
  * these two params only add the `"Back to <your app>"` banner
14
- * (`ApplicationReturnBar` on Connect's side). It validates `return_to`
14
+ * (the hand-off strip in `ShellAnnouncements` on Connect's side). It validates `return_to`
15
15
  * against this `client_id`'s registered redirect origins and silently drops
16
16
  * the banner if they don't match, so a wrong or stale `appUrl`/`clientId`
17
17
  * pairing degrades to what linking there did before rather than erroring.
@@ -37,8 +37,17 @@ class ConnectError extends Error {
37
37
  * already retries these internally (see `ConnectClientOptions.maxRetries`);
38
38
  * this is for callers deciding whether to queue their own retry after the
39
39
  * SDK has given up.
40
+ *
41
+ * **A 503 is retryable only when it carries `Retry-After`.** Connect sends
42
+ * that header on every 503 it means to be retried - a dependency blip, a
43
+ * contended transaction - and leaves it off when the dependency is
44
+ * *misconfigured* (a revoked provider key, say), where every retry gets the
45
+ * identical answer. Retrying those anyway cost a page three round trips and
46
+ * two sleeps per read to learn nothing.
40
47
  */
41
48
  get isRetryable() {
49
+ if (this.status === 503)
50
+ return this.retryAfterMs !== null;
42
51
  return this.status === 0 || this.status === 429 || this.status >= 500;
43
52
  }
44
53
  /**
@@ -183,4 +183,4 @@ class ConnectClient {
183
183
  }
184
184
  exports.ConnectClient = ConnectClient;
185
185
  /** Kept in step with `package.json` by `sdk.test.ts`. */
186
- exports.SDK_VERSION = "0.45.0";
186
+ exports.SDK_VERSION = "0.47.0";
@@ -189,7 +189,10 @@ async function request(options, spec) {
189
189
  return (await readBody(response));
190
190
  }
191
191
  lastError = toConnectError(response, await readBody(response), endpoint);
192
- if (!RETRYABLE_STATUS.has(response.status))
192
+ // The status decides, and then the error's own verdict - which is what
193
+ // keeps a 503 sent without `Retry-After` from being replayed. See
194
+ // `ConnectError.isRetryable`.
195
+ if (!RETRYABLE_STATUS.has(response.status) || !lastError.isRetryable)
193
196
  throw lastError;
194
197
  }
195
198
  finally {
@@ -35,5 +35,16 @@ class DriveResource {
35
35
  });
36
36
  return result.url;
37
37
  }
38
+ /**
39
+ * How much of the workspace's Drive is used, against its limit. Needs
40
+ * `drive:read` and an active subscription to your application.
41
+ */
42
+ getUsage(input) {
43
+ return (0, http_js_1.request)(this.transport, {
44
+ method: "GET",
45
+ path: "/drive/usage",
46
+ query: { workspaceId: input.workspaceId },
47
+ });
48
+ }
38
49
  }
39
50
  exports.DriveResource = DriveResource;
@@ -56,6 +56,22 @@ class IntegrationsResource {
56
56
  json: { workspaceId, toolkitSlug },
57
57
  });
58
58
  }
59
+ /**
60
+ * One raw request to a connected provider's own API, for an endpoint the
61
+ * toolkit has no action for (Zoho Books ships no "create sales order").
62
+ *
63
+ * **Resolves whenever Connect relayed the request**, whatever the provider
64
+ * answered: read `status` and `data` for that. A provider-side 4xx is a
65
+ * business answer ("that GSTIN is invalid") rather than a transport
66
+ * failure, so it is not thrown as one.
67
+ */
68
+ async request(workspaceId, input) {
69
+ return (0, http_js_1.request)(this.transport, {
70
+ method: "POST",
71
+ path: "/integrations/proxy",
72
+ json: { workspaceId, ...input },
73
+ });
74
+ }
59
75
  /** Calls a Composio action against this workspace's connected account for the named toolkit. */
60
76
  async execute(workspaceId, input) {
61
77
  return (0, http_js_1.request)(this.transport, {
@@ -113,11 +113,20 @@ class WorkspacesResource {
113
113
  * Connect roles - so you never reimplement Connect's role resolution, and
114
114
  * you are told nothing about the customer's Connect account beyond what your
115
115
  * application needs.
116
+ *
117
+ * **Seat holders only, by default.** A workspace member with no seat on your
118
+ * application cannot sign in to it, so they are not part of your team and
119
+ * do not belong in an owner picker or an assignee list. Pass
120
+ * `includeUnassigned: true` from the screen where an admin *gives* a seat -
121
+ * it needs the people who do not have one yet. That form needs the signed-in
122
+ * member to be able to change access (a 403 otherwise), and every row
123
+ * carries `assigned` so the screen can tell the two apart.
116
124
  */
117
125
  async members(options) {
118
126
  const result = await (0, http_js_1.request)(this.transport, {
119
127
  method: "GET",
120
128
  path: "/workspaces/members",
129
+ query: { include: options.includeUnassigned ? "unassigned" : undefined },
121
130
  headers: (0, acting_as_js_1.actingAs)(options.asUser),
122
131
  });
123
132
  return result.members;
@@ -1,10 +1,58 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.ConnectPageShell = ConnectPageShell;
4
37
  const jsx_runtime_1 = require("react/jsx-runtime");
38
+ const React = __importStar(require("react"));
5
39
  const react_1 = require("react");
6
40
  const page_header_js_1 = require("./page-header.js");
7
41
  const cn_js_1 = require("../cn.js");
42
+ /**
43
+ * React's `<ViewTransition>`, when the host's React has one.
44
+ *
45
+ * It ships in the React canary that Next's App Router bundles and in no stable
46
+ * React yet - 19.2 exports nothing by that name. So it is looked up rather
47
+ * than imported: a static `import { ViewTransition }` resolves to `undefined`
48
+ * on a Vite or Storybook host and renders as an invalid element type, which
49
+ * would take down every page drawn in this shell to animate it. Absent, the
50
+ * page renders exactly as it did before, with no motion.
51
+ *
52
+ * Read by a computed key so a bundler doing strict export checks does not
53
+ * warn that `react` has no such export - on those hosts it genuinely has not.
54
+ */
55
+ const RouteTransition = React["ViewTransition"];
8
56
  /**
9
57
  * The frame a page opens with: its vertical rhythm, its header, and the
10
58
  * suspended regions beneath.
@@ -23,9 +71,23 @@ const cn_js_1 = require("../cn.js");
23
71
  * never be made to wait, not that every title can be static.
24
72
  *
25
73
  * `space-y-5` is the page gap, owned here so it has exactly one definition.
74
+ *
75
+ * **It is also where the route transition lives**, because it is the one
76
+ * thing every page opens with. A transition has to wrap the *page*: a layout
77
+ * persists across navigation, so its enter and exit never fire, and a
78
+ * `template.tsx` remounts its subtree on every navigation, discarding the
79
+ * form drafts and scroll positions Next's `<Activity>` keeps. Where a layout
80
+ * draws the shell instead - a record with tabs - entering and leaving the
81
+ * record animates and switching tabs does not, which is right: a tab is the
82
+ * same place. `default="none"` keeps it still during anything else - a
83
+ * filter written to the URL, a `router.refresh()` after a save. The
84
+ * animations are `connect-page-enter`/`connect-page-exit` in `theme.css`.
26
85
  */
27
- function ConnectPageShell({ title, description, action, clamp, above, children, className, }) {
28
- return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)("space-y-5", className), children: [above, title !== undefined && ((0, jsx_runtime_1.jsx)(page_header_js_1.ConnectPageHeader, { title: title, description: description, action: action, clamp: clamp })), children] }));
86
+ function ConnectPageShell({ title, description, action, clamp, above, children, className, transition = true, }) {
87
+ const page = ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)("space-y-5", className), children: [above, title !== undefined && ((0, jsx_runtime_1.jsx)(page_header_js_1.ConnectPageHeader, { title: title, description: description, action: action, clamp: clamp })), children] }));
88
+ if (!transition || !RouteTransition)
89
+ return page;
90
+ return ((0, jsx_runtime_1.jsx)(RouteTransition, { enter: "connect-page-enter", exit: "connect-page-exit", default: "none", children: page }));
29
91
  }
30
92
  /**
31
93
  * The page header, for a detail route that can only name itself once its data
@@ -304,7 +304,7 @@ function PaymentRetryBanner({ blocker, className, }) {
304
304
  const [dismissed, setDismissed] = useState(() => dismissKey !== null && typeof window !== "undefined" && window.localStorage.getItem(dismissKey) === "1");
305
305
  if (dismissed)
306
306
  return null;
307
- return (_jsx(MotionProvider, { children: _jsxs(m.div, { className: cn("flex flex-wrap items-center justify-between gap-3 rounded-lg bg-accent-soft px-3 py-2 text-sm text-accent", className), role: "status", initial: { opacity: 0, y: -6 }, animate: { opacity: 1, y: 0 }, transition: ENTER, children: [_jsxs("span", { className: "flex items-center gap-2", children: [_jsx(AlertTriangle, { className: "size-4 shrink-0", "aria-hidden": "true" }), blocker.message, blocker.graceEndsAt && ` Resolve by ${formatDate(blocker.graceEndsAt)} to avoid losing access.`] }), _jsxs("div", { className: "flex items-center gap-2", children: [blocker.resolveUrl && (_jsxs(Button, { variant: "secondary", onPress: () => openConnect(blocker.resolveUrl), children: ["Resolve payment", _jsx(ExternalLink, { className: "size-3.5" })] })), _jsx(Button, { variant: "tertiary", isIconOnly: true, "aria-label": "Dismiss", onPress: () => {
307
+ return (_jsx(MotionProvider, { children: _jsxs(m.div, { className: cn("flex flex-wrap items-center justify-between gap-3 rounded-lg bg-accent-soft px-3 py-2 text-sm text-accent-soft-foreground", className), role: "status", initial: { opacity: 0, y: -6 }, animate: { opacity: 1, y: 0 }, transition: ENTER, children: [_jsxs("span", { className: "flex items-center gap-2", children: [_jsx(AlertTriangle, { className: "size-4 shrink-0", "aria-hidden": "true" }), blocker.message, blocker.graceEndsAt && ` Resolve by ${formatDate(blocker.graceEndsAt)} to avoid losing access.`] }), _jsxs("div", { className: "flex items-center gap-2", children: [blocker.resolveUrl && (_jsxs(Button, { variant: "secondary", onPress: () => openConnect(blocker.resolveUrl), children: ["Resolve payment", _jsx(ExternalLink, { className: "size-3.5" })] })), _jsx(Button, { variant: "tertiary", isIconOnly: true, "aria-label": "Dismiss", onPress: () => {
308
308
  setDismissed(true);
309
309
  if (dismissKey && typeof window !== "undefined")
310
310
  window.localStorage.setItem(dismissKey, "1");
@@ -92,7 +92,7 @@ export function ConnectAccountSessions({ className, maxVisible = 4 }) {
92
92
  const hasOtherSessions = sessions.some((session) => !session.isCurrent);
93
93
  const isCollapsible = maxVisible > 0 && sessions.length > maxVisible;
94
94
  const visible = isCollapsible && !showAll ? sessions.slice(0, maxVisible) : sessions;
95
- return (_jsx(MotionProvider, { children: _jsxs("section", { className: cn("flex flex-col gap-3", className), children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("p", { className: "flex items-center gap-2 text-sm font-medium text-foreground", children: ["Active sessions", _jsx(Chip, { size: "sm", variant: "soft", children: sessions.length })] }), _jsx("p", { className: "text-xs text-muted", children: "Devices signed in to your Connect account." })] }), hasOtherSessions && (_jsx(Button, { size: "sm", variant: "tertiary", className: "shrink-0 text-xs text-danger", isPending: isRevokingOthers, isDisabled: isRevokingOthers, onPress: () => {
95
+ return (_jsx(MotionProvider, { children: _jsxs("section", { className: cn("flex flex-col gap-3", className), children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("p", { className: "flex items-center gap-2 text-sm font-medium text-foreground", children: ["Active sessions", _jsx(Chip, { size: "sm", variant: "soft", children: sessions.length })] }), _jsx("p", { className: "text-xs text-muted", children: "Devices signed in to your Connect account." })] }), hasOtherSessions && (_jsx(Button, { size: "sm", variant: "tertiary", className: "shrink-0 text-xs text-danger-soft-foreground", isPending: isRevokingOthers, isDisabled: isRevokingOthers, onPress: () => {
96
96
  setIsRevokingOthers(true);
97
97
  void mutateResource("/sessions/revoke-others")
98
98
  .then(() => {
@@ -117,9 +117,9 @@ export function aiStatusTone(value) {
117
117
  /** HeroUI semantic tokens only, so these inherit whatever theme the host defined. */
118
118
  export const AI_TONE_CHIP = {
119
119
  neutral: "bg-surface-tertiary text-foreground",
120
- positive: "bg-success/15 text-success",
121
- warning: "bg-warning/20 text-warning",
122
- danger: "bg-danger/15 text-danger",
120
+ positive: "bg-success-soft text-success-soft-foreground",
121
+ warning: "bg-warning-soft text-warning-soft-foreground",
122
+ danger: "bg-danger-soft text-danger-soft-foreground",
123
123
  };
124
124
  export const AI_TONE_DOT = {
125
125
  neutral: "bg-muted",
@@ -24,9 +24,9 @@ function Panel({ title, children }) {
24
24
  }
25
25
  const TONE_TEXT = {
26
26
  neutral: "text-muted",
27
- positive: "text-success",
28
- warning: "text-warning",
29
- danger: "text-danger",
27
+ positive: "text-success-soft-foreground",
28
+ warning: "text-warning-soft-foreground",
29
+ danger: "text-danger-soft-foreground",
30
30
  };
31
31
  function StatusChip({ value }) {
32
32
  return (_jsx("span", { className: `inline-block rounded-full px-1.5 py-0.5 text-[10px] font-medium ${AI_TONE_CHIP[aiStatusTone(value)]}`, children: String(value) }));
@@ -196,7 +196,7 @@ export function AiMetrics({ title, metrics }) {
196
196
  : metric.deltaMeaning === "up-is-good"
197
197
  ? rising
198
198
  : !rising;
199
- return (_jsxs("div", { className: "rounded-md bg-surface-secondary p-2.5", children: [_jsx("p", { className: "truncate text-[11px] text-muted", children: metric.label }), _jsx("p", { className: "mt-0.5 text-base font-semibold tabular-nums", children: formatAiValue(metric.value, metric.format) }), delta !== null && (_jsxs("p", { className: `mt-0.5 flex items-center gap-0.5 text-[11px] tabular-nums ${good === null ? "text-muted" : good ? "text-success" : "text-danger"}`, children: [_jsx(Arrow, { className: "size-3", "aria-hidden": true }), formatAiValue(Math.abs(delta), "percent")] })), metric.hint && _jsx("p", { className: "mt-0.5 text-[10px] text-muted", children: metric.hint })] }, metric.label));
199
+ return (_jsxs("div", { className: "rounded-md bg-surface-secondary p-2.5", children: [_jsx("p", { className: "truncate text-[11px] text-muted", children: metric.label }), _jsx("p", { className: "mt-0.5 text-base font-semibold tabular-nums", children: formatAiValue(metric.value, metric.format) }), delta !== null && (_jsxs("p", { className: `mt-0.5 flex items-center gap-0.5 text-[11px] tabular-nums ${good === null ? "text-muted" : good ? "text-success-soft-foreground" : "text-danger-soft-foreground"}`, children: [_jsx(Arrow, { className: "size-3", "aria-hidden": true }), formatAiValue(Math.abs(delta), "percent")] })), metric.hint && _jsx("p", { className: "mt-0.5 text-[10px] text-muted", children: metric.hint })] }, metric.label));
200
200
  }) }) }));
201
201
  }
202
202
  /** `showTimeline`: dated events, oldest first, on a single rail. */
@@ -209,7 +209,7 @@ export function AiDetails({ title, fields }) {
209
209
  }
210
210
  /** `showSteps`: an ordered procedure. `renderDetail` is how the panel feeds each step back through its markdown renderer. */
211
211
  export function AiSteps({ title, steps, renderDetail, }) {
212
- return (_jsx(Panel, { title: title, children: _jsx("ol", { className: "space-y-2.5", children: steps.map((step, index) => (_jsxs("li", { className: "flex gap-2.5", children: [_jsx("span", { className: "mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-full bg-accent/12 text-[10px] font-semibold text-accent", children: index + 1 }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-xs font-medium", children: step.title }), step.detail && (_jsx("div", { className: "mt-0.5 text-xs text-muted", children: renderDetail ? renderDetail(step.detail) : _jsx("span", { className: "whitespace-pre-wrap", children: step.detail }) }))] })] }, step.title))) }) }));
212
+ return (_jsx(Panel, { title: title, children: _jsx("ol", { className: "space-y-2.5", children: steps.map((step, index) => (_jsxs("li", { className: "flex gap-2.5", children: [_jsx("span", { className: "mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-full bg-accent-soft text-[10px] font-semibold text-accent-soft-foreground", children: index + 1 }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-xs font-medium", children: step.title }), step.detail && (_jsx("div", { className: "mt-0.5 text-xs text-muted", children: renderDetail ? renderDetail(step.detail) : _jsx("span", { className: "whitespace-pre-wrap", children: step.detail }) }))] })] }, step.title))) }) }));
213
213
  }
214
214
  /**
215
215
  * `showSources`: what a grounded answer actually rested on. The point of a
@@ -55,7 +55,7 @@ function AppTile({ app, onOpen }) {
55
55
  // there is nothing to open and nothing to buy.
56
56
  const disabled = app.subscribed && app.launchUrl === null;
57
57
  const shared = "group/tile flex w-full flex-col items-center gap-2 rounded-2xl p-3 text-center outline-offset-2 transition-colors duration-200 focus-visible:outline-2 focus-visible:outline-focus";
58
- const body = (_jsxs(_Fragment, { children: [_jsxs("span", { className: "relative flex size-14 items-center justify-center overflow-hidden rounded-[0.8125rem] bg-surface-secondary text-muted transition-colors duration-200 group-hover/tile:bg-default group-hover/tile:text-foreground", children: [_jsx(AppGlyph, { app: app }), app.needsAttention && (_jsx("span", { className: "absolute -right-0.5 -top-0.5 flex size-4 items-center justify-center rounded-full bg-warning text-warning-foreground", children: _jsx(AlertCircle, { className: "size-3", "aria-hidden": "true" }) })), !app.subscribed && (_jsx("span", { className: "absolute -right-0.5 -top-0.5 flex size-4 items-center justify-center rounded-full bg-default text-muted opacity-0 transition-opacity duration-200 group-hover/tile:opacity-100", children: _jsx(Plus, { className: "size-3", "aria-hidden": "true" }) }))] }), _jsx("span", { className: "line-clamp-2 text-xs font-medium leading-tight text-foreground", children: app.name }), app.isCurrent ? (_jsx("span", { className: "text-[0.625rem] uppercase tracking-wide text-accent", children: "Current" })) : (!app.subscribed && (_jsxs("span", { className: "flex items-center gap-0.5 text-[0.625rem] uppercase tracking-wide text-muted opacity-0 transition-opacity duration-200 group-hover/tile:opacity-100", children: ["Subscribe", _jsx(ArrowUpRight, { className: "size-2.5", "aria-hidden": "true" })] })))] }));
58
+ const body = (_jsxs(_Fragment, { children: [_jsxs("span", { className: "relative flex size-14 items-center justify-center overflow-hidden rounded-[0.8125rem] bg-surface-secondary text-muted transition-colors duration-200 group-hover/tile:bg-default group-hover/tile:text-foreground", children: [_jsx(AppGlyph, { app: app }), app.needsAttention && (_jsx("span", { className: "absolute -right-0.5 -top-0.5 flex size-4 items-center justify-center rounded-full bg-warning text-warning-foreground", children: _jsx(AlertCircle, { className: "size-3", "aria-hidden": "true" }) })), !app.subscribed && (_jsx("span", { className: "absolute -right-0.5 -top-0.5 flex size-4 items-center justify-center rounded-full bg-default text-muted opacity-0 transition-opacity duration-200 group-hover/tile:opacity-100", children: _jsx(Plus, { className: "size-3", "aria-hidden": "true" }) }))] }), _jsx("span", { className: "line-clamp-2 text-xs font-medium leading-tight text-foreground", children: app.name }), app.isCurrent ? (_jsx("span", { className: "text-[0.625rem] uppercase tracking-wide text-accent-soft-foreground", children: "Current" })) : (!app.subscribed && (_jsxs("span", { className: "flex items-center gap-0.5 text-[0.625rem] uppercase tracking-wide text-muted opacity-0 transition-opacity duration-200 group-hover/tile:opacity-100", children: ["Subscribe", _jsx(ArrowUpRight, { className: "size-2.5", "aria-hidden": "true" })] })))] }));
59
59
  if (disabled) {
60
60
  return (_jsx("div", { className: `${shared} cursor-default opacity-60`, title: `${app.name} has no launch URL configured`, children: body }));
61
61
  }
@@ -136,7 +136,7 @@ export function ConnectAppsGrid({ className, label = "Switch app", variant = "ro
136
136
  };
137
137
  const showSearch = apps.length > SEARCH_THRESHOLD;
138
138
  const isEmpty = !isLoading && !error && mine.length === 0 && explore.length === 0;
139
- return (_jsx(MotionProvider, { children: _jsxs(Modal, { children: [variant === "row" ? (_jsxs(Button, { variant: "tertiary", "aria-label": label, className: sidebarRowClass({ className }), children: [_jsx("span", { className: "flex size-5 shrink-0 items-center justify-center", children: _jsx(LayoutGrid, { className: "size-5" }) }), _jsx(SidebarLabel, { children: label })] })) : (_jsx(Button, { isIconOnly: true, variant: "primary", "aria-label": label, className: className, children: _jsx(LayoutGrid, { className: "size-5" }) })), _jsx(Modal.Backdrop, { children: _jsx(Modal.Container, { children: _jsxs(Modal.Dialog, { className: "sm:max-w-lg", children: [_jsx(Modal.CloseTrigger, {}), _jsx(Modal.Header, { children: _jsx(Modal.Heading, { children: "Your apps" }) }), _jsxs(Modal.Body, { className: "space-y-4", children: [showSearch && (_jsxs(TextField, { className: "relative", variant: "secondary", value: query, onChange: setQuery, children: [_jsx(Label, { className: "sr-only", children: "Filter apps" }), _jsx(Input, { className: "pr-9", placeholder: "Filter apps" }), _jsx(Search, { className: "pointer-events-none absolute right-3 top-1/2 size-4 -translate-y-1/2 text-muted", "aria-hidden": "true" })] })), isLoading && _jsx(GridSkeleton, {}), !isLoading && error && (_jsx("p", { className: "py-6 text-center text-sm text-danger", children: "Could not load your apps." })), isEmpty && (_jsx("p", { className: "py-6 text-center text-sm text-muted", children: query
139
+ return (_jsx(MotionProvider, { children: _jsxs(Modal, { children: [variant === "row" ? (_jsxs(Button, { variant: "tertiary", "aria-label": label, className: sidebarRowClass({ className }), children: [_jsx("span", { className: "flex size-5 shrink-0 items-center justify-center", children: _jsx(LayoutGrid, { className: "size-5" }) }), _jsx(SidebarLabel, { children: label })] })) : (_jsx(Button, { isIconOnly: true, variant: "primary", "aria-label": label, className: className, children: _jsx(LayoutGrid, { className: "size-5" }) })), _jsx(Modal.Backdrop, { children: _jsx(Modal.Container, { children: _jsxs(Modal.Dialog, { className: "sm:max-w-lg", children: [_jsx(Modal.CloseTrigger, {}), _jsx(Modal.Header, { children: _jsx(Modal.Heading, { children: "Your apps" }) }), _jsxs(Modal.Body, { className: "space-y-4", children: [showSearch && (_jsxs(TextField, { className: "relative", variant: "secondary", value: query, onChange: setQuery, children: [_jsx(Label, { className: "sr-only", children: "Filter apps" }), _jsx(Input, { className: "pr-9", placeholder: "Filter apps" }), _jsx(Search, { className: "pointer-events-none absolute right-3 top-1/2 size-4 -translate-y-1/2 text-muted", "aria-hidden": "true" })] })), isLoading && _jsx(GridSkeleton, {}), !isLoading && error && (_jsx("p", { className: "py-6 text-center text-sm text-danger-soft-foreground", children: "Could not load your apps." })), isEmpty && (_jsx("p", { className: "py-6 text-center text-sm text-muted", children: query
140
140
  ? `Nothing matches “${query}”.`
141
141
  : "No apps yet. Anything this workspace can open shows up here." })), !isLoading && !error && (_jsxs(_Fragment, { children: [_jsx(AppsSection, { title: "Your apps", hint: "Open", apps: mine, offset: 0, onOpen: openApp }), _jsx(AppsSection, { title: "Explore", hint: "Subscribe on Connect", apps: explore, offset: mine.length, onOpen: openApp })] }))] })] }) }) })] }) }));
142
142
  }
@@ -93,9 +93,9 @@ export function AskAiHistory({ refreshKey, activeConversationId, onOpenConversat
93
93
  return (_jsx("div", { className: "space-y-2 p-4", "aria-busy": "true", children: [0, 1, 2].map((row) => (_jsx("div", { className: "h-9 animate-pulse rounded-md bg-surface-secondary" }, row))) }));
94
94
  }
95
95
  if (conversations.length === 0) {
96
- return (_jsx("div", { className: "p-4", children: error ? (_jsx("p", { className: "text-xs text-danger", children: error })) : (_jsx("p", { className: "text-center text-xs text-muted", children: "Nothing here yet. Conversations you have are saved automatically." })) }));
96
+ return (_jsx("div", { className: "p-4", children: error ? (_jsx("p", { className: "text-xs text-danger-soft-foreground", children: error })) : (_jsx("p", { className: "text-center text-xs text-muted", children: "Nothing here yet. Conversations you have are saved automatically." })) }));
97
97
  }
98
- return (_jsxs("div", { className: "p-2", children: [error && _jsx("p", { className: "px-2 pb-2 text-xs text-danger", children: error }), _jsx("ul", { className: "space-y-0.5", children: conversations.map((conversation) => {
98
+ return (_jsxs("div", { className: "p-2", children: [error && _jsx("p", { className: "px-2 pb-2 text-xs text-danger-soft-foreground", children: error }), _jsx("ul", { className: "space-y-0.5", children: conversations.map((conversation) => {
99
99
  const isConfirming = confirmingId === conversation.id;
100
100
  const isActive = conversation.id === activeConversationId;
101
101
  return (_jsx("li", { children: isConfirming ? (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-border px-2 py-1.5", children: [_jsx("span", { className: "flex-1 truncate text-xs text-muted", children: "Delete this conversation?" }), _jsx(Button, { size: "sm", variant: "danger", className: "h-7 px-2 text-xs", isDisabled: busyId === conversation.id, onPress: () => void remove(conversation.id), children: "Delete" }), _jsx(Button, { size: "sm", variant: "tertiary", className: "h-7 px-2 text-xs", onPress: () => setConfirmingId(null), children: "Cancel" })] })) : (_jsxs("div", { className: `group flex items-center gap-1 rounded-md ${isActive ? "bg-surface-secondary" : ""}`, children: [_jsxs(Button, { variant: "ghost", className: "h-auto min-w-0 flex-1 justify-start gap-2 px-2 py-1.5 text-start", isDisabled: busyId === conversation.id, onPress: () => void open(conversation.id), children: [_jsx(MessageSquare, { className: "size-3.5 shrink-0 text-muted" }), _jsx("span", { className: "min-w-0 flex-1 truncate text-xs font-normal", children: conversation.title })] }), _jsx(Button, { isIconOnly: true, size: "sm", variant: "ghost", className: "shrink-0", "aria-label": `Delete "${conversation.title}"`, onPress: () => setConfirmingId(conversation.id), children: _jsx(Trash2, { className: "size-3.5 text-muted" }) })] })) }, conversation.id));
@@ -511,7 +511,7 @@ export function ConnectAskAi({ title = "Ask AI", assistantName = "Assistant", pl
511
511
  // Disabled on an empty one rather than hidden: a control that
512
512
  // appears and disappears as you use the panel is harder to find
513
513
  // than one that is always in the same place.
514
- isDisabled: turns.length === 0, onPress: startNewConversation, children: _jsx(Plus, { className: "size-4" }) }), _jsx(Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": showHistory ? "Hide past conversations" : "Past conversations", "aria-pressed": showHistory, onPress: () => setShowHistory((open) => !open), children: _jsx(History, { className: "size-4" }) }), _jsx(Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": "Close", onPress: () => setIsOpen(false), children: _jsx(X, { className: "size-4" }) })] }), showHistory ? (_jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", children: _jsx(AskAiHistory, { refreshKey: writeCount, activeConversationId: conversationId, onOpenConversation: openConversation }) })) : (_jsxs("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [turns.length === 0 && (_jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm text-muted", children: hint }), currentScreen?.suggestions?.length ? (_jsx("div", { className: "flex flex-wrap gap-1.5", children: currentScreen.suggestions.map((suggestion) => (_jsx(Button, { size: "sm", variant: "secondary", className: "h-auto rounded-full px-2.5 py-1 text-start text-xs whitespace-normal", isDisabled: isBusy, onPress: () => void send(suggestion), children: suggestion }, suggestion))) })) : null] })), rendered, isBusy && lastTurn?.role === "user" && (_jsxs("div", { className: "rounded-md border border-border bg-accent/5 p-2.5 text-sm", children: [_jsx("p", { className: "mb-1 text-[11px] font-semibold tracking-wide text-muted uppercase", children: assistantName }), _jsx(ThinkingShimmer, {})] })), error && (_jsxs("div", { role: "alert", className: "flex items-start gap-2 rounded-md border border-danger/30 bg-danger/5 p-2.5 text-xs text-danger", children: [_jsx(TriangleAlert, { className: "mt-px size-3.5 shrink-0", "aria-hidden": true }), _jsx("p", { className: "flex-1", children: error.message }), error.retry && (_jsxs(Button, { size: "sm", variant: "tertiary", className: "h-6 shrink-0 px-2 text-xs", onPress: retry, isDisabled: isBusy, children: [_jsx(RotateCcw, { className: "size-3", "aria-hidden": true }), "Try again"] }))] })), _jsx("div", { ref: bottomRef })] })), _jsxs("form", { onSubmit: handleSubmit, className: "flex gap-2 border-t border-border p-3", children: [_jsxs(TextField, { variant: "secondary", className: "flex-1", children: [_jsx(Label, { className: "sr-only", children: placeholder }), _jsx(Input, { placeholder: placeholder, value: input, onChange: (event) => setInput(event.currentTarget.value), disabled: isBusy })] }), isBusy ? (_jsx(Button, { type: "button", isIconOnly: true, variant: "secondary", "aria-label": "Stop generating", onPress: stop, children: _jsx(Square, { className: "size-3.5" }) })) : (_jsx(Button, { type: "submit", isIconOnly: true, variant: "primary", "aria-label": "Send", isDisabled: !input.trim(), children: _jsx(Send, { className: "size-3.5" }) }))] })] }) })] }));
514
+ isDisabled: turns.length === 0, onPress: startNewConversation, children: _jsx(Plus, { className: "size-4" }) }), _jsx(Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": showHistory ? "Hide past conversations" : "Past conversations", "aria-pressed": showHistory, onPress: () => setShowHistory((open) => !open), children: _jsx(History, { className: "size-4" }) }), _jsx(Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": "Close", onPress: () => setIsOpen(false), children: _jsx(X, { className: "size-4" }) })] }), showHistory ? (_jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", children: _jsx(AskAiHistory, { refreshKey: writeCount, activeConversationId: conversationId, onOpenConversation: openConversation }) })) : (_jsxs("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [turns.length === 0 && (_jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm text-muted", children: hint }), currentScreen?.suggestions?.length ? (_jsx("div", { className: "flex flex-wrap gap-1.5", children: currentScreen.suggestions.map((suggestion) => (_jsx(Button, { size: "sm", variant: "secondary", className: "h-auto rounded-full px-2.5 py-1 text-start text-xs whitespace-normal", isDisabled: isBusy, onPress: () => void send(suggestion), children: suggestion }, suggestion))) })) : null] })), rendered, isBusy && lastTurn?.role === "user" && (_jsxs("div", { className: "rounded-md border border-border bg-accent/5 p-2.5 text-sm", children: [_jsx("p", { className: "mb-1 text-[11px] font-semibold tracking-wide text-muted uppercase", children: assistantName }), _jsx(ThinkingShimmer, {})] })), error && (_jsxs("div", { role: "alert", className: "flex items-start gap-2 rounded-md border border-danger/30 bg-danger-soft p-2.5 text-xs text-danger-soft-foreground", children: [_jsx(TriangleAlert, { className: "mt-px size-3.5 shrink-0", "aria-hidden": true }), _jsx("p", { className: "flex-1", children: error.message }), error.retry && (_jsxs(Button, { size: "sm", variant: "tertiary", className: "h-6 shrink-0 px-2 text-xs", onPress: retry, isDisabled: isBusy, children: [_jsx(RotateCcw, { className: "size-3", "aria-hidden": true }), "Try again"] }))] })), _jsx("div", { ref: bottomRef })] })), _jsxs("form", { onSubmit: handleSubmit, className: "flex gap-2 border-t border-border p-3", children: [_jsxs(TextField, { variant: "secondary", className: "flex-1", children: [_jsx(Label, { className: "sr-only", children: placeholder }), _jsx(Input, { placeholder: placeholder, value: input, onChange: (event) => setInput(event.currentTarget.value), disabled: isBusy })] }), isBusy ? (_jsx(Button, { type: "button", isIconOnly: true, variant: "secondary", "aria-label": "Stop generating", onPress: stop, children: _jsx(Square, { className: "size-3.5" }) })) : (_jsx(Button, { type: "submit", isIconOnly: true, variant: "primary", "aria-label": "Send", isDisabled: !input.trim(), children: _jsx(Send, { className: "size-3.5" }) }))] })] }) })] }));
515
515
  }
516
516
  /** Hoisted for the same reference-equality reason as `ANIMATED` - Streamdown compares it by identity. */
517
517
  const LINK_SAFETY = { enabled: false };
@@ -96,5 +96,5 @@ function ScheduleFields({ editing, onCancel, onSaved, }) {
96
96
  const interval = weeklyHours[day][0];
97
97
  const isOpenDay = interval !== undefined;
98
98
  return (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Switch, { isSelected: isOpenDay, onChange: (open) => toggleDay(day, open), isDisabled: isPending, children: _jsxs(Switch.Content, { children: [_jsx(Switch.Control, { children: _jsx(Switch.Thumb, {}) }), _jsx("span", { className: "w-24 shrink-0 text-sm", children: WEEKDAY_LABELS[day] })] }) }), isOpenDay && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { type: "time", value: minutesToTimeInput(interval.startMinute), onChange: (e) => setDayTime(day, "startMinute", e.currentTarget.value), disabled: isPending }), _jsx("span", { className: "text-xs text-muted", children: "to" }), _jsx(Input, { type: "time", value: minutesToTimeInput(interval.endMinute), onChange: (e) => setDayTime(day, "endMinute", e.currentTarget.value), disabled: isPending })] }))] }, day));
99
- }) }), error && _jsx("p", { className: "text-sm text-danger", children: error }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", onPress: onCancel, isDisabled: isPending, children: "Cancel" }), _jsx(Button, { type: "submit", variant: "primary", isDisabled: !isFormValid, children: isPending ? "Saving..." : isEdit ? "Save changes" : "Save" })] })] }));
99
+ }) }), error && _jsx("p", { className: "text-sm text-danger-soft-foreground", children: error }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", onPress: onCancel, isDisabled: isPending, children: "Cancel" }), _jsx(Button, { type: "submit", variant: "primary", isDisabled: !isFormValid, children: isPending ? "Saving..." : isEdit ? "Save changes" : "Save" })] })] }));
100
100
  }