gridsmith-ui 0.30.0 → 0.30.2

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 (70) hide show
  1. package/CHANGELOG.md +61 -1
  2. package/dist/charts.js +21 -8
  3. package/dist/charts.js.map +1 -1
  4. package/dist/chat.js +15 -9
  5. package/dist/chat.js.map +1 -1
  6. package/dist/{chunk-OIOII5L6.js → chunk-4HJDOZ2M.js} +303 -20
  7. package/dist/chunk-4HJDOZ2M.js.map +1 -0
  8. package/dist/{chunk-INGF2CPW.js → chunk-6C2IUMY7.js} +3 -3
  9. package/dist/{chunk-INGF2CPW.js.map → chunk-6C2IUMY7.js.map} +1 -1
  10. package/dist/chunk-ACLKDE5W.js +59 -0
  11. package/dist/chunk-ACLKDE5W.js.map +1 -0
  12. package/dist/{chunk-NLXQ323J.js → chunk-BZRZENBS.js} +5 -4
  13. package/dist/chunk-BZRZENBS.js.map +1 -0
  14. package/dist/{chunk-RHWTMTU5.js → chunk-DBHVJ7Z3.js} +5 -49
  15. package/dist/chunk-DBHVJ7Z3.js.map +1 -0
  16. package/dist/{chunk-Z4MCD73B.js → chunk-HVGHGBKV.js} +6 -7
  17. package/dist/chunk-HVGHGBKV.js.map +1 -0
  18. package/dist/{chunk-UNS4TGGK.js → chunk-K6IDOCGE.js} +77 -27
  19. package/dist/chunk-K6IDOCGE.js.map +1 -0
  20. package/dist/{chunk-Q36JVJX4.js → chunk-MIUSSYVE.js} +4 -4
  21. package/dist/{chunk-Q36JVJX4.js.map → chunk-MIUSSYVE.js.map} +1 -1
  22. package/dist/{chunk-F2BNJSON.js → chunk-SIWERRP2.js} +70 -69
  23. package/dist/chunk-SIWERRP2.js.map +1 -0
  24. package/dist/{chunk-KTPGMJUK.js → chunk-SQHKC4HA.js} +8 -5
  25. package/dist/chunk-SQHKC4HA.js.map +1 -0
  26. package/dist/{chunk-NI5542ZM.js → chunk-USHW523V.js} +81 -24
  27. package/dist/chunk-USHW523V.js.map +1 -0
  28. package/dist/{chunk-GQVBZ4GW.js → chunk-WWN6CQKI.js} +31 -104
  29. package/dist/chunk-WWN6CQKI.js.map +1 -0
  30. package/dist/{chunk-JTU7OBYJ.js → chunk-YL5KD5RC.js} +4 -3
  31. package/dist/chunk-YL5KD5RC.js.map +1 -0
  32. package/dist/{chunk-6A5RHW2W.js → chunk-ZASGFL5D.js} +4 -5
  33. package/dist/chunk-ZASGFL5D.js.map +1 -0
  34. package/dist/chunk-ZCDA74RQ.js +110 -0
  35. package/dist/chunk-ZCDA74RQ.js.map +1 -0
  36. package/dist/data-grid.js +8 -10
  37. package/dist/data-grid.js.map +1 -1
  38. package/dist/editor-mentions.js +3 -5
  39. package/dist/editor-mentions.js.map +1 -1
  40. package/dist/editor.js +8 -7
  41. package/dist/editor.js.map +1 -1
  42. package/dist/index.d.ts +14 -1
  43. package/dist/index.js +198 -105
  44. package/dist/index.js.map +1 -1
  45. package/dist/kanban.js +11 -12
  46. package/dist/kanban.js.map +1 -1
  47. package/dist/resizable.js +42 -14
  48. package/dist/resizable.js.map +1 -1
  49. package/dist/utils.js.map +1 -1
  50. package/dist/virtualized.d.ts +6 -1
  51. package/dist/virtualized.js +12 -11
  52. package/dist/virtualized.js.map +1 -1
  53. package/package.json +1 -1
  54. package/dist/chunk-6A5RHW2W.js.map +0 -1
  55. package/dist/chunk-F2BNJSON.js.map +0 -1
  56. package/dist/chunk-GQVBZ4GW.js.map +0 -1
  57. package/dist/chunk-HP7WUZPK.js +0 -81
  58. package/dist/chunk-HP7WUZPK.js.map +0 -1
  59. package/dist/chunk-HTXP5BMM.js +0 -38
  60. package/dist/chunk-HTXP5BMM.js.map +0 -1
  61. package/dist/chunk-JTU7OBYJ.js.map +0 -1
  62. package/dist/chunk-KTPGMJUK.js.map +0 -1
  63. package/dist/chunk-NI5542ZM.js.map +0 -1
  64. package/dist/chunk-NLXQ323J.js.map +0 -1
  65. package/dist/chunk-OIOII5L6.js.map +0 -1
  66. package/dist/chunk-RHWTMTU5.js.map +0 -1
  67. package/dist/chunk-UNS4TGGK.js.map +0 -1
  68. package/dist/chunk-WH2FUFAC.js +0 -15
  69. package/dist/chunk-WH2FUFAC.js.map +0 -1
  70. package/dist/chunk-Z4MCD73B.js.map +0 -1
package/dist/chat.js CHANGED
@@ -1,13 +1,14 @@
1
1
  "use client";
2
- import { Divider, CodeBlock, Link } from './chunk-KTPGMJUK.js';
3
- import './chunk-NLXQ323J.js';
4
- import './chunk-GQVBZ4GW.js';
5
- import './chunk-7Q46JXSK.js';
2
+ import { Divider, CodeBlock, Link } from './chunk-SQHKC4HA.js';
3
+ import './chunk-BZRZENBS.js';
4
+ import { useKeyboardScroll } from './chunk-ACLKDE5W.js';
5
+ import './chunk-ZCDA74RQ.js';
6
+ import './chunk-WWN6CQKI.js';
6
7
  import './chunk-L6HWACK7.js';
7
- import './chunk-HP7WUZPK.js';
8
- import './chunk-WH2FUFAC.js';
9
- import { cn } from './chunk-JTU7OBYJ.js';
10
- import { memo, Children, isValidElement } from 'react';
8
+ import './chunk-7Q46JXSK.js';
9
+ import './chunk-4HJDOZ2M.js';
10
+ import { cn, FOCUS_INSET } from './chunk-YL5KD5RC.js';
11
+ import { memo, useState, Children, isValidElement } from 'react';
11
12
  import Markdown from 'react-markdown';
12
13
  import remarkGfm from 'remark-gfm';
13
14
  import { jsx } from 'react/jsx-runtime';
@@ -21,6 +22,11 @@ function codeFromPre(children) {
21
22
  const code = Children.toArray(raw).map((c) => typeof c === "string" || typeof c === "number" ? String(c) : "").join("").replace(/\n$/, "");
22
23
  return { code, language };
23
24
  }
25
+ function TableFrame(props) {
26
+ const [frame, setFrame] = useState(null);
27
+ useKeyboardScroll(frame, "Table", true, false, "x");
28
+ return /* @__PURE__ */ jsx("div", { ref: setFrame, className: cn("overflow-x-auto rounded-[var(--ds-radius-md)] border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]", FOCUS_INSET), children: /* @__PURE__ */ jsx("table", { className: "w-full border-collapse text-sm", ...props }) });
29
+ }
24
30
  var components = {
25
31
  p: ({ node: _n, ...props }) => /* @__PURE__ */ jsx("p", { ...props }),
26
32
  a: ({ node: _n, href = "#", children, ...props }) => /* @__PURE__ */ jsx(
@@ -67,7 +73,7 @@ var components = {
67
73
  hr: () => /* @__PURE__ */ jsx(Divider, {}),
68
74
  strong: ({ node: _n, ...props }) => /* @__PURE__ */ jsx("strong", { className: "font-semibold", ...props }),
69
75
  // Wide content scrolls inside its own container; the page never scrolls sideways.
70
- table: ({ node: _n, ...props }) => /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-[var(--ds-radius-md)] border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]", children: /* @__PURE__ */ jsx("table", { className: "w-full border-collapse text-sm", ...props }) }),
76
+ table: ({ node: _n, ...props }) => /* @__PURE__ */ jsx(TableFrame, { ...props }),
71
77
  thead: ({ node: _n, ...props }) => /* @__PURE__ */ jsx("thead", { className: "bg-[var(--ds-bg-muted)]", ...props }),
72
78
  th: ({ node: _n, ...props }) => /* @__PURE__ */ jsx("th", { className: "px-ds-3 py-ds-2 text-left text-xs font-semibold uppercase tracking-wide text-[var(--ds-text-secondary)]", ...props }),
73
79
  td: ({ node: _n, ...props }) => /* @__PURE__ */ jsx("td", { className: "border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] px-ds-3 py-ds-2 align-top", ...props }),
package/dist/chat.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../showcase/src/components/MarkdownContent.tsx"],"names":[],"mappings":"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","file":"chat.js","sourcesContent":["import { memo, Children, isValidElement, type ComponentProps, type ReactElement, type ReactNode } from \"react\";\nimport Markdown, { type Components } from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\nimport { cn } from \"../lib/utils\";\nimport { CodeBlock } from \"./CodeBlock\";\nimport { Link } from \"./Link\";\nimport { Divider } from \"./Divider\";\n\nexport interface MarkdownContentProps {\n /** The markdown source — a model's reply, an API's description. Raw HTML in it is never rendered. */\n children: string;\n /** Show a caret at the end of the last block while the source is still arriving. Pass a message's status === \"streaming\" from ChatMessageList's renderContent. */\n streaming?: boolean;\n className?: string;\n}\n\nconst isExternal = (href: string) => /^(https?:)?\\/\\//i.test(href);\n\n/** Pull the code string and language out of the single <code> child react-markdown gives a <pre>. */\nfunction codeFromPre(children: ReactNode): { code: string; language?: string } {\n const only = Children.toArray(children).find(isValidElement) as ReactElement<{ className?: string; children?: ReactNode }> | undefined;\n const cls = only?.props.className ?? \"\";\n const language = /language-([\\w+-]+)/.exec(cls)?.[1];\n const raw = only ? only.props.children : children;\n const code = Children.toArray(raw)\n .map((c) => (typeof c === \"string\" || typeof c === \"number\" ? String(c) : \"\"))\n .join(\"\")\n .replace(/\\n$/, \"\");\n return { code, language };\n}\n\ntype P<T extends keyof React.JSX.IntrinsicElements> = ComponentProps<T> & { node?: unknown };\n\nconst components: Components = {\n p: ({ node: _n, ...props }: P<\"p\">) => <p {...props} />,\n a: ({ node: _n, href = \"#\", children, ...props }: P<\"a\">) => (\n <Link\n href={href}\n underline=\"always\"\n {...(isExternal(href) ? { target: \"_blank\", rel: \"noopener noreferrer\" } : {})}\n {...(props as Record<string, unknown>)}\n >\n {children}\n </Link>\n ),\n pre: ({ node: _n, children }: P<\"pre\">) => {\n const { code, language } = codeFromPre(children);\n return <CodeBlock code={code} language={language} />;\n },\n code: ({ node: _n, className, children, ...props }: P<\"code\">) => (\n <code\n className={cn(\n \"rounded-[var(--ds-radius-sm)] bg-[var(--ds-bg-muted)] px-ds-1 py-[1px] font-[family-name:var(--ds-font-mono)] text-[0.9em]\",\n className,\n )}\n {...props}\n >\n {children}\n </code>\n ),\n ul: ({ node: _n, ...props }: P<\"ul\">) => <ul className=\"list-disc space-y-ds-1 pl-ds-5\" {...props} />,\n ol: ({ node: _n, ...props }: P<\"ol\">) => <ol className=\"list-decimal space-y-ds-1 pl-ds-5\" {...props} />,\n li: ({ node: _n, ...props }: P<\"li\">) => <li {...props} />,\n h1: ({ node: _n, ...props }: P<\"h1\">) => <h3 className=\"text-base font-semibold text-[var(--ds-text-primary)]\" {...props} />,\n h2: ({ node: _n, ...props }: P<\"h2\">) => <h3 className=\"text-base font-semibold text-[var(--ds-text-primary)]\" {...props} />,\n h3: ({ node: _n, ...props }: P<\"h3\">) => <h4 className=\"text-sm font-semibold text-[var(--ds-text-primary)]\" {...props} />,\n h4: ({ node: _n, ...props }: P<\"h4\">) => <h4 className=\"text-sm font-semibold text-[var(--ds-text-primary)]\" {...props} />,\n h5: ({ node: _n, ...props }: P<\"h5\">) => <h5 className=\"text-sm font-medium text-[var(--ds-text-primary)]\" {...props} />,\n h6: ({ node: _n, ...props }: P<\"h6\">) => <h6 className=\"text-sm font-medium text-[var(--ds-text-secondary)]\" {...props} />,\n blockquote: ({ node: _n, ...props }: P<\"blockquote\">) => (\n <blockquote\n className=\"border-l-[length:calc(var(--ds-border-width)*2)] border-[var(--ds-border-default)] pl-ds-3 text-[var(--ds-text-secondary)]\"\n {...props}\n />\n ),\n hr: () => <Divider />,\n strong: ({ node: _n, ...props }: P<\"strong\">) => <strong className=\"font-semibold\" {...props} />,\n // Wide content scrolls inside its own container; the page never scrolls sideways.\n table: ({ node: _n, ...props }: P<\"table\">) => (\n <div className=\"overflow-x-auto rounded-[var(--ds-radius-md)] border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]\">\n <table className=\"w-full border-collapse text-sm\" {...props} />\n </div>\n ),\n thead: ({ node: _n, ...props }: P<\"thead\">) => <thead className=\"bg-[var(--ds-bg-muted)]\" {...props} />,\n th: ({ node: _n, ...props }: P<\"th\">) => (\n <th className=\"px-ds-3 py-ds-2 text-left text-xs font-semibold uppercase tracking-wide text-[var(--ds-text-secondary)]\" {...props} />\n ),\n td: ({ node: _n, ...props }: P<\"td\">) => (\n <td className=\"border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] px-ds-3 py-ds-2 align-top\" {...props} />\n ),\n img: ({ node: _n, alt = \"\", ...props }: P<\"img\">) => <img alt={alt} className=\"max-w-full rounded-[var(--ds-radius-md)]\" {...props} />,\n};\n\n/**\n * Renders markdown (GitHub flavour: tables, task lists, strikethrough) with\n * Gridsmith primitives: fenced code becomes CodeBlock, links become Link,\n * a rule becomes Divider, everything else is token-styled markup. Raw HTML is\n * not rendered — the source is untrusted by default. Memoised: an unchanged\n * message never re-parses; a streaming message re-parses only its own string.\n *\n * Lives on the `gridsmith-ui/chat` entry point (react-markdown and remark-gfm\n * are optional peers), so apps without a chat never load a markdown parser.\n */\n// The caret is a pseudo-element on the last block, so it sits at the end of the\n// last line of text instead of on a line of its own below a block element.\nconst STREAMING_CARET = cn(\n \"[&>:last-child]:after:content-['▍'] [&>:last-child]:after:ml-ds-0.5\",\n \"[&>:last-child]:after:text-[var(--ds-accent-ink)] [&>:last-child]:after:animate-pulse\",\n);\n\nconst Memoised = memo(function MarkdownContentInner({ children, streaming, className }: MarkdownContentProps) {\n return (\n <div\n className={cn(\"space-y-ds-4 text-sm leading-relaxed text-[var(--ds-text-primary)]\", streaming && STREAMING_CARET, className)}\n data-markdown=\"\"\n data-streaming={streaming || undefined}\n >\n <Markdown remarkPlugins={[remarkGfm]} components={components} skipHtml>\n {children}\n </Markdown>\n </div>\n );\n});\n\n// A plain function export so the registry extractor (which reads `export function`)\n// documents it; the memo above still skips re-parsing unchanged sources.\nexport function MarkdownContent(props: MarkdownContentProps) {\n return <Memoised {...props} />;\n}\n"]}
1
+ {"version":3,"sources":["../../../showcase/src/components/MarkdownContent.tsx"],"names":[],"mappings":"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","file":"chat.js","sourcesContent":["import { memo, Children, isValidElement, useState, type ComponentProps, type ReactElement, type ReactNode } from \"react\";\nimport Markdown, { type Components } from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\nimport { cn, FOCUS_INSET } from \"../lib/utils\";\nimport { useKeyboardScroll } from \"../lib/use-keyboard-scroll\";\nimport { CodeBlock } from \"./CodeBlock\";\nimport { Link } from \"./Link\";\nimport { Divider } from \"./Divider\";\n\nexport interface MarkdownContentProps {\n /** The markdown source — a model's reply, an API's description. Raw HTML in it is never rendered. */\n children: string;\n /** Show a caret at the end of the last block while the source is still arriving. Pass a message's status === \"streaming\" from ChatMessageList's renderContent. */\n streaming?: boolean;\n className?: string;\n}\n\nconst isExternal = (href: string) => /^(https?:)?\\/\\//i.test(href);\n\n/** Pull the code string and language out of the single <code> child react-markdown gives a <pre>. */\nfunction codeFromPre(children: ReactNode): { code: string; language?: string } {\n const only = Children.toArray(children).find(isValidElement) as ReactElement<{ className?: string; children?: ReactNode }> | undefined;\n const cls = only?.props.className ?? \"\";\n const language = /language-([\\w+-]+)/.exec(cls)?.[1];\n const raw = only ? only.props.children : children;\n const code = Children.toArray(raw)\n .map((c) => (typeof c === \"string\" || typeof c === \"number\" ? String(c) : \"\"))\n .join(\"\")\n .replace(/\\n$/, \"\");\n return { code, language };\n}\n\n/**\n * A table in its own frame, which scrolls sideways when the table is wider; with nothing in it to Tab to, the frame is\n * a Tab stop while it scrolls, so a keyboard can scroll it too (Sprint 25.30 item 2).\n */\nfunction TableFrame(props: ComponentProps<\"table\">) {\n const [frame, setFrame] = useState<HTMLDivElement | null>(null);\n useKeyboardScroll(frame, \"Table\", true, false, \"x\");\n return (\n <div ref={setFrame} className={cn(\"overflow-x-auto rounded-[var(--ds-radius-md)] border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]\", FOCUS_INSET)}>\n <table className=\"w-full border-collapse text-sm\" {...props} />\n </div>\n );\n}\n\ntype P<T extends keyof React.JSX.IntrinsicElements> = ComponentProps<T> & { node?: unknown };\n\nconst components: Components = {\n p: ({ node: _n, ...props }: P<\"p\">) => <p {...props} />,\n a: ({ node: _n, href = \"#\", children, ...props }: P<\"a\">) => (\n <Link\n href={href}\n underline=\"always\"\n {...(isExternal(href) ? { target: \"_blank\", rel: \"noopener noreferrer\" } : {})}\n {...(props as Record<string, unknown>)}\n >\n {children}\n </Link>\n ),\n pre: ({ node: _n, children }: P<\"pre\">) => {\n const { code, language } = codeFromPre(children);\n return <CodeBlock code={code} language={language} />;\n },\n code: ({ node: _n, className, children, ...props }: P<\"code\">) => (\n <code\n className={cn(\n \"rounded-[var(--ds-radius-sm)] bg-[var(--ds-bg-muted)] px-ds-1 py-[1px] font-[family-name:var(--ds-font-mono)] text-[0.9em]\",\n className,\n )}\n {...props}\n >\n {children}\n </code>\n ),\n ul: ({ node: _n, ...props }: P<\"ul\">) => <ul className=\"list-disc space-y-ds-1 pl-ds-5\" {...props} />,\n ol: ({ node: _n, ...props }: P<\"ol\">) => <ol className=\"list-decimal space-y-ds-1 pl-ds-5\" {...props} />,\n li: ({ node: _n, ...props }: P<\"li\">) => <li {...props} />,\n h1: ({ node: _n, ...props }: P<\"h1\">) => <h3 className=\"text-base font-semibold text-[var(--ds-text-primary)]\" {...props} />,\n h2: ({ node: _n, ...props }: P<\"h2\">) => <h3 className=\"text-base font-semibold text-[var(--ds-text-primary)]\" {...props} />,\n h3: ({ node: _n, ...props }: P<\"h3\">) => <h4 className=\"text-sm font-semibold text-[var(--ds-text-primary)]\" {...props} />,\n h4: ({ node: _n, ...props }: P<\"h4\">) => <h4 className=\"text-sm font-semibold text-[var(--ds-text-primary)]\" {...props} />,\n h5: ({ node: _n, ...props }: P<\"h5\">) => <h5 className=\"text-sm font-medium text-[var(--ds-text-primary)]\" {...props} />,\n h6: ({ node: _n, ...props }: P<\"h6\">) => <h6 className=\"text-sm font-medium text-[var(--ds-text-secondary)]\" {...props} />,\n blockquote: ({ node: _n, ...props }: P<\"blockquote\">) => (\n <blockquote\n className=\"border-l-[length:calc(var(--ds-border-width)*2)] border-[var(--ds-border-default)] pl-ds-3 text-[var(--ds-text-secondary)]\"\n {...props}\n />\n ),\n hr: () => <Divider />,\n strong: ({ node: _n, ...props }: P<\"strong\">) => <strong className=\"font-semibold\" {...props} />,\n // Wide content scrolls inside its own container; the page never scrolls sideways.\n table: ({ node: _n, ...props }: P<\"table\">) => <TableFrame {...props} />,\n thead: ({ node: _n, ...props }: P<\"thead\">) => <thead className=\"bg-[var(--ds-bg-muted)]\" {...props} />,\n th: ({ node: _n, ...props }: P<\"th\">) => (\n <th className=\"px-ds-3 py-ds-2 text-left text-xs font-semibold uppercase tracking-wide text-[var(--ds-text-secondary)]\" {...props} />\n ),\n td: ({ node: _n, ...props }: P<\"td\">) => (\n <td className=\"border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] px-ds-3 py-ds-2 align-top\" {...props} />\n ),\n img: ({ node: _n, alt = \"\", ...props }: P<\"img\">) => <img alt={alt} className=\"max-w-full rounded-[var(--ds-radius-md)]\" {...props} />,\n};\n\n/**\n * Renders markdown (GitHub flavour: tables, task lists, strikethrough) with\n * Gridsmith primitives: fenced code becomes CodeBlock, links become Link,\n * a rule becomes Divider, everything else is token-styled markup. Raw HTML is\n * not rendered — the source is untrusted by default. Memoised: an unchanged\n * message never re-parses; a streaming message re-parses only its own string.\n *\n * Lives on the `gridsmith-ui/chat` entry point (react-markdown and remark-gfm\n * are optional peers), so apps without a chat never load a markdown parser.\n */\n// The caret is a pseudo-element on the last block, so it sits at the end of the\n// last line of text instead of on a line of its own below a block element.\nconst STREAMING_CARET = cn(\n \"[&>:last-child]:after:content-['▍'] [&>:last-child]:after:ml-ds-0.5\",\n \"[&>:last-child]:after:text-[var(--ds-accent-ink)] [&>:last-child]:after:animate-pulse\",\n);\n\nconst Memoised = memo(function MarkdownContentInner({ children, streaming, className }: MarkdownContentProps) {\n return (\n <div\n className={cn(\"space-y-ds-4 text-sm leading-relaxed text-[var(--ds-text-primary)]\", streaming && STREAMING_CARET, className)}\n data-markdown=\"\"\n data-streaming={streaming || undefined}\n >\n <Markdown remarkPlugins={[remarkGfm]} components={components} skipHtml>\n {children}\n </Markdown>\n </div>\n );\n});\n\n// A plain function export so the registry extractor (which reads `export function`)\n// documents it; the memo above still skips re-parsing unchanged sources.\nexport function MarkdownContent(props: MarkdownContentProps) {\n return <Memoised {...props} />;\n}\n"]}
@@ -1,8 +1,17 @@
1
1
  "use client";
2
- import { useLatest } from './chunk-WH2FUFAC.js';
3
- import { useRef, useInsertionEffect, useEffect, useCallback, useState, useMemo, useLayoutEffect } from 'react';
2
+ import { useRef, useLayoutEffect, useInsertionEffect, useEffect, useCallback, useState, useMemo } from 'react';
4
3
  import { flushSync } from 'react-dom';
5
4
 
5
+ // ../../showcase/src/lib/focus.ts
6
+ function useLatest(value) {
7
+ const ref = useRef(value);
8
+ useLayoutEffect(() => {
9
+ ref.current = value;
10
+ });
11
+ return ref;
12
+ }
13
+
14
+ // ../../showcase/src/lib/focus.ts
6
15
  var FOCUSABLE = 'a[href], button:not(:disabled), input:not(:disabled), select:not(:disabled), textarea:not(:disabled), [tabindex]:not([tabindex="-1"]), [contenteditable]:not([contenteditable="false"])';
7
16
  var FIELD = "input, select, textarea, [contenteditable='true']";
8
17
  var CONTROL_ANCESTOR = 'a[href], button, summary, label, [role="button"], [role="link"], [role="option"], [role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"], [role="tab"], [role="treeitem"], [role="row"], [role="gridcell"], [role="checkbox"], [role="radio"], [role="switch"]';
@@ -55,10 +64,34 @@ function focusInitial(root, opts = {}) {
55
64
  function useFocusBeforeOpen(open) {
56
65
  const before = useRef(null);
57
66
  useInsertionEffect(() => {
58
- if (open) before.current = document.activeElement;
67
+ trackFocus();
68
+ if (!open) return;
69
+ const active = document.activeElement;
70
+ before.current = !active || active === document.body ? justLost() ?? active : active;
59
71
  }, [open]);
60
72
  return before;
61
73
  }
74
+ var lastFocused = null;
75
+ var lastFocusedAt = 0;
76
+ var lastActedAt = 0;
77
+ var tracking = false;
78
+ function trackFocus() {
79
+ if (tracking || typeof document === "undefined") return;
80
+ tracking = true;
81
+ document.addEventListener("focusin", (e) => {
82
+ lastFocused = e.target instanceof Element ? e.target : null;
83
+ lastFocusedAt = performance.now();
84
+ }, true);
85
+ const acted = () => {
86
+ lastActedAt = performance.now();
87
+ };
88
+ for (const type of ["keydown", "pointerdown", "click"]) document.addEventListener(type, acted, true);
89
+ }
90
+ function justLost() {
91
+ const recent = lastActedAt >= lastFocusedAt && performance.now() - lastActedAt < 1e3;
92
+ return lastFocused && !lastFocused.isConnected && recent ? lastFocused : null;
93
+ }
94
+ trackFocus();
62
95
  function pathAbove(to, from) {
63
96
  const up = to.replace(/\/+$/, "");
64
97
  return from.startsWith(`${up}/`) && from.replace(/\/+$/, "") !== up;
@@ -74,6 +107,64 @@ function deletedRow(before, now) {
74
107
  for (let j = k; j < (refilled ? last : now.length); j++) if (now[j] !== before[j + 1]) return -1;
75
108
  return k;
76
109
  }
110
+ function focusForTheMoment(el) {
111
+ const own = el.getAttribute("tabindex");
112
+ if (own === null) el.setAttribute("tabindex", "-1");
113
+ el.focus();
114
+ if (document.activeElement !== el) {
115
+ if (own === null) el.removeAttribute("tabindex");
116
+ return false;
117
+ }
118
+ let ring = false;
119
+ try {
120
+ ring = el.matches(":focus-visible");
121
+ } catch {
122
+ }
123
+ const { outline, outlineOffset } = el.style;
124
+ if (ring) {
125
+ el.style.outline = "2px solid var(--ds-focus)";
126
+ el.style.outlineOffset = "2px";
127
+ }
128
+ el.addEventListener("blur", () => {
129
+ if (own === null) el.removeAttribute("tabindex");
130
+ if (ring) Object.assign(el.style, { outline, outlineOffset });
131
+ }, { once: true });
132
+ return true;
133
+ }
134
+ var HEADING = 'h1, h2, h3, h4, h5, h6, [role="heading"]';
135
+ var LAST_RESORT_MS = 200;
136
+ function placesOf(el) {
137
+ const places = [];
138
+ for (let c = el, box = el.parentElement; box && box !== document.body && box.parentElement && box.parentElement !== document.body; c = box, box = box.parentElement) {
139
+ places.push({ box, prev: c.previousElementSibling, next: c.nextElementSibling });
140
+ }
141
+ return places;
142
+ }
143
+ function landLastResort(places, closing) {
144
+ const counts = (el) => !closing?.contains(el);
145
+ const areas = [];
146
+ const at = places.findIndex((p) => p.box.isConnected);
147
+ if (at >= 0) {
148
+ const { box, prev, next } = places[at];
149
+ const kids = Array.from(box.children);
150
+ const from = prev?.parentElement === box ? kids.indexOf(prev) + 1 : 0;
151
+ const to = next?.parentElement === box ? kids.indexOf(next) : kids.length;
152
+ areas.push(kids.slice(from, Math.max(from, to)), ...places.slice(at).map((p) => [p.box]));
153
+ }
154
+ const main = document.querySelector('main, [role="main"]');
155
+ const mainCounts = !!main && counts(main) && isRendered(main);
156
+ if (mainCounts) areas.push([main]);
157
+ for (const area of areas) {
158
+ const stop = area.flatMap((a) => [...isTabStop(a) ? [a] : [], ...tabStops(a)]).find(counts);
159
+ if (stop) {
160
+ stop.focus();
161
+ if (document.activeElement === stop) return true;
162
+ }
163
+ const heading = area.flatMap((a) => [...a.matches(HEADING) ? [a] : [], ...a.querySelectorAll(HEADING)]).find((h) => h instanceof HTMLElement && isRendered(h) && counts(h));
164
+ if (heading && focusForTheMoment(heading)) return true;
165
+ }
166
+ return mainCounts && focusForTheMoment(main);
167
+ }
77
168
  function rememberFocus(el, { ignoreRoute = false } = {}) {
78
169
  const { href, pathname } = location;
79
170
  const row = el.closest(ROW);
@@ -86,6 +177,7 @@ function rememberFocus(el, { ignoreRoute = false } = {}) {
86
177
  const texts = rows.map((r) => r.textContent ?? "");
87
178
  const keyOf = (r) => r?.getAttribute("data-row-key") ?? null;
88
179
  const key = keyOf(row);
180
+ const places = placesOf(el);
89
181
  const pageChanged = () => {
90
182
  if (ignoreRoute) return false;
91
183
  if (location.pathname !== pathname) return !pathAbove(location.pathname, pathname);
@@ -118,16 +210,27 @@ function rememberFocus(el, { ignoreRoute = false } = {}) {
118
210
  return k >= 0 && k <= at ? list.children[Math.max(0, at - 1)] ?? void 0 : void 0;
119
211
  };
120
212
  const neighbour = () => inRow(byKey(keyOf(before)) ?? before) || inRow(byKey(keyOf(after)) ?? after) || !!list?.isConnected && land(tabStops(list)[0]);
213
+ const lastResort = () => {
214
+ setTimeout(() => {
215
+ if (typeof document === "undefined") return;
216
+ const active = document.activeElement;
217
+ if (active && active !== document.body && active.isConnected || pageChanged()) return;
218
+ landLastResort(places);
219
+ }, LAST_RESORT_MS);
220
+ return false;
221
+ };
121
222
  let unwatch = null;
122
223
  const watch = () => {
123
224
  unwatch?.();
124
225
  if (!list || typeof MutationObserver === "undefined") return;
125
- const observer = new MutationObserver(() => {
226
+ const observer = new MutationObserver((changes) => {
126
227
  if (el.isConnected) return;
127
228
  const active = document.activeElement;
128
229
  const dropped = !active || active === document.body || !active.isConnected;
129
230
  stop();
130
- if (dropped && !pageChanged()) neighbour();
231
+ if (!dropped || pageChanged()) return;
232
+ const fromList = changes.some((c) => c.target === list || c.target === list.parentNode);
233
+ if (!(fromList && neighbour())) lastResort();
131
234
  });
132
235
  const onFocusIn = (e) => {
133
236
  if (e.target !== el) stop();
@@ -136,11 +239,12 @@ function rememberFocus(el, { ignoreRoute = false } = {}) {
136
239
  function stop() {
137
240
  observer.disconnect();
138
241
  clearTimeout(timer);
139
- document.removeEventListener("focusin", onFocusIn, true);
242
+ if (typeof document !== "undefined") document.removeEventListener("focusin", onFocusIn, true);
140
243
  unwatch = null;
141
244
  }
142
245
  observer.observe(list, { childList: true });
143
246
  if (list.parentNode) observer.observe(list.parentNode, { childList: true });
247
+ for (const { box } of places) observer.observe(box, { childList: true });
144
248
  document.addEventListener("focusin", onFocusIn, true);
145
249
  unwatch = stop;
146
250
  };
@@ -154,7 +258,90 @@ function rememberFocus(el, { ignoreRoute = false } = {}) {
154
258
  return true;
155
259
  }
156
260
  if (row && !row.isConnected && neighbour()) return true;
157
- return land(fallback);
261
+ return land(fallback) || lastResort();
262
+ }
263
+ };
264
+ }
265
+ function useFocusKeptInList(listRef) {
266
+ useEffect(() => {
267
+ if (typeof MutationObserver === "undefined") return;
268
+ let held = null;
269
+ let observer = null;
270
+ const forget = () => {
271
+ held = null;
272
+ observer?.disconnect();
273
+ observer = null;
274
+ };
275
+ const onFocusIn = (e) => {
276
+ const list = listRef.current;
277
+ const t = e.target;
278
+ if (!list || !(t instanceof HTMLElement) || !list.contains(t)) {
279
+ if (held) forget();
280
+ return;
281
+ }
282
+ held = rememberFocus(t);
283
+ if (observer) return;
284
+ observer = new MutationObserver(() => {
285
+ const h = held;
286
+ if (!h || h.el.isConnected) return;
287
+ const active = document.activeElement;
288
+ forget();
289
+ if (!active || active === document.body || !active.isConnected) h.restore();
290
+ });
291
+ observer.observe(list, { childList: true, subtree: true });
292
+ };
293
+ const onFocusOut = (e) => {
294
+ if (!held || e.target !== held.el) return;
295
+ queueMicrotask(() => {
296
+ if (held?.el.isConnected && !listRef.current?.contains(document.activeElement)) forget();
297
+ });
298
+ };
299
+ document.addEventListener("focusin", onFocusIn, true);
300
+ document.addEventListener("focusout", onFocusOut, true);
301
+ return () => {
302
+ document.removeEventListener("focusin", onFocusIn, true);
303
+ document.removeEventListener("focusout", onFocusOut, true);
304
+ const h = held;
305
+ forget();
306
+ const active = document.activeElement;
307
+ if (h && !h.el.isConnected && (!active || active === document.body || !active.isConnected)) h.restore();
308
+ };
309
+ }, [listRef]);
310
+ }
311
+ var openers = /* @__PURE__ */ new WeakMap();
312
+ function rememberOpener(active, root) {
313
+ let own = active instanceof HTMLElement && active !== document.body && active.isConnected ? rememberFocus(active) : null;
314
+ const controller = !own && root?.id ? document.querySelector(`[aria-controls~="${CSS.escape(root.id)}"]`) : null;
315
+ if (controller) own = rememberFocus(controller);
316
+ let around = null;
317
+ for (let n = active; n && !around; n = n.parentElement) {
318
+ const opener = openers.get(n);
319
+ if (opener) around = { root: n, opener };
320
+ }
321
+ const back = own ?? around?.opener;
322
+ if (!back) {
323
+ if (!root) return null;
324
+ const places = placesOf(root);
325
+ const { pathname } = location;
326
+ return {
327
+ el: null,
328
+ restore() {
329
+ setTimeout(() => {
330
+ if (typeof document === "undefined") return;
331
+ const now = document.activeElement;
332
+ if (now && now !== document.body && now.isConnected && !root.contains(now) || location.pathname !== pathname && !pathAbove(location.pathname, pathname)) return;
333
+ landLastResort(places, root);
334
+ }, LAST_RESORT_MS);
335
+ return false;
336
+ }
337
+ };
338
+ }
339
+ if (root) openers.set(root, back);
340
+ return {
341
+ el: own?.el ?? null,
342
+ restore(fallback) {
343
+ if (own?.restore(fallback)) return true;
344
+ return !!around && !around.root.isConnected && around.opener.restore();
158
345
  }
159
346
  };
160
347
  }
@@ -163,8 +350,7 @@ function useReturnFocus(open, root) {
163
350
  const before = useFocusBeforeOpen(open);
164
351
  useEffect(() => {
165
352
  if (open) {
166
- const active2 = before.current;
167
- remembered.current = active2 instanceof HTMLElement && active2 !== document.body && active2.isConnected ? rememberFocus(active2) : null;
353
+ remembered.current = rememberOpener(before.current, root?.current);
168
354
  return;
169
355
  }
170
356
  const r = remembered.current;
@@ -191,10 +377,14 @@ function rovingTabStop(items, isChecked, isDisabled = () => false) {
191
377
  function focusAfterRemoval(items, index, fallback) {
192
378
  (items[index - 1] ?? items[index] ?? fallback)?.focus();
193
379
  }
194
- function focusAfterSelfRemoval(control, { item, group } = {}) {
380
+ function mayStop(root) {
381
+ const disabled = (el) => (el instanceof HTMLButtonElement || el instanceof HTMLInputElement || el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement) && el.disabled;
382
+ return Array.from(root.querySelectorAll(`${FOCUSABLE}, button:disabled, input:disabled, select:disabled, textarea:disabled`)).filter((el) => disabled(el) ? !(el instanceof HTMLInputElement && el.type === "hidden") : isTabStop(el)).sort((a, b) => a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1);
383
+ }
384
+ function focusAfterSelfRemoval(control, { item, group, to } = {}) {
195
385
  const goes = item ?? control;
196
386
  const within = group === void 0 ? goes.parentElement : group;
197
- const stops = within ? tabStops(within).filter((el) => !goes.contains(el)) : [];
387
+ const stops = within ? mayStop(within).filter((el) => !goes.contains(el)) : [];
198
388
  const before = stops.filter((el) => goes.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_PRECEDING).reverse();
199
389
  const after = stops.filter((el) => goes.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING);
200
390
  const stopsOf = (el) => [...isTabStop(el) ? [el] : [], ...tabStops(el)];
@@ -224,7 +414,8 @@ function focusAfterSelfRemoval(control, { item, group } = {}) {
224
414
  within.focus();
225
415
  return document.activeElement === within;
226
416
  };
227
- const place = () => land(sameBefore) || land(before[0]) || land(sameAfter) || land(after[0]) || landOnGroup();
417
+ const landTo = (el) => land(el) || !!el && el.isConnected && !isTabStop(el) && isRendered(el) && focusForTheMoment(el);
418
+ const place = () => landTo(typeof to === "function" ? to() : to) || land(sameBefore) || before.some(land) || land(sameAfter) || after.some(land) || landOnGroup();
228
419
  if (typeof MutationObserver === "undefined") return () => {
229
420
  };
230
421
  const observer = new MutationObserver(() => {
@@ -239,7 +430,7 @@ function focusAfterSelfRemoval(control, { item, group } = {}) {
239
430
  function stop() {
240
431
  observer.disconnect();
241
432
  clearTimeout(timer);
242
- document.removeEventListener("focusin", onFocusIn, true);
433
+ if (typeof document !== "undefined") document.removeEventListener("focusin", onFocusIn, true);
243
434
  }
244
435
  observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ["disabled"] });
245
436
  document.addEventListener("focusin", onFocusIn, true);
@@ -338,6 +529,7 @@ function usePopupFocus(open, { trigger, panel, close, tabExits, onTabOut, marked
338
529
  const inPanel = active instanceof Node && !!panel.current?.contains(active);
339
530
  const opener = active instanceof HTMLElement && active !== document.body && !inPanel ? active : trigger.current;
340
531
  remembered.current = opener ? rememberFocus(opener) : null;
532
+ if (panel.current && remembered.current) openers.set(panel.current, remembered.current);
341
533
  lastPress.current = null;
342
534
  }, [open, trigger, panel, before]);
343
535
  useEffect(() => {
@@ -517,12 +709,13 @@ function useGridKeyboard({ container, active, setActive, keyOf, move, onActivate
517
709
  return { onKeyDown, tabIndexOf, focusActive, focus };
518
710
  }
519
711
  var ALWAYS_TOP = () => true;
520
- function useFocusTrap(root, active, isTop = ALWAYS_TOP) {
712
+ function useFocusTrap(root, active, isTop = ALWAYS_TOP, also) {
521
713
  useEffect(() => {
522
714
  if (!active) return;
523
715
  const onKeyDown = (e) => {
524
716
  if (e.key !== "Tab" || e.defaultPrevented || !root.current || !isTop()) return;
525
- const focusable = tabStops(root.current);
717
+ const scope = [root.current, also?.current].filter((el) => !!el);
718
+ const focusable = scope.flatMap((el) => tabStops(el)).sort((a, b) => a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1);
526
719
  if (focusable.length === 0) {
527
720
  e.preventDefault();
528
721
  return;
@@ -530,7 +723,7 @@ function useFocusTrap(root, active, isTop = ALWAYS_TOP) {
530
723
  const first = focusable[0];
531
724
  const last = focusable[focusable.length - 1];
532
725
  const current = document.activeElement;
533
- const inside = current instanceof Node && root.current.contains(current);
726
+ const inside = current instanceof Node && scope.some((el) => el.contains(current));
534
727
  if (e.shiftKey) {
535
728
  if (!inside || current === first) {
536
729
  e.preventDefault();
@@ -543,7 +736,24 @@ function useFocusTrap(root, active, isTop = ALWAYS_TOP) {
543
736
  };
544
737
  document.addEventListener("keydown", onKeyDown);
545
738
  return () => document.removeEventListener("keydown", onKeyDown);
546
- }, [root, active, isTop]);
739
+ }, [root, active, isTop, also]);
740
+ }
741
+ function inertOutside(keep, within) {
742
+ const made = [];
743
+ const visit = (el) => {
744
+ for (const child of Array.from(el.children)) {
745
+ if (keep.includes(child)) continue;
746
+ if (keep.some((k) => child.contains(k))) visit(child);
747
+ else if (!child.hasAttribute("inert")) {
748
+ child.setAttribute("inert", "");
749
+ made.push(child);
750
+ }
751
+ }
752
+ };
753
+ visit(within);
754
+ return () => {
755
+ for (const el of made) el.removeAttribute("inert");
756
+ };
547
757
  }
548
758
  function useNonModalDialog(root, active, onClose, isTop = ALWAYS_TOP) {
549
759
  const onCloseRef = useLatest(onClose);
@@ -566,7 +776,80 @@ function useNonModalDialog(root, active, onClose, isTop = ALWAYS_TOP) {
566
776
  return () => document.removeEventListener("keydown", onKeyDown);
567
777
  }, [active, root, isTop, onCloseRef]);
568
778
  }
779
+ var VIEWPORT_PADDING = 8;
780
+ function usePosition(triggerRef, floatingRef, open, options = {}) {
781
+ const { side = "bottom", align = "center", offset = 8, flip = true } = options;
782
+ const [pos, setPos] = useState({ top: 0, left: 0, placement: side });
783
+ const rafId = useRef(0);
784
+ const compute = useCallback(() => {
785
+ const trigger = triggerRef.current;
786
+ const floating = floatingRef.current;
787
+ if (!trigger || !floating) return;
788
+ const tRect = trigger.getBoundingClientRect();
789
+ const fRect = floating.getBoundingClientRect();
790
+ const vw = window.innerWidth;
791
+ const vh = window.innerHeight;
792
+ let placement = side;
793
+ if (flip) {
794
+ if (placement === "bottom" && tRect.bottom + offset + fRect.height > vh - VIEWPORT_PADDING && tRect.top - offset - fRect.height > VIEWPORT_PADDING) {
795
+ placement = "top";
796
+ } else if (placement === "top" && tRect.top - offset - fRect.height < VIEWPORT_PADDING && tRect.bottom + offset + fRect.height < vh - VIEWPORT_PADDING) {
797
+ placement = "bottom";
798
+ } else if (placement === "right" && tRect.right + offset + fRect.width > vw - VIEWPORT_PADDING && tRect.left - offset - fRect.width > VIEWPORT_PADDING) {
799
+ placement = "left";
800
+ } else if (placement === "left" && tRect.left - offset - fRect.width < VIEWPORT_PADDING && tRect.right + offset + fRect.width < vw - VIEWPORT_PADDING) {
801
+ placement = "right";
802
+ }
803
+ }
804
+ let top;
805
+ let left;
806
+ if (placement === "bottom" || placement === "top") {
807
+ top = placement === "bottom" ? tRect.bottom + offset : tRect.top - fRect.height - offset;
808
+ switch (align) {
809
+ case "start":
810
+ left = tRect.left;
811
+ break;
812
+ case "end":
813
+ left = tRect.right - fRect.width;
814
+ break;
815
+ default:
816
+ left = tRect.left + (tRect.width - fRect.width) / 2;
817
+ }
818
+ } else {
819
+ left = placement === "right" ? tRect.right + offset : tRect.left - fRect.width - offset;
820
+ switch (align) {
821
+ case "start":
822
+ top = tRect.top;
823
+ break;
824
+ case "end":
825
+ top = tRect.bottom - fRect.height;
826
+ break;
827
+ default:
828
+ top = tRect.top + (tRect.height - fRect.height) / 2;
829
+ }
830
+ }
831
+ left = Math.max(VIEWPORT_PADDING, Math.min(left, vw - fRect.width - VIEWPORT_PADDING));
832
+ top = Math.max(VIEWPORT_PADDING, Math.min(top, vh - fRect.height - VIEWPORT_PADDING));
833
+ setPos((cur) => cur.top === top && cur.left === left && cur.placement === placement ? cur : { top, left, placement });
834
+ }, [triggerRef, floatingRef, side, align, offset, flip]);
835
+ useLayoutEffect(() => {
836
+ if (!open) return;
837
+ compute();
838
+ const onUpdate = () => {
839
+ cancelAnimationFrame(rafId.current);
840
+ rafId.current = requestAnimationFrame(compute);
841
+ };
842
+ window.addEventListener("scroll", onUpdate, true);
843
+ window.addEventListener("resize", onUpdate);
844
+ return () => {
845
+ cancelAnimationFrame(rafId.current);
846
+ window.removeEventListener("scroll", onUpdate, true);
847
+ window.removeEventListener("resize", onUpdate);
848
+ };
849
+ }, [open, compute]);
850
+ return pos;
851
+ }
569
852
 
570
- export { CONTROL_ANCESTOR, FIELD, FOCUSABLE, OVERLAY_CLOSE_ATTR, ZOOM_KEYS, focusAfterRemoval, focusAfterSelfRemoval, focusInitial, isTabStop, rememberFocus, rovingTabStop, tabStops, useCloseOnFocusOut, useFocusBeforeOpen, useFocusLeave, useFocusTrap, useGridKeyboard, useNonModalDialog, useOverlayLayer, usePopupFocus, usePressMarks, useReturnFocus };
571
- //# sourceMappingURL=chunk-OIOII5L6.js.map
572
- //# sourceMappingURL=chunk-OIOII5L6.js.map
853
+ export { CONTROL_ANCESTOR, FIELD, FOCUSABLE, OVERLAY_CLOSE_ATTR, ZOOM_KEYS, focusAfterRemoval, focusAfterSelfRemoval, focusInitial, inertOutside, isTabStop, rememberFocus, rememberOpener, rovingTabStop, tabStops, useCloseOnFocusOut, useFocusBeforeOpen, useFocusKeptInList, useFocusLeave, useFocusTrap, useGridKeyboard, useLatest, useNonModalDialog, useOverlayLayer, usePopupFocus, usePosition, usePressMarks, useReturnFocus };
854
+ //# sourceMappingURL=chunk-4HJDOZ2M.js.map
855
+ //# sourceMappingURL=chunk-4HJDOZ2M.js.map