@marktiderman/genesis-ui 2.6.0 → 2.8.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 (70) hide show
  1. package/EXTENDING.md +40 -1
  2. package/SKILL.md +145 -9
  3. package/dist/{AppShell-CX0M-dfc.d.ts → AppShell-LXQykEqe.d.ts} +47 -5
  4. package/dist/{chunk-M62CHO3N.js → chunk-4C4ESW6O.js} +9 -4
  5. package/dist/chunk-4C4ESW6O.js.map +1 -0
  6. package/dist/{chunk-IXGAEECT.js → chunk-DWCAC2E7.js} +6 -3
  7. package/dist/chunk-DWCAC2E7.js.map +1 -0
  8. package/dist/chunk-FUVWKTPL.js +19 -0
  9. package/dist/chunk-FUVWKTPL.js.map +1 -0
  10. package/dist/{chunk-PLIYF6KI.js → chunk-MPI33I4K.js} +2 -2
  11. package/dist/{chunk-PLIYF6KI.js.map → chunk-MPI33I4K.js.map} +1 -1
  12. package/dist/{chunk-JA3ZKULS.js → chunk-OITWCIG2.js} +3 -3
  13. package/dist/chunk-OITWCIG2.js.map +1 -0
  14. package/dist/chunk-PKFDGGZV.js +65 -0
  15. package/dist/chunk-PKFDGGZV.js.map +1 -0
  16. package/dist/chunk-S7NXD37O.js +321 -0
  17. package/dist/chunk-S7NXD37O.js.map +1 -0
  18. package/dist/{chunk-DWM7ELFI.js → chunk-UYI3GLFJ.js} +163 -20
  19. package/dist/chunk-UYI3GLFJ.js.map +1 -0
  20. package/dist/{chunk-CDYZRZ6U.js → chunk-WGMEVX3Y.js} +2 -2
  21. package/dist/chunk-WGMEVX3Y.js.map +1 -0
  22. package/dist/chunk-ZDHWNQTR.js +552 -0
  23. package/dist/chunk-ZDHWNQTR.js.map +1 -0
  24. package/dist/components/data/index.d.ts +239 -15
  25. package/dist/components/data/index.js +524 -109
  26. package/dist/components/data/index.js.map +1 -1
  27. package/dist/components/layout/form-page.d.ts +1 -1
  28. package/dist/components/layout/form-page.js +1 -1
  29. package/dist/components/layout/index.d.ts +2 -2
  30. package/dist/components/layout/index.js +9 -9
  31. package/dist/components/patterns/index.d.ts +1 -0
  32. package/dist/components/patterns/index.js +6 -4
  33. package/dist/components/patterns/sortable-list.d.ts +78 -0
  34. package/dist/components/patterns/sortable-list.js +5 -0
  35. package/dist/components/patterns/sortable-list.js.map +1 -0
  36. package/dist/components/rich-text/rich-text-editor.d.ts +194 -0
  37. package/dist/components/rich-text/rich-text-editor.js +833 -0
  38. package/dist/components/rich-text/rich-text-editor.js.map +1 -0
  39. package/dist/components/rich-text/rich-text.d.ts +51 -0
  40. package/dist/components/rich-text/rich-text.js +4 -0
  41. package/dist/components/rich-text/rich-text.js.map +1 -0
  42. package/dist/components/ui/alert-dialog.d.ts +12 -1
  43. package/dist/components/ui/alert-dialog.js +1 -1
  44. package/dist/hooks/index.d.ts +3 -3
  45. package/dist/hooks/index.js +4 -4
  46. package/dist/hooks/use-data-filters.d.ts +5 -3
  47. package/dist/hooks/use-data-filters.js +2 -2
  48. package/dist/hooks/use-resource-page.d.ts +2 -1
  49. package/dist/hooks/use-resource-page.js +3 -2
  50. package/dist/hooks/use-view-preference.d.ts +17 -2
  51. package/dist/hooks/use-view-preference.js +2 -1
  52. package/dist/index.d.ts +2 -1
  53. package/dist/index.js +30 -29
  54. package/dist/{navigation-DBwcZ6vb.d.ts → navigation-Bha0R3Fu.d.ts} +6 -3
  55. package/dist/rich-text-doc-C54fME2C.d.ts +180 -0
  56. package/dist/router/react-router.d.ts +47 -0
  57. package/dist/router/react-router.js +53 -0
  58. package/dist/router/react-router.js.map +1 -0
  59. package/dist/router/tanstack-router.d.ts +47 -0
  60. package/dist/router/tanstack-router.js +105 -0
  61. package/dist/router/tanstack-router.js.map +1 -0
  62. package/dist/{use-resource-page-Dd2VUv30.d.ts → use-resource-page-D4byuwAs.d.ts} +20 -3
  63. package/package.json +62 -7
  64. package/dist/chunk-CDYZRZ6U.js.map +0 -1
  65. package/dist/chunk-DWM7ELFI.js.map +0 -1
  66. package/dist/chunk-FTTNGGLA.js +0 -20
  67. package/dist/chunk-FTTNGGLA.js.map +0 -1
  68. package/dist/chunk-IXGAEECT.js.map +0 -1
  69. package/dist/chunk-JA3ZKULS.js.map +0 -1
  70. package/dist/chunk-M62CHO3N.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/rich-text/rich-text-doc.ts","../src/components/rich-text/rich-text.tsx"],"names":[],"mappings":"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","file":"chunk-ZDHWNQTR.js","sourcesContent":["/**\n * Rich text documents: the JSON shape, the allow-list that sanitizes it, and\n * the serializers (HTML, plain text) built on that allow-list.\n *\n * This file has no React and no editor dependency, so a server can import it\n * (through `@marktiderman/genesis-ui/rich-text`) to validate what a client\n * posted and to produce the HTML it stores or sends. Everything here works in\n * Node, edge runtimes and browsers alike: no DOM, no `DOMParser`.\n *\n * WHY JSON IS CANONICAL. The editor's document is a tree (ProseMirror). JSON\n * is that tree written down; HTML is one rendering of it. Storing JSON keeps\n * the structure the editor understands, so a later schema change is a data\n * migration instead of an HTML scraping job. HTML is derived: produce it from\n * the JSON with `richTextToHtml`, on the server, after `sanitizeRichTextDoc`.\n *\n * WHY AN ALLOW-LIST. Anything not named below is dropped: unknown node and\n * mark types, unknown attributes, links whose URL is not http(s), mailto, tel\n * or relative, and embeds whose URL is not a recognised YouTube, Vimeo or Loom\n * video. The serializers only ever emit tags this file writes itself, and\n * every text and attribute value goes through `escapeHtml`, so the output\n * cannot carry a `<script>`, an event handler or a `javascript:` URL no matter\n * what JSON came in.\n */\n\n/** A mark on a text node. `link` carries its URL in `attrs.href`. */\nexport interface RichTextMark {\n type: string;\n attrs?: Record<string, unknown>;\n}\n\n/**\n * One node of a rich text document. Structurally the same as Tiptap's\n * `JSONContent`, so editor output can be passed straight in.\n */\nexport interface RichTextNode {\n type: string;\n attrs?: Record<string, unknown>;\n content?: RichTextNode[];\n marks?: RichTextMark[];\n text?: string;\n}\n\n/** The root node of a rich text document. */\nexport interface RichTextDoc extends RichTextNode {\n type: \"doc\";\n content: RichTextNode[];\n}\n\n/** Heading levels a document may contain. `h1` is left to the page title. */\nexport type RichTextHeadingLevel = 2 | 3 | 4;\n\n/** Video hosts the `videoEmbed` node accepts. */\nexport type VideoProvider = \"youtube\" | \"vimeo\" | \"loom\";\n\n/** A video reference, reduced to what is needed to rebuild its player URL. */\nexport interface VideoEmbedAttrs {\n provider: VideoProvider;\n /** The host's video id (YouTube 11 chars, Vimeo digits, Loom alphanumeric). */\n id: string;\n /** Vimeo's privacy hash for unlisted videos (`vimeo.com/123/abcdef`). */\n hash?: string;\n /** Accessible name for the player frame. */\n title?: string;\n}\n\nexport interface SanitizeRichTextOptions {\n /** Heading levels to keep. Others move to the nearest kept level. Default `[2, 3, 4]`. */\n headingLevels?: readonly RichTextHeadingLevel[];\n /** Keep `videoEmbed` nodes. Default `true`. When false they are dropped. */\n embeds?: boolean;\n /**\n * Reject documents whose text is longer than this (characters, counted\n * after sanitizing). Throws `RichTextLimitError`. Off by default; set it on\n * the server for every write that accepts rich text from a client.\n */\n maxCharacters?: number;\n /**\n * Reject documents with more nodes than this (every node except the root,\n * text included, counted after sanitizing). Throws `RichTextLimitError`.\n * Off by default; set it on the server alongside `maxCharacters`.\n */\n maxNodes?: number;\n /**\n * What to do with content nested deeper than 32 levels. `\"drop\"` (default)\n * leaves it out, which suits rendering stored documents; `\"throw\"` rejects\n * the whole document with `RichTextLimitError`, which suits server writes,\n * so nothing an author wrote disappears without them being told.\n */\n onTooDeep?: \"drop\" | \"throw\";\n}\n\n/** Thrown by `sanitizeRichTextDoc` when a document breaks a limit it was given. */\nexport class RichTextLimitError extends Error {\n readonly reason: \"characters\" | \"nodes\" | \"depth\";\n readonly limit: number;\n /** The measured size; for `\"depth\"`, the limit plus one. */\n readonly actual: number;\n constructor(reason: \"characters\" | \"nodes\" | \"depth\", limit: number, actual: number) {\n super(\n reason === \"depth\"\n ? `Rich text is nested more than ${limit} levels deep`\n : `Rich text has ${actual} ${reason}; the limit is ${limit}`,\n );\n this.name = \"RichTextLimitError\";\n this.reason = reason;\n this.limit = limit;\n this.actual = actual;\n }\n}\n\nexport interface RichTextToHtmlOptions extends SanitizeRichTextOptions {\n /**\n * How video embeds are written. `\"iframe\"` (default) emits a player frame\n * pointing at the host's embed URL; `\"link\"` emits a plain link to the\n * video, for email and other places iframes do not run.\n */\n embedAs?: \"iframe\" | \"link\";\n}\n\nconst MAX_DEPTH = 32;\nconst MAX_TITLE_LENGTH = 200;\n\n/** An empty document: one empty paragraph, which is what the editor produces. */\nexport function emptyRichTextDoc(): RichTextDoc {\n return { type: \"doc\", content: [{ type: \"paragraph\" }] };\n}\n\n// ---------------------------------------------------------------------------\n// URLs\n// ---------------------------------------------------------------------------\n\nconst SAFE_PROTOCOLS = new Set([\"http:\", \"https:\", \"mailto:\", \"tel:\"]);\n\n/**\n * True when `url` is safe to put in an `href`: an absolute http(s), mailto or\n * tel URL, a root-relative path (`/lessons/1`) or a fragment (`#notes`).\n *\n * Rejects `javascript:`, `data:`, `vbscript:` and every other scheme, as well\n * as protocol-relative `//host` URLs and anything containing whitespace or\n * control characters (browsers strip those, which is how `java\\tscript:`\n * slips past naive checks).\n */\nexport function isSafeUrl(url: unknown): url is string {\n if (typeof url !== \"string\") return false;\n const value = url.trim();\n if (value === \"\" || value.length > 2048) return false;\n // eslint-disable-next-line no-control-regex\n if (/[\\u0000- \\u007f-\\u009f]/.test(value)) return false;\n if (value.startsWith(\"#\")) return true;\n if (value.startsWith(\"/\")) return !value.startsWith(\"//\") && !value.startsWith(\"/\\\\\");\n let parsed: URL;\n try {\n parsed = new URL(value);\n } catch {\n return false;\n }\n return SAFE_PROTOCOLS.has(parsed.protocol);\n}\n\n/**\n * Turn what a person typed into the link field into a safe href, or `null`.\n * `example.com/page` becomes `https://example.com/page`; an email address\n * becomes a `mailto:` link.\n */\nexport function normalizeLinkUrl(input: string): string | null {\n const value = input.trim();\n if (value === \"\") return null;\n if (isSafeUrl(value)) return value;\n if (/^[^\\s@/:]+@[^\\s@/]+\\.[^\\s@/]+$/.test(value)) {\n const mail = `mailto:${value}`;\n return isSafeUrl(mail) ? mail : null;\n }\n // No scheme but looks like a host: assume https.\n if (!/^[a-z][a-z0-9+.-]*:/i.test(value) && /^[^\\s/]+\\.[^\\s/]+/.test(value)) {\n const withScheme = `https://${value}`;\n return isSafeUrl(withScheme) ? withScheme : null;\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Video embeds\n// ---------------------------------------------------------------------------\n\nconst YOUTUBE_ID = /^[A-Za-z0-9_-]{11}$/;\nconst VIMEO_ID = /^\\d{1,12}$/;\nconst VIMEO_HASH = /^[a-f0-9]{6,32}$/;\nconst LOOM_ID = /^[A-Za-z0-9]{8,64}$/;\n\nfunction hostOf(url: URL): string {\n return url.hostname.toLowerCase().replace(/^www\\./, \"\").replace(/^m\\./, \"\");\n}\n\n/**\n * Recognise a YouTube, Vimeo or Loom video URL (watch, share, short and embed\n * forms) and reduce it to its provider and id. Returns `null` for anything\n * else, including other hosts that merely look similar.\n */\nexport function parseVideoUrl(input: string): VideoEmbedAttrs | null {\n let url: URL;\n try {\n url = new URL(input.trim());\n } catch {\n return null;\n }\n if (url.protocol !== \"https:\" && url.protocol !== \"http:\") return null;\n const host = hostOf(url);\n const parts = url.pathname.split(\"/\").filter(Boolean);\n\n if (host === \"youtube.com\" || host === \"youtube-nocookie.com\" || host === \"music.youtube.com\") {\n let id: string | null | undefined = null;\n if (parts[0] === \"watch\") id = url.searchParams.get(\"v\");\n else if (parts[0] === \"embed\" || parts[0] === \"shorts\" || parts[0] === \"live\" || parts[0] === \"v\")\n id = parts[1];\n return id && YOUTUBE_ID.test(id) ? { provider: \"youtube\", id } : null;\n }\n if (host === \"youtu.be\") {\n const id = parts[0];\n return id && YOUTUBE_ID.test(id) ? { provider: \"youtube\", id } : null;\n }\n if (host === \"vimeo.com\" || host === \"player.vimeo.com\") {\n // vimeo.com/123, vimeo.com/123/abcdef, vimeo.com/channels/x/123,\n // player.vimeo.com/video/123?h=abcdef\n // vimeo.com/showcase/1/video/2 and album/…/video/2: the video follows \"video\".\n const videoAt = parts.lastIndexOf(\"video\");\n const idIndex =\n videoAt !== -1 && parts[videoAt + 1] && VIMEO_ID.test(parts[videoAt + 1]!)\n ? videoAt + 1\n : parts.findIndex((p) => VIMEO_ID.test(p));\n if (idIndex === -1) return null;\n const id = parts[idIndex]!;\n // A privacy hash is the path segment right after the id only in the short\n // `vimeo.com/<id>/<hash>` form; otherwise it is the `h` query parameter.\n const hash = url.searchParams.get(\"h\") ?? (host === \"vimeo.com\" && idIndex === 0 ? parts[1] : undefined);\n return hash && VIMEO_HASH.test(hash)\n ? { provider: \"vimeo\", id, hash }\n : { provider: \"vimeo\", id };\n }\n if (host === \"loom.com\") {\n if (parts[0] !== \"share\" && parts[0] !== \"embed\") return null;\n const id = parts[1];\n return id && LOOM_ID.test(id) ? { provider: \"loom\", id } : null;\n }\n return null;\n}\n\n/** Validate untrusted embed attributes, returning a clean copy or `null`. */\nexport function sanitizeVideoEmbedAttrs(attrs: unknown): VideoEmbedAttrs | null {\n if (!attrs || typeof attrs !== \"object\") return null;\n const a = attrs as Record<string, unknown>;\n const id = typeof a.id === \"string\" ? a.id : \"\";\n let clean: VideoEmbedAttrs | null = null;\n if (a.provider === \"youtube\" && YOUTUBE_ID.test(id)) clean = { provider: \"youtube\", id };\n else if (a.provider === \"vimeo\" && VIMEO_ID.test(id)) {\n clean = { provider: \"vimeo\", id };\n if (typeof a.hash === \"string\" && VIMEO_HASH.test(a.hash)) clean.hash = a.hash;\n } else if (a.provider === \"loom\" && LOOM_ID.test(id)) clean = { provider: \"loom\", id };\n if (!clean) return null;\n if (typeof a.title === \"string\" && a.title.trim() !== \"\") {\n clean.title = a.title.trim().slice(0, MAX_TITLE_LENGTH);\n }\n return clean;\n}\n\nconst PROVIDER_NAMES: Record<VideoProvider, string> = {\n youtube: \"YouTube\",\n vimeo: \"Vimeo\",\n loom: \"Loom\",\n};\n\n/** The player URL for an embed. Built from validated parts only. */\nexport function videoEmbedSrc(attrs: VideoEmbedAttrs): string {\n switch (attrs.provider) {\n case \"youtube\":\n // The privacy-enhanced host: no tracking cookies until the viewer plays.\n return `https://www.youtube-nocookie.com/embed/${attrs.id}`;\n case \"vimeo\":\n return `https://player.vimeo.com/video/${attrs.id}${attrs.hash ? `?h=${attrs.hash}` : \"\"}`;\n case \"loom\":\n return `https://www.loom.com/embed/${attrs.id}`;\n }\n}\n\n/** The page URL for an embed (what a viewer would open in a new tab). */\nexport function videoWatchUrl(attrs: VideoEmbedAttrs): string {\n switch (attrs.provider) {\n case \"youtube\":\n return `https://www.youtube.com/watch?v=${attrs.id}`;\n case \"vimeo\":\n return `https://vimeo.com/${attrs.id}${attrs.hash ? `/${attrs.hash}` : \"\"}`;\n case \"loom\":\n return `https://www.loom.com/share/${attrs.id}`;\n }\n}\n\n/** Accessible name for an embed's frame: its title, or \"YouTube video\" etc. */\nexport function videoEmbedTitle(attrs: VideoEmbedAttrs): string {\n return attrs.title || `${PROVIDER_NAMES[attrs.provider]} video`;\n}\n\n/**\n * Attributes for an embed's `<iframe>`. Shared by the HTML serializer, the\n * React renderer and the editor so all three emit the same frame.\n *\n * `sandbox` keeps the player away from top-level navigation and forms.\n * `allow-same-origin` is safe here because the frame is always cross-origin\n * (the player host), and the players need it for their own storage.\n */\nexport const VIDEO_IFRAME_ATTRS = {\n allow:\n \"accelerometer; autoplay; clipboard-write; encrypted-media; fullscreen; gyroscope; picture-in-picture\",\n loading: \"lazy\",\n referrerPolicy: \"strict-origin-when-cross-origin\",\n sandbox:\n \"allow-scripts allow-same-origin allow-presentation allow-popups allow-popups-to-escape-sandbox\",\n} as const;\n\n// ---------------------------------------------------------------------------\n// Sanitizer\n// ---------------------------------------------------------------------------\n\nconst BLOCK_TYPES = new Set([\n \"paragraph\",\n \"heading\",\n \"bulletList\",\n \"orderedList\",\n \"listItem\",\n \"blockquote\",\n \"videoEmbed\",\n]);\n\ninterface Ctx {\n levels: readonly RichTextHeadingLevel[];\n embeds: boolean;\n throwTooDeep: boolean;\n}\n\n/** True when `depth` is past the limit; throws instead when the caller asked for that. */\nfunction tooDeep(depth: number, ctx: Ctx): boolean {\n if (depth <= MAX_DEPTH) return false;\n if (ctx.throwTooDeep) throw new RichTextLimitError(\"depth\", MAX_DEPTH, depth);\n return true;\n}\n\nfunction measure(nodes: RichTextNode[] | undefined, size = { nodes: 0, characters: 0 }) {\n for (const node of nodes ?? []) {\n size.nodes += 1;\n if (typeof node.text === \"string\") size.characters += node.text.length;\n measure(node.content, size);\n }\n return size;\n}\n\n/**\n * A cheap check on the raw input before sanitizing, so an oversized payload\n * is refused without walking all of it. It runs only when a limit is set,\n * iterates without recursion, and stops as soon as the input is clearly over:\n * more than 4x `maxNodes` raw nodes (the sanitizer drops wrappers and\n * unknown nodes) or 2x `maxCharacters` of raw text. Inputs within those\n * margins get the exact check after sanitizing.\n */\nfunction precheckRawSize(root: RichTextNode, options: SanitizeRichTextOptions): void {\n const nodeBudget = options.maxNodes !== undefined ? options.maxNodes * 4 : Infinity;\n const charBudget = options.maxCharacters !== undefined ? options.maxCharacters * 2 : Infinity;\n let nodes = 0;\n let characters = 0;\n const stack: unknown[] = [root.content];\n while (stack.length > 0) {\n const list = stack.pop();\n if (!Array.isArray(list)) continue;\n for (const node of list) {\n if (!node || typeof node !== \"object\") continue;\n nodes += 1;\n const text = (node as RichTextNode).text;\n if (typeof text === \"string\") characters += text.length;\n if (nodes > nodeBudget) throw new RichTextLimitError(\"nodes\", options.maxNodes!, nodes);\n if (characters > charBudget) {\n throw new RichTextLimitError(\"characters\", options.maxCharacters!, characters);\n }\n stack.push((node as RichTextNode).content);\n }\n }\n}\n\nfunction sanitizeMarks(marks: unknown): RichTextMark[] | undefined {\n if (!Array.isArray(marks)) return undefined;\n const out: RichTextMark[] = [];\n const seen = new Set<string>();\n for (const mark of marks) {\n if (!mark || typeof mark !== \"object\") continue;\n const type = (mark as RichTextMark).type;\n if (seen.has(type)) continue;\n if (type === \"bold\" || type === \"italic\" || type === \"underline\") {\n out.push({ type });\n seen.add(type);\n } else if (type === \"link\") {\n const href = (mark as RichTextMark).attrs?.href;\n if (isSafeUrl(href)) {\n out.push({ type: \"link\", attrs: { href: href.trim() } });\n seen.add(type);\n }\n }\n }\n return out.length > 0 ? out : undefined;\n}\n\nfunction sanitizeInline(nodes: unknown, depth: number, ctx: Ctx): RichTextNode[] {\n if (!Array.isArray(nodes) || tooDeep(depth, ctx)) return [];\n const out: RichTextNode[] = [];\n for (const node of nodes) {\n if (!node || typeof node !== \"object\") continue;\n const n = node as RichTextNode;\n if (n.type === \"text\") {\n if (typeof n.text !== \"string\" || n.text === \"\") continue;\n const marks = sanitizeMarks(n.marks);\n out.push(marks ? { type: \"text\", text: n.text, marks } : { type: \"text\", text: n.text });\n } else if (n.type === \"hardBreak\") {\n out.push({ type: \"hardBreak\" });\n } else if (Array.isArray(n.content)) {\n // An inline wrapper we do not know: keep its text, drop the wrapper.\n for (const child of sanitizeInline(n.content, depth + 1, ctx)) out.push(child);\n }\n }\n return out;\n}\n\nfunction withContent(\n type: string,\n content: RichTextNode[],\n attrs?: Record<string, unknown>,\n): RichTextNode {\n const node: RichTextNode = { type };\n if (attrs) node.attrs = attrs;\n if (content.length > 0) node.content = content;\n return node;\n}\n\nfunction nearestLevel(\n level: unknown,\n levels: readonly RichTextHeadingLevel[],\n): RichTextHeadingLevel {\n const n = typeof level === \"number\" ? level : 2;\n if ((levels as readonly number[]).includes(n)) return n as RichTextHeadingLevel;\n const sorted = [...levels].sort((a, b) => Math.abs(a - n) - Math.abs(b - n) || a - b);\n return sorted[0] ?? 2;\n}\n\nfunction sanitizeListItems(nodes: unknown, ctx: Ctx, depth: number): RichTextNode[] {\n if (!Array.isArray(nodes) || tooDeep(depth, ctx)) return [];\n const items: RichTextNode[] = [];\n for (const node of nodes) {\n if (!node || typeof node !== \"object\") continue;\n const n = node as RichTextNode;\n const inner = n.type === \"listItem\" ? n.content : [n];\n // The editor's list item is `paragraph block*`, so headings, quotes and\n // embeds may follow the first paragraph; keep them rather than losing\n // content the author can see.\n const blocks = sanitizeBlocks(inner, ctx, depth + 1);\n // A list item starts with a paragraph in the editor's schema.\n if (blocks.length === 0 || blocks[0]!.type !== \"paragraph\") blocks.unshift({ type: \"paragraph\" });\n items.push({ type: \"listItem\", content: blocks });\n }\n return items;\n}\n\nfunction sanitizeBlocks(nodes: unknown, ctx: Ctx, depth: number): RichTextNode[] {\n if (!Array.isArray(nodes) || tooDeep(depth, ctx)) return [];\n const out: RichTextNode[] = [];\n let pendingInline: RichTextNode[] = [];\n const flushInline = () => {\n if (pendingInline.length > 0) out.push(withContent(\"paragraph\", pendingInline));\n pendingInline = [];\n };\n\n for (const node of nodes) {\n if (!node || typeof node !== \"object\") continue;\n const n = node as RichTextNode;\n if (n.type === \"text\" || n.type === \"hardBreak\") {\n // Stray inline content at block level: gather it into a paragraph.\n for (const child of sanitizeInline([n], depth, ctx)) pendingInline.push(child);\n continue;\n }\n flushInline();\n switch (n.type) {\n case \"paragraph\":\n out.push(withContent(\"paragraph\", sanitizeInline(n.content, depth + 1, ctx)));\n break;\n case \"heading\":\n out.push(\n withContent(\"heading\", sanitizeInline(n.content, depth + 1, ctx), {\n level: nearestLevel(n.attrs?.level, ctx.levels),\n }),\n );\n break;\n case \"bulletList\":\n case \"orderedList\": {\n const items = sanitizeListItems(n.content, ctx, depth);\n if (items.length === 0) break;\n const start = n.attrs?.start;\n const attrs =\n n.type === \"orderedList\" &&\n typeof start === \"number\" &&\n Number.isInteger(start) &&\n start > 1\n ? { start: Math.min(start, 1_000_000) }\n : undefined;\n out.push(withContent(n.type, items, attrs));\n break;\n }\n case \"listItem\":\n // A list item outside a list: keep its blocks.\n for (const child of sanitizeBlocks(n.content, ctx, depth + 1)) out.push(child);\n break;\n case \"blockquote\": {\n const inner = sanitizeBlocks(n.content, ctx, depth + 1);\n out.push(withContent(\"blockquote\", inner.length > 0 ? inner : [{ type: \"paragraph\" }]));\n break;\n }\n case \"videoEmbed\": {\n if (!ctx.embeds) break;\n const attrs = sanitizeVideoEmbedAttrs(n.attrs);\n if (attrs) out.push({ type: \"videoEmbed\", attrs: { ...attrs } });\n break;\n }\n default:\n // Unknown block (code block, table, image…): keep its text as a\n // paragraph rather than silently losing what someone wrote.\n if (Array.isArray(n.content)) {\n const nested = n.content.some(\n (c) => !!c && typeof c === \"object\" && BLOCK_TYPES.has((c as RichTextNode).type),\n );\n if (nested) for (const child of sanitizeBlocks(n.content, ctx, depth + 1)) out.push(child);\n else {\n const inline = sanitizeInline(n.content, depth + 1, ctx);\n if (inline.length > 0) out.push(withContent(\"paragraph\", inline));\n }\n }\n }\n }\n flushInline();\n return out;\n}\n\n/**\n * Validate an untrusted value as a rich text document and return a clean\n * copy that contains only allow-listed nodes, marks and attributes.\n *\n * `null`, a string or a malformed object becomes an empty document. Run\n * this on the server on every write, before storing the JSON or deriving\n * HTML from it, with limits so a client cannot store an oversized or\n * pathologically nested document:\n *\n * ```ts\n * sanitizeRichTextDoc(body, { maxCharacters: 50_000, maxNodes: 5_000, onTooDeep: \"throw\" });\n * ```\n *\n * Without limits it never throws; with them it throws `RichTextLimitError`.\n */\nexport function sanitizeRichTextDoc(\n input: unknown,\n options: SanitizeRichTextOptions = {},\n): RichTextDoc {\n const levels =\n options.headingLevels && options.headingLevels.length > 0\n ? options.headingLevels\n : ([2, 3, 4] as const);\n const ctx: Ctx = { levels, embeds: options.embeds ?? true, throwTooDeep: options.onTooDeep === \"throw\" };\n const root = input && typeof input === \"object\" ? (input as RichTextNode) : null;\n if (root && (options.maxCharacters !== undefined || options.maxNodes !== undefined)) {\n precheckRawSize(root, options);\n }\n const content = root && root.type === \"doc\" ? sanitizeBlocks(root.content, ctx, 0) : [];\n // Trailing empty paragraphs carry no meaning (the editor keeps one after\n // the last block so there is always somewhere to type); drop them so\n // stored documents and derived HTML do not end in `<p></p>`.\n while (content.length > 1) {\n const last = content[content.length - 1]!;\n if (last.type !== \"paragraph\" || last.content) break;\n content.pop();\n }\n if (options.maxCharacters !== undefined || options.maxNodes !== undefined) {\n const size = measure(content);\n if (options.maxCharacters !== undefined && size.characters > options.maxCharacters) {\n throw new RichTextLimitError(\"characters\", options.maxCharacters, size.characters);\n }\n if (options.maxNodes !== undefined && size.nodes > options.maxNodes) {\n throw new RichTextLimitError(\"nodes\", options.maxNodes, size.nodes);\n }\n }\n return { type: \"doc\", content: content.length > 0 ? content : [{ type: \"paragraph\" }] };\n}\n\n/** True when `value` is shaped like a rich text document (`{ type: \"doc\" }`). */\nexport function isRichTextDoc(value: unknown): value is RichTextDoc {\n return (\n !!value &&\n typeof value === \"object\" &&\n (value as RichTextNode).type === \"doc\" &&\n Array.isArray((value as RichTextNode).content)\n );\n}\n\n// ---------------------------------------------------------------------------\n// Serializers\n// ---------------------------------------------------------------------------\n\n/** Escape text for use in HTML element content and quoted attributes. */\nexport function escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\n/** True for absolute http(s) links, which open in a new tab. */\nexport function isExternalHref(href: string): boolean {\n return /^https?:\\/\\//i.test(href);\n}\n\nconst MARK_ORDER = [\"link\", \"bold\", \"italic\", \"underline\"];\n\n/** Marks in a fixed nesting order (link outermost) so output is stable. */\nexport function orderedMarks(marks: RichTextMark[] | undefined): RichTextMark[] {\n if (!marks) return [];\n return [...marks].sort((a, b) => MARK_ORDER.indexOf(a.type) - MARK_ORDER.indexOf(b.type));\n}\n\nfunction linkAttrs(href: string): string {\n const safe = escapeHtml(href);\n return isExternalHref(href)\n ? `href=\"${safe}\" target=\"_blank\" rel=\"noopener noreferrer nofollow\"`\n : `href=\"${safe}\"`;\n}\n\nfunction inlineToHtml(nodes: RichTextNode[] | undefined): string {\n if (!nodes) return \"\";\n let html = \"\";\n for (const node of nodes) {\n if (node.type === \"hardBreak\") {\n html += \"<br>\";\n continue;\n }\n let text = escapeHtml(node.text ?? \"\");\n const marks = orderedMarks(node.marks);\n for (let i = marks.length - 1; i >= 0; i--) {\n const mark = marks[i]!;\n if (mark.type === \"bold\") text = `<strong>${text}</strong>`;\n else if (mark.type === \"italic\") text = `<em>${text}</em>`;\n else if (mark.type === \"underline\") text = `<u>${text}</u>`;\n else if (mark.type === \"link\") text = `<a ${linkAttrs(String(mark.attrs?.href))}>${text}</a>`;\n }\n html += text;\n }\n return html;\n}\n\nfunction embedToHtml(attrs: VideoEmbedAttrs, embedAs: \"iframe\" | \"link\"): string {\n const title = escapeHtml(videoEmbedTitle(attrs));\n if (embedAs === \"link\") {\n return `<p><a ${linkAttrs(videoWatchUrl(attrs))}>${title}</a></p>`;\n }\n const a = VIDEO_IFRAME_ATTRS;\n const data = `data-video-embed=\"\" data-provider=\"${attrs.provider}\" data-id=\"${escapeHtml(attrs.id)}\"${\n attrs.hash ? ` data-hash=\"${escapeHtml(attrs.hash)}\"` : \"\"\n }${attrs.title ? ` data-title=\"${title}\"` : \"\"}`;\n return (\n `<div ${data}>` +\n `<iframe src=\"${escapeHtml(videoEmbedSrc(attrs))}\" title=\"${title}\" allow=\"${a.allow}\" allowfullscreen ` +\n `loading=\"${a.loading}\" referrerpolicy=\"${a.referrerPolicy}\" sandbox=\"${a.sandbox}\"></iframe>` +\n `</div>`\n );\n}\n\nfunction blocksToHtml(nodes: RichTextNode[] | undefined, embedAs: \"iframe\" | \"link\"): string {\n if (!nodes) return \"\";\n let html = \"\";\n for (const node of nodes) {\n switch (node.type) {\n case \"paragraph\":\n html += `<p>${inlineToHtml(node.content)}</p>`;\n break;\n case \"heading\": {\n const level = node.attrs?.level as number;\n html += `<h${level}>${inlineToHtml(node.content)}</h${level}>`;\n break;\n }\n case \"bulletList\":\n html += `<ul>${blocksToHtml(node.content, embedAs)}</ul>`;\n break;\n case \"orderedList\": {\n const start = node.attrs?.start;\n html += `<ol${typeof start === \"number\" ? ` start=\"${start}\"` : \"\"}>${blocksToHtml(node.content, embedAs)}</ol>`;\n break;\n }\n case \"listItem\":\n html += `<li>${blocksToHtml(node.content, embedAs)}</li>`;\n break;\n case \"blockquote\":\n html += `<blockquote>${blocksToHtml(node.content, embedAs)}</blockquote>`;\n break;\n case \"videoEmbed\":\n html += embedToHtml(node.attrs as unknown as VideoEmbedAttrs, embedAs);\n break;\n }\n }\n return html;\n}\n\n/**\n * Serialize a document to HTML. The input is sanitized first, so this is\n * safe to call on untrusted JSON; the output contains only the tags listed\n * in this file's header, with every value escaped.\n */\nexport function richTextToHtml(doc: unknown, options: RichTextToHtmlOptions = {}): string {\n const clean = sanitizeRichTextDoc(doc, options);\n return blocksToHtml(clean.content, options.embedAs ?? \"iframe\");\n}\n\nfunction collectText(nodes: RichTextNode[] | undefined, out: string[]): void {\n if (!nodes) return;\n for (const node of nodes) {\n if (node.type === \"text\") out.push(node.text ?? \"\");\n else if (node.type === \"hardBreak\") out.push(\"\\n\");\n else if (node.type === \"videoEmbed\") {\n const attrs = sanitizeVideoEmbedAttrs(node.attrs);\n if (attrs) out.push(`${videoEmbedTitle(attrs)} (${videoWatchUrl(attrs)})`, \"\\n\\n\");\n } else if (\n node.type === \"listItem\" ||\n node.type === \"bulletList\" ||\n node.type === \"orderedList\" ||\n node.type === \"blockquote\"\n ) {\n collectText(node.content, out);\n } else {\n collectText(node.content, out);\n out.push(\"\\n\\n\");\n }\n }\n}\n\n/**\n * Plain text for search indexes, previews and notifications. Blocks are\n * separated by blank lines; embeds become \"Title (url)\".\n */\nexport function richTextToPlainText(doc: unknown): string {\n const clean = sanitizeRichTextDoc(doc);\n const out: string[] = [];\n collectText(clean.content, out);\n return out.join(\"\").replace(/\\n{3,}/g, \"\\n\\n\").trim();\n}\n\n/** True when the document has no visible text and no embeds. */\nexport function isRichTextEmpty(doc: unknown): boolean {\n const clean = sanitizeRichTextDoc(doc);\n const walk = (nodes: RichTextNode[] | undefined): boolean =>\n !nodes ||\n nodes.every((n) => {\n if (n.type === \"videoEmbed\") return false;\n if (n.type === \"text\") return (n.text ?? \"\").trim() === \"\";\n return walk(n.content);\n });\n return walk(clean.content);\n}\n","/**\n * RichText — read-only rendering of a rich text document (the JSON that\n * `RichTextEditor` produces), plus the server-safe helpers for that JSON.\n *\n * This subpath has no editor dependency and no `\"use client\"`: it renders in\n * a React Server Component, during SSR, or on the client, and a server can\n * import the helpers on their own to sanitize posted JSON and derive HTML.\n *\n * @stability Beta\n */\nimport * as React from \"react\";\nimport { cn } from \"../../utils\";\nimport {\n type RichTextDoc,\n type RichTextHeadingLevel,\n type RichTextNode,\n type VideoEmbedAttrs,\n VIDEO_IFRAME_ATTRS,\n isExternalHref,\n orderedMarks,\n sanitizeRichTextDoc,\n videoEmbedSrc,\n videoEmbedTitle,\n videoWatchUrl,\n} from \"./rich-text-doc\";\n\n// The public, server-safe surface. `orderedMarks` and `isExternalHref` stay\n// internal: they are rendering details, not API.\nexport {\n emptyRichTextDoc,\n escapeHtml,\n isRichTextDoc,\n isRichTextEmpty,\n isSafeUrl,\n normalizeLinkUrl,\n parseVideoUrl,\n RichTextLimitError,\n richTextToHtml,\n richTextToPlainText,\n sanitizeRichTextDoc,\n sanitizeVideoEmbedAttrs,\n videoEmbedSrc,\n videoEmbedTitle,\n videoWatchUrl,\n VIDEO_IFRAME_ATTRS,\n} from \"./rich-text-doc\";\nexport type {\n RichTextDoc,\n RichTextHeadingLevel,\n RichTextMark,\n RichTextNode,\n RichTextToHtmlOptions,\n SanitizeRichTextOptions,\n VideoEmbedAttrs,\n VideoProvider,\n} from \"./rich-text-doc\";\n\n/**\n * Typography for rendered rich text, shared by `RichText` and the editor's\n * editable area so a document looks the same while writing and reading.\n * Exported for apps that render stored HTML themselves.\n */\nexport const richTextClassName = cn(\n \"text-sm leading-relaxed text-foreground break-words\",\n \"[&>*+*]:mt-3\",\n \"[&_h2]:text-xl [&_h2]:font-semibold [&_h2]:tracking-tight [&_h2]:mt-6 first:[&_h2]:mt-0\",\n \"[&_h3]:text-lg [&_h3]:font-semibold [&_h3]:mt-5 first:[&_h3]:mt-0\",\n \"[&_h4]:text-base [&_h4]:font-semibold [&_h4]:mt-4 first:[&_h4]:mt-0\",\n \"[&_ul]:list-disc [&_ol]:list-decimal [&_ul]:pl-6 [&_ol]:pl-6 [&_li]:my-1 [&_li>p]:m-0\",\n \"[&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-4 [&_blockquote]:text-muted-foreground [&_blockquote]:italic\",\n \"[&_a]:text-primary [&_a]:underline [&_a]:underline-offset-4\",\n \"[&_[data-video-embed]]:relative [&_[data-video-embed]]:aspect-video [&_[data-video-embed]]:w-full [&_[data-video-embed]]:overflow-hidden [&_[data-video-embed]]:rounded-md [&_[data-video-embed]]:bg-muted\",\n \"[&_[data-video-embed]_iframe]:absolute [&_[data-video-embed]_iframe]:inset-0 [&_[data-video-embed]_iframe]:h-full [&_[data-video-embed]_iframe]:w-full [&_[data-video-embed]_iframe]:border-0\",\n);\n\nexport interface RichTextProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\"> {\n /**\n * The document to render (the editor's JSON). Untrusted input is fine: it\n * is sanitized before rendering, and nothing is set as raw HTML.\n * `null`/`undefined` render an empty container.\n */\n value: RichTextDoc | null | undefined;\n /** Render video embeds as players (`true`, default) or as plain links. */\n embeds?: boolean;\n /** Heading levels to keep; others move to the nearest kept level. Default `[2, 3, 4]`. */\n headingLevels?: readonly RichTextHeadingLevel[];\n /** Stable test/automation selector (emitted as `data-testid`). */\n testID?: string;\n}\n\nfunction renderInline(nodes: RichTextNode[] | undefined): React.ReactNode[] {\n if (!nodes) return [];\n return nodes.map((node, i) => {\n if (node.type === \"hardBreak\") return <br key={i} />;\n let el: React.ReactNode = node.text ?? \"\";\n const marks = orderedMarks(node.marks);\n for (let m = marks.length - 1; m >= 0; m--) {\n const mark = marks[m]!;\n if (mark.type === \"bold\") el = <strong>{el}</strong>;\n else if (mark.type === \"italic\") el = <em>{el}</em>;\n else if (mark.type === \"underline\") el = <u>{el}</u>;\n else if (mark.type === \"link\") {\n const href = String(mark.attrs?.href);\n el = isExternalHref(href) ? (\n <a href={href} target=\"_blank\" rel=\"noopener noreferrer nofollow\">\n {el}\n </a>\n ) : (\n <a href={href}>{el}</a>\n );\n }\n }\n return <React.Fragment key={i}>{el}</React.Fragment>;\n });\n}\n\nfunction VideoEmbedView({ attrs, asLink }: { attrs: VideoEmbedAttrs; asLink: boolean }) {\n const title = videoEmbedTitle(attrs);\n if (asLink) {\n return (\n <p>\n <a href={videoWatchUrl(attrs)} target=\"_blank\" rel=\"noopener noreferrer nofollow\">\n {title}\n </a>\n </p>\n );\n }\n return (\n <div data-video-embed=\"\" data-provider={attrs.provider}>\n <iframe\n src={videoEmbedSrc(attrs)}\n title={title}\n allow={VIDEO_IFRAME_ATTRS.allow}\n allowFullScreen\n loading={VIDEO_IFRAME_ATTRS.loading}\n referrerPolicy={VIDEO_IFRAME_ATTRS.referrerPolicy}\n sandbox={VIDEO_IFRAME_ATTRS.sandbox}\n />\n </div>\n );\n}\n\nfunction renderBlocks(nodes: RichTextNode[] | undefined, asLink: boolean): React.ReactNode[] {\n if (!nodes) return [];\n return nodes.map((node, i) => {\n switch (node.type) {\n case \"paragraph\":\n return <p key={i}>{renderInline(node.content)}</p>;\n case \"heading\": {\n const Tag = `h${node.attrs?.level as RichTextHeadingLevel}` as \"h2\" | \"h3\" | \"h4\";\n return <Tag key={i}>{renderInline(node.content)}</Tag>;\n }\n case \"bulletList\":\n return <ul key={i}>{renderBlocks(node.content, asLink)}</ul>;\n case \"orderedList\":\n return (\n <ol key={i} start={node.attrs?.start as number | undefined}>\n {renderBlocks(node.content, asLink)}\n </ol>\n );\n case \"listItem\":\n return <li key={i}>{renderBlocks(node.content, asLink)}</li>;\n case \"blockquote\":\n return <blockquote key={i}>{renderBlocks(node.content, asLink)}</blockquote>;\n case \"videoEmbed\":\n return (\n <VideoEmbedView key={i} attrs={node.attrs as unknown as VideoEmbedAttrs} asLink={asLink} />\n );\n default:\n return null;\n }\n });\n}\n\n/**\n * RichText — renders a rich text document as React elements.\n *\n * The document is passed through `sanitizeRichTextDoc` first, and rendering\n * never uses `dangerouslySetInnerHTML`: text is React-escaped, links keep\n * only http(s)/mailto/tel/relative URLs, and the only iframes are YouTube,\n * Vimeo and Loom players rebuilt from a validated id. That makes it safe for\n * stored or user-supplied JSON.\n *\n * Server-component safe (no hooks, no `\"use client\"`).\n *\n * @stability Beta\n */\nexport function RichText({\n value,\n embeds = true,\n headingLevels,\n className,\n testID,\n ...props\n}: RichTextProps) {\n // No hooks on purpose, so this stays usable as a React Server Component.\n const doc = value ? sanitizeRichTextDoc(value, headingLevels ? { headingLevels } : {}) : null;\n return (\n <div data-testid={testID} className={cn(richTextClassName, className)} {...props}>\n {doc ? renderBlocks(doc.content, !embeds) : null}\n </div>\n );\n}\n"]}
@@ -1,22 +1,25 @@
1
1
  import * as React from 'react';
2
2
  import { ReactNode, ComponentType, ComponentProps, RefObject } from 'react';
3
- import { e as PageHeaderAction, c as PageHeaderOptions, P as PageHeader$1, A as AppShell$1 } from '../../AppShell-CX0M-dfc.js';
4
- export { a as AppShellOptions, b as AppShellProps, N as NavItem, d as PageHeaderProps } from '../../AppShell-CX0M-dfc.js';
3
+ import { f as PageHeaderAction, c as PageHeaderOptions, P as PageHeader$1, A as AppShell$1 } from '../../AppShell-LXQykEqe.js';
4
+ export { a as AppShellOptions, b as AppShellProps, N as NavItem, d as PageHeaderProps, e as ParentNavigation } from '../../AppShell-LXQykEqe.js';
5
5
  import { V as ViewMode } from '../../view-toggle-CahQ87PW.js';
6
6
  export { a as ViewToggle } from '../../view-toggle-CahQ87PW.js';
7
7
  import { LucideIcon } from 'lucide-react';
8
8
  import { Density } from '../../hooks/use-view-settings.js';
9
- import { R as ResourceActions, a as ResourceColumnDef, D as DataTable } from '../../use-resource-page-Dd2VUv30.js';
10
- export { c as DataTableColumn, d as DataTableProps, L as LinkCell, T as ThumbnailCell } from '../../use-resource-page-Dd2VUv30.js';
9
+ import { R as ResourceActions, a as ResourceColumnDef, D as DataTable } from '../../use-resource-page-D4byuwAs.js';
10
+ export { c as DataTableColumn, d as DataTableProps, L as LinkCell, T as ThumbnailCell } from '../../use-resource-page-D4byuwAs.js';
11
11
  export { EmptyState } from '../patterns/empty-state.js';
12
12
  export { V as ViewSettings } from '../../view-settings-kHKCHMjj.js';
13
- import { L as LinkComponent, N as NavigateFn } from '../../navigation-DBwcZ6vb.js';
14
- export { D as DefaultLink, G as GenesisLinkProps, I as IsPathActiveOptions, a as NavigationNode, R as ResolvedNavigation, d as defaultNavigate, i as isPathActive, r as resolveNavigation } from '../../navigation-DBwcZ6vb.js';
13
+ import { L as LinkComponent, a as NavigateFn } from '../../navigation-Bha0R3Fu.js';
14
+ export { D as DefaultLink, G as GenesisLinkProps, I as IsPathActiveOptions, N as NavigationNode, R as ResolvedNavigation, d as defaultNavigate, i as isPathActive, r as resolveNavigation } from '../../navigation-Bha0R3Fu.js';
15
15
  import { UseFormReturn, Control, FieldErrors } from 'react-hook-form';
16
16
  import { ResourceFormFieldDef } from '@marktiderman/genesis-core';
17
17
  export { FieldType, ResourceFormFieldDef, SelectOption } from '@marktiderman/genesis-core';
18
18
  import { WizardStep, UseResourceOptions } from '@marktiderman/genesis-core/hooks';
19
19
  import { b as Slots, a as SlotPropsFor } from '../../slots-tR0wf89f.js';
20
+ export { S as SlotOverride } from '../../slots-tR0wf89f.js';
21
+ import { ViewModeDefault } from '../../hooks/use-view-preference.js';
22
+ export { ResponsiveViewMode } from '../../hooks/use-view-preference.js';
20
23
  import { FilterParam, SortParam, BaseRecord, Identifier } from '@marktiderman/genesis-core/provider';
21
24
  export { CommandPalette, CommandPaletteItem, CommandPaletteProps, useCommandPaletteHotkey } from '../ui/command-palette.js';
22
25
  export { UseSavedViewsReturn, useSavedViews } from '../../hooks/use-saved-views.js';
@@ -36,6 +39,12 @@ interface DataPageShellProps {
36
39
  isEmpty?: boolean;
37
40
  emptyIcon?: LucideIcon;
38
41
  emptyMessage?: string;
42
+ /**
43
+ * A sentence under the empty-state title — what this list is for, or what
44
+ * to do first. Forwarded to `EmptyState`'s `description`; hidden while
45
+ * filters are active, where "no results" is the whole story.
46
+ */
47
+ emptyDescription?: string;
39
48
  emptyAction?: ReactNode;
40
49
  /** Error state */
41
50
  hasError?: boolean;
@@ -91,10 +100,15 @@ interface DataPageShellProps {
91
100
  * menu, a status strip, a second row — rather than a reimplementation.
92
101
  *
93
102
  * `null` ejects it: shell layout, states and filter row, no header.
103
+ *
104
+ * `"toolbar"` keeps the header's actions — count, view toggle, view
105
+ * settings, create, overflow menu — and drops the title block, for a list
106
+ * embedded in a tab or card whose page already has the heading. `title`
107
+ * still feeds the generated copy ("Failed to load …", the count's noun).
94
108
  */
95
- header?: ComponentType<ComponentProps<typeof PageHeader$1>> | null;
109
+ header?: ComponentType<ComponentProps<typeof PageHeader$1>> | null | "toolbar";
96
110
  }
97
- declare function DataPageShell({ title, icon, entityName, subtitle, createAction, secondaryActions, isLoading, isEmpty, emptyIcon, emptyMessage, emptyAction, hasError, errorMessage, onRetry, count, totalCount, viewMode, onViewModeChange, pageSize, onPageSizeChange, density, onDensityChange, stats, filters, hasActiveFilters, onClearFilters, filtersCollapsible, defaultFiltersOpen, filtersOpen, onFiltersOpenChange, filtersLabel, filtersHint, headerOptions, headerOptionsStorageKey, children, className, header, }: DataPageShellProps): React.JSX.Element;
111
+ declare function DataPageShell({ title, icon, entityName, subtitle, createAction, secondaryActions, isLoading, isEmpty, emptyIcon, emptyMessage, emptyDescription, emptyAction, hasError, errorMessage, onRetry, count, totalCount, viewMode, onViewModeChange, pageSize, onPageSizeChange, density, onDensityChange, stats, filters, hasActiveFilters, onClearFilters, filtersCollapsible, defaultFiltersOpen, filtersOpen, onFiltersOpenChange, filtersLabel, filtersHint, headerOptions, headerOptionsStorageKey, children, className, header, }: DataPageShellProps): React.JSX.Element;
98
112
 
99
113
  interface FilterOption {
100
114
  value: string;
@@ -207,8 +221,18 @@ interface DataGridProps<T> {
207
221
  focusedIndex?: number;
208
222
  /** Keyboard navigation: container ref for scroll management */
209
223
  keyboardContainerRef?: RefObject<HTMLDivElement | null>;
224
+ /**
225
+ * Open an item. Makes each card (and list row) one keyboard-reachable
226
+ * button: click, Enter or Space activates it, with a focus ring. Clicks on
227
+ * a link, button or input inside the card are left to that control, so a
228
+ * card can still carry its own actions. Use this instead of wrapping cards
229
+ * in a hand-rolled `div role="button"`.
230
+ */
231
+ onItemClick?: ((item: T) => void) | undefined;
232
+ /** Accessible name for an activatable item, e.g. `Open ${item.name}`. Used with `onItemClick`. */
233
+ getItemLabel?: ((item: T) => string) | undefined;
210
234
  }
211
- declare function DataGrid<T>({ items, viewMode, batchSize, gridCols, renderCard, renderTable, renderListItem, staggerAnimation, getKey, focusedIndex, keyboardContainerRef, }: DataGridProps<T>): React.JSX.Element;
235
+ declare function DataGrid<T>({ items, viewMode, batchSize, gridCols, renderCard, renderTable, renderListItem, staggerAnimation, getKey, focusedIndex, keyboardContainerRef, onItemClick, getItemLabel, }: DataGridProps<T>): React.JSX.Element;
212
236
 
213
237
  /**
214
238
  * One button in the bulk action bar.
@@ -426,9 +450,57 @@ interface ResourceFormProps {
426
450
  steps?: WizardStep[];
427
451
  /** Number of columns for the form layout. Default 1. */
428
452
  columns?: 1 | 2;
453
+ /**
454
+ * Pre-fill a create form from the page URL: a query param named like a
455
+ * field (`?status=draft`) becomes that field's starting value. Default
456
+ * `true`, as before. Pass `false` when the page's query string means
457
+ * something else — a list's own filters, say — and must not leak into the
458
+ * new record.
459
+ */
460
+ prefillFromUrl?: boolean;
461
+ /**
462
+ * Generate a placeholder ("Enter name") for fields whose `placeholder` is
463
+ * unset. Default `false`: a placeholder that restates the label adds noise,
464
+ * and reads as a filled value at a glance. A field's own `placeholder`
465
+ * always shows. (Select fields keep their "Select…" prompt either way.)
466
+ */
467
+ autoPlaceholder?: boolean;
429
468
  }
430
469
  declare function ResourceForm(props: ResourceFormProps): React.JSX.Element | null;
431
470
 
471
+ /**
472
+ * Props for {@link ConfirmDeleteDialog}.
473
+ *
474
+ * @stability Beta
475
+ */
476
+ interface ConfirmDeleteDialogProps {
477
+ open: boolean;
478
+ onOpenChange: (open: boolean) => void;
479
+ /**
480
+ * Runs the delete. The dialog stays open while it is pending, closes when
481
+ * it resolves, and shows the error's message (staying open, so the user
482
+ * can retry or cancel) when it rejects.
483
+ */
484
+ onConfirm: () => void | Promise<void>;
485
+ /** Default "Delete this record?". */
486
+ title?: string;
487
+ /** Default "This can't be undone." */
488
+ description?: ReactNode;
489
+ /** Default "Delete". */
490
+ confirmLabel?: string;
491
+ /** Default "Cancel". */
492
+ cancelLabel?: string;
493
+ }
494
+ /**
495
+ * The confirm step in front of a destructive delete: an `AlertDialog` (Radix
496
+ * owns focus trap, Escape and focus return), a pending state, and the
497
+ * failure shown in place rather than swallowed. `ResourcePage` renders it
498
+ * for `allowDelete` / `confirmDelete`, as its `deleteDialog` slot.
499
+ *
500
+ * @stability Beta
501
+ */
502
+ declare function ConfirmDeleteDialog({ open, onOpenChange, onConfirm, title, description, confirmLabel, cancelLabel, }: ConfirmDeleteDialogProps): React.JSX.Element;
503
+
432
504
  /**
433
505
  * Row-bound actions handed to `renderCard`, on top of the resource-wide
434
506
  * `ResourceActions`. `open` is per-row, which is why it lives here rather
@@ -501,6 +573,11 @@ type ResourcePageSlotMap<T extends Record<string, unknown> = Record<string, unkn
501
573
  detail: ComponentProps<typeof DetailPanel<T>>;
502
574
  /** The create / edit form. */
503
575
  form: ComponentProps<typeof ResourceForm>;
576
+ /**
577
+ * The confirm step in front of a delete. Rendered only when a delete is
578
+ * confirmed (`allowDelete`, or `onDelete` with `confirmDelete`).
579
+ */
580
+ deleteDialog: ComponentProps<typeof ConfirmDeleteDialog>;
504
581
  };
505
582
  /**
506
583
  * `slots` for {@link ResourcePage}, with one asymmetry the map cannot express:
@@ -516,6 +593,27 @@ type ResourcePageSlotMap<T extends Record<string, unknown> = Record<string, unkn
516
593
  type ResourcePageSlots<T extends Record<string, unknown> = Record<string, unknown>> = Omit<Slots<ResourcePageSlotMap<T>>, "shell"> & {
517
594
  shell?: ComponentType<ComponentProps<typeof DataPageShell>>;
518
595
  };
596
+ /**
597
+ * `slotProps` for {@link ResourcePage}: extra props merged into a part you
598
+ * are keeping, winning over ResourcePage's own.
599
+ */
600
+ type ResourcePageSlotProps<T extends Record<string, unknown> = Record<string, unknown>> = SlotPropsFor<ResourcePageSlotMap<T>>;
601
+ /**
602
+ * Copy for the built-in delete confirmation. Every field is optional;
603
+ * omitted ones fall back to `ConfirmDeleteDialog`'s defaults.
604
+ *
605
+ * @stability Beta
606
+ */
607
+ interface ResourceDeleteConfirmCopy<T> {
608
+ /** Default "Delete this record?". A function receives the row. */
609
+ title?: string | ((item: T) => string);
610
+ /** Default "This can't be undone." */
611
+ description?: ReactNode | ((item: T) => ReactNode);
612
+ /** Default "Delete". */
613
+ confirmLabel?: string;
614
+ /** Default "Cancel". */
615
+ cancelLabel?: string;
616
+ }
519
617
  interface ResourcePageProps<T extends Record<string, unknown> = Record<string, unknown>> {
520
618
  /** Client-side mode: resource name passed to useResource / DataProvider. */
521
619
  resource?: string;
@@ -533,6 +631,32 @@ interface ResourcePageProps<T extends Record<string, unknown> = Record<string, u
533
631
  * {@link onPageChange}.
534
632
  */
535
633
  total?: number;
634
+ /**
635
+ * How `resource` mode reaches rows past the first page. Ignored with
636
+ * `data` (server-data mode pages through the controlled props instead).
637
+ *
638
+ * - `"client"` (default): fetch up to `clientRowLimit` rows in one request
639
+ * and search, sort, filter and page them in the browser — header-click
640
+ * sort and instant search keep working. If the resource holds more rows
641
+ * than that, a notice under the list says how many are shown (and a
642
+ * console warning names this prop), rather than silently truncating.
643
+ * - `"server"`: fetch one page at a time. The pager under the list drives
644
+ * `useResource`'s page, the header's page size its `perPage`, and
645
+ * search / sort / status chips become provider queries (search is
646
+ * debounced), and the current page stays on screen while the next one
647
+ * loads. Give `statusFilter` explicit `options` here — derived ones can
648
+ * only see the current page's values. Search needs `searchFields` (or
649
+ * `resourceOptions.searchFields`) — the provider must be told which
650
+ * columns to match — so the search box is hidden without them. Table
651
+ * header sorting is withdrawn, as in server-data mode; the sort control
652
+ * in the filter row drives the query.
653
+ */
654
+ pagination?: "client" | "server";
655
+ /**
656
+ * `pagination="client"` only: the most rows fetched. Default 1000. An
657
+ * explicit `resourceOptions.defaultPerPage` wins.
658
+ */
659
+ clientRowLimit?: number;
536
660
  title: string;
537
661
  /** Page subtitle shown below the title. */
538
662
  subtitle?: string;
@@ -551,6 +675,17 @@ interface ResourcePageProps<T extends Record<string, unknown> = Record<string, u
551
675
  detailWidth?: "sm" | "md" | "lg";
552
676
  titleField?: string;
553
677
  subtitleField?: string;
678
+ /**
679
+ * Name a record from more than one field (`${first} ${last}`). Wins over
680
+ * `titleField` for the detail title and the card's accessible name.
681
+ */
682
+ getTitle?: (item: T) => string;
683
+ /**
684
+ * The detail peek's subtitle, computed from the row ("2 staff", "Due
685
+ * Friday"). Wins over `subtitleField`, which can only show one column
686
+ * verbatim.
687
+ */
688
+ getSubtitle?: (item: T) => string | undefined;
554
689
  renderDetail?: (item: T) => ReactNode;
555
690
  detailFields?: Array<{
556
691
  key: string;
@@ -704,8 +839,54 @@ interface ResourcePageProps<T extends Record<string, unknown> = Record<string, u
704
839
  pageSizeOptions?: number[];
705
840
  onCreate?: () => void;
706
841
  onEdit?: (item: T) => void;
707
- onDelete?: (item: T) => void;
842
+ /**
843
+ * Delete handler, shown as the detail peek's Delete button. Called
844
+ * directly, as before, unless `confirmDelete` is set — then it runs after
845
+ * the confirm dialog, which shows its error if the returned promise
846
+ * rejects, and closes the peek when it resolves.
847
+ */
848
+ onDelete?: (item: T) => void | Promise<void>;
849
+ /**
850
+ * Whether the signed-in user may change this record at all: shorthand for
851
+ * `canEdit` and `canDelete` together. When it returns false the peek
852
+ * offers neither Edit nor Delete.
853
+ *
854
+ * Precedence: `canEditItem` is checked first. A row it refuses gets no Edit
855
+ * and no Delete, whatever `canEdit` / `canDelete` say. A row it allows is
856
+ * then gated by `canEdit` (Edit) and `canDelete` (Delete) when given. Use
857
+ * `canEditItem` alone for one owner rule, or the pair for separate rules.
858
+ */
859
+ canEditItem?: (item: T) => boolean;
708
860
  createLabel?: string;
861
+ /**
862
+ * Whether this row may be edited. The peek's Edit button is hidden for a
863
+ * row that returns `false` (and the built-in edit form will not open for
864
+ * it). Omitted, every row is editable, as before. A UI gate only: the
865
+ * server must still refuse the write.
866
+ */
867
+ canEdit?: (item: T) => boolean;
868
+ /**
869
+ * Whether this row may be deleted. The peek's Delete button is hidden for
870
+ * a row that returns `false`. Omitted, every row is deletable. A UI gate
871
+ * only: the server must still refuse the write.
872
+ */
873
+ canDelete?: (item: T) => boolean;
874
+ /**
875
+ * Built-in delete for `resource` mode (ignored if `onDelete` is
876
+ * provided): the peek's Delete button opens a confirm dialog, then calls
877
+ * `useResource`'s `remove` with the row's `getKey` (when supplied) or its
878
+ * `id` — which invalidates the list — and closes the peek. A failure shows
879
+ * in the dialog.
880
+ */
881
+ allowDelete?: boolean;
882
+ /**
883
+ * Confirm before deleting. Defaults to `true` for `allowDelete` and
884
+ * `false` for `onDelete` (which keeps its old, unconfirmed behaviour).
885
+ * Pass an object to set the dialog's copy.
886
+ */
887
+ confirmDelete?: boolean | ResourceDeleteConfirmCopy<T>;
888
+ /** Called after a confirmed or built-in delete succeeds — e.g. a toast. */
889
+ onDeleted?: (item: T) => void;
709
890
  /**
710
891
  * Extra header actions beyond the create button, in one overflow menu.
711
892
  * See {@link PageHeaderAction} — the budget is one primary, the rest here.
@@ -772,6 +953,14 @@ interface ResourcePageProps<T extends Record<string, unknown> = Record<string, u
772
953
  * Omitted, the default `DataTable` render is used unchanged.
773
954
  */
774
955
  renderTable?: (items: T[]) => ReactNode;
956
+ /**
957
+ * With a custom `renderCard`: make each whole card (and list row) one
958
+ * keyboard-reachable button that opens the detail, so the card needs no
959
+ * `actions.open()` wiring and no hand-rolled `div role="button"`. Links and
960
+ * buttons inside the card keep their own clicks. Off by default, because
961
+ * an existing card that already calls `open()` would fire it twice.
962
+ */
963
+ openOnCardClick?: boolean;
775
964
  /**
776
965
  * Tailwind grid-column classes for the card grid, passed through to
777
966
  * `DataGrid`'s `gridCols` — e.g. `"grid-cols-1 sm:grid-cols-2 lg:grid-cols-3"`.
@@ -798,7 +987,21 @@ interface ResourcePageProps<T extends Record<string, unknown> = Record<string, u
798
987
  */
799
988
  keyboardNavigation?: boolean;
800
989
  toolbar?: ReactNode;
990
+ /** The empty state's action, e.g. the create button. */
801
991
  emptyState?: ReactNode;
992
+ /** The empty state's icon. */
993
+ emptyIcon?: LucideIcon;
994
+ /** The empty state's title, e.g. "No tasks yet". Default "No results found". */
995
+ emptyMessage?: string;
996
+ /** A sentence under the empty-state title — forwarded to `DataPageShell`. */
997
+ emptyDescription?: string;
998
+ /**
999
+ * `"page"` (default) renders the full header with the title. `"toolbar"`
1000
+ * keeps the header's actions (count, view toggle, settings, create) and
1001
+ * drops the title block — for a list inside a tab or card whose page
1002
+ * already has the heading. See `DataPageShell`'s `header`.
1003
+ */
1004
+ header?: "page" | "toolbar";
802
1005
  /**
803
1006
  * Imperative navigation used only when `detail="route"` (navigates to
804
1007
  * `/{resource}/{id}` on row click). Defaults to a full-page navigation.
@@ -807,7 +1010,13 @@ interface ResourcePageProps<T extends Record<string, unknown> = Record<string, u
807
1010
  */
808
1011
  onNavigate?: NavigateFn;
809
1012
  resourceOptions?: UseResourceOptions<T>;
810
- defaultView?: ViewMode;
1013
+ /**
1014
+ * The view shown until the user picks one with the view toggle. Default
1015
+ * `"table"`. Pass `{ mobile: "grid", desktop: "table" }` for cards on
1016
+ * phones and a table on desktops. Only an explicit pick is persisted, so
1017
+ * the responsive default stays responsive until the user chooses.
1018
+ */
1019
+ defaultView?: ViewModeDefault;
811
1020
  /**
812
1021
  * Persist key for density / page size via `useViewSettings`.
813
1022
  * Defaults to `resource` or a slug of `title`.
@@ -824,7 +1033,7 @@ interface ResourcePageProps<T extends Record<string, unknown> = Record<string, u
824
1033
  */
825
1034
  slots?: ResourcePageSlots<T>;
826
1035
  /** Extra props merged into a part you are KEEPING. Wins over ours. */
827
- slotProps?: SlotPropsFor<ResourcePageSlotMap<T>>;
1036
+ slotProps?: ResourcePageSlotProps<T>;
828
1037
  /** Merged onto the page shell's root. */
829
1038
  className?: string;
830
1039
  }
@@ -914,16 +1123,31 @@ interface RelationFieldProps {
914
1123
  value: string;
915
1124
  onChange: (value: string) => void;
916
1125
  disabled?: boolean;
1126
+ /** Id for the trigger, so a `<label htmlFor>` names it. */
1127
+ id?: string;
1128
+ "aria-describedby"?: string;
1129
+ "aria-invalid"?: boolean;
1130
+ "aria-required"?: boolean;
917
1131
  }
918
- declare function RelationField({ field, value, onChange, disabled, }: RelationFieldProps): React.JSX.Element;
1132
+ declare function RelationField({ field, value, onChange, disabled, id, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, }: RelationFieldProps): React.JSX.Element;
919
1133
 
920
1134
  interface FileUploadFieldProps {
921
1135
  field: ResourceFormFieldDef;
922
1136
  value: File | File[] | null;
923
1137
  onChange: (value: File | File[] | null) => void;
924
1138
  disabled?: boolean;
1139
+ /** Id for the file input, so a `<label htmlFor>` names it. */
1140
+ id?: string;
1141
+ /**
1142
+ * The label's id. The drop zone is what takes keyboard focus (the input
1143
+ * is hidden), so it is named by `aria-labelledby` rather than `htmlFor`.
1144
+ */
1145
+ "aria-labelledby"?: string;
1146
+ "aria-describedby"?: string;
1147
+ "aria-invalid"?: boolean;
1148
+ "aria-required"?: boolean;
925
1149
  }
926
- declare function FileUploadField({ field, value, onChange, disabled, }: FileUploadFieldProps): React.JSX.Element;
1150
+ declare function FileUploadField({ field, value, onChange, disabled, id, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, }: FileUploadFieldProps): React.JSX.Element;
927
1151
 
928
1152
  declare function FieldArrayRenderer({ field, control, action, watchedValues, }: {
929
1153
  field: ResourceFormFieldDef;
@@ -959,4 +1183,4 @@ declare const AppShell: typeof AppShell$1;
959
1183
  */
960
1184
  declare const PageHeader: typeof PageHeader$1;
961
1185
 
962
- export { AppShell, BadgeCell, type BulkAction, type ColumnStatus, CurrencyCell, DataBulkBar, type DataFilterConfig, DataFilters, DataGrid, DataList, type DataListProps, DataPageShell, type DataPageShellProps, DataTable, DateCell, Density, DetailPanel, type DetailPanelProps, FieldArrayRenderer, FileUploadField, FilterCombobox, type FilterOption, KanbanBoard, type KanbanBoardProps, type KanbanColumn, LinkComponent, NavigateFn, NumberCell, PageHeader, PageHeaderOptions, RelationField, ResourceActions, type ResourceCardActions, ResourceColumnDef, ResourceDetailPage, type ResourceDetailPageProps, ResourceForm, type ResourceFormProps, ResourcePage, type ResourcePageProps, type SavedViewItem, StatCard, type StatCardColor, type StatCardProps, type StatusChipOption, type StatusChipsVariant, ViewMode, WizardForm };
1186
+ export { AppShell, BadgeCell, type BulkAction, type ColumnStatus, ConfirmDeleteDialog, type ConfirmDeleteDialogProps, CurrencyCell, DataBulkBar, type DataFilterConfig, DataFilters, DataGrid, DataList, type DataListProps, DataPageShell, type DataPageShellProps, DataTable, DateCell, Density, DetailPanel, type DetailPanelProps, FieldArrayRenderer, FileUploadField, FilterCombobox, type FilterOption, KanbanBoard, type KanbanBoardProps, type KanbanColumn, LinkComponent, NavigateFn, NumberCell, PageHeader, PageHeaderOptions, RelationField, ResourceActions, type ResourceCardActions, ResourceColumnDef, type ResourceDeleteConfirmCopy, ResourceDetailPage, type ResourceDetailPageProps, ResourceForm, type ResourceFormProps, ResourcePage, type ResourcePageProps, type ResourcePageSlotMap, type ResourcePageSlotProps, type ResourcePageSlots, type SavedViewItem, SlotPropsFor, Slots, StatCard, type StatCardColor, type StatCardProps, type StatusChipOption, type StatusChipsVariant, ViewMode, ViewModeDefault, WizardForm };