@ai-matrx/design-system 0.86.354 → 0.86.356

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 (60) hide show
  1. package/CHANGELOG.md +31 -2
  2. package/dist/carousel.js +3 -3
  3. package/dist/{chunk-UGQRAQTE.js → chunk-2PO3N42Q.js} +3 -3
  4. package/dist/{chunk-ZRHVA3DL.js → chunk-AV5C4YLL.js} +2 -1
  5. package/dist/{chunk-ZRHVA3DL.js.map → chunk-AV5C4YLL.js.map} +1 -1
  6. package/dist/chunk-LJKF267O.js +168 -0
  7. package/dist/chunk-LJKF267O.js.map +1 -0
  8. package/dist/{chunk-CFCWQDGC.js → chunk-RLM4RSED.js} +5 -23
  9. package/dist/chunk-RLM4RSED.js.map +1 -0
  10. package/dist/{chunk-FLWSEDUW.js → chunk-ZMUPFGQO.js} +2 -2
  11. package/dist/controls.cjs +86 -1
  12. package/dist/controls.cjs.map +1 -1
  13. package/dist/controls.js +3 -3
  14. package/dist/data-table/drill-chart.cjs +85 -1
  15. package/dist/data-table/drill-chart.cjs.map +1 -1
  16. package/dist/data-table/drill-chart.js +1 -1
  17. package/dist/data-table/index.cjs +85 -1
  18. package/dist/data-table/index.cjs.map +1 -1
  19. package/dist/data-table/index.js +1 -1
  20. package/dist/desktop.cjs +159 -81
  21. package/dist/desktop.cjs.map +1 -1
  22. package/dist/desktop.css +8 -5
  23. package/dist/desktop.d.cts +0 -5
  24. package/dist/desktop.d.ts +0 -5
  25. package/dist/desktop.js +70 -70
  26. package/dist/desktop.js.map +1 -1
  27. package/dist/email-input-dialog.cjs +85 -1
  28. package/dist/email-input-dialog.cjs.map +1 -1
  29. package/dist/email-input-dialog.js +1 -1
  30. package/dist/index.cjs +114 -19
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +38 -22
  33. package/dist/index.d.ts +38 -22
  34. package/dist/index.js +16 -8
  35. package/dist/index.js.map +1 -1
  36. package/dist/lite-table.cjs +86 -19
  37. package/dist/lite-table.cjs.map +1 -1
  38. package/dist/lite-table.js +2 -2
  39. package/dist/notice-material.css +28 -0
  40. package/dist/record-list.cjs +86 -19
  41. package/dist/record-list.cjs.map +1 -1
  42. package/dist/record-list.js +2 -2
  43. package/dist/settings.cjs +1 -0
  44. package/dist/settings.cjs.map +1 -1
  45. package/dist/settings.js +1 -1
  46. package/dist/styles.css +1 -0
  47. package/dist/swipe-deck.cjs +95 -2
  48. package/dist/swipe-deck.cjs.map +1 -1
  49. package/dist/swipe-deck.css +2 -0
  50. package/dist/swipe-deck.js +11 -2
  51. package/dist/swipe-deck.js.map +1 -1
  52. package/dist/text-input-dialog.cjs +85 -1
  53. package/dist/text-input-dialog.cjs.map +1 -1
  54. package/dist/text-input-dialog.js +1 -1
  55. package/package.json +2 -2
  56. package/dist/chunk-CFCWQDGC.js.map +0 -1
  57. package/dist/chunk-HKLIRHT2.js +0 -71
  58. package/dist/chunk-HKLIRHT2.js.map +0 -1
  59. /package/dist/{chunk-UGQRAQTE.js.map → chunk-2PO3N42Q.js.map} +0 -0
  60. /package/dist/{chunk-FLWSEDUW.js.map → chunk-ZMUPFGQO.js.map} +0 -0
package/dist/desktop.css CHANGED
@@ -1309,12 +1309,15 @@
1309
1309
  [data-slot="notices"][data-float] > * {
1310
1310
  pointer-events: auto;
1311
1311
  }
1312
+ /* In a SplitView the stack floats over the viewer only, never over the list or its controls: its
1313
+ left bound is the viewer's edge and its top the columns' top (split-view.tsx useNoticeBounds). */
1314
+ [data-slot="split-view"] > [data-slot="notices"][data-float] {
1315
+ top: calc(12px + var(--split-notices-top, 0px));
1316
+ max-width: calc(100% - 24px - var(--split-notices-left, 0px));
1317
+ }
1318
+ /* The card's material (background, blur, radius, shadow) is THE notice material, shared with the
1319
+ web's toast: ../notice-material.css (imported by styles.css). */
1312
1320
  [data-slot="banner"][data-float] {
1313
- border-radius: 14px;
1314
- background: var(--mx-popover);
1315
- -webkit-backdrop-filter: blur(30px) saturate(1.8);
1316
- backdrop-filter: blur(30px) saturate(1.8);
1317
- box-shadow: var(--mx-shadow-popover);
1318
1321
  color: var(--mx-label);
1319
1322
  padding: 10px 10px 10px 12px;
1320
1323
  }
@@ -1718,11 +1718,6 @@ interface SplitViewProps extends React.HTMLAttributes<HTMLDivElement> {
1718
1718
  */
1719
1719
  narrow?: "list" | "detail";
1720
1720
  }
1721
- /**
1722
- * Mail's, Messages' and Notes' window body: columns side by side under the unified toolbar, each
1723
- * scrolling on its own, divided by hairlines — an optional source column, the list, and the viewer.
1724
- * The columns fill the window's height; nothing scrolls the frame itself.
1725
- */
1726
1721
  declare function SplitView({ header, notices, source, list, listHeader, listFooter, detail, listWidth, detailScrolls, narrow, className, ...props }: SplitViewProps): React.JSX.Element;
1727
1722
 
1728
1723
  interface ResizablePaneProps extends React.HTMLAttributes<HTMLDivElement> {
package/dist/desktop.d.ts CHANGED
@@ -1718,11 +1718,6 @@ interface SplitViewProps extends React.HTMLAttributes<HTMLDivElement> {
1718
1718
  */
1719
1719
  narrow?: "list" | "detail";
1720
1720
  }
1721
- /**
1722
- * Mail's, Messages' and Notes' window body: columns side by side under the unified toolbar, each
1723
- * scrolling on its own, divided by hairlines — an optional source column, the list, and the viewer.
1724
- * The columns fill the window's height; nothing scrolls the frame itself.
1725
- */
1726
1721
  declare function SplitView({ header, notices, source, list, listHeader, listFooter, detail, listWidth, detailScrolls, narrow, className, ...props }: SplitViewProps): React.JSX.Element;
1727
1722
 
1728
1723
  interface ResizablePaneProps extends React.HTMLAttributes<HTMLDivElement> {
package/dist/desktop.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  matchesOrganizationQuery,
20
20
  orderOrganizationsForPicker,
21
21
  organizationColor
22
- } from "./chunk-FLWSEDUW.js";
22
+ } from "./chunk-ZMUPFGQO.js";
23
23
  import {
24
24
  Collapsible,
25
25
  CollapsibleContent,
@@ -42,7 +42,11 @@ import "./chunk-VQQ3O7J5.js";
42
42
  import {
43
43
  Tile
44
44
  } from "./chunk-GMNROZQJ.js";
45
- import "./chunk-HKLIRHT2.js";
45
+ import {
46
+ describeError,
47
+ isRawErrorText,
48
+ peopleErrorSentence
49
+ } from "./chunk-LJKF267O.js";
46
50
  import {
47
51
  Skeleton
48
52
  } from "./chunk-DRSBLHOF.js";
@@ -3072,44 +3076,6 @@ function PlaceStrip({ className, ...props }) {
3072
3076
  // src/desktop/state.tsx
3073
3077
  import * as React20 from "react";
3074
3078
  import { copyText } from "@ai-matrx/kit/clipboard";
3075
-
3076
- // src/desktop/errors.ts
3077
- var RULES = [
3078
- { code: "ENOENT", test: /\b(spawn\s+\S+\s+)?ENOENT\b|command not found|no such file or directory/i, cause: "A program or file it needs isn't on this computer. Install it or update Matrx, then try again." },
3079
- { code: "EACCES", test: /\bEACCES\b|\bEPERM\b|permission denied|operation not permitted|not authori[sz]ed by the user/i, cause: "Matrx isn't allowed to do that. Grant access in System Settings \u203A Privacy & Security, then try again." },
3080
- { code: "EADDRINUSE", test: /\bEADDRINUSE\b|address already in use|port .* (is )?(already )?in use/i, cause: "Another program is already using that port. Stop it, or choose another port." },
3081
- { code: "ECONNREFUSED", test: /\bECONNREFUSED\b|connection refused/i, cause: "The service isn't answering. It may have stopped; start it again." },
3082
- { code: "OFFLINE", test: /\bENOTFOUND\b|\bEAI_AGAIN\b|\bENETUNREACH\b|getaddrinfo|network (is )?unreachable|failed to fetch|offline/i, cause: "Can't reach the internet. Check the connection and try again." },
3083
- { code: "TIMEOUT", test: /\bETIMEDOUT\b|timed? ?out|\bTIMEOUT\b|deadline exceeded/i, cause: "It took too long to answer. Try again in a moment." },
3084
- { code: "ENOSPC", test: /\bENOSPC\b|no space left/i, cause: "The disk is full. Free some space, then try again." },
3085
- { code: "ENOMEM", test: /\bENOMEM\b|out of memory|cannot allocate memory/i, cause: "The computer ran out of memory. Close something and try again." },
3086
- { code: "UNAUTHORIZED", test: /\b401\b|unauthori[sz]ed|invalid (api )?key|not signed in|jwt expired|session expired/i, cause: "You're signed out, or the key was refused. Sign in again." },
3087
- { code: "FORBIDDEN", test: /\b403\b|forbidden/i, cause: "This account isn't allowed to do that." },
3088
- { code: "NOT_FOUND", test: /\b404\b|not found/i, cause: "It isn't there any more. Refresh and try again." },
3089
- { code: "RATE_LIMIT", test: /\b429\b|rate.?limit|too many requests/i, cause: "Too many requests just now. Wait a moment and try again." },
3090
- { code: "SERVER", test: /\b5\d\d\b|internal server error|bad gateway|service unavailable/i, cause: "The server had a problem. Try again in a moment." },
3091
- { code: "EXITED", test: /exited with code|process exited|crashed|SIGKILL|SIGSEGV|SIGTERM/i, cause: "The service stopped unexpectedly. Start it again; Copy details if it keeps happening." }
3092
- ];
3093
- var RAW = /\b[A-Z]{2,}[A-Z_]*\b|[\\/][\w.-]+[\\/]|\bat \S+ \(|\{|\}|errno|stack|undefined|null|\bspawn\b/;
3094
- function rawText(error) {
3095
- if (error instanceof Error) return error.message || error.name;
3096
- if (typeof error === "string") return error;
3097
- if (error && typeof error === "object" && "message" in error && typeof error.message === "string") {
3098
- return error.message;
3099
- }
3100
- return "";
3101
- }
3102
- function describeError(error, fallback = "Something went wrong. Try again.") {
3103
- const detail = rawText(error).trim();
3104
- if (!detail) return { cause: fallback, detail: "" };
3105
- for (const rule of RULES) {
3106
- if (rule.test.test(detail)) return { cause: rule.cause, detail, code: rule.code };
3107
- }
3108
- if (!RAW.test(detail) && detail.length <= 160) return { cause: detail, detail: "" };
3109
- return { cause: fallback, detail };
3110
- }
3111
-
3112
- // src/desktop/state.tsx
3113
3079
  import { jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
3114
3080
  var EMPTY_ICON = /* @__PURE__ */ jsxs22("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.75, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
3115
3081
  /* @__PURE__ */ jsx28("path", { d: "M22 12h-6l-2 3h-4l-2-3H2" }),
@@ -3187,6 +3153,8 @@ var BANNER_ICON = {
3187
3153
  };
3188
3154
  function Banner({ tone, icon, title, message, actions, detail, onDismiss, count, className, ...props }) {
3189
3155
  const [copied, setCopied] = React20.useState(false);
3156
+ if (typeof title === "string" && isRawErrorText(title)) title = peopleErrorSentence(null, title);
3157
+ if (typeof message === "string" && isRawErrorText(message)) message = peopleErrorSentence(null, message);
3190
3158
  const registry = React20.useContext(NoticeStackContext);
3191
3159
  const floating = registry !== null;
3192
3160
  const id = React20.useId();
@@ -4513,15 +4481,47 @@ function EndOfList({ onEndReached }) {
4513
4481
  }
4514
4482
 
4515
4483
  // src/desktop/split-view.tsx
4484
+ import * as React25 from "react";
4516
4485
  import { jsx as jsx33, jsxs as jsxs27 } from "react/jsx-runtime";
4517
4486
  var LIST_WIDTH = { md: "w-70", lg: "w-85" };
4487
+ function useNoticeBounds(frame) {
4488
+ React25.useLayoutEffect(() => {
4489
+ const el = frame.current;
4490
+ if (!el) return;
4491
+ const measure = () => {
4492
+ const box = el.getBoundingClientRect();
4493
+ const columns2 = el.querySelector(':scope > [data-slot="split-columns"]');
4494
+ const viewer = columns2?.querySelector(':scope > [data-slot="split-view-detail"]');
4495
+ const top = columns2 ? columns2.getBoundingClientRect().top - box.top : 0;
4496
+ let left = 0;
4497
+ let below = 0;
4498
+ if (viewer && viewer.offsetWidth > 0) {
4499
+ left = viewer.getBoundingClientRect().left - box.left;
4500
+ } else {
4501
+ const listHeader = columns2?.querySelector('[data-slot="split-view-list-header"]');
4502
+ if (listHeader && listHeader.offsetHeight > 0) below = listHeader.getBoundingClientRect().bottom - box.top - top;
4503
+ }
4504
+ el.style.setProperty("--split-notices-left", `${Math.max(0, Math.round(left))}px`);
4505
+ el.style.setProperty("--split-notices-top", `${Math.max(0, Math.round(top + below))}px`);
4506
+ };
4507
+ measure();
4508
+ if (typeof ResizeObserver === "undefined") return;
4509
+ const observer = new ResizeObserver(measure);
4510
+ observer.observe(el);
4511
+ const columns = el.querySelector(':scope > [data-slot="split-columns"]');
4512
+ if (columns) for (const child of Array.from(columns.children)) observer.observe(child);
4513
+ return () => observer.disconnect();
4514
+ });
4515
+ }
4518
4516
  function SplitView({ header, notices, source, list, listHeader, listFooter, detail, listWidth = "lg", detailScrolls = true, narrow, className, ...props }) {
4519
- return /* @__PURE__ */ jsxs27("div", { "data-slot": "split-view", className: cn2("relative flex h-full min-h-0 flex-col bg-content", className), ...props, children: [
4517
+ const frame = React25.useRef(null);
4518
+ useNoticeBounds(frame);
4519
+ return /* @__PURE__ */ jsxs27("div", { ref: frame, "data-slot": "split-view", className: cn2("relative flex h-full min-h-0 flex-col bg-content", className), ...props, children: [
4520
4520
  header ? /* @__PURE__ */ jsx33("div", { className: "shrink-0 px-5 empty:hidden", children: header }) : null,
4521
4521
  /* @__PURE__ */ jsxs27("div", { "data-slot": "split-columns", "data-narrow": detail !== void 0 ? narrow : void 0, "data-has-source": source ? "" : void 0, className: "flex min-h-0 flex-1", children: [
4522
4522
  source ? /* @__PURE__ */ jsx33("div", { "data-slot": "split-view-source", className: "flex w-52 min-w-40 shrink flex-col overflow-y-auto border-r border-separator py-2", children: source }) : null,
4523
4523
  /* @__PURE__ */ jsxs27("div", { "data-slot": "split-view-list", className: cn2("flex flex-col", detail === void 0 ? "min-w-0 flex-1" : cn2("min-w-56 shrink border-r border-separator", LIST_WIDTH[listWidth])), children: [
4524
- listHeader ? /* @__PURE__ */ jsx33("div", { className: "flex shrink-0 flex-col gap-2 px-3 pb-2 pt-1", children: listHeader }) : null,
4524
+ listHeader ? /* @__PURE__ */ jsx33("div", { "data-slot": "split-view-list-header", className: "flex shrink-0 flex-col gap-2 px-3 pb-2 pt-1", children: listHeader }) : null,
4525
4525
  /* @__PURE__ */ jsx33("div", { className: "min-h-0 flex-1 overflow-y-auto", children: list }),
4526
4526
  listFooter ? /* @__PURE__ */ jsx33("div", { className: "flex shrink-0 items-center justify-center border-t border-separator px-3 py-3", children: listFooter }) : null
4527
4527
  ] }),
@@ -4532,7 +4532,7 @@ function SplitView({ header, notices, source, list, listHeader, listFooter, deta
4532
4532
  }
4533
4533
 
4534
4534
  // src/desktop/resizable-pane.tsx
4535
- import * as React25 from "react";
4535
+ import * as React26 from "react";
4536
4536
  import { jsx as jsx34, jsxs as jsxs28 } from "react/jsx-runtime";
4537
4537
  var STEP = 16;
4538
4538
  var key = (name) => `mx-split:${name}`;
@@ -4586,16 +4586,16 @@ function ResizablePane({
4586
4586
  children,
4587
4587
  ...props
4588
4588
  }) {
4589
- const ref = React25.useRef(null);
4590
- const [size, setSize] = React25.useState(() => saved(autosaveName) ?? defaultSize);
4591
- const [ownState, setOwnState] = React25.useState(() => savedState(autosaveName) ?? (defaultCollapsed ? "collapsed" : "open"));
4592
- const [room, setRoom] = React25.useState(null);
4589
+ const ref = React26.useRef(null);
4590
+ const [size, setSize] = React26.useState(() => saved(autosaveName) ?? defaultSize);
4591
+ const [ownState, setOwnState] = React26.useState(() => savedState(autosaveName) ?? (defaultCollapsed ? "collapsed" : "open"));
4592
+ const [room, setRoom] = React26.useState(null);
4593
4593
  const collapsed = collapsedProp ?? ownState === "collapsed";
4594
4594
  const maximized = !collapsed && (maximizedProp ?? ownState === "maximized");
4595
4595
  const state = collapsed ? "collapsed" : maximized ? "maximized" : "open";
4596
- const live = React25.useRef({ state, onCollapsedChange, onMaximizedChange });
4596
+ const live = React26.useRef({ state, onCollapsedChange, onMaximizedChange });
4597
4597
  live.current = { state, onCollapsedChange, onMaximizedChange };
4598
- const setState = React25.useCallback((next) => {
4598
+ const setState = React26.useCallback((next) => {
4599
4599
  const { state: was, onCollapsedChange: oc, onMaximizedChange: om } = live.current;
4600
4600
  if (next === was) return;
4601
4601
  live.current = { ...live.current, state: next };
@@ -4603,8 +4603,8 @@ function ResizablePane({
4603
4603
  if (was === "collapsed" !== (next === "collapsed")) oc?.(next === "collapsed");
4604
4604
  if (was === "maximized" !== (next === "maximized")) om?.(next === "maximized");
4605
4605
  }, []);
4606
- React25.useEffect(() => saveState(autosaveName, state), [autosaveName, state]);
4607
- React25.useLayoutEffect(() => {
4606
+ React26.useEffect(() => saveState(autosaveName, state), [autosaveName, state]);
4607
+ React26.useLayoutEffect(() => {
4608
4608
  const el = ref.current;
4609
4609
  const frame = el?.closest('[data-slot="page-frame"], [data-slot="split-view"]') ?? el?.parentElement ?? null;
4610
4610
  if (!frame) return;
@@ -4870,7 +4870,7 @@ function CallButton({ icon, label, tone = "default", className, type = "button",
4870
4870
  }
4871
4871
 
4872
4872
  // src/desktop/media.tsx
4873
- import * as React26 from "react";
4873
+ import * as React27 from "react";
4874
4874
  import { jsx as jsx37, jsxs as jsxs30 } from "react/jsx-runtime";
4875
4875
  function Waveform({ peaks, progress = 0, onSeek, className, "aria-label": label, ...props }) {
4876
4876
  const bars = peaks.length;
@@ -4926,7 +4926,7 @@ function peaksOf(samples, bars) {
4926
4926
  const loudest = Math.max(...out);
4927
4927
  return loudest > 0 ? out.map((p) => p / loudest) : out;
4928
4928
  }
4929
- var RecordButton = React26.forwardRef(function RecordButton2({ recording = false, label, className, type = "button", ...props }, ref) {
4929
+ var RecordButton = React27.forwardRef(function RecordButton2({ recording = false, label, className, type = "button", ...props }, ref) {
4930
4930
  return /* @__PURE__ */ jsx37(
4931
4931
  "button",
4932
4932
  {
@@ -5039,7 +5039,7 @@ function SymbolTile({ children, color, size = "sm", className, ...props }) {
5039
5039
  }
5040
5040
 
5041
5041
  // src/desktop/library.tsx
5042
- import * as React27 from "react";
5042
+ import * as React28 from "react";
5043
5043
  import { Fragment as Fragment15, jsx as jsx41, jsxs as jsxs33 } from "react/jsx-runtime";
5044
5044
  var TILE_COLOR = {
5045
5045
  red: "bg-system-red",
@@ -5089,7 +5089,7 @@ function MoreGlyph() {
5089
5089
  ] });
5090
5090
  }
5091
5091
  function ShortcutTile({ color, glyph, title, selected, onSelect, onOpen, run, menu, className, onKeyDown, ...props }) {
5092
- const [asking, setAsking] = React27.useState(null);
5092
+ const [asking, setAsking] = React28.useState(null);
5093
5093
  const confirm = /* @__PURE__ */ jsx41(
5094
5094
  ConfirmDialog,
5095
5095
  {
@@ -5210,7 +5210,7 @@ function FavoriteTile({ title, icon, className, ...props }) {
5210
5210
  }
5211
5211
 
5212
5212
  // src/desktop/store.tsx
5213
- import * as React28 from "react";
5213
+ import * as React29 from "react";
5214
5214
  import { jsx as jsx43, jsxs as jsxs35 } from "react/jsx-runtime";
5215
5215
  function ProgressRing({ value, label, className, ...props }) {
5216
5216
  const r = 10;
@@ -5302,7 +5302,7 @@ function GetButton({ state, name, onPress, onStop, className, disabled, ...props
5302
5302
  ] });
5303
5303
  }
5304
5304
  function StoreShelf({ title, subtitle, action, className, children, ...props }) {
5305
- const id = React28.useId();
5305
+ const id = React29.useId();
5306
5306
  return /* @__PURE__ */ jsxs35("section", { "aria-labelledby": id, "data-slot": "store-shelf", className: cn2("flex flex-col gap-2", className), ...props, children: [
5307
5307
  /* @__PURE__ */ jsxs35("header", { className: "flex items-end gap-3 border-t border-separator pt-3", children: [
5308
5308
  /* @__PURE__ */ jsxs35("span", { className: "flex min-w-0 flex-1 flex-col", children: [
@@ -5370,7 +5370,7 @@ function InfoStrip({ items, className, ...props }) {
5370
5370
  }
5371
5371
 
5372
5372
  // src/desktop/credentials.tsx
5373
- import * as React29 from "react";
5373
+ import * as React30 from "react";
5374
5374
  import { formatDurationMs, groupCode } from "@ai-matrx/kit/format";
5375
5375
  import { jsx as jsx44, jsxs as jsxs36 } from "react/jsx-runtime";
5376
5376
  function CredentialHeader({ icon, title, subtitle, actions, className, ...props }) {
@@ -5450,15 +5450,15 @@ function CodeTimer({ fraction, label, className }) {
5450
5450
  ] });
5451
5451
  }
5452
5452
  function TimedCode({ code, validUntilMs, periodSeconds, now = Date.now, onExpire, className, ...props }) {
5453
- const [at, setAt] = React29.useState(() => now());
5454
- const expired = React29.useRef(false);
5455
- React29.useEffect(() => {
5453
+ const [at, setAt] = React30.useState(() => now());
5454
+ const expired = React30.useRef(false);
5455
+ React30.useEffect(() => {
5456
5456
  expired.current = false;
5457
5457
  const id = setInterval(() => setAt(now()), 1e3);
5458
5458
  return () => clearInterval(id);
5459
5459
  }, [validUntilMs, now]);
5460
5460
  const left = Math.max(0, validUntilMs - at);
5461
- React29.useEffect(() => {
5461
+ React30.useEffect(() => {
5462
5462
  if (left === 0 && !expired.current) {
5463
5463
  expired.current = true;
5464
5464
  onExpire?.();
@@ -5684,10 +5684,10 @@ import { formatCount, formatRelativeTime as formatRelativeTime2 } from "@ai-matr
5684
5684
 
5685
5685
  // src/desktop/popover.tsx
5686
5686
  import * as PopoverPrimitive from "@radix-ui/react-popover";
5687
- import * as React30 from "react";
5687
+ import * as React31 from "react";
5688
5688
  import { jsx as jsx46, jsxs as jsxs38 } from "react/jsx-runtime";
5689
5689
  var POPOVER_ARROW = { width: 18, height: 9 };
5690
- var PopoverContent2 = React30.forwardRef(
5690
+ var PopoverContent2 = React31.forwardRef(
5691
5691
  ({ arrow = true, sideOffset, className, children, ...props }, ref) => /* @__PURE__ */ jsxs38(
5692
5692
  PopoverContent,
5693
5693
  {
@@ -5728,7 +5728,7 @@ function ListRow({ icon, title, secondary, accessory, onSelect, selected = false
5728
5728
  }
5729
5729
 
5730
5730
  // src/desktop/info-hint.tsx
5731
- import * as React31 from "react";
5731
+ import * as React32 from "react";
5732
5732
  import { jsx as jsx48, jsxs as jsxs40 } from "react/jsx-runtime";
5733
5733
  var INFO_HINT_MAX = 140;
5734
5734
  function infoHintText(label, text) {
@@ -5744,7 +5744,7 @@ function InfoGlyph2() {
5744
5744
  ] });
5745
5745
  }
5746
5746
  function InfoHint({ label, text, side = "top", className }) {
5747
- const shown = React31.useMemo(() => infoHintText(label, text), [label, text]);
5747
+ const shown = React32.useMemo(() => infoHintText(label, text), [label, text]);
5748
5748
  return /* @__PURE__ */ jsxs40(Popover, { children: [
5749
5749
  /* @__PURE__ */ jsx48(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx48(
5750
5750
  Button,
@@ -5762,7 +5762,7 @@ function InfoHint({ label, text, side = "top", className }) {
5762
5762
  }
5763
5763
 
5764
5764
  // src/desktop/organization-picker.tsx
5765
- import * as React32 from "react";
5765
+ import * as React33 from "react";
5766
5766
  import { jsx as jsx49, jsxs as jsxs41 } from "react/jsx-runtime";
5767
5767
  var ROLE = { owner: "Owner", admin: "Admin", member: "Member" };
5768
5768
  function roleLabel(role) {
@@ -5777,9 +5777,9 @@ function OrganizationPickerSheet({
5777
5777
  onSwitch,
5778
5778
  onToggleFavorite
5779
5779
  }) {
5780
- const [query, setQuery] = React32.useState("");
5781
- const [chosen, setChosen] = React32.useState(activeOrganizationId);
5782
- React32.useEffect(() => {
5780
+ const [query, setQuery] = React33.useState("");
5781
+ const [chosen, setChosen] = React33.useState(activeOrganizationId);
5782
+ React33.useEffect(() => {
5783
5783
  if (open) {
5784
5784
  setQuery("");
5785
5785
  setChosen(activeOrganizationId);