@tai42/studio-sdk 0.12.0 → 0.13.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/dist/components/code-block.d.ts.map +1 -1
  2. package/dist/components/code-block.js +26 -13
  3. package/dist/components/code-block.js.map +1 -1
  4. package/dist/components/components.css +20 -0
  5. package/dist/components/copy-field.d.ts.map +1 -1
  6. package/dist/components/copy-field.js +8 -73
  7. package/dist/components/copy-field.js.map +1 -1
  8. package/dist/components/date-range-picker.d.ts +73 -0
  9. package/dist/components/date-range-picker.d.ts.map +1 -0
  10. package/dist/components/date-range-picker.js +146 -0
  11. package/dist/components/date-range-picker.js.map +1 -0
  12. package/dist/components/dialog.d.ts +19 -1
  13. package/dist/components/dialog.d.ts.map +1 -1
  14. package/dist/components/dialog.js +16 -13
  15. package/dist/components/dialog.js.map +1 -1
  16. package/dist/components/dirty-guard.d.ts +28 -0
  17. package/dist/components/dirty-guard.d.ts.map +1 -0
  18. package/dist/components/dirty-guard.js +93 -0
  19. package/dist/components/dirty-guard.js.map +1 -0
  20. package/dist/components/extension-picker.d.ts.map +1 -1
  21. package/dist/components/extension-picker.js +7 -0
  22. package/dist/components/extension-picker.js.map +1 -1
  23. package/dist/components/form-dialog.d.ts +45 -0
  24. package/dist/components/form-dialog.d.ts.map +1 -0
  25. package/dist/components/form-dialog.js +71 -0
  26. package/dist/components/form-dialog.js.map +1 -0
  27. package/dist/components/json-tree.d.ts +7 -1
  28. package/dist/components/json-tree.d.ts.map +1 -1
  29. package/dist/components/json-tree.js +300 -13
  30. package/dist/components/json-tree.js.map +1 -1
  31. package/dist/components/markdown.d.ts +60 -0
  32. package/dist/components/markdown.d.ts.map +1 -0
  33. package/dist/components/markdown.js +435 -0
  34. package/dist/components/markdown.js.map +1 -0
  35. package/dist/components/tabs.d.ts +3 -1
  36. package/dist/components/tabs.d.ts.map +1 -1
  37. package/dist/components/tabs.js +2 -2
  38. package/dist/components/tabs.js.map +1 -1
  39. package/dist/hooks/useApi.d.ts +56 -28
  40. package/dist/hooks/useApi.d.ts.map +1 -1
  41. package/dist/hooks/useClipboardCopy.d.ts +26 -0
  42. package/dist/hooks/useClipboardCopy.d.ts.map +1 -0
  43. package/dist/hooks/useClipboardCopy.js +72 -0
  44. package/dist/hooks/useClipboardCopy.js.map +1 -0
  45. package/dist/index.d.ts +9 -2
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +5 -1
  48. package/dist/index.js.map +1 -1
  49. package/dist/navigation/context.d.ts +13 -1
  50. package/dist/navigation/context.d.ts.map +1 -1
  51. package/dist/navigation/context.js +205 -7
  52. package/dist/navigation/context.js.map +1 -1
  53. package/dist/navigation/index.d.ts +2 -2
  54. package/dist/navigation/index.d.ts.map +1 -1
  55. package/dist/navigation/index.js +1 -1
  56. package/dist/navigation/index.js.map +1 -1
  57. package/dist/navigation/types.d.ts +11 -0
  58. package/dist/navigation/types.d.ts.map +1 -1
  59. package/package.json +2 -2
  60. package/src/components/code-block.tsx +72 -9
  61. package/src/components/components.css +20 -0
  62. package/src/components/copy-field.tsx +25 -81
  63. package/src/components/date-range-picker.tsx +256 -0
  64. package/src/components/dialog.tsx +59 -18
  65. package/src/components/dirty-guard.tsx +153 -0
  66. package/src/components/extension-picker.tsx +12 -0
  67. package/src/components/form-dialog.tsx +133 -0
  68. package/src/components/json-tree.tsx +477 -42
  69. package/src/components/markdown.tsx +552 -0
  70. package/src/components/tabs.tsx +10 -1
  71. package/src/hooks/useClipboardCopy.ts +105 -0
  72. package/src/index.ts +18 -0
  73. package/src/navigation/context.tsx +245 -7
  74. package/src/navigation/index.ts +2 -0
  75. package/src/navigation/types.ts +12 -0
@@ -1 +1 @@
1
- {"version":3,"file":"code-block.d.ts","sourceRoot":"","sources":["../../src/components/code-block.tsx"],"names":[],"mappings":"AAcA,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAKD,wBAAgB,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,cAAc,+BAa3D"}
1
+ {"version":3,"file":"code-block.d.ts","sourceRoot":"","sources":["../../src/components/code-block.tsx"],"names":[],"mappings":"AAqBA,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AA2BD,wBAAgB,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,cAAc,+BA+C3D"}
@@ -1,23 +1,36 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /**
3
- * `CodeBlock` — escaped, monospaced preformatted text for tool results and string
4
- * payloads, on the design system's terminal ground (`tai-code-block`). The content
5
- * is a React text child inside `<pre><code>`, so it is escaped by React and
6
- * rendered verbatim; this component is NEVER an HTML sink (no
7
- * `dangerouslySetInnerHTML`).
8
- *
9
- * The `<pre>` IS the scrolling box, so it carries the region attributes itself
10
- * rather than sitting inside a `ScrollRegion` wrapper that would add a second
11
- * scroller: a long line makes it a named keyboard target, a short one leaves it
12
- * an ordinary block.
13
- */
2
+ import { CheckIcon, CopyIcon, XCircleIcon } from './icons';
14
3
  import { useOverflowRegion } from './scroll-region';
4
+ import { COPIED_LABEL, useClipboardCopy } from '../hooks/useClipboardCopy';
15
5
  /** The region's name when no language caption supplies one. */
16
6
  const DEFAULT_LABEL = 'Code';
7
+ /** The button's resting accessible name; whichever face shows is also its name. */
8
+ const COPY_LABEL = 'Copy code';
9
+ /** Shown when the browser offers no clipboard at all (any non-secure context). */
10
+ const NO_CLIPBOARD = 'This browser will not write to the clipboard here. Select the code and copy it.';
11
+ /** Shown when the write is offered and refused. */
12
+ function writeFailed(reason) {
13
+ const detail = reason instanceof Error ? reason.message : String(reason);
14
+ return `Copy failed: ${detail}. Select the code and copy it.`;
15
+ }
16
+ /** The frame the copy button is pinned to, over the top-right of the code. */
17
+ const frameStyle = { position: 'relative' };
18
+ const copyButtonStyle = {
19
+ position: 'absolute',
20
+ top: 'var(--tai-space-2)',
21
+ right: 'var(--tai-space-2)',
22
+ };
17
23
  export function CodeBlock({ code, language }) {
18
24
  // No consumer ref: the measurement's own callback ref is the only thing that
19
25
  // needs the `<pre>`, and this component publishes no `ref` prop.
20
26
  const region = useOverflowRegion(undefined, language ?? DEFAULT_LABEL);
21
- return (_jsxs("div", { className: "tai-stack tai-stack-2", children: [language !== undefined ? _jsx("span", { className: "tai-label", children: language }) : null, _jsx("pre", { className: "tai-code-block", ...region, children: _jsx("code", { children: code }) })] }));
27
+ const { copied, error: copyError, announcement, copy, } = useClipboardCopy({
28
+ noClipboard: NO_CLIPBOARD,
29
+ writeFailed,
30
+ });
31
+ const handleCopy = () => {
32
+ void copy(() => code);
33
+ };
34
+ return (_jsxs("div", { className: "tai-stack tai-stack-2", children: [language !== undefined ? _jsx("span", { className: "tai-label", children: language }) : null, _jsxs("div", { style: frameStyle, children: [_jsx("pre", { className: "tai-code-block", ...region, children: _jsx("code", { children: code }) }), _jsx("button", { type: "button", className: "tai-icon-btn", style: copyButtonStyle, "aria-label": copied ? COPIED_LABEL : COPY_LABEL, onClick: handleCopy, children: copied ? _jsx(CheckIcon, {}) : _jsx(CopyIcon, {}) }), _jsx("span", { "aria-live": "polite", className: "tai-visually-hidden", children: announcement })] }), copyError !== undefined ? (_jsxs("span", { role: "alert", className: "tai-field-error", children: [_jsx(XCircleIcon, {}), copyError] })) : null] }));
22
35
  }
23
36
  //# sourceMappingURL=code-block.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"code-block.js","sourceRoot":"","sources":["../../src/components/code-block.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAQpD,+DAA+D;AAC/D,MAAM,aAAa,GAAG,MAAM,CAAC;AAE7B,MAAM,UAAU,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAkB;IAC1D,6EAA6E;IAC7E,iEAAiE;IACjE,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,EAAE,QAAQ,IAAI,aAAa,CAAC,CAAC;IAEvE,OAAO,CACL,eAAK,SAAS,EAAC,uBAAuB,aACnC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,eAAM,SAAS,EAAC,WAAW,YAAE,QAAQ,GAAQ,CAAC,CAAC,CAAC,IAAI,EAC9E,cAAK,SAAS,EAAC,gBAAgB,KAAK,MAAM,YACxC,yBAAO,IAAI,GAAQ,GACf,IACF,CACP,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"code-block.js","sourceRoot":"","sources":["../../src/components/code-block.tsx"],"names":[],"mappings":";AAiBA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAQ3E,+DAA+D;AAC/D,MAAM,aAAa,GAAG,MAAM,CAAC;AAE7B,mFAAmF;AACnF,MAAM,UAAU,GAAG,WAAW,CAAC;AAE/B,kFAAkF;AAClF,MAAM,YAAY,GAChB,iFAAiF,CAAC;AAEpF,mDAAmD;AACnD,SAAS,WAAW,CAAC,MAAe;IAClC,MAAM,MAAM,GAAG,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACzE,OAAO,gBAAgB,MAAM,gCAAgC,CAAC;AAChE,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,GAAkB,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;AAE3D,MAAM,eAAe,GAAkB;IACrC,QAAQ,EAAE,UAAU;IACpB,GAAG,EAAE,oBAAoB;IACzB,KAAK,EAAE,oBAAoB;CAC5B,CAAC;AAEF,MAAM,UAAU,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAkB;IAC1D,6EAA6E;IAC7E,iEAAiE;IACjE,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,EAAE,QAAQ,IAAI,aAAa,CAAC,CAAC;IAEvE,MAAM,EACJ,MAAM,EACN,KAAK,EAAE,SAAS,EAChB,YAAY,EACZ,IAAI,GACL,GAAG,gBAAgB,CAAC;QACnB,WAAW,EAAE,YAAY;QACzB,WAAW;KACZ,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,GAAS,EAAE;QAC5B,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC,CAAC;IAEF,OAAO,CACL,eAAK,SAAS,EAAC,uBAAuB,aACnC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,eAAM,SAAS,EAAC,WAAW,YAAE,QAAQ,GAAQ,CAAC,CAAC,CAAC,IAAI,EAC9E,eAAK,KAAK,EAAE,UAAU,aACpB,cAAK,SAAS,EAAC,gBAAgB,KAAK,MAAM,YACxC,yBAAO,IAAI,GAAQ,GACf,EACN,iBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,cAAc,EACxB,KAAK,EAAE,eAAe,gBACV,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU,EAC9C,OAAO,EAAE,UAAU,YAElB,MAAM,CAAC,CAAC,CAAC,KAAC,SAAS,KAAG,CAAC,CAAC,CAAC,KAAC,QAAQ,KAAG,GAC/B,EACT,4BAAgB,QAAQ,EAAC,SAAS,EAAC,qBAAqB,YACrD,YAAY,GACR,IACH,EACL,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,CACzB,gBAAM,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,iBAAiB,aAC5C,KAAC,WAAW,KAAG,EACd,SAAS,IACL,CACR,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAC;AACJ,CAAC"}
@@ -1316,6 +1316,26 @@
1316
1316
  color: var(--tai-color-text);
1317
1317
  }
1318
1318
 
1319
+ /* Fullscreen variant: fills the viewport instead of centring at a fixed size.
1320
+ Composes with `.tai-dialog`, overriding only geometry and edge chrome. */
1321
+ .tai-dialog-fullscreen {
1322
+ top: 0;
1323
+ left: 0;
1324
+ transform: none;
1325
+ /* `border-box` so `100vw` is the outer edge and never overflows; declared here,
1326
+ not inherited from `.tai-dialog`. */
1327
+ box-sizing: border-box;
1328
+ width: 100vw;
1329
+ max-width: none;
1330
+ /* `dvh` is the live viewport with a `vh` fallback for browsers below the `dvh` floor. */
1331
+ height: 100vh;
1332
+ height: 100dvh;
1333
+ max-height: none;
1334
+ padding: 0;
1335
+ border: 0;
1336
+ border-radius: 0;
1337
+ }
1338
+
1319
1339
  .tai-dialog-title {
1320
1340
  margin: 0 0 var(--tai-space-2);
1321
1341
  font-size: var(--tai-text-section);
@@ -1 +1 @@
1
- {"version":3,"file":"copy-field.d.ts","sourceRoot":"","sources":["../../src/components/copy-field.tsx"],"names":[],"mappings":"AA8BA,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAyDD,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,QAAuB,EAAE,KAAK,EAAE,EAAE,cAAc,+BAqF3F"}
1
+ {"version":3,"file":"copy-field.d.ts","sourceRoot":"","sources":["../../src/components/copy-field.tsx"],"names":[],"mappings":"AAsBA,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAoCD,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,QAAuB,EAAE,KAAK,EAAE,EAAE,cAAc,+BA0D3F"}
@@ -1,41 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /**
3
- * `CopyField` — a show-once box for a freshly-minted secret (an API key or a
4
- * minted backup key). It renders the value in a monospace, break-all,
5
- * select-all box beside a copy button that writes the value to the clipboard and
6
- * shows a transient "Copied" affordance. It is PRESENTATIONAL: the caller owns
7
- * the value; this component fetches nothing and never re-reads the secret.
8
- *
9
- * The button holds BOTH states stacked in one grid cell, so the widest of them
10
- * sets the width once and the flip never reflows the row. Only the active state
11
- * is exposed to assistive tech, and that state's own words are the button's
12
- * accessible name: a constant `aria-label` would leave a button reading "Copied"
13
- * named "Copy", which fails WCAG 2.5.3 (Label in Name) and leaves a voice-control
14
- * user naming a control they cannot see. The polite live region beside the button
15
- * is what actually announces the flip.
16
- *
17
- * A clipboard write can FAIL — a permissions policy blocks it, or the page is
18
- * not in a secure context, where `navigator.clipboard` does not exist at all.
19
- * Either way the value is a secret shown once, so the failure is rendered as a
20
- * visible `role="alert"` telling the reader to copy it by hand: throwing it at
21
- * the console would leave the button looking inert and the secret unrecoverable.
22
- *
23
- * SAFETY: the value and caption render as TEXT (React escapes them) — never an
24
- * HTML sink. The value is never logged, and neither is a failure — the alert
25
- * names the reason, never the value. Pinned by a test.
26
- */
27
- import { useEffect, useRef, useState } from 'react';
28
2
  import { CheckIcon, CopyIcon, XCircleIcon } from './icons';
29
- const COPIED_RESET_MS = 2000;
30
- /** The button's two faces. Whichever is exposed is also its accessible name. */
3
+ import { COPIED_LABEL, useClipboardCopy } from '../hooks/useClipboardCopy';
4
+ /** The button's resting face; whichever face is exposed is also its accessible name. */
31
5
  const COPY_LABEL = 'Copy';
32
- const COPIED_LABEL = 'Copied';
33
- /** Announced once, and worded so it is never mistaken for the visible label. */
34
- const COPIED_ANNOUNCEMENT = 'Copied to clipboard';
35
- /** The clipboard this browser actually offers, or `undefined` when it offers none. */
36
- function clipboardOf(host) {
37
- return host.clipboard;
38
- }
39
6
  /** Shown when the browser offers no clipboard at all (any non-secure context). */
40
7
  const NO_CLIPBOARD = 'This browser will not write to the clipboard here. Select the value above and copy it.';
41
8
  /** Shown when the write is offered and refused. */
@@ -62,45 +29,13 @@ const stateStyle = {
62
29
  gap: 'var(--tai-space-2)',
63
30
  };
64
31
  export function CopyField({ value, caption, idPrefix = 'copy-field', label }) {
65
- const [copied, setCopied] = useState(false);
66
- const [copyError, setCopyError] = useState(undefined);
67
- const resetTimer = useRef(null);
68
- // The clipboard write is async, so a copy can still be in flight when the
69
- // component goes away — the caller's dialog closes on the same click. Clearing
70
- // the timer alone would not cover that: at unmount there is no timer yet, and
71
- // the resolution that follows would start one nothing is left to clear. This
72
- // flag is what the resolution checks before it touches state at all.
73
- const mounted = useRef(true);
74
- useEffect(() => {
75
- mounted.current = true;
76
- return () => {
77
- mounted.current = false;
78
- if (resetTimer.current !== null)
79
- clearTimeout(resetTimer.current);
80
- };
81
- }, []);
32
+ const { copied, error: copyError, announcement, copy, } = useClipboardCopy({
33
+ noClipboard: NO_CLIPBOARD,
34
+ writeFailed,
35
+ });
82
36
  const handleCopy = () => {
83
- const clipboard = clipboardOf(navigator);
84
- if (clipboard === undefined) {
85
- setCopyError(NO_CLIPBOARD);
86
- return;
87
- }
88
- setCopyError(undefined);
89
- void clipboard.writeText(value).then(() => {
90
- if (!mounted.current)
91
- return;
92
- setCopied(true);
93
- if (resetTimer.current !== null)
94
- clearTimeout(resetTimer.current);
95
- resetTimer.current = setTimeout(() => {
96
- setCopied(false);
97
- }, COPIED_RESET_MS);
98
- }, (error) => {
99
- if (!mounted.current)
100
- return;
101
- setCopyError(writeFailed(error));
102
- });
37
+ void copy(() => value);
103
38
  };
104
- return (_jsxs("div", { "data-testid": idPrefix, className: "tai-stack tai-stack-2", children: [label !== undefined ? _jsx("span", { className: "tai-field-label", children: label }) : null, _jsxs("div", { className: "tai-row", children: [_jsx("code", { className: "tai-code", style: valueStyle, children: value }), _jsx("button", { type: "button", className: "tai-btn tai-btn-secondary", "data-testid": `${idPrefix}-copy`, onClick: handleCopy, children: _jsxs("span", { style: statesStyle, children: [_jsxs("span", { "aria-hidden": copied, style: { ...stateStyle, visibility: copied ? 'hidden' : 'visible' }, children: [_jsx(CopyIcon, {}), COPY_LABEL] }), _jsxs("span", { "aria-hidden": !copied, style: { ...stateStyle, visibility: copied ? 'visible' : 'hidden' }, children: [_jsx(CheckIcon, {}), COPIED_LABEL] })] }) }), _jsx("span", { "aria-live": "polite", className: "tai-visually-hidden", children: copied ? COPIED_ANNOUNCEMENT : '' })] }), copyError !== undefined ? (_jsxs("span", { role: "alert", className: "tai-field-error", children: [_jsx(XCircleIcon, {}), copyError] })) : null, caption !== undefined ? _jsx("span", { className: "tai-field-hint", children: caption }) : null] }));
39
+ return (_jsxs("div", { "data-testid": idPrefix, className: "tai-stack tai-stack-2", children: [label !== undefined ? _jsx("span", { className: "tai-field-label", children: label }) : null, _jsxs("div", { className: "tai-row", children: [_jsx("code", { className: "tai-code", style: valueStyle, children: value }), _jsx("button", { type: "button", className: "tai-btn tai-btn-secondary", "data-testid": `${idPrefix}-copy`, onClick: handleCopy, children: _jsxs("span", { style: statesStyle, children: [_jsxs("span", { "aria-hidden": copied, style: { ...stateStyle, visibility: copied ? 'hidden' : 'visible' }, children: [_jsx(CopyIcon, {}), COPY_LABEL] }), _jsxs("span", { "aria-hidden": !copied, style: { ...stateStyle, visibility: copied ? 'visible' : 'hidden' }, children: [_jsx(CheckIcon, {}), COPIED_LABEL] })] }) }), _jsx("span", { "aria-live": "polite", className: "tai-visually-hidden", children: announcement })] }), copyError !== undefined ? (_jsxs("span", { role: "alert", className: "tai-field-error", children: [_jsx(XCircleIcon, {}), copyError] })) : null, caption !== undefined ? _jsx("span", { className: "tai-field-hint", children: caption }) : null] }));
105
40
  }
106
41
  //# sourceMappingURL=copy-field.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"copy-field.js","sourceRoot":"","sources":["../../src/components/copy-field.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"copy-field.js","sourceRoot":"","sources":["../../src/components/copy-field.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * `DateRangePicker` — the compact time-window control observability screens share.
3
+ * It emits ONE of two shapes:
4
+ *
5
+ * - a relative token from a PRESET chip, `{ kind: 'relative', token }` — the backend
6
+ * resolves `\d+[hdw]` tokens, so a preset stays a token rather than freezing to an
7
+ * instant at click time;
8
+ * - an ABSOLUTE range from the two native `datetime-local` inputs,
9
+ * `{ kind: 'absolute', from, to }` with ISO instants — native inputs keep the
10
+ * control inside the platform date UI, no third-party calendar.
11
+ *
12
+ * Absolute ranges are normalized by {@link normalizeCustomRange}; the active window
13
+ * is rendered by {@link formatRangeLabel}, always with the YEAR present.
14
+ */
15
+ import { type ReactNode } from 'react';
16
+ /** A relative preset: a `\d+[hdw]` token the backend resolves, plus its chip label. */
17
+ export interface DateRangePreset {
18
+ readonly token: string;
19
+ readonly label: string;
20
+ }
21
+ /** The presets a picker offers when the caller names none. */
22
+ export declare const DEFAULT_DATE_RANGE_PRESETS: readonly DateRangePreset[];
23
+ /**
24
+ * A named time window: either a relative token the backend resolves against its
25
+ * own clock, or an absolute pair of ISO instants.
26
+ */
27
+ export type DateRangeValue = {
28
+ readonly kind: 'relative';
29
+ readonly token: string;
30
+ } | {
31
+ readonly kind: 'absolute';
32
+ readonly from: string;
33
+ readonly to: string;
34
+ };
35
+ export declare function formatRangeLabel(value: DateRangeValue, presets?: readonly DateRangePreset[]): string;
36
+ /**
37
+ * A raw from/to pair resolved to a clean absolute range. Order matters: SWAP the
38
+ * reversed pair first (earlier instant becomes start), THEN cap both ends at `now` —
39
+ * capping first can leave a future instant on the post-swap end. The end is
40
+ * INCLUSIVE of its minute, extended to `:59.999`, so `23:59` reaches the day's end.
41
+ */
42
+ export declare function normalizeCustomRange(fromInput: string, toInput: string, now: Date): {
43
+ readonly from: string;
44
+ readonly to: string;
45
+ };
46
+ export interface DateRangePickerProps {
47
+ readonly value: DateRangeValue;
48
+ readonly onValueChange: (value: DateRangeValue) => void;
49
+ /** The preset chips to offer. Defaults to {@link DEFAULT_DATE_RANGE_PRESETS}. */
50
+ readonly presets?: readonly DateRangePreset[];
51
+ /** The control group's accessible name. */
52
+ readonly 'aria-label'?: string;
53
+ readonly disabled?: boolean;
54
+ /**
55
+ * The clock the absolute-range clamp reads. Defaults to the wall clock; a caller
56
+ * (or a test) injects a fixed instant to pin the "no future" boundary.
57
+ */
58
+ readonly now?: () => Date;
59
+ }
60
+ /**
61
+ * The range control: a row of preset chips over the two `datetime-local` inputs and
62
+ * an Apply action, with the active window echoed as text below.
63
+ *
64
+ * Controlled: the caller owns `value` and receives the next window through
65
+ * `onValueChange` — a preset click emits its token immediately, while the custom
66
+ * inputs hold a working draft until Apply commits the normalized absolute range.
67
+ *
68
+ * The drafts track the controlled value, re-seeded on a NEW absolute `value` (keyed
69
+ * on `from`/`to`), so edits against an unchanged value are preserved but a genuinely
70
+ * different value replaces an in-progress draft.
71
+ */
72
+ export declare function DateRangePicker({ value, onValueChange, presets, 'aria-label': ariaLabel, disabled, now, }: DateRangePickerProps): ReactNode;
73
+ //# sourceMappingURL=date-range-picker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date-range-picker.d.ts","sourceRoot":"","sources":["../../src/components/date-range-picker.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAMrE,uFAAuF;AACvF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,8DAA8D;AAC9D,eAAO,MAAM,0BAA0B,EAAE,SAAS,eAAe,EAKhE,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAgC9E,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,cAAc,EACrB,OAAO,GAAE,SAAS,eAAe,EAA+B,GAC/D,MAAM,CAQR;AAuBD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,IAAI,GACR;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAiBhD;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IACxD,iFAAiF;IACjF,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAC9C,2CAA2C;IAC3C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAKD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,aAAa,EACb,OAAoC,EACpC,YAAY,EAAE,SAAwB,EACtC,QAAgB,EAChB,GAAsB,GACvB,EAAE,oBAAoB,GAAG,SAAS,CAmFlC"}
@@ -0,0 +1,146 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * `DateRangePicker` — the compact time-window control observability screens share.
4
+ * It emits ONE of two shapes:
5
+ *
6
+ * - a relative token from a PRESET chip, `{ kind: 'relative', token }` — the backend
7
+ * resolves `\d+[hdw]` tokens, so a preset stays a token rather than freezing to an
8
+ * instant at click time;
9
+ * - an ABSOLUTE range from the two native `datetime-local` inputs,
10
+ * `{ kind: 'absolute', from, to }` with ISO instants — native inputs keep the
11
+ * control inside the platform date UI, no third-party calendar.
12
+ *
13
+ * Absolute ranges are normalized by {@link normalizeCustomRange}; the active window
14
+ * is rendered by {@link formatRangeLabel}, always with the YEAR present.
15
+ */
16
+ import { useState } from 'react';
17
+ import { Field } from './field';
18
+ import { TextInput } from './inputs';
19
+ import { Button } from './primitives';
20
+ /** The presets a picker offers when the caller names none. */
21
+ export const DEFAULT_DATE_RANGE_PRESETS = [
22
+ { token: '24h', label: 'Last 24 hours' },
23
+ { token: '7d', label: 'Last 7 days' },
24
+ { token: '30d', label: 'Last 30 days' },
25
+ { token: '90d', label: 'Last 90 days' },
26
+ ];
27
+ const RELATIVE_TOKEN = /^(\d+)([hdw])$/;
28
+ const UNIT_NOUN = { h: 'hour', d: 'day', w: 'week' };
29
+ /**
30
+ * A relative token as a readable phrase (`7d` → `Last 7 days`). A token outside the
31
+ * `\d+[hdw]` grammar raises rather than rendering a silent placeholder.
32
+ */
33
+ function relativePhrase(token) {
34
+ const match = RELATIVE_TOKEN.exec(token);
35
+ if (match === null) {
36
+ throw new Error(`Invalid relative range token: ${token}`);
37
+ }
38
+ const amount = Number(match[1]);
39
+ const noun = UNIT_NOUN[match[2] ?? ''] ?? '';
40
+ return `Last ${String(amount)} ${noun}${amount === 1 ? '' : 's'}`;
41
+ }
42
+ /**
43
+ * The active window as human text: a relative token as its preset label (or a
44
+ * derived phrase), an absolute range as both instants with the YEAR present.
45
+ */
46
+ const ABSOLUTE_FORMAT = new Intl.DateTimeFormat(undefined, {
47
+ year: 'numeric',
48
+ month: 'short',
49
+ day: 'numeric',
50
+ hour: '2-digit',
51
+ minute: '2-digit',
52
+ });
53
+ export function formatRangeLabel(value, presets = DEFAULT_DATE_RANGE_PRESETS) {
54
+ if (value.kind === 'relative') {
55
+ const preset = presets.find((candidate) => candidate.token === value.token);
56
+ return preset?.label ?? relativePhrase(value.token);
57
+ }
58
+ const from = new Date(value.from);
59
+ const to = new Date(value.to);
60
+ return `${ABSOLUTE_FORMAT.format(from)} – ${ABSOLUTE_FORMAT.format(to)}`;
61
+ }
62
+ /** A `datetime-local` string parsed as a local instant, or a loud error on garbage. */
63
+ function parseLocalInput(value, field) {
64
+ const parsed = new Date(value);
65
+ if (Number.isNaN(parsed.getTime())) {
66
+ throw new Error(`Invalid ${field} datetime: ${value}`);
67
+ }
68
+ return parsed;
69
+ }
70
+ function pad(value) {
71
+ return String(value).padStart(2, '0');
72
+ }
73
+ /** A local instant as the `YYYY-MM-DDTHH:mm` a `datetime-local` input reads. */
74
+ function toLocalInputValue(date) {
75
+ return (`${String(date.getFullYear())}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}` +
76
+ `T${pad(date.getHours())}:${pad(date.getMinutes())}`);
77
+ }
78
+ /**
79
+ * A raw from/to pair resolved to a clean absolute range. Order matters: SWAP the
80
+ * reversed pair first (earlier instant becomes start), THEN cap both ends at `now` —
81
+ * capping first can leave a future instant on the post-swap end. The end is
82
+ * INCLUSIVE of its minute, extended to `:59.999`, so `23:59` reaches the day's end.
83
+ */
84
+ export function normalizeCustomRange(fromInput, toInput, now) {
85
+ let start = parseLocalInput(fromInput, 'from');
86
+ let end = parseLocalInput(toInput, 'to');
87
+ if (start.getTime() > end.getTime()) {
88
+ [start, end] = [end, start];
89
+ }
90
+ // Inclusive end: extend the selected minute to its final millisecond.
91
+ end = new Date(end);
92
+ end.setSeconds(59, 999);
93
+ const nowMs = now.getTime();
94
+ if (start.getTime() > nowMs)
95
+ start = new Date(nowMs);
96
+ if (end.getTime() > nowMs)
97
+ end = new Date(nowMs);
98
+ return { from: start.toISOString(), to: end.toISOString() };
99
+ }
100
+ const CUSTOM_ROW_STYLE = { alignItems: 'flex-end' };
101
+ const FIELD_STYLE = { flex: 1 };
102
+ /**
103
+ * The range control: a row of preset chips over the two `datetime-local` inputs and
104
+ * an Apply action, with the active window echoed as text below.
105
+ *
106
+ * Controlled: the caller owns `value` and receives the next window through
107
+ * `onValueChange` — a preset click emits its token immediately, while the custom
108
+ * inputs hold a working draft until Apply commits the normalized absolute range.
109
+ *
110
+ * The drafts track the controlled value, re-seeded on a NEW absolute `value` (keyed
111
+ * on `from`/`to`), so edits against an unchanged value are preserved but a genuinely
112
+ * different value replaces an in-progress draft.
113
+ */
114
+ export function DateRangePicker({ value, onValueChange, presets = DEFAULT_DATE_RANGE_PRESETS, 'aria-label': ariaLabel = 'Date range', disabled = false, now = () => new Date(), }) {
115
+ const nextFrom = value.kind === 'absolute' ? value.from : null;
116
+ const nextTo = value.kind === 'absolute' ? value.to : null;
117
+ const [fromDraft, setFromDraft] = useState(() => nextFrom !== null ? toLocalInputValue(new Date(nextFrom)) : '');
118
+ const [toDraft, setToDraft] = useState(() => nextTo !== null ? toLocalInputValue(new Date(nextTo)) : '');
119
+ // The absolute ends that last seeded the drafts (null while a preset is active).
120
+ // Comparing against the incoming ends re-seeds only when the caller swaps in a
121
+ // different absolute value, leaving drafts typed against an unchanged value alone.
122
+ const [seeded, setSeeded] = useState(() => ({
123
+ from: nextFrom,
124
+ to: nextTo,
125
+ }));
126
+ if (seeded.from !== nextFrom || seeded.to !== nextTo) {
127
+ setSeeded({ from: nextFrom, to: nextTo });
128
+ if (nextFrom !== null && nextTo !== null) {
129
+ setFromDraft(toLocalInputValue(new Date(nextFrom)));
130
+ setToDraft(toLocalInputValue(new Date(nextTo)));
131
+ }
132
+ }
133
+ const activeToken = value.kind === 'relative' ? value.token : null;
134
+ const canApply = !disabled && fromDraft !== '' && toDraft !== '';
135
+ const applyCustom = () => {
136
+ onValueChange({ kind: 'absolute', ...normalizeCustomRange(fromDraft, toDraft, now()) });
137
+ };
138
+ return (_jsxs("div", { role: "group", "aria-label": ariaLabel, className: "tai-stack tai-stack-3", children: [_jsx("div", { className: "tai-row", children: presets.map((preset) => (_jsx("button", { type: "button", className: "tai-chip", "aria-pressed": activeToken === preset.token, onClick: () => {
139
+ onValueChange({ kind: 'relative', token: preset.token });
140
+ }, disabled: disabled, children: preset.label }, preset.token))) }), _jsxs("div", { className: "tai-row", style: CUSTOM_ROW_STYLE, children: [_jsx(Field, { label: "From", style: FIELD_STYLE, children: _jsx(TextInput, { type: "datetime-local", value: fromDraft, onChange: (event) => {
141
+ setFromDraft(event.target.value);
142
+ }, disabled: disabled }) }), _jsx(Field, { label: "To", style: FIELD_STYLE, children: _jsx(TextInput, { type: "datetime-local", value: toDraft, onChange: (event) => {
143
+ setToDraft(event.target.value);
144
+ }, disabled: disabled }) }), _jsx(Button, { type: "button", variant: "primary", onClick: applyCustom, disabled: !canApply, children: "Apply range" })] }), _jsx("p", { role: "status", className: "tai-muted", style: { margin: 0 }, children: formatRangeLabel(value, presets) })] }));
145
+ }
146
+ //# sourceMappingURL=date-range-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date-range-picker.js","sourceRoot":"","sources":["../../src/components/date-range-picker.tsx"],"names":[],"mappings":"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"}
@@ -16,6 +16,24 @@ export interface DialogProps {
16
16
  readonly open?: boolean;
17
17
  readonly defaultOpen?: boolean;
18
18
  readonly onOpenChange?: (open: boolean) => void;
19
+ /**
20
+ * Fill the viewport edge to edge instead of centring a fixed-size panel. The
21
+ * overlay, the focus trap and every other Radix behaviour are unchanged.
22
+ */
23
+ readonly fullscreen?: boolean;
24
+ /**
25
+ * A class merged onto `RadixDialog.Content` after the surface classes, for a
26
+ * host that must reach the content element itself — e.g. a plugin hanging its
27
+ * CSS-scoping root on the panel so its styles apply inside the Radix portal.
28
+ */
29
+ readonly contentClassName?: string;
30
+ /**
31
+ * Drop the forced visual `Title` and the `tai-stack` children wrapper, rendering
32
+ * `children` directly for a chrome-free content mode. The `title` is still
33
+ * required and still names the dialog — it renders visually-hidden — so the
34
+ * accessible name never depends on the visible chrome.
35
+ */
36
+ readonly chromeless?: boolean;
19
37
  }
20
- export declare function Dialog({ title, description, children, trigger, open, defaultOpen, onOpenChange, }: DialogProps): import("react").JSX.Element;
38
+ export declare function Dialog({ title, description, children, trigger, open, defaultOpen, onOpenChange, fullscreen, contentClassName, chromeless, }: DialogProps): import("react").JSX.Element;
21
39
  //# sourceMappingURL=dialog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../src/components/dialog.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKrD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;IAC9B;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACjD;AAED,wBAAgB,MAAM,CAAC,EACrB,KAAK,EACL,WAAW,EACX,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,WAAW,EACX,YAAY,GACb,EAAE,WAAW,+BA4Bb"}
1
+ {"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../src/components/dialog.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKrD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;IAC9B;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAChD;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,wBAAgB,MAAM,CAAC,EACrB,KAAK,EACL,WAAW,EACX,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,WAAW,EACX,YAAY,EACZ,UAAkB,EAClB,gBAAgB,EAChB,UAAkB,GACnB,EAAE,WAAW,+BAkDb"}
@@ -1,25 +1,28 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /**
3
3
  * `Dialog` — a design-system wrapper over Radix Dialog: an accessible modal (role
4
- * `dialog`, focus trap, Escape to close) labelled by its title. Modality is
5
- * enforced by the focus trap and by Radix marking the rest of the page inert, not
6
- * by an `aria-modal` attribute — the panel ships none.
4
+ * `dialog`, focus trap, Escape to close) labelled by its title. Modality comes from
5
+ * the focus trap and Radix marking the page inert, not an `aria-modal` attribute.
7
6
  *
8
- * Radix owns every modal behaviour — the focus trap, the background scroll lock,
9
- * Escape, and focus return to the trigger — so this component adds no second
10
- * mechanism. It contributes only the design-system surface: `tai-overlay` paints
11
- * the scrim from `--tai-color-scrim`, and `tai-dialog` sizes the panel with a
12
- * `min()` so it still fits a 320 px viewport. The one gap Radix leaves — focus
13
- * return for a dialog that renders no trigger — is filled by
14
- * `useModalFocusReturn`, which stands down whenever a trigger IS rendered.
7
+ * Radix owns every modal behaviour (focus trap, scroll lock, Escape, focus return
8
+ * to the trigger); this component adds only the design-system surface (`tai-overlay`
9
+ * scrim, `tai-dialog` panel). The one gap Radix leaves — focus return for a dialog
10
+ * with no trigger — is filled by `useModalFocusReturn`, which stands down when a
11
+ * trigger IS rendered. The `fullscreen`, `contentClassName` and `chromeless` opt-ins
12
+ * (see the props) reshape the panel without giving up any Radix behaviour.
15
13
  */
16
14
  import * as RadixDialog from '@radix-ui/react-dialog';
17
15
  import { assertSlotElement } from '../element-slot';
18
16
  import { useModalFocusReturn } from './modal-focus';
19
- export function Dialog({ title, description, children, trigger, open, defaultOpen, onOpenChange, }) {
17
+ export function Dialog({ title, description, children, trigger, open, defaultOpen, onOpenChange, fullscreen = false, contentClassName, chromeless = false, }) {
20
18
  const focusReturn = useModalFocusReturn(trigger !== undefined);
21
19
  if (trigger !== undefined)
22
20
  assertSlotElement(trigger, 'Dialog `trigger`');
23
- return (_jsxs(RadixDialog.Root, { open: open, defaultOpen: defaultOpen, onOpenChange: onOpenChange, children: [trigger !== undefined ? _jsx(RadixDialog.Trigger, { asChild: true, children: trigger }) : null, _jsxs(RadixDialog.Portal, { children: [_jsx(RadixDialog.Overlay, { className: "tai-overlay" }), _jsxs(RadixDialog.Content, { className: "tai-dialog", ...focusReturn, ...(description === undefined ? { 'aria-describedby': undefined } : {}), children: [_jsx(RadixDialog.Title, { className: "tai-dialog-title", children: title }), _jsxs("div", { className: "tai-stack", children: [description !== undefined ? (_jsx(RadixDialog.Description, { className: "tai-muted", children: description })) : null, children] })] })] })] }));
21
+ const contentClasses = ['tai-dialog'];
22
+ if (fullscreen)
23
+ contentClasses.push('tai-dialog-fullscreen');
24
+ if (contentClassName !== undefined)
25
+ contentClasses.push(contentClassName);
26
+ return (_jsxs(RadixDialog.Root, { open: open, defaultOpen: defaultOpen, onOpenChange: onOpenChange, children: [trigger !== undefined ? _jsx(RadixDialog.Trigger, { asChild: true, children: trigger }) : null, _jsxs(RadixDialog.Portal, { children: [_jsx(RadixDialog.Overlay, { className: "tai-overlay" }), _jsxs(RadixDialog.Content, { className: contentClasses.join(' '), ...focusReturn, ...(description === undefined ? { 'aria-describedby': undefined } : {}), children: [_jsx(RadixDialog.Title, { className: chromeless ? 'tai-visually-hidden' : 'tai-dialog-title', children: title }), chromeless ? (_jsxs(_Fragment, { children: [description !== undefined ? (_jsx(RadixDialog.Description, { className: "tai-visually-hidden", children: description })) : null, children] })) : (_jsxs("div", { className: "tai-stack", children: [description !== undefined ? (_jsx(RadixDialog.Description, { className: "tai-muted", children: description })) : null, children] }))] })] })] }));
24
27
  }
25
28
  //# sourceMappingURL=dialog.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dialog.js","sourceRoot":"","sources":["../../src/components/dialog.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,WAAW,MAAM,wBAAwB,CAAC;AAGtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAqBpD,MAAM,UAAU,MAAM,CAAC,EACrB,KAAK,EACL,WAAW,EACX,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,WAAW,EACX,YAAY,GACA;IACZ,MAAM,WAAW,GAAG,mBAAmB,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC;IAC/D,IAAI,OAAO,KAAK,SAAS;QAAE,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;IAC1E,OAAO,CACL,MAAC,WAAW,CAAC,IAAI,IAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,aAC/E,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,KAAC,WAAW,CAAC,OAAO,IAAC,OAAO,kBAAE,OAAO,GAAuB,CAAC,CAAC,CAAC,IAAI,EAC5F,MAAC,WAAW,CAAC,MAAM,eACjB,KAAC,WAAW,CAAC,OAAO,IAAC,SAAS,EAAC,aAAa,GAAG,EAK/C,MAAC,WAAW,CAAC,OAAO,IAClB,SAAS,EAAC,YAAY,KAClB,WAAW,KACX,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAExE,KAAC,WAAW,CAAC,KAAK,IAAC,SAAS,EAAC,kBAAkB,YAAE,KAAK,GAAqB,EAC3E,eAAK,SAAS,EAAC,WAAW,aACvB,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,CAC3B,KAAC,WAAW,CAAC,WAAW,IAAC,SAAS,EAAC,WAAW,YAAE,WAAW,GAA2B,CACvF,CAAC,CAAC,CAAC,IAAI,EACP,QAAQ,IACL,IACc,IACH,IACJ,CACpB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"dialog.js","sourceRoot":"","sources":["../../src/components/dialog.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,WAAW,MAAM,wBAAwB,CAAC;AAGtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAuCpD,MAAM,UAAU,MAAM,CAAC,EACrB,KAAK,EACL,WAAW,EACX,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,WAAW,EACX,YAAY,EACZ,UAAU,GAAG,KAAK,EAClB,gBAAgB,EAChB,UAAU,GAAG,KAAK,GACN;IACZ,MAAM,WAAW,GAAG,mBAAmB,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC;IAC/D,IAAI,OAAO,KAAK,SAAS;QAAE,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;IAE1E,MAAM,cAAc,GAAG,CAAC,YAAY,CAAC,CAAC;IACtC,IAAI,UAAU;QAAE,cAAc,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAC7D,IAAI,gBAAgB,KAAK,SAAS;QAAE,cAAc,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAE1E,OAAO,CACL,MAAC,WAAW,CAAC,IAAI,IAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,aAC/E,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,KAAC,WAAW,CAAC,OAAO,IAAC,OAAO,kBAAE,OAAO,GAAuB,CAAC,CAAC,CAAC,IAAI,EAC5F,MAAC,WAAW,CAAC,MAAM,eACjB,KAAC,WAAW,CAAC,OAAO,IAAC,SAAS,EAAC,aAAa,GAAG,EAK/C,MAAC,WAAW,CAAC,OAAO,IAClB,SAAS,EAAE,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,KAC/B,WAAW,KACX,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAIxE,KAAC,WAAW,CAAC,KAAK,IAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,kBAAkB,YAClF,KAAK,GACY,EACnB,UAAU,CAAC,CAAC,CAAC,CACZ,8BACG,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,CAC3B,KAAC,WAAW,CAAC,WAAW,IAAC,SAAS,EAAC,qBAAqB,YACrD,WAAW,GACY,CAC3B,CAAC,CAAC,CAAC,IAAI,EACP,QAAQ,IACR,CACJ,CAAC,CAAC,CAAC,CACF,eAAK,SAAS,EAAC,WAAW,aACvB,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,CAC3B,KAAC,WAAW,CAAC,WAAW,IAAC,SAAS,EAAC,WAAW,YAC3C,WAAW,GACY,CAC3B,CAAC,CAAC,CAAC,IAAI,EACP,QAAQ,IACL,CACP,IACmB,IACH,IACJ,CACpB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Unsaved-changes guard for tabbed editors. `Tabs` unmount inactive panels, so a
3
+ * tab switch tears an editor down and drops its edits. One registry closes the gap:
4
+ * editors report dirtiness via {@link useRegisterDirty}; {@link GuardedTabs} holds a
5
+ * switch away from a dirty editor behind a confirm; the same registry arms
6
+ * {@link useNavigationGuard}, so a route navigation confirms through the SAME dialog
7
+ * and a full-page unload raises the native prompt. The dialog is one shared
8
+ * {@link ConfirmDialog} driven by a pending-promise resolver, so the guard's
9
+ * `Promise<boolean>` handler and the tab switch both settle against it.
10
+ */
11
+ import { type ReactNode } from 'react';
12
+ import type { TabItem } from './tabs';
13
+ /**
14
+ * Report an editor's dirty state to the enclosing {@link GuardedTabs}. A no-op
15
+ * outside a provider (an editor rendered standalone, e.g. in isolation tests) so
16
+ * the hook is always safe to call.
17
+ */
18
+ export declare function useRegisterDirty(dirty: boolean): void;
19
+ /**
20
+ * A drop-in replacement for {@link Tabs} that guards a switch away from a dirty
21
+ * editor. Wraps the tabs in a {@link DirtyGuardProvider}, so an editor rendered in
22
+ * any panel can arm the guard through {@link useRegisterDirty}.
23
+ */
24
+ export declare function GuardedTabs({ items, defaultValue, }: {
25
+ readonly items: readonly TabItem[];
26
+ readonly defaultValue?: string;
27
+ }): ReactNode;
28
+ //# sourceMappingURL=dirty-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dirty-guard.d.ts","sourceRoot":"","sources":["../../src/components/dirty-guard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AA0EtC;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CASrD;AAwBD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,YAAY,GACb,EAAE;IACD,QAAQ,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,CAAC;IACnC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC,GAAG,SAAS,CAMZ"}