@ai-matrx/design-system 0.86.355 → 0.86.357

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 (88) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/dist/auth-gate-dialog.js +2 -2
  3. package/dist/carousel.js +8 -7
  4. package/dist/carousel.js.map +1 -1
  5. package/dist/{chunk-UWTODOMD.js → chunk-2BQAHLU3.js} +4 -4
  6. package/dist/chunk-5Z5GTXKL.js +79 -0
  7. package/dist/chunk-5Z5GTXKL.js.map +1 -0
  8. package/dist/{chunk-XXZA2JUP.js → chunk-ACZALXD6.js} +4 -4
  9. package/dist/{chunk-ZRHVA3DL.js → chunk-AV5C4YLL.js} +2 -1
  10. package/dist/{chunk-ZRHVA3DL.js.map → chunk-AV5C4YLL.js.map} +1 -1
  11. package/dist/{chunk-4WSXAP24.js → chunk-BB5LA7FQ.js} +3 -1
  12. package/dist/chunk-BB5LA7FQ.js.map +1 -0
  13. package/dist/{chunk-RLM4RSED.js → chunk-GUGBVQ4T.js} +6 -4
  14. package/dist/{chunk-RLM4RSED.js.map → chunk-GUGBVQ4T.js.map} +1 -1
  15. package/dist/{chunk-LJKF267O.js → chunk-HS6GMHKZ.js} +18 -76
  16. package/dist/chunk-HS6GMHKZ.js.map +1 -0
  17. package/dist/{chunk-ZMUPFGQO.js → chunk-TV2IDHPZ.js} +2 -2
  18. package/dist/{chunk-CFUXEGW2.js → chunk-XZGYKKCE.js} +2 -2
  19. package/dist/clipboard-fallback-dialog.js +2 -2
  20. package/dist/controls.cjs +16 -3
  21. package/dist/controls.cjs.map +1 -1
  22. package/dist/controls.js +6 -5
  23. package/dist/data-table/data-row-window.js +3 -3
  24. package/dist/data-table/drill-chart.cjs +15 -3
  25. package/dist/data-table/drill-chart.cjs.map +1 -1
  26. package/dist/data-table/drill-chart.js +2 -1
  27. package/dist/data-table/drill-chart.js.map +1 -1
  28. package/dist/data-table/index.cjs +15 -3
  29. package/dist/data-table/index.cjs.map +1 -1
  30. package/dist/data-table/index.js +8 -7
  31. package/dist/data-table/index.js.map +1 -1
  32. package/dist/desktop.cjs +26 -5
  33. package/dist/desktop.cjs.map +1 -1
  34. package/dist/desktop.js +15 -7
  35. package/dist/desktop.js.map +1 -1
  36. package/dist/email-input-dialog.cjs +15 -3
  37. package/dist/email-input-dialog.cjs.map +1 -1
  38. package/dist/email-input-dialog.js +4 -3
  39. package/dist/email-input-dialog.js.map +1 -1
  40. package/dist/error-sentence.cjs +130 -0
  41. package/dist/error-sentence.cjs.map +1 -0
  42. package/dist/error-sentence.d.cts +49 -0
  43. package/dist/error-sentence.d.ts +49 -0
  44. package/dist/error-sentence.js +22 -0
  45. package/dist/error-sentence.js.map +1 -0
  46. package/dist/index.cjs +17 -4
  47. package/dist/index.cjs.map +1 -1
  48. package/dist/index.d.cts +3 -1
  49. package/dist/index.d.ts +3 -1
  50. package/dist/index.js +18 -16
  51. package/dist/index.js.map +1 -1
  52. package/dist/item.js +2 -2
  53. package/dist/lite-table.cjs +15 -3
  54. package/dist/lite-table.cjs.map +1 -1
  55. package/dist/lite-table.js +3 -2
  56. package/dist/lite-table.js.map +1 -1
  57. package/dist/notice-stack.cjs +2 -0
  58. package/dist/notice-stack.cjs.map +1 -1
  59. package/dist/notice-stack.d.cts +11 -1
  60. package/dist/notice-stack.d.ts +11 -1
  61. package/dist/notice-stack.js +3 -1
  62. package/dist/record-list.cjs +15 -3
  63. package/dist/record-list.cjs.map +1 -1
  64. package/dist/record-list.js +3 -2
  65. package/dist/record-list.js.map +1 -1
  66. package/dist/sandbox-gate-dialog.js +2 -2
  67. package/dist/scope-mismatch-dialog.js +2 -2
  68. package/dist/settings.cjs +1 -0
  69. package/dist/settings.cjs.map +1 -1
  70. package/dist/settings.js +1 -1
  71. package/dist/swipe-deck.cjs +25 -4
  72. package/dist/swipe-deck.cjs.map +1 -1
  73. package/dist/swipe-deck.css +2 -0
  74. package/dist/swipe-deck.js +12 -2
  75. package/dist/swipe-deck.js.map +1 -1
  76. package/dist/text-input-dialog.cjs +15 -3
  77. package/dist/text-input-dialog.cjs.map +1 -1
  78. package/dist/text-input-dialog.js +4 -3
  79. package/dist/text-input-dialog.js.map +1 -1
  80. package/dist/value-prompts-dialog.js +2 -2
  81. package/dist/writing-field.js +2 -2
  82. package/package.json +12 -2
  83. package/dist/chunk-4WSXAP24.js.map +0 -1
  84. package/dist/chunk-LJKF267O.js.map +0 -1
  85. /package/dist/{chunk-UWTODOMD.js.map → chunk-2BQAHLU3.js.map} +0 -0
  86. /package/dist/{chunk-XXZA2JUP.js.map → chunk-ACZALXD6.js.map} +0 -0
  87. /package/dist/{chunk-ZMUPFGQO.js.map → chunk-TV2IDHPZ.js.map} +0 -0
  88. /package/dist/{chunk-CFUXEGW2.js.map → chunk-XZGYKKCE.js.map} +0 -0
@@ -15,7 +15,8 @@ import {
15
15
  } from "./chunk-K4XCB576.js";
16
16
  import {
17
17
  ErrorActions
18
- } from "./chunk-LJKF267O.js";
18
+ } from "./chunk-5Z5GTXKL.js";
19
+ import "./chunk-HS6GMHKZ.js";
19
20
  import "./chunk-TDMVH77G.js";
20
21
  import {
21
22
  Dialog,
@@ -24,13 +25,13 @@ import {
24
25
  DialogFooter,
25
26
  DialogHeader,
26
27
  DialogTitle
27
- } from "./chunk-UWTODOMD.js";
28
+ } from "./chunk-2BQAHLU3.js";
29
+ import "./chunk-LEPD6TV7.js";
28
30
  import "./chunk-IMQ3RX35.js";
29
31
  import {
30
32
  useIsMobile
31
33
  } from "./chunk-U24MOJWK.js";
32
34
  import "./chunk-NDTDBBMM.js";
33
- import "./chunk-LEPD6TV7.js";
34
35
  import "./chunk-BD77DHUO.js";
35
36
  import "./chunk-BKCJUFD3.js";
36
37
  import "./chunk-MOZRIHJF.js";
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/text-input-dialog.tsx","../src/opener-reclick.ts"],"sourcesContent":["/**\n * TextInputDialog — moved from matrx-frontend components/dialogs/text-input\n * (chat host slots get real package defaults, 2026-10-08).\n *\n * Drop-in replacement for `window.prompt`. Captures a single string of\n * input from the user. Renders a `<Drawer>` (bottom sheet) on mobile\n * and a `<Dialog>` on desktop, per the matrx-admin layout standard\n * (see CLAUDE.md \"Mobile Layout\" — never Dialog on mobile).\n *\n * Local-state component — no global host. Use it like any controlled\n * dialog: hold `open` and `value` in your component, render once.\n *\n * @example\n * const [open, setOpen] = useState(false);\n * const [busy, setBusy] = useState(false);\n *\n * <TextInputDialog\n * open={open}\n * onOpenChange={(o) => !busy && setOpen(o)}\n * title=\"New folder\"\n * placeholder=\"Folder name\"\n * confirmLabel=\"Create\"\n * busy={busy}\n * onConfirm={async (name) => {\n * setBusy(true);\n * try { await createFolder(name); setOpen(false); }\n * finally { setBusy(false); }\n * }}\n * />\n */\n\n\"use client\";\n\nimport * as React from \"react\";\nimport { Loader2Icon as Loader2 } from \"./icons\";\n\nimport { cn } from \"./cn\";\nimport { Button } from \"./button\";\nimport { Input } from \"./controls/doors\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"./dialog\";\nimport {\n Drawer,\n DrawerContent,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerTitle,\n} from \"./drawer\";\nimport { useIsMobile } from \"./use-is-mobile\";\nimport { useWritingBox } from \"./writing-box\";\nimport { isPointOnOpener } from \"./opener-reclick\";\nimport { ErrorActions } from \"./error-box\";\n\nexport interface TextInputDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n title: React.ReactNode;\n description?: React.ReactNode;\n placeholder?: string;\n defaultValue?: string;\n /** Use a textarea instead of a single-line input (for longer descriptions). */\n multiline?: boolean;\n /** Visible rows when `multiline` is true. Default 5. */\n rows?: number;\n confirmLabel?: string;\n cancelLabel?: string;\n /** When true, both buttons are disabled and a spinner shows on confirm. */\n busy?: boolean;\n /**\n * Optional sync validator. Return an error message to display, or\n * null/empty to accept. Empty/whitespace-only input is rejected by\n * default (treated as \"Required\").\n */\n validate?: (value: string) => string | null | undefined;\n /**\n * When true, an empty value is a valid answer: Confirm is enabled and submits \"\".\n * For an \"(optional)\" note. Default false (empty is \"Required\").\n */\n allowEmpty?: boolean;\n onConfirm: (value: string) => void | Promise<void>;\n}\n\nexport function TextInputDialog({\n open,\n onOpenChange,\n title,\n description,\n placeholder,\n defaultValue = \"\",\n multiline = false,\n rows = 5,\n confirmLabel = \"Confirm\",\n cancelLabel = \"Cancel\",\n busy = false,\n validate,\n allowEmpty = false,\n onConfirm,\n}: TextInputDialogProps) {\n const isMobile = useIsMobile();\n const ProTextarea = useWritingBox();\n const [value, setValue] = React.useState(defaultValue);\n const [error, setError] = React.useState<string | null>(null);\n\n /**\n * THE DEFECT THIS CLOSES (live on www.aimatrx.com, 2026-09-22). Every\n * caller of this dialog opens it from a toolbar button that stays on\n * screen, dimmed but perfectly readable, UNDER the dialog's own full-screen\n * scrim. Clicking that button again is therefore an \"interact outside\" and\n * DISMISSES the dialog. A person who does not notice the dialog and clicks\n * the button again — the most natural thing in the world — closes it; an\n * even number of clicks leaves the page looking untouched. That is exactly\n * how the battle page's \"Save as…\" was reported dead: four clicks, no\n * dialog, no toast, no overlay (verified click-by-click on the live site —\n * click 2 closed it, click 3 reopened it).\n *\n * So the element that OPENED this dialog is never an outside click. It is\n * remembered at open time (it is the focused element the instant before\n * Radix moves focus into the dialog) and a pointer-down whose point lands\n * on it is swallowed: the dialog stays open and focus returns to the field.\n * Every other outside click still dismisses, as it should.\n */\n const openerRef = React.useRef<HTMLElement | null>(null);\n const lastPointerTargetRef = React.useRef<HTMLElement | null>(null);\n\n // The opener cannot be read as `document.activeElement` once the dialog is\n // open — Radix has already pulled focus into the field — so the last\n // pointer-down target is remembered continuously and frozen the moment the\n // dialog opens.\n React.useEffect(() => {\n const remember = (event: Event) => {\n const target = event.target;\n lastPointerTargetRef.current =\n target instanceof HTMLElement ? target.closest(\"button\") ?? target : null;\n };\n document.addEventListener(\"pointerdown\", remember, true);\n return () => document.removeEventListener(\"pointerdown\", remember, true);\n }, []);\n\n React.useEffect(() => {\n if (open) openerRef.current = lastPointerTargetRef.current;\n }, [open]);\n\n const pointIsOnOpener = React.useCallback(\n (event: Event) =>\n isPointOnOpener(openerRef.current, event as unknown as MouseEvent),\n [],\n );\n\n // Reset state on every open. Keeps the component pure — opening it\n // twice in a row doesn't keep stale text from the prior session.\n React.useEffect(() => {\n if (open) {\n setValue(defaultValue);\n setError(null);\n }\n }, [open, defaultValue]);\n\n const submit = React.useCallback(() => {\n const trimmed = value.trim();\n if (!trimmed && !allowEmpty) {\n setError(\"Required\");\n return;\n }\n if (validate) {\n const err = validate(trimmed);\n if (err) {\n setError(err);\n return;\n }\n }\n void onConfirm(trimmed);\n }, [value, validate, allowEmpty, onConfirm]);\n\n const handleFormSubmit = (event: React.FormEvent) => {\n event.preventDefault();\n if (busy) return;\n submit();\n };\n\n const body = (\n <form onSubmit={handleFormSubmit} className=\"space-y-2\">\n {multiline ? (\n <ProTextarea\n autoFocus\n onFocus={(event) => event.currentTarget.select()}\n value={value}\n onChange={(event) => {\n setValue(event.target.value);\n if (error) setError(null);\n }}\n placeholder={placeholder}\n disabled={busy}\n rows={rows}\n className={cn(\n \"min-h-[8rem] resize-y text-base\",\n error && \"border-destructive\",\n )}\n aria-invalid={!!error}\n aria-describedby={error ? \"text-input-dialog-error\" : undefined}\n />\n ) : (\n <Input\n autoFocus\n onFocus={(event) => event.currentTarget.select()}\n value={value}\n onChange={(event) => {\n setValue(event.target.value);\n if (error) setError(null);\n }}\n placeholder={placeholder}\n disabled={busy}\n // text-base = 16px, prevents iOS Safari zoom-on-focus\n \n aria-invalid={!!error}\n aria-describedby={error ? \"text-input-dialog-error\" : undefined}\n />\n )}\n {error ? (\n <p id=\"text-input-dialog-error\" className=\"text-sm text-destructive\">\n {error}\n <ErrorActions message={error} operation=\"Enter a value\" source=\"inline\" origin=\"design-system/TextInputDialog\" />\n </p>\n ) : null}\n {/* Hidden submit so Enter key submits the form. */}\n <button type=\"submit\" className=\"hidden\" tabIndex={-1} aria-hidden />\n </form>\n );\n\n const buttons = (\n <>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n disabled={busy}\n >\n {cancelLabel}\n </Button>\n <Button type=\"button\" onClick={submit} disabled={busy || (!allowEmpty && !value.trim())}>\n {busy ? <Loader2 className=\"mr-2 h-4 w-4 matrx-spin\" /> : null}\n {confirmLabel}\n </Button>\n </>\n );\n\n if (isMobile) {\n return (\n <Drawer open={open} onOpenChange={onOpenChange}>\n <DrawerContent>\n <DrawerHeader>\n <DrawerTitle>{title}</DrawerTitle>\n {description ? (\n <DrawerDescription>{description}</DrawerDescription>\n ) : null}\n </DrawerHeader>\n <div className=\"px-4 pb-2\">{body}</div>\n <DrawerFooter className=\"flex-row justify-end gap-2\">\n {buttons}\n </DrawerFooter>\n </DrawerContent>\n </Drawer>\n );\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent\n className=\"sm:max-w-md\"\n onPointerDownOutside={(event) => {\n if (pointIsOnOpener(event.detail.originalEvent)) {\n event.preventDefault();\n }\n }}\n >\n <DialogHeader>\n <DialogTitle>{title}</DialogTitle>\n {description ? (\n <DialogDescription>{description}</DialogDescription>\n ) : null}\n </DialogHeader>\n {body}\n <DialogFooter>{buttons}</DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","/**\n * \"Is this outside-click actually a re-click of the button that opened me?\"\n *\n * THE DEFECT THIS CLOSES (live on www.aimatrx.com, 2026-09-22). A dialog's\n * trigger stays on screen, readable, UNDER the dialog's own full-screen\n * scrim. Clicking it again is an outside interaction, so it DISMISSES what it\n * opened. A person who does not notice the dialog and clicks the button again\n * closes it, and an even number of clicks leaves the page looking untouched:\n * the battle page's \"Save as…\" was reported dead after four clicks — no\n * dialog, no toast, no overlay — and click-by-click on the live site the\n * dialog count went 1 → 0 → 1.\n *\n * WHY GEOMETRY AND NOT A HIT TEST. The scrim swallows the event, so the\n * event's TARGET is the scrim, never the trigger — `contains(target)` is\n * always false. `document.elementsFromPoint` is no better: a modal Radix\n * dialog puts `pointer-events: none` on the body, and a hit test skips\n * elements that cannot be hit, so the trigger drops out of the stack\n * unpredictably (observed live: two re-clicks held, the third dismissed).\n * The opener's own rectangle is the one thing that is always true.\n */\nexport function isPointOnOpener(\n opener: HTMLElement | null,\n event: { clientX?: number; clientY?: number },\n): boolean {\n if (!opener || !opener.isConnected) return false;\n const { clientX, clientY } = event;\n if (typeof clientX !== \"number\" || typeof clientY !== \"number\") return false;\n // A keyboard \"click\" reports 0,0; never treat that as a re-click.\n if (clientX === 0 && clientY === 0) return false;\n const rect = opener.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) return false;\n return (\n clientX >= rect.left &&\n clientX <= rect.right &&\n clientY >= rect.top &&\n clientY <= rect.bottom\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/text-input-dialog.tsx","../src/opener-reclick.ts"],"sourcesContent":["/**\n * TextInputDialog — moved from matrx-frontend components/dialogs/text-input\n * (chat host slots get real package defaults, 2026-10-08).\n *\n * Drop-in replacement for `window.prompt`. Captures a single string of\n * input from the user. Renders a `<Drawer>` (bottom sheet) on mobile\n * and a `<Dialog>` on desktop, per the matrx-admin layout standard\n * (see CLAUDE.md \"Mobile Layout\" — never Dialog on mobile).\n *\n * Local-state component — no global host. Use it like any controlled\n * dialog: hold `open` and `value` in your component, render once.\n *\n * @example\n * const [open, setOpen] = useState(false);\n * const [busy, setBusy] = useState(false);\n *\n * <TextInputDialog\n * open={open}\n * onOpenChange={(o) => !busy && setOpen(o)}\n * title=\"New folder\"\n * placeholder=\"Folder name\"\n * confirmLabel=\"Create\"\n * busy={busy}\n * onConfirm={async (name) => {\n * setBusy(true);\n * try { await createFolder(name); setOpen(false); }\n * finally { setBusy(false); }\n * }}\n * />\n */\n\n\"use client\";\n\nimport * as React from \"react\";\nimport { Loader2Icon as Loader2 } from \"./icons\";\n\nimport { cn } from \"./cn\";\nimport { Button } from \"./button\";\nimport { Input } from \"./controls/doors\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"./dialog\";\nimport {\n Drawer,\n DrawerContent,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerTitle,\n} from \"./drawer\";\nimport { useIsMobile } from \"./use-is-mobile\";\nimport { useWritingBox } from \"./writing-box\";\nimport { isPointOnOpener } from \"./opener-reclick\";\nimport { ErrorActions } from \"./error-box\";\n\nexport interface TextInputDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n title: React.ReactNode;\n description?: React.ReactNode;\n placeholder?: string;\n defaultValue?: string;\n /** Use a textarea instead of a single-line input (for longer descriptions). */\n multiline?: boolean;\n /** Visible rows when `multiline` is true. Default 5. */\n rows?: number;\n confirmLabel?: string;\n cancelLabel?: string;\n /** When true, both buttons are disabled and a spinner shows on confirm. */\n busy?: boolean;\n /**\n * Optional sync validator. Return an error message to display, or\n * null/empty to accept. Empty/whitespace-only input is rejected by\n * default (treated as \"Required\").\n */\n validate?: (value: string) => string | null | undefined;\n /**\n * When true, an empty value is a valid answer: Confirm is enabled and submits \"\".\n * For an \"(optional)\" note. Default false (empty is \"Required\").\n */\n allowEmpty?: boolean;\n onConfirm: (value: string) => void | Promise<void>;\n}\n\nexport function TextInputDialog({\n open,\n onOpenChange,\n title,\n description,\n placeholder,\n defaultValue = \"\",\n multiline = false,\n rows = 5,\n confirmLabel = \"Confirm\",\n cancelLabel = \"Cancel\",\n busy = false,\n validate,\n allowEmpty = false,\n onConfirm,\n}: TextInputDialogProps) {\n const isMobile = useIsMobile();\n const ProTextarea = useWritingBox();\n const [value, setValue] = React.useState(defaultValue);\n const [error, setError] = React.useState<string | null>(null);\n\n /**\n * THE DEFECT THIS CLOSES (live on www.aimatrx.com, 2026-09-22). Every\n * caller of this dialog opens it from a toolbar button that stays on\n * screen, dimmed but perfectly readable, UNDER the dialog's own full-screen\n * scrim. Clicking that button again is therefore an \"interact outside\" and\n * DISMISSES the dialog. A person who does not notice the dialog and clicks\n * the button again — the most natural thing in the world — closes it; an\n * even number of clicks leaves the page looking untouched. That is exactly\n * how the battle page's \"Save as…\" was reported dead: four clicks, no\n * dialog, no toast, no overlay (verified click-by-click on the live site —\n * click 2 closed it, click 3 reopened it).\n *\n * So the element that OPENED this dialog is never an outside click. It is\n * remembered at open time (it is the focused element the instant before\n * Radix moves focus into the dialog) and a pointer-down whose point lands\n * on it is swallowed: the dialog stays open and focus returns to the field.\n * Every other outside click still dismisses, as it should.\n */\n const openerRef = React.useRef<HTMLElement | null>(null);\n const lastPointerTargetRef = React.useRef<HTMLElement | null>(null);\n\n // The opener cannot be read as `document.activeElement` once the dialog is\n // open — Radix has already pulled focus into the field — so the last\n // pointer-down target is remembered continuously and frozen the moment the\n // dialog opens.\n React.useEffect(() => {\n const remember = (event: Event) => {\n const target = event.target;\n lastPointerTargetRef.current =\n target instanceof HTMLElement ? target.closest(\"button\") ?? target : null;\n };\n document.addEventListener(\"pointerdown\", remember, true);\n return () => document.removeEventListener(\"pointerdown\", remember, true);\n }, []);\n\n React.useEffect(() => {\n if (open) openerRef.current = lastPointerTargetRef.current;\n }, [open]);\n\n const pointIsOnOpener = React.useCallback(\n (event: Event) =>\n isPointOnOpener(openerRef.current, event as unknown as MouseEvent),\n [],\n );\n\n // Reset state on every open. Keeps the component pure — opening it\n // twice in a row doesn't keep stale text from the prior session.\n React.useEffect(() => {\n if (open) {\n setValue(defaultValue);\n setError(null);\n }\n }, [open, defaultValue]);\n\n const submit = React.useCallback(() => {\n const trimmed = value.trim();\n if (!trimmed && !allowEmpty) {\n setError(\"Required\");\n return;\n }\n if (validate) {\n const err = validate(trimmed);\n if (err) {\n setError(err);\n return;\n }\n }\n void onConfirm(trimmed);\n }, [value, validate, allowEmpty, onConfirm]);\n\n const handleFormSubmit = (event: React.FormEvent) => {\n event.preventDefault();\n if (busy) return;\n submit();\n };\n\n const body = (\n <form onSubmit={handleFormSubmit} className=\"space-y-2\">\n {multiline ? (\n <ProTextarea\n autoFocus\n onFocus={(event) => event.currentTarget.select()}\n value={value}\n onChange={(event) => {\n setValue(event.target.value);\n if (error) setError(null);\n }}\n placeholder={placeholder}\n disabled={busy}\n rows={rows}\n className={cn(\n \"min-h-[8rem] resize-y text-base\",\n error && \"border-destructive\",\n )}\n aria-invalid={!!error}\n aria-describedby={error ? \"text-input-dialog-error\" : undefined}\n />\n ) : (\n <Input\n autoFocus\n onFocus={(event) => event.currentTarget.select()}\n value={value}\n onChange={(event) => {\n setValue(event.target.value);\n if (error) setError(null);\n }}\n placeholder={placeholder}\n disabled={busy}\n // text-base = 16px, prevents iOS Safari zoom-on-focus\n \n aria-invalid={!!error}\n aria-describedby={error ? \"text-input-dialog-error\" : undefined}\n />\n )}\n {error ? (\n <p id=\"text-input-dialog-error\" className=\"text-sm text-destructive\">\n {error}\n <ErrorActions message={error} operation=\"Enter a value\" source=\"inline\" origin=\"design-system/TextInputDialog\" />\n </p>\n ) : null}\n {/* Hidden submit so Enter key submits the form. */}\n <button type=\"submit\" className=\"hidden\" tabIndex={-1} aria-hidden />\n </form>\n );\n\n const buttons = (\n <>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n disabled={busy}\n >\n {cancelLabel}\n </Button>\n <Button type=\"button\" onClick={submit} disabled={busy || (!allowEmpty && !value.trim())}>\n {busy ? <Loader2 className=\"mr-2 h-4 w-4 matrx-spin\" /> : null}\n {confirmLabel}\n </Button>\n </>\n );\n\n if (isMobile) {\n return (\n <Drawer open={open} onOpenChange={onOpenChange}>\n <DrawerContent>\n <DrawerHeader>\n <DrawerTitle>{title}</DrawerTitle>\n {description ? (\n <DrawerDescription>{description}</DrawerDescription>\n ) : null}\n </DrawerHeader>\n <div className=\"px-4 pb-2\">{body}</div>\n <DrawerFooter className=\"flex-row justify-end gap-2\">\n {buttons}\n </DrawerFooter>\n </DrawerContent>\n </Drawer>\n );\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent\n className=\"sm:max-w-md\"\n onPointerDownOutside={(event) => {\n if (pointIsOnOpener(event.detail.originalEvent)) {\n event.preventDefault();\n }\n }}\n >\n <DialogHeader>\n <DialogTitle>{title}</DialogTitle>\n {description ? (\n <DialogDescription>{description}</DialogDescription>\n ) : null}\n </DialogHeader>\n {body}\n <DialogFooter>{buttons}</DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","/**\n * \"Is this outside-click actually a re-click of the button that opened me?\"\n *\n * THE DEFECT THIS CLOSES (live on www.aimatrx.com, 2026-09-22). A dialog's\n * trigger stays on screen, readable, UNDER the dialog's own full-screen\n * scrim. Clicking it again is an outside interaction, so it DISMISSES what it\n * opened. A person who does not notice the dialog and clicks the button again\n * closes it, and an even number of clicks leaves the page looking untouched:\n * the battle page's \"Save as…\" was reported dead after four clicks — no\n * dialog, no toast, no overlay — and click-by-click on the live site the\n * dialog count went 1 → 0 → 1.\n *\n * WHY GEOMETRY AND NOT A HIT TEST. The scrim swallows the event, so the\n * event's TARGET is the scrim, never the trigger — `contains(target)` is\n * always false. `document.elementsFromPoint` is no better: a modal Radix\n * dialog puts `pointer-events: none` on the body, and a hit test skips\n * elements that cannot be hit, so the trigger drops out of the stack\n * unpredictably (observed live: two re-clicks held, the third dismissed).\n * The opener's own rectangle is the one thing that is always true.\n */\nexport function isPointOnOpener(\n opener: HTMLElement | null,\n event: { clientX?: number; clientY?: number },\n): boolean {\n if (!opener || !opener.isConnected) return false;\n const { clientX, clientY } = event;\n if (typeof clientX !== \"number\" || typeof clientY !== \"number\") return false;\n // A keyboard \"click\" reports 0,0; never treat that as a re-click.\n if (clientX === 0 && clientY === 0) return false;\n const rect = opener.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) return false;\n return (\n clientX >= rect.left &&\n clientX <= rect.right &&\n clientY >= rect.top &&\n clientY <= rect.bottom\n );\n}\n"],"mappings":"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","names":[]}
@@ -14,11 +14,11 @@ import {
14
14
  DialogFooter,
15
15
  DialogHeader,
16
16
  DialogTitle
17
- } from "./chunk-UWTODOMD.js";
17
+ } from "./chunk-2BQAHLU3.js";
18
+ import "./chunk-LEPD6TV7.js";
18
19
  import "./chunk-IMQ3RX35.js";
19
20
  import "./chunk-U24MOJWK.js";
20
21
  import "./chunk-NDTDBBMM.js";
21
- import "./chunk-LEPD6TV7.js";
22
22
  import "./chunk-BD77DHUO.js";
23
23
  import "./chunk-MOZRIHJF.js";
24
24
  import "./chunk-WAT2XW7L.js";
@@ -26,14 +26,14 @@ import {
26
26
  Dialog,
27
27
  DialogContent,
28
28
  DialogTitle
29
- } from "./chunk-UWTODOMD.js";
29
+ } from "./chunk-2BQAHLU3.js";
30
+ import "./chunk-LEPD6TV7.js";
30
31
  import {
31
32
  UNCONFIGURED,
32
33
  announceStandIn
33
34
  } from "./chunk-IMQ3RX35.js";
34
35
  import "./chunk-U24MOJWK.js";
35
36
  import "./chunk-NDTDBBMM.js";
36
- import "./chunk-LEPD6TV7.js";
37
37
  import "./chunk-B3LDW3IQ.js";
38
38
  import {
39
39
  TapTargetButtonSolid
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-matrx/design-system",
3
- "version": "0.86.355",
3
+ "version": "0.86.357",
4
4
  "description": "AI Matrx semantic React primitives shared across Vite, Next.js, desktop, and customer-built applications.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -111,6 +111,16 @@
111
111
  "default": "./dist/notice-stack.cjs"
112
112
  }
113
113
  },
114
+ "./error-sentence": {
115
+ "import": {
116
+ "types": "./dist/error-sentence.d.ts",
117
+ "default": "./dist/error-sentence.js"
118
+ },
119
+ "require": {
120
+ "types": "./dist/error-sentence.d.cts",
121
+ "default": "./dist/error-sentence.cjs"
122
+ }
123
+ },
114
124
  "./menu-look.css": "./dist/menu-look.css",
115
125
  "./clipboard-fallback-dialog": {
116
126
  "import": {
@@ -770,5 +780,5 @@
770
780
  "confirm-dialog",
771
781
  "alert-dialog"
772
782
  ],
773
- "gitHead": "83b6419fe3bbf936d9fc1abc87ca3a24ce74856c"
783
+ "gitHead": "3cc6206acf6ccc79f329f97c275e7ac5fc1e8d12"
774
784
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/notice-stack.ts"],"sourcesContent":["/**\n * @ai-matrx/design-system/notice-stack — THE ONE NOTICE LOOK AND STACK RULE, for the web and Matrx 2.\n *\n * Arman, 2026-10-23, looking at two full-width red bars across Matrx 2 Cloud Chat: \"Let's eliminate\n * this terrible pattern of having giant errors that shit the entire ui. If we're going to have a\n * place for errors, it should be a small something that resembles errors from a normal macos\".\n *\n * Champion: the macOS notification banner. A small rounded card of translucent material in the\n * window's TOP-RIGHT corner (where macOS puts every notification, so the eye already looks there;\n * the bottom of our windows holds the composer and the list's foot, which a notice must not sit on),\n * the tone's icon, a one-line title, an optional second line, at most one action, and ×.\n *\n * The stack rule, the same on both hosts:\n * - identical notices (same tone, title and line) collapse into ONE card with a count;\n * - at most 3 cards show; the rest fold into \"N more\";\n * - info and success go after NOTICE_STACK.infoMs; errors and warnings stay until dismissed\n * or until NOTICE_STACK.errorMs (long, so a notice nobody saw does not sit there for ever).\n *\n * The desktop renders it through `Notices` + `Banner` (desktop/layout.tsx, desktop/state.tsx); the\n * web through its one sonner Toaster (matrx-frontend components/ui/sonner.tsx), which reads these\n * constants and keys every toast with `noticeKey` so the same notice never stacks twice.\n */\n\nexport const NOTICE_STACK = {\n /** The corner every notice floats in. */\n corner: \"top-right\",\n /** Cards shown at once; the rest fold into \"N more\". */\n maxVisible: 3,\n /** Info and success: gone after this long. */\n infoMs: 4000,\n /** Errors and warnings: stay until dismissed, or this long at most. */\n errorMs: 30000,\n /** The card's width: small, never the window's. */\n widthRem: 22,\n} as const;\n\nexport type NoticeStackTone = \"neutral\" | \"info\" | \"warning\" | \"danger\" | \"success\";\n\n/** The identity two notices share when they say the same thing (tone, title, line). */\nexport function noticeKey(tone: string, title: unknown, message?: unknown): string {\n const text = (v: unknown): string => (typeof v === \"string\" || typeof v === \"number\" ? String(v).trim() : \"\");\n return `${tone}\\u0000${text(title)}\\u0000${text(message)}`;\n}\n\nexport interface CollapsedNotice<T> {\n /** The latest notice of the group (its dismiss and action are the live ones). */\n readonly item: T;\n /** Every notice the group holds, oldest first. */\n readonly members: readonly T[];\n /** How many identical notices it stands for. */\n readonly count: number;\n}\n\n/**\n * Identical notices become one with a count, in the order each first appeared. A notice whose key\n * is null (a rich one that says nothing comparable) stands alone.\n */\nexport function collapseNotices<T>(items: readonly T[], keyOf: (item: T) => string | null): CollapsedNotice<T>[] {\n const groups: { key: string | null; members: T[] }[] = [];\n const byKey = new Map<string, { key: string | null; members: T[] }>();\n for (const item of items) {\n const key = keyOf(item);\n const found = key !== null ? byKey.get(key) : undefined;\n if (found) {\n found.members.push(item);\n continue;\n }\n const group = { key, members: [item] };\n groups.push(group);\n if (key !== null) byKey.set(key, group);\n }\n return groups.map((g) => ({ item: g.members[g.members.length - 1] as T, members: g.members, count: g.members.length }));\n}\n\n/** The cards on screen and how many fold away (the newest show). */\nexport function visibleNotices<T>(groups: readonly T[], max: number = NOTICE_STACK.maxVisible): { shown: T[]; folded: number } {\n if (groups.length <= max) return { shown: [...groups], folded: 0 };\n return { shown: groups.slice(groups.length - max), folded: groups.length - max };\n}\n\n/** How long a notice of this tone stays (ms; Infinity never happens: errors cap at errorMs). */\nexport function noticeLifetimeMs(tone: NoticeStackTone | \"error\"): number {\n return tone === \"danger\" || tone === \"warning\" || tone === \"error\" ? NOTICE_STACK.errorMs : NOTICE_STACK.infoMs;\n}\n"],"mappings":";;;AAuBO,IAAM,eAAe;AAAA;AAAA,EAE1B,QAAQ;AAAA;AAAA,EAER,YAAY;AAAA;AAAA,EAEZ,QAAQ;AAAA;AAAA,EAER,SAAS;AAAA;AAAA,EAET,UAAU;AACZ;AAKO,SAAS,UAAU,MAAc,OAAgB,SAA2B;AACjF,QAAM,OAAO,CAAC,MAAwB,OAAO,MAAM,YAAY,OAAO,MAAM,WAAW,OAAO,CAAC,EAAE,KAAK,IAAI;AAC1G,SAAO,GAAG,IAAI,KAAS,KAAK,KAAK,CAAC,KAAS,KAAK,OAAO,CAAC;AAC1D;AAeO,SAAS,gBAAmB,OAAqB,OAAyD;AAC/G,QAAM,SAAiD,CAAC;AACxD,QAAM,QAAQ,oBAAI,IAAkD;AACpE,aAAW,QAAQ,OAAO;AACxB,UAAM,MAAM,MAAM,IAAI;AACtB,UAAM,QAAQ,QAAQ,OAAO,MAAM,IAAI,GAAG,IAAI;AAC9C,QAAI,OAAO;AACT,YAAM,QAAQ,KAAK,IAAI;AACvB;AAAA,IACF;AACA,UAAM,QAAQ,EAAE,KAAK,SAAS,CAAC,IAAI,EAAE;AACrC,WAAO,KAAK,KAAK;AACjB,QAAI,QAAQ,KAAM,OAAM,IAAI,KAAK,KAAK;AAAA,EACxC;AACA,SAAO,OAAO,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,SAAS,CAAC,GAAQ,SAAS,EAAE,SAAS,OAAO,EAAE,QAAQ,OAAO,EAAE;AACxH;AAGO,SAAS,eAAkB,QAAsB,MAAc,aAAa,YAA4C;AAC7H,MAAI,OAAO,UAAU,IAAK,QAAO,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,QAAQ,EAAE;AACjE,SAAO,EAAE,OAAO,OAAO,MAAM,OAAO,SAAS,GAAG,GAAG,QAAQ,OAAO,SAAS,IAAI;AACjF;AAGO,SAAS,iBAAiB,MAAyC;AACxE,SAAO,SAAS,YAAY,SAAS,aAAa,SAAS,UAAU,aAAa,UAAU,aAAa;AAC3G;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/desktop/errors.ts","../src/error-sentence.ts","../src/error-box.tsx"],"sourcesContent":["/**\n * THE error-wording layer. A failure is shown as a cause the person can act on (\"A program it\n * needs isn't installed\"), never as the raw text a process threw (\"spawn uv ENOENT\"). The raw text\n * is kept, whole, as `detail` — the banner offers it behind Copy details and a tooltip, for a bug\n * report or an agent. One table, so every screen and every service says the same thing.\n */\n\nexport interface DescribedError {\n /** One plain sentence: what went wrong and, where it helps, what to do. */\n cause: string;\n /** The raw text, unedited (empty when it would only repeat `cause`). */\n detail: string;\n /** A short machine code when one was recognized (\"ENOENT\", \"TIMEOUT\"). */\n code?: string;\n}\n\ninterface Rule {\n code: string;\n test: RegExp;\n cause: string;\n}\n\n/** Ordered: the first rule whose pattern matches the raw text wins. */\nconst RULES: ReadonlyArray<Rule> = [\n { 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.\" },\n { 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 › Privacy & Security, then try again.\" },\n { 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.\" },\n { code: \"ECONNREFUSED\", test: /\\bECONNREFUSED\\b|connection refused/i, cause: \"The service isn't answering. It may have stopped; start it again.\" },\n { 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.\" },\n { code: \"TIMEOUT\", test: /\\bETIMEDOUT\\b|timed? ?out|\\bTIMEOUT\\b|deadline exceeded/i, cause: \"It took too long to answer. Try again in a moment.\" },\n { code: \"ENOSPC\", test: /\\bENOSPC\\b|no space left/i, cause: \"The disk is full. Free some space, then try again.\" },\n { code: \"ENOMEM\", test: /\\bENOMEM\\b|out of memory|cannot allocate memory/i, cause: \"The computer ran out of memory. Close something and try again.\" },\n { 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.\" },\n { code: \"FORBIDDEN\", test: /\\b403\\b|forbidden/i, cause: \"This account isn't allowed to do that.\" },\n { code: \"NOT_FOUND\", test: /\\b404\\b|not found/i, cause: \"It isn't there any more. Refresh and try again.\" },\n { code: \"RATE_LIMIT\", test: /\\b429\\b|rate.?limit|too many requests/i, cause: \"Too many requests just now. Wait a moment and try again.\" },\n { 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.\" },\n { 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.\" },\n];\n\nconst RAW = /\\b[A-Z]{2,}[A-Z_]*\\b|[\\\\/][\\w.-]+[\\\\/]|\\bat \\S+ \\(|\\{|\\}|errno|stack|undefined|null|\\bspawn\\b/;\n\nfunction rawText(error: unknown): string {\n if (error instanceof Error) return error.message || error.name;\n if (typeof error === \"string\") return error;\n if (error && typeof error === \"object\" && \"message\" in error && typeof (error as { message: unknown }).message === \"string\") {\n return (error as { message: string }).message;\n }\n return \"\";\n}\n\n/**\n * Turn any thrown or returned error into words a person can act on. Already-humane text (a full\n * sentence the app wrote itself, with no codes, paths or stack) passes through unchanged.\n */\nexport function describeError(error: unknown, fallback = \"Something went wrong. Try again.\"): DescribedError {\n const detail = rawText(error).trim();\n if (!detail) return { cause: fallback, detail: \"\" };\n for (const rule of RULES) {\n if (rule.test.test(detail)) return { cause: rule.cause, detail, code: rule.code };\n }\n if (!RAW.test(detail) && detail.length <= 160) return { cause: detail, detail: \"\" };\n return { cause: fallback, detail };\n}\n","/**\n * THE SENTENCE A PERSON READS FOR AN ERROR — the one funnel every error surface draws through\n * (ErrorNotice, ErrorBox, the desktop Banner). Raw error text never reaches the screen: no\n * \"TypeError: Failed to fetch\", no `sidebar_conversations:` RPC prefix, no stack frame or file\n * path (Arman, 2026-10-24: notices are small and plain). The raw error still travels to the\n * Alchemy Menu / AI payload through the `error` prop; only the screen gets the sentence.\n *\n * The wording table is desktop/errors.ts `describeError` (one table for every screen); this file\n * adds what decides WHEN a sentence must go through it, narrow enough that an app's own sentence\n * (\"PDF too large\") passes untouched.\n */\nimport { describeError } from \"./desktop/errors\";\n\n/**\n * The sentence an error carries: its own message (string, Error, or any\n * object with a `message`). Empty when it has none.\n */\nexport function errorSentenceOf(error: unknown): string {\n if (error == null || error === true || error === false) return \"\";\n if (typeof error === \"string\") return error;\n if (typeof error !== \"object\") return String(error);\n const message = (error as { message?: unknown }).message;\n return typeof message === \"string\" ? message : \"\";\n}\n\n/**\n * What a person reads when the store cancelled a call at its statement timeout\n * (SQLSTATE 57014). Postgres' own \"canceling statement due to statement\n * timeout\" is about the database, not the person; the cancelled call rolled\n * back, so nothing was written (lane AT-SLOW, 2026-10-19). The same sentence\n * @ai-matrx/records' `TIMED_OUT_MESSAGE` gives.\n */\nexport const TIMED_OUT_SENTENCE = \"That took too long. Nothing was lost; try again.\";\n\n/** What a person reads when an error says nothing a person can use. */\nexport const FALLBACK_ERROR_SENTENCE = \"Something went wrong. Try again.\";\n\n/** True when an error, its code, or its sentence is the store's statement timeout. */\nexport function isTimedOutError(error: unknown, message?: string | null, code?: string | number): boolean {\n if (code === \"57014\" || code === \"timed_out\") return true;\n if (error && typeof error === \"object\") {\n const o = error as { code?: unknown; sqlstate?: unknown };\n if (o.code === \"57014\" || o.code === \"timed_out\" || o.sqlstate === \"57014\") return true;\n }\n const text = `${message ?? \"\"} ${errorSentenceOf(error)}`;\n return /canceling statement due to statement timeout/i.test(text);\n}\n\n/** True when the call failed because the device is offline or the network never answered. */\nexport function isOfflineError(error: unknown, message?: string | null): boolean {\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) return true;\n return offlineText(`${message ?? \"\"} ${errorSentenceOf(error)}`);\n}\n\nfunction offlineText(text: string): boolean {\n const t = text.trim();\n return t !== \"\" && (describeError(t).code === \"OFFLINE\" || /networkerror|network request failed|load failed/i.test(t));\n}\n\n/** describeError's causes that hold on every host (its process codes — ENOENT, EACCES, ports — are\n * the desktop's words for a local program and would mislead on a database refusal). */\nconst ANY_HOST_CODES = new Set([\"OFFLINE\", \"TIMEOUT\", \"UNAUTHORIZED\", \"FORBIDDEN\", \"NOT_FOUND\", \"RATE_LIMIT\", \"SERVER\"]);\n\n/** A leading machine label: `sidebar_conversations: …`, `chat.rpc_name: …`, `[chat-sidebar] …`. */\nconst MACHINE_PREFIX = /^\\s*(?:\\[[\\w./-]+\\]\\s*|[a-z][\\w]*(?:[._][\\w]+)+:\\s+)/;\n/** Text no person should read: JS error class names, stack frames, source paths. */\nconst RAW_TEXT =\n /\\b(?:Type|Reference|Syntax|Range|Internal|Eval|URI)Error\\b|\\bat\\s+[\\w$.<>]+\\s*\\(|\\.(?:[cm]?[jt]sx?|py):\\d+|(?:^|[\\s(])(?:\\/[\\w.@-]+){2,}|\\bundefined is not\\b|\\bcannot read propert(?:y|ies)\\b|\\bis not a function\\b|\\bunexpected token\\b/i;\n\n/** True when a sentence carries raw error text or a code path (what the guard refuses on screen). */\nexport function isRawErrorText(text: string): boolean {\n return MACHINE_PREFIX.test(text) || RAW_TEXT.test(text);\n}\n\n/**\n * The sentence for the screen: the timeout sentence for a statement timeout; a caller's plain\n * sentence as is; a machine label stripped from the front; anything raw or offline worded by\n * `describeError` (its table, else `fallback`).\n */\nexport function peopleErrorSentence(\n error: unknown,\n message?: string | null,\n options: { code?: string | number | undefined; fallback?: string | undefined } = {},\n): string {\n if (isTimedOutError(error, message, options.code)) return TIMED_OUT_SENTENCE;\n const fallback = options.fallback ?? FALLBACK_ERROR_SENTENCE;\n const raw = message?.trim() || errorSentenceOf(error).trim();\n if (!raw) return fallback;\n let text = raw;\n for (let i = 0; i < 3 && MACHINE_PREFIX.test(text); i++) text = text.replace(MACHINE_PREFIX, \"\").trim();\n if (!text) return fallback;\n if (offlineText(text) || RAW_TEXT.test(text) || text !== raw) {\n const said = describeError(text, fallback);\n return said.code && ANY_HOST_CODES.has(said.code) ? said.cause : fallback;\n }\n return text;\n}\n","/**\n * ErrorBox — THE one way a package draws an error on screen, and the seam a\n * host uses to put its own actions on every one of them.\n *\n * Every error sentence this package (and every sibling package that depends\n * on it) shows is an `ErrorBox`. The box is a plain `role=\"alert\"` element —\n * its tag and classes are the caller's, so nothing about the look changes — and\n * after its children it renders whatever the host's `ErrorActionsProvider`\n * returns for that error. With no provider it renders nothing extra.\n *\n * Why a slot and not a menu: the package cannot know the host's AI copy, bug\n * report, or support flow (Matrx fills it with its Alchemy copy-for-AI menu at\n * the app root). The package supplies the error's facts — the sentence, the\n * raw error, the operation, the records — and the host decides the actions.\n *\n * Guard: `error-box.census.test.ts` fails when a package file renders\n * `role=\"alert\"` for an error without going through `ErrorBox` (or, for the\n * `Alert` family, `ErrorActions`).\n */\nimport * as React from \"react\";\nimport { peopleErrorSentence } from \"./error-sentence\";\n\nexport interface ErrorActionsRecord {\n type: string;\n id: string;\n label?: string;\n}\n\n/** The facts a package knows about an error it is showing. All optional. */\nexport interface ErrorActionsInput {\n /** The sentence shown. Omit when the words are only in the rendered box. */\n message?: string | undefined;\n title?: string | undefined;\n /** The raw error (code/status/stack stay available to the host). */\n error?: unknown | undefined;\n /** What was being attempted (\"Load saved views\"). */\n operation?: string | undefined;\n records?: readonly ErrorActionsRecord[] | undefined;\n /** The person's input that did not land. */\n unsavedInput?: unknown | undefined;\n /** The package + component that drew it (\"design-system/SavedViewsControl\"). */\n origin?: string | undefined;\n /** Extra structured facts for the AI payload (never shown). */\n details?: Record<string, unknown> | undefined;\n code?: string | number | undefined;\n status?: number | undefined;\n /** The tables / RPCs the failed call used — pins its captured request as the cause. */\n calls?: readonly string[] | undefined;\n /** Where it was drawn: an `ErrorNotice` card/line (\"inline\") or a package alert box (\"alert\", the default). */\n source?: \"inline\" | \"alert\" | undefined;\n}\n\nexport type ErrorActionsRenderer = (input: ErrorActionsInput) => React.ReactNode;\n\n/**\n * ONE context for every copy of this module. tsup builds each entry point\n * (`.`, `./data-table`, …) without code splitting, so this file is inlined into\n * several bundles; a plain `createContext` would give each bundle its own\n * context and the host's provider (imported from `.`) would never reach an\n * error drawn by the data table. The registry key makes them one.\n */\nconst CONTEXT_KEY = Symbol.for(\"@ai-matrx/design-system/error-actions\");\ntype ContextRegistry = Record<symbol, React.Context<ErrorActionsRenderer | null> | undefined>;\nconst registry = globalThis as unknown as ContextRegistry;\nconst ErrorActionsContext: React.Context<ErrorActionsRenderer | null> =\n registry[CONTEXT_KEY] ??\n (registry[CONTEXT_KEY] = React.createContext<ErrorActionsRenderer | null>(null));\n\n/** Mount once at the host's app root; every ErrorBox below renders `render(...)`. */\nexport function ErrorActionsProvider({\n render,\n children,\n}: {\n render: ErrorActionsRenderer;\n children: React.ReactNode;\n}) {\n return (\n <ErrorActionsContext.Provider value={render}>{children}</ErrorActionsContext.Provider>\n );\n}\n\n/**\n * The host's content menu around an error card (`ErrorNotice`): the corner ⋯\n * button and the right-click boundary that show the host's content actions\n * over the error's sentence. Read-only — nothing here may change a record.\n * Without a provider the card draws neither (the actions slot still renders).\n */\nexport interface ErrorCardMenus {\n /** The corner ⋯ button over `{ title, text }`. */\n Button: React.ComponentType<{ title: string; text: string; className?: string }>;\n /** Wraps the whole card so right-click opens the same menu. */\n Boundary: React.ComponentType<{ title: string; text: string; children: React.ReactNode }>;\n}\n\nconst MENUS_KEY = Symbol.for(\"@ai-matrx/design-system/error-card-menus\");\ntype MenusRegistry = Record<symbol, React.Context<ErrorCardMenus | null> | undefined>;\nconst menusRegistry = globalThis as unknown as MenusRegistry;\nconst ErrorCardMenusContext: React.Context<ErrorCardMenus | null> =\n menusRegistry[MENUS_KEY] ??\n (menusRegistry[MENUS_KEY] = React.createContext<ErrorCardMenus | null>(null));\n\n/** Mount once at the host's app root beside `ErrorActionsProvider`. */\nexport function ErrorCardMenusProvider({\n menus,\n children,\n}: {\n menus: ErrorCardMenus;\n children: React.ReactNode;\n}) {\n return <ErrorCardMenusContext.Provider value={menus}>{children}</ErrorCardMenusContext.Provider>;\n}\n\nexport function useErrorCardMenus(): ErrorCardMenus | null {\n return React.useContext(ErrorCardMenusContext);\n}\n\nexport function useErrorActions(): ErrorActionsRenderer | null {\n return React.useContext(ErrorActionsContext);\n}\n\n/**\n * The host's actions for one error, placed wherever the caller puts this\n * element. Renders nothing without a provider. Use inside a box you do not\n * render yourself (the `Alert` family); everything else uses `ErrorBox`.\n */\nexport function ErrorActions(props: ErrorActionsInput) {\n const render = useErrorActions();\n if (!render) return null;\n return <>{render(props)}</>;\n}\n\ntype ErrorBoxTag = \"p\" | \"div\" | \"span\" | \"section\" | \"li\";\n\nexport interface ErrorBoxProps\n extends Omit<React.HTMLAttributes<HTMLElement>, \"role\" | \"title\">,\n ErrorActionsInput {\n /** The element drawn — the caller's existing tag, so layout never shifts. */\n as?: ErrorBoxTag;\n}\n\n/** A plain-string body goes through the one funnel (error-sentence.ts); rich children are the caller's. */\nfunction sayable(body: React.ReactNode, error: unknown): React.ReactNode {\n if (typeof body === \"string\") return peopleErrorSentence(error, body);\n if (Array.isArray(body)) return body.map((b) => (typeof b === \"string\" && b.trim() ? peopleErrorSentence(error, b) : b));\n return body;\n}\n\nexport const ErrorBox = React.forwardRef<HTMLElement, ErrorBoxProps>(function ErrorBox(\n {\n as = \"p\",\n message,\n title,\n error,\n operation,\n records,\n unsavedInput,\n origin,\n children,\n ...rest\n },\n ref,\n) {\n // A component type, never a bare ElementType: an app compiling this source may carry global JSX\n // elements (react-three-fiber) that make a bare ElementType's props `never` (2026-10-22).\n const Tag = as as unknown as React.ComponentType<React.HTMLAttributes<HTMLElement> & { ref?: React.Ref<HTMLElement> }>;\n const facts: ErrorActionsInput = {\n ...(message !== undefined ? { message } : {}),\n ...(title !== undefined ? { title } : {}),\n ...(error !== undefined ? { error } : {}),\n ...(operation !== undefined ? { operation } : {}),\n ...(records !== undefined ? { records } : {}),\n ...(unsavedInput !== undefined ? { unsavedInput } : {}),\n ...(origin !== undefined ? { origin } : {}),\n };\n return (\n <Tag ref={ref} role=\"alert\" data-error-box=\"\" {...rest}>\n {sayable(children ?? message, error)}\n <ErrorActions {...facts} />\n </Tag>\n );\n});\n"],"mappings":"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","names":["ErrorBox"]}