@hanzo/ui 8.0.23 → 8.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +59 -0
  2. package/dist/chunk-3NG33DV2.cjs +607 -0
  3. package/dist/chunk-3NG33DV2.cjs.map +1 -0
  4. package/dist/chunk-5CZOYONP.js +343 -0
  5. package/dist/chunk-5CZOYONP.js.map +1 -0
  6. package/dist/{chunk-P2P6ST6J.js → chunk-7ZNK6JSS.js} +9 -394
  7. package/dist/chunk-7ZNK6JSS.js.map +1 -0
  8. package/dist/chunk-GVCOAJLO.js +397 -0
  9. package/dist/chunk-GVCOAJLO.js.map +1 -0
  10. package/dist/chunk-GWNNLYX4.cjs +353 -0
  11. package/dist/chunk-GWNNLYX4.cjs.map +1 -0
  12. package/dist/{chunk-NBZDIMP3.cjs → chunk-ONVERVFG.cjs} +33 -438
  13. package/dist/chunk-ONVERVFG.cjs.map +1 -0
  14. package/dist/chunk-P2QU5MN5.js +555 -0
  15. package/dist/chunk-P2QU5MN5.js.map +1 -0
  16. package/dist/chunk-WHJR5S4L.cjs +418 -0
  17. package/dist/chunk-WHJR5S4L.cjs.map +1 -0
  18. package/dist/framework/CollectionBuilder.d.ts +12 -0
  19. package/dist/framework/CollectionsBrowser.d.ts +26 -19
  20. package/dist/framework/DocTypeRecords.d.ts +31 -11
  21. package/dist/framework/MediaGrid.d.ts +42 -0
  22. package/dist/framework/RecordCards.d.ts +35 -0
  23. package/dist/framework/builder-logic.d.ts +76 -0
  24. package/dist/framework/client.d.ts +31 -44
  25. package/dist/framework/core.cjs +213 -0
  26. package/dist/framework/core.cjs.map +1 -0
  27. package/dist/framework/core.d.ts +6 -0
  28. package/dist/framework/core.js +4 -0
  29. package/dist/framework/core.js.map +1 -0
  30. package/dist/framework/data.d.ts +2 -2
  31. package/dist/framework/fields.d.ts +47 -4
  32. package/dist/framework/index.cjs +1340 -0
  33. package/dist/framework/index.cjs.map +1 -0
  34. package/dist/framework/index.d.ts +9 -23
  35. package/dist/framework/index.js +1119 -0
  36. package/dist/framework/index.js.map +1 -0
  37. package/dist/framework/media.d.ts +53 -0
  38. package/dist/framework/parts.d.ts +74 -0
  39. package/dist/framework/responsive.d.ts +41 -0
  40. package/dist/framework/types.d.ts +13 -7
  41. package/dist/product/SelectMenu.d.ts +13 -1
  42. package/dist/product/index.cjs +2061 -336
  43. package/dist/product/index.cjs.map +1 -1
  44. package/dist/product/index.js +1909 -3
  45. package/dist/product/index.js.map +1 -1
  46. package/dist/product/social/index.cjs +17 -16
  47. package/dist/product/social/index.js +3 -2
  48. package/package.json +11 -5
  49. package/dist/chunk-2BM42AZR.cjs +0 -2301
  50. package/dist/chunk-2BM42AZR.cjs.map +0 -1
  51. package/dist/chunk-7P7R7FC5.js +0 -2231
  52. package/dist/chunk-7P7R7FC5.js.map +0 -1
  53. package/dist/chunk-NBZDIMP3.cjs.map +0 -1
  54. package/dist/chunk-P2P6ST6J.js.map +0 -1
  55. package/dist/framework/Loader.d.ts +0 -4
  56. package/dist/framework.cjs +0 -757
  57. package/dist/framework.cjs.map +0 -1
  58. package/dist/framework.d.ts +0 -1
  59. package/dist/framework.js +0 -727
  60. package/dist/framework.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/product/host.tsx","../src/product/BackendState.tsx","../src/product/PageHeader.tsx","../src/product/accent.ts","../src/product/PrimaryButton.tsx","../src/product/EmptyState.tsx","../src/product/Field.tsx"],"names":["createContext","jsx","useContext","jsxs","Card","XStack","TriangleAlert","Text","Button","CreditCard","YStack","useSyncExternalStore","useEmit","labelOf","Check","ExternalLink","ArrowRight","Label","Input","textSize","TextArea","Switch","Slider"],"mappings":"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","file":"chunk-WHJR5S4L.cjs","sourcesContent":["'use client'\n\n/**\n * The host seam — the ONE place an app injects its effects into this layer.\n *\n * The product layer is presentational and host-agnostic: it never imports a\n * router or an auth module. An honest state card still has to OFFER \"Sign in\n * again\" (401) or \"Add credits\" (402), so the app provides those two effects\n * once at its root and every card below renders the right affordance. Without a\n * provider the affordance is simply not offered — nothing is faked.\n */\nimport { createContext, useContext, type ReactNode } from 'react'\n\nexport type HostActions = {\n /** Re-authenticate and return to this exact page (401). */\n signIn?: () => void\n /** Navigate to the org's top-up surface (402). */\n addCredits?: () => void\n}\n\nconst HostCtx = createContext<HostActions>({})\n\nexport const HostProvider = ({ actions, children }: { actions: HostActions; children: ReactNode }) => (\n <HostCtx.Provider value={actions}>{children}</HostCtx.Provider>\n)\n\nexport const useHost = (): HostActions => useContext(HostCtx)\n","'use client'\n\n/**\n * Honest backend states for `/v1`-backed modules — ONE place that maps a thrown\n * HTTP error to a truthful explanation.\n *\n * A 503 means the runtime/keys aren't wired on this deployment yet, a 404/405\n * means the route isn't mounted here, 401/403 means the session can't read it,\n * 402 means the org has no funded balance. Nothing is fabricated — on failure the\n * caller shows this card instead of placeholder rows/charts.\n *\n * Classification is duck-typed on `status`, so any client's error class works\n * (console's `ApiError`, commerce's `CommerceError`, …) without this layer\n * depending on one. Effects come from `useHost()`.\n */\nimport type { ReactNode } from 'react'\nimport { Button, Card, Text, XStack } from '@hanzo/gui'\nimport { TriangleAlert, CreditCard } from '@hanzogui/lucide-icons-2'\n\nimport { useHost } from './host'\n\nexport type BackendStateKind =\n | 'not-initialized'\n | 'unavailable'\n | 'not-implemented'\n | 'access'\n | 'signin'\n | 'billing'\n | 'error'\n\nexport type BackendState = { kind: BackendStateKind; message: string }\n\n/**\n * Classify a thrown `/v1` error into an honest kind + raw message.\n *\n * 401 and 403 are DISTINCT for a signed-in user: 401 = the session is expired /\n * not recognized (re-auth fixes it → `signin`), 403 = the account IS signed in\n * but this surface isn't authorized/enabled for its org (`access`). A signed-in\n * user is NEVER told to \"sign in\" for a 403 — that would be a false instruction.\n */\nexport function classifyBackend(e: unknown): BackendState {\n const status = typeof (e as { status?: unknown })?.status === 'number' ? (e as { status: number }).status : 0\n const message = e instanceof Error ? e.message : String(e)\n if (status === 503) return { kind: 'not-initialized', message }\n if (status === 404 || status === 405) return { kind: 'unavailable', message }\n // 501 — the route EXISTS and answers honestly that its body ships later (the\n // Cloudflare R2/KV/D1 Phase-2 seams). Distinct from 404 (not mounted here): the\n // capability is real and planned, so say that rather than \"not available yet\".\n if (status === 501) return { kind: 'not-implemented', message }\n if (status === 402) return { kind: 'billing', message }\n if (status === 401) return { kind: 'signin', message }\n if (status === 403) return { kind: 'access', message }\n return { kind: 'error', message }\n}\n\n/**\n * Classify a READ (GET/list) failure. Identical to `classifyBackend` EXCEPT a 402\n * is NOT a wall: listing/reading a resource is never credit-gated, so a 402 on a\n * read means \"nothing is provisioned yet\" — the caller shows its honest EMPTY\n * state (e.g. \"No buckets yet · Create one\"), never an \"add credits\" paywall\n * (that belongs only on a paid WRITE). Returns `null` for a 402 → treat as empty.\n */\nexport function classifyRead(e: unknown): BackendState | null {\n const s = classifyBackend(e)\n return s.kind === 'billing' ? null : s\n}\n\nconst TITLES: Record<BackendStateKind, string> = {\n 'not-initialized': 'Backend not initialized',\n unavailable: 'Not available on this deployment yet',\n 'not-implemented': 'Not yet available',\n access: 'Not enabled for your account',\n signin: 'Your session expired',\n billing: 'Add credits to continue',\n error: 'Could not reach the backend',\n}\n\nconst BODIES: Record<BackendStateKind, string> = {\n 'not-initialized':\n 'The /v1 route is mounted but its runtime (or the console API key it proxies to) is not configured on this deployment yet. Real data appears here once it is — no placeholder data is shown.',\n unavailable:\n 'This endpoint is not mounted at the gateway on this host yet. The view lights up automatically once the route is live.',\n // 501 — a real, planned capability whose body ships in a later phase.\n 'not-implemented':\n 'This capability is planned but not wired yet. The backend answers honestly rather than pretending it worked — nothing here is fabricated, and the view lights up automatically once it ships.',\n // 403 for a SIGNED-IN user — never \"sign in\".\n access:\n \"You're signed in, but this isn't enabled for your organization on this deployment, or it's an admin-only surface. It appears here automatically once your account has access — nothing is fabricated.\",\n // 401 — the session itself lapsed; re-auth returns to this exact page.\n signin: 'Your session has expired or isn’t recognized here. Sign in again to continue where you left off.',\n // Empty → the card shows the backend's own message (the honest \"Insufficient\n // balance. Please add credits…\" from the gateway billing gate).\n billing: '',\n error: '',\n}\n\n/** A truthful state card for a `/v1` load failure, with an optional retry + hint. */\nexport function BackendStateCard({\n state,\n onRetry,\n hint,\n}: {\n state: BackendState\n onRetry?: () => void\n hint?: ReactNode\n}) {\n const { signIn, addCredits } = useHost()\n return (\n <Card borderWidth={1} borderColor=\"$borderColor\" p=\"$4\" gap=\"$2\" maxWidth={640}>\n <XStack gap=\"$2\" items=\"center\">\n <TriangleAlert size={16} />\n <Text fontSize=\"$4\" fontWeight=\"700\">\n {TITLES[state.kind]}\n </Text>\n </XStack>\n <Text fontSize=\"$3\" color=\"$color11\">\n {BODIES[state.kind] || state.message}\n </Text>\n {hint ? (\n <Text fontSize=\"$2\" color=\"$color10\">\n {hint}\n </Text>\n ) : null}\n {state.kind === 'signin' && signIn ? (\n <Button size=\"$2\" theme=\"light\" self=\"flex-start\" onPress={signIn}>\n Sign in again\n </Button>\n ) : state.kind === 'billing' && addCredits ? (\n // 402 — the org has no funded balance. Offer a top-up CTA (and keep Retry so\n // a returning, funded caller can reload in place).\n <XStack gap=\"$2\" items=\"center\" flexWrap=\"wrap\">\n <Button size=\"$2\" theme=\"light\" self=\"flex-start\" icon={<CreditCard size={14} />} onPress={addCredits}>\n Add credits\n </Button>\n {onRetry ? (\n <Button size=\"$2\" self=\"flex-start\" onPress={onRetry}>\n Retry\n </Button>\n ) : null}\n </XStack>\n ) : onRetry ? (\n <Button size=\"$2\" self=\"flex-start\" onPress={onRetry}>\n Retry\n </Button>\n ) : null}\n </Card>\n )\n}\n","'use client'\n\nimport type { ReactNode } from 'react'\nimport { Text, XStack, YStack } from '@hanzo/gui'\n\n/** Section title + optional subtitle and right-aligned actions. */\nexport function PageHeader({\n title,\n subtitle,\n actions,\n}: {\n title: string\n subtitle?: string\n actions?: ReactNode\n}) {\n return (\n // flexWrap lets the actions drop below the title on narrow screens; flex={1}\n // minW={0} lets the title column shrink so a long subtitle WRAPS instead of\n // running off the viewport (a flex child defaults to min-width:auto = content\n // width, which would overflow on mobile).\n <XStack justify=\"space-between\" items=\"flex-start\" gap=\"$3\" flexWrap=\"wrap\">\n <YStack gap=\"$1\" flex={1} minW={200}>\n <Text fontSize=\"$7\" fontWeight=\"500\" letterSpacing={-0.4}>\n {title}\n </Text>\n {subtitle ? (\n <Text fontSize=\"$3\" color=\"$color11\">\n {subtitle}\n </Text>\n ) : null}\n </YStack>\n {/* Actions take a FULL line below the title on phones (width 100%) and WRAP —\n so a header with many buttons (e.g. Tracker's Projects/Refresh/List/Board/\n New issue/Delete) never runs off-screen and clips the last controls. At $md+\n they collapse back to a right-aligned inline row beside the title. */}\n {actions ? (\n <XStack gap=\"$2\" items=\"center\" flexWrap=\"wrap\" width=\"100%\" $md={{ width: 'auto', justify: 'flex-end' }}>\n {actions}\n </XStack>\n ) : null}\n </XStack>\n )\n}\n","'use client'\n\n/**\n * Org accent theme — the ONE place that turns an organization's saved brand color\n * (`themeData.colorPrimary`, when `themeData.isEnabled`) into the LIVE console accent.\n *\n * The console is monochrome by default; an org opts into a custom accent in Settings →\n * Branding. Saving persists the color on the org record (that half already works). This\n * module is the missing APPLY half: a tiny external store holds the resolved accent\n * (hex + a readable contrast, or null); `setOrgAccent` updates it on load AND on save\n * (one mechanism, two callers), and `useAccent()` lets the genuine accent surfaces (the\n * primary button, the active tabs, the active nav item) read it and recolor with real\n * `@hanzo/gui` props — reverting cleanly to the default monochrome when the org disables\n * its theme (or the hex is invalid). React-prop driven (Tamagui's own styling path), NOT\n * a CSS class — Tamagui does not forward `className` to a Button's DOM node.\n *\n * Pure + SSR-safe: the resolve/contrast helpers are pure (unit-tested); the store starts\n * from a stable default snapshot so the server render and first client paint agree.\n */\nimport { useSyncExternalStore } from 'react'\n\n/** An org's persisted theme block (mirrors IAM `themeData`; only the fields we apply). */\nexport type OrgThemeData = {\n colorPrimary?: string\n isEnabled?: boolean\n}\n\n/** The live accent — a resolved hex + a readable text color for content on it, or null. */\nexport type Accent = { accent: string | null; contrast: string }\n\n/** True for a 3- or 6-digit CSS hex color (`#RGB` / `#RRGGBB`). */\nexport function isHexColor(v: string | undefined | null): boolean {\n return typeof v === 'string' && /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(v.trim())\n}\n\n/**\n * The accent hex to apply for an org, or `null` when no custom accent should be used —\n * i.e. the theme is disabled, missing, or the color is not a valid hex. PURE.\n */\nexport function resolveAccent(theme: OrgThemeData | null | undefined): string | null {\n if (!theme || !theme.isEnabled) return null\n const hex = (theme.colorPrimary ?? '').trim()\n return isHexColor(hex) ? hex : null\n}\n\n/** Expand `#abc` → `#aabbcc`; pass a 6-digit hex through (lowercased). */\nfunction expandHex(hex: string): string {\n const h = hex.trim().toLowerCase()\n if (h.length === 4) return `#${h[1]}${h[1]}${h[2]}${h[2]}${h[3]}${h[3]}`\n return h\n}\n\n/**\n * A readable text color (`#000000` / `#ffffff`) for content placed ON the accent,\n * chosen by the accent's relative luminance (WCAG-style). PURE. Falls back to white\n * for a non-hex input (never throws).\n */\nexport function contrastText(hex: string): '#000000' | '#ffffff' {\n if (!isHexColor(hex)) return '#ffffff'\n const h = expandHex(hex)\n const r = parseInt(h.slice(1, 3), 16) / 255\n const g = parseInt(h.slice(3, 5), 16) / 255\n const b = parseInt(h.slice(5, 7), 16) / 255\n const lin = (c: number) => (c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4)\n const luminance = 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b)\n return luminance > 0.5 ? '#000000' : '#ffffff'\n}\n\n/** Resolve a theme block to the `Accent` value it should apply (accent+contrast or null). PURE. */\nexport function accentFor(theme: OrgThemeData | null | undefined): Accent {\n const hex = resolveAccent(theme)\n return { accent: hex, contrast: hex ? contrastText(hex) : '#ffffff' }\n}\n\n// ── The live accent store (an external store React subscribes to) ─────────────\n\n/** The default (no custom accent) — a STABLE reference so SSR + first paint agree. */\nconst DEFAULT: Accent = { accent: null, contrast: '#ffffff' }\n\nlet current: Accent = DEFAULT\nconst listeners = new Set<() => void>()\n\n/**\n * Set the live accent from an org theme block. Called on load (from the fetched org) AND\n * immediately on save — the SINGLE mechanism, so the accent survives reload and updates\n * without one. A no-op when the resolved accent is unchanged (keeps the snapshot stable).\n */\nexport function setOrgAccent(theme: OrgThemeData | null | undefined): void {\n const next = accentFor(theme)\n if (next.accent === current.accent) return\n current = next.accent ? next : DEFAULT\n for (const l of listeners) l()\n}\n\nfunction subscribe(cb: () => void): () => void {\n listeners.add(cb)\n return () => {\n listeners.delete(cb)\n }\n}\n\n/** The live accent (subscribes to the store). Returns the default until an org theme is applied. */\nexport function useAccent(): Accent {\n return useSyncExternalStore(\n subscribe,\n () => current,\n () => DEFAULT,\n )\n}\n","'use client'\n\n/**\n * Primary button — the one white, high-emphasis action for the console.\n *\n * Monochrome brand: `theme=\"light\"` flips the button to the light theme inside\n * the dark console, giving a white fill with a near-black label and icon — no\n * hue accent. Use it for the single primary action in a view (sign in, save,\n * get started). Secondary and destructive actions use the default neutral\n * `Button`.\n *\n * When an org enables a custom brand color, this — the console's one primary\n * action — recolors to the org accent (inline bg + the color prop the label/icon\n * inherit), reading the live accent from `useAccent()`; with no custom theme it\n * stays the default white. A caller's own `style`/`color` still win (spread last).\n */\nimport type { ComponentProps } from 'react'\nimport { Button } from '@hanzo/gui'\n\nimport { useAccent } from './accent'\nimport { labelOf, useEmit } from './instrument'\n\nexport function PrimaryButton({ onPress, ...rest }: ComponentProps<typeof Button>) {\n const { accent, contrast } = useAccent()\n const track = useEmit()\n // DESTRUCTURE the caller's handler out first. Wrapping it while still reading it\n // off a rebound `props` makes the wrapper call ITSELF — an unbounded recursion\n // that a real browser click turns into \"Maximum call stack size exceeded\".\n const press = (e: unknown) => {\n // The label IS the identity of a primary action — no app has to name it.\n track({\n component: 'PrimaryButton',\n action: 'click',\n id: labelOf(rest.children) ?? rest['aria-label'],\n })\n ;(onPress as ((e: unknown) => void) | undefined)?.(e)\n }\n const handler = press as ComponentProps<typeof Button>['onPress']\n // Accent set → a filled accent button (bg + readable text, via inline style Tamagui\n // forwards to the DOM). No accent → the default monochrome white (theme=\"light\").\n if (accent) {\n return <Button style={{ backgroundColor: accent, color: contrast, borderColor: accent }} onPress={handler} {...rest} />\n }\n return <Button theme=\"light\" onPress={handler} {...rest} />\n}\n","'use client'\n\n/**\n * Rich empty state — the ONE way the console turns a real, honest \"nothing here\n * yet\" into a helpful onboarding moment (DigitalOcean / Vercel class) instead of\n * a dead screen.\n *\n * It is NOT a fabricated-data fallback: callers render it only when a load\n * genuinely succeeded with zero rows (or a resource has no surface yet). It shows\n * the product icon, a one-line \"what this is\", a couple of guidance bullets, a\n * single primary call-to-action (Deploy / Create / Connect …) and an optional\n * secondary link (Docs / CLI). Orthogonal to `BackendStateCard` (failures) and\n * `DataTable`'s thin inline empty (in-table) — this is the full-bleed first-run\n * surface a module shows in place of its table.\n */\nimport type { ReactElement, ReactNode } from 'react'\nimport { Button, Card, Text, XStack, YStack } from '@hanzo/gui'\nimport { ArrowRight, Check, ExternalLink } from '@hanzogui/lucide-icons-2'\n\nimport type { IconLike } from './color'\nimport { PrimaryButton } from './PrimaryButton'\n\n/** A single call-to-action — primary (filled) or a secondary/external link. */\nexport type EmptyAction = {\n label: string\n /** In-app navigation / handler. */\n onPress?: () => void\n /** External URL — opens in a new tab with an external-link affordance. */\n href?: string\n icon?: ReactElement\n}\n\n/** Open an external URL in a new tab (no-opener), guarded for SSR. */\nfunction openHref(href: string): void {\n if (typeof window !== 'undefined') window.open(href, '_blank', 'noopener')\n}\n\nimport { useEmit } from './instrument'\n\nexport function EmptyState({\n icon: Icon,\n title,\n description,\n bullets,\n primary,\n secondary,\n}: {\n /** Product/section icon — any lucide/Gui icon component (size + optional color). */\n icon: IconLike\n title: string\n description: string\n /** Optional \"here's how to get started\" guidance bullets. */\n bullets?: string[]\n /** The single high-emphasis action (Deploy / Create / Connect). */\n primary?: EmptyAction\n /** An optional lower-emphasis action (Docs / CLI / Learn more). */\n secondary?: EmptyAction\n}) {\n const track = useEmit()\n return (\n <Card\n borderWidth={1}\n borderColor=\"$borderColor\"\n borderStyle=\"dashed\"\n p=\"$6\"\n gap=\"$4\"\n items=\"center\"\n maxWidth={640}\n self=\"center\"\n width=\"100%\"\n >\n <YStack\n width={48}\n height={48}\n items=\"center\"\n justify=\"center\"\n rounded=\"$4\"\n bg=\"$color3\"\n >\n <Icon size={24} />\n </YStack>\n\n <YStack gap=\"$2\" items=\"center\" maxW={480}>\n <Text fontSize=\"$6\" fontWeight=\"500\" text=\"center\" letterSpacing={-0.3}>\n {title}\n </Text>\n <Text fontSize=\"$3\" color=\"$color11\" text=\"center\">\n {description}\n </Text>\n </YStack>\n\n {bullets && bullets.length ? (\n <YStack gap=\"$2\" self=\"stretch\" maxW={420} mx=\"auto\">\n {bullets.map((b) => (\n <XStack key={b} gap=\"$2\" items=\"flex-start\">\n <YStack pt=\"$1\">\n <Check size={14} color=\"$color10\" />\n </YStack>\n <Text fontSize=\"$3\" color=\"$color11\" flex={1}>\n {b}\n </Text>\n </XStack>\n ))}\n </YStack>\n ) : null}\n\n {primary || secondary ? (\n <XStack gap=\"$2\" items=\"center\" flexWrap=\"wrap\" justify=\"center\">\n {primary ? (\n <PrimaryButton\n icon={primary.icon}\n iconAfter={primary.href ? <ExternalLink size={15} /> : <ArrowRight size={15} />}\n onPress={() => {\n track({ component: 'EmptyState', action: 'click', id: title, value: primary.label })\n if (primary.href) openHref(primary.href)\n else primary.onPress?.()\n }}\n >\n {primary.label}\n </PrimaryButton>\n ) : null}\n {secondary ? (\n <Button\n chromeless\n icon={secondary.icon}\n iconAfter={secondary.href ? <ExternalLink size={14} /> : undefined}\n onPress={() => {\n track({ component: 'EmptyState', action: 'click', id: title, value: secondary.label })\n if (secondary.href) openHref(secondary.href)\n else secondary.onPress?.()\n }}\n >\n {secondary.label}\n </Button>\n ) : null}\n </XStack>\n ) : null}\n </Card>\n )\n}\n","'use client'\n\n/**\n * Form field primitives — labeled rows over Hanzo GUI inputs.\n *\n * One way to render a labeled control. Every editor field goes through these, so\n * spacing, disabled styling, and the label column stay consistent. Style props\n * use the v5 shorthand set (p/px/items/justify/...).\n */\nimport { createContext, useContext, type CSSProperties, type ReactNode } from 'react'\nimport {\n Input,\n Label,\n Switch,\n TextArea,\n Slider,\n Text,\n XStack,\n YStack,\n} from '@hanzo/gui'\n\nimport { textSize, useEmit, type UiEvent } from './instrument'\n\n// The label the caller ALREADY typed on the FieldRow becomes the analytics name —\n// so an app never labels a field twice and no field reports as \"unnamed\".\nconst FieldNameContext = createContext<string | undefined>(undefined)\n\n/** Every Field* control reports `{component, action:'change', id:<row label>}`;\n * the VALUE is only ever a scalar or a text LENGTH, never free text. */\nfunction useFieldTrack(component: string): (e: Omit<UiEvent, 'component'>) => void {\n const track = useEmit()\n const name = useContext(FieldNameContext)\n return (e) => track({ component, id: name, ...e })\n}\n\n/**\n * Labeled row. STACKED on phones (label full-width above a full-width control) so a\n * form inside a SlideOver stays usable and never clips its labels; a fixed 180px\n * label column + flexing control at $md+ (the two-column desktop/tablet layout).\n */\nexport function FieldRow({ label, children }: { label: string; children: ReactNode }) {\n return (\n <FieldNameContext.Provider value={label}>\n <XStack gap=\"$3\" items=\"flex-start\" flexWrap=\"wrap\">\n <Label width=\"100%\" pt=\"$2\" color=\"$color11\" fontSize=\"$3\" $md={{ width: 180 }}>\n {label}\n </Label>\n <YStack flex={1} minW={0} $md={{ minW: 240 }}>\n {children}\n </YStack>\n </XStack>\n </FieldNameContext.Provider>\n )\n}\n\nexport function FieldText({\n value,\n onChange,\n disabled,\n secure,\n placeholder,\n}: {\n value: string\n onChange: (v: string) => void\n disabled?: boolean\n secure?: boolean\n placeholder?: string\n}) {\n const track = useFieldTrack('FieldText')\n return (\n <Input\n value={value}\n onChangeText={(v: string) => {\n track({ action: 'change', value: secure ? 0 : textSize(v) })\n onChange(v)\n }}\n disabled={disabled}\n secureTextEntry={secure}\n placeholder={placeholder}\n autoCapitalize=\"none\"\n />\n )\n}\n\nexport function FieldTextArea({\n value,\n onChange,\n disabled,\n rows = 6,\n}: {\n value: string\n onChange: (v: string) => void\n disabled?: boolean\n rows?: number\n}) {\n const track = useFieldTrack('FieldTextArea')\n return (\n <TextArea\n value={value}\n onChangeText={(v: string) => {\n track({ action: 'change', value: textSize(v) })\n onChange(v)\n }}\n disabled={disabled}\n numberOfLines={rows}\n />\n )\n}\n\nexport function FieldSwitch({\n checked,\n onChange,\n disabled,\n}: {\n checked: boolean\n onChange: (v: boolean) => void\n disabled?: boolean\n}) {\n const track = useFieldTrack('FieldSwitch')\n return (\n <Switch\n checked={checked}\n onCheckedChange={(v: boolean) => {\n track({ action: 'change', value: v })\n onChange(v)\n }}\n disabled={disabled}\n size=\"$3\"\n >\n <Switch.Thumb />\n </Switch>\n )\n}\n\n/**\n * A labeled dropdown.\n *\n * Renders a REAL native `<select>` on web (and the OS-native picker on mobile),\n * NOT the `@hanzo/gui` `<Select native>` — that primitive is broken in gui 7.3.0:\n * `native` emits bare `<option>` elements with NO `<select>` wrapper, so every\n * dropdown across the console (region/size pickers, all edit forms) rendered as a\n * non-interactive flat list of option text that buried the surrounding controls.\n * A native `<select>` is compact, keyboard/ARIA-accessible, gives the native\n * mobile picker (best small-screen UX), and is themed via the Tamagui CSS custom\n * properties (`--background`/`--color12`/`--borderColor`) so it tracks light/dark.\n */\nconst CHEVRON = `data:image/svg+xml,${encodeURIComponent(\n '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#9a9a9a\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m6 9 6 6 6-6\"/></svg>',\n)}`\n\nfunction selectStyle(disabled?: boolean): CSSProperties {\n return {\n width: '100%',\n boxSizing: 'border-box',\n appearance: 'none',\n WebkitAppearance: 'none',\n MozAppearance: 'none',\n background: `var(--background) url(\"${CHEVRON}\") no-repeat right 10px center`,\n color: 'var(--color12)',\n border: '1px solid var(--borderColor)',\n borderRadius: 9,\n padding: '9px 34px 9px 12px',\n fontSize: 14,\n lineHeight: '20px',\n fontFamily: 'inherit',\n height: 40,\n outline: 'none',\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n }\n}\n\nconst OPTION_STYLE: CSSProperties = { background: 'var(--color2)', color: 'var(--color12)' }\n\nexport function FieldSelect({\n value,\n options,\n onChange,\n disabled,\n placeholder = 'Select…',\n}: {\n value: string\n options: string[]\n onChange: (v: string) => void\n disabled?: boolean\n placeholder?: string\n}) {\n // When the current value isn't one of the options (empty/unset), show the\n // placeholder as a selected-but-disabled row so the control reads honestly.\n const track = useFieldTrack('FieldSelect')\n const showPlaceholder = value === '' || !options.includes(value)\n return (\n <select\n value={showPlaceholder ? '' : value}\n onChange={(e) => {\n track({ action: 'change', value: e.currentTarget.value })\n onChange(e.currentTarget.value)\n }}\n disabled={disabled}\n aria-label={placeholder}\n style={selectStyle(disabled)}\n >\n {showPlaceholder ? (\n <option value=\"\" disabled style={OPTION_STYLE}>\n {placeholder}\n </option>\n ) : null}\n {options.map((opt) => (\n <option key={opt} value={opt} style={OPTION_STYLE}>\n {opt}\n </option>\n ))}\n </select>\n )\n}\n\nexport function FieldSlider({\n value,\n min,\n max,\n step,\n onChange,\n disabled,\n}: {\n value: number\n min: number\n max: number\n step: number\n onChange: (v: number) => void\n disabled?: boolean\n}) {\n const track = useFieldTrack('FieldSlider')\n return (\n <XStack gap=\"$3\" items=\"center\">\n <Text width={56} fontSize=\"$3\" color=\"$color11\">\n {value}\n </Text>\n <Slider\n flex={1}\n min={min}\n max={max}\n step={step}\n value={[value]}\n onValueChange={(v) => {\n track({ action: 'change', value: v[0] ?? min })\n onChange(v[0] ?? min)\n }}\n disabled={disabled}\n >\n <Slider.Track>\n <Slider.TrackActive />\n </Slider.Track>\n <Slider.Thumb index={0} circular />\n </Slider>\n </XStack>\n )\n}\n"]}
@@ -0,0 +1,12 @@
1
+ import type { FrameworkClient } from './client';
2
+ import type { DocType } from './types';
3
+ export interface CollectionBuilderProps {
4
+ client: FrameworkClient;
5
+ module: string;
6
+ /** Editing an existing collection's schema (name locked), else creating a new one. */
7
+ existing?: DocType;
8
+ /** Called with the saved collection's name after create/update. */
9
+ onSaved: (name: string) => void;
10
+ onCancel: () => void;
11
+ }
12
+ export declare function CollectionBuilder({ client, module, existing, onSaved, onCancel }: CollectionBuilderProps): import("react/jsx-runtime").JSX.Element;
@@ -1,20 +1,26 @@
1
1
  /**
2
2
  * CollectionsBrowser — the app-lane home: the DocTypes of a `module` (a CMS
3
- * "collection" IS a framework DocType tagged with the lane's module). It lists
4
- * them as cards, offers a first-run "Set up" that installs the lane's fixtures
5
- * (POST /v1/framework/modules/:module/install), and a "New collection" that opens
6
- * the dynamic content-type builder (name + typed fields, on-page). Everything is
7
- * per-org and honest — an org with the lane not yet installed sees the setup CTA,
8
- * never a fabricated collection.
3
+ * "collection", an ERP document type, a Helpdesk ticket type are all just a
4
+ * framework DocType tagged with that lane's module). It lists them, offers a
5
+ * first-run "Set up" that installs the lane's fixtures
6
+ * (`POST /v1/framework/modules/:module/install`), and a "New collection" that
7
+ * opens the on-page content-type builder.
9
8
  *
10
- * This is generic over `module`, so CMS (`cms`), ERP (`erp`), and Helpdesk
11
- * (`help`) all reuse it the ONE collections home for every lane.
9
+ * Generic over the lane by construction: `module`, `label` and the first-run copy
10
+ * are props, and where copy is unset it is DERIVED FROM THE LANE rather than
11
+ * borrowed from one — the old default was the CMS's words, so an ERP org read
12
+ * about Pages and Posts. CMS, ERP, Helpdesk and anything later render this same
13
+ * component.
14
+ *
15
+ * Honest by construction — an org with the lane not yet installed sees the setup
16
+ * call to action, never a fabricated collection.
12
17
  */
13
18
  import { type ReactNode } from 'react';
19
+ import type { IconLike } from '../product/color';
14
20
  import type { FrameworkClient } from './client';
15
21
  export interface CollectionsBrowserProps {
16
22
  client: FrameworkClient;
17
- /** The app lane: 'cms' | 'erp' | 'help' | … */
23
+ /** The app lane: 'cms' | 'erp' | 'help' | 'crm' | … */
18
24
  module: string;
19
25
  /** Human label for the lane (e.g. "Content"). */
20
26
  label: string;
@@ -22,23 +28,24 @@ export interface CollectionsBrowserProps {
22
28
  /** Open a collection's records. */
23
29
  onOpen: (doctype: string) => void;
24
30
  /**
25
- * Lane-appropriate copy for the first-run (pre-install) empty state. Optional and
26
- * defaulted to the CMS wording, so this component stays generic over the lane: a
27
- * CMS caller renders identically, while ERP/Help pass their own description +
28
- * bullets. Additive only no behavior/permission/proxy change.
31
+ * First-run (pre-install) copy. The defect this fixes was not "there is a
32
+ * default" it was that the default was ONE LANE'S copy (the CMS's), so an ERP
33
+ * org read about Pages and Posts. Unset now falls back to copy derived from the
34
+ * lane's own `label`, which is true for every lane; a host that has better words
35
+ * passes them.
29
36
  */
30
37
  setupDescription?: string;
31
38
  setupBullets?: string[];
32
39
  /**
33
- * Schema AUTHORING is a separate concern from schema USE, so it is injected
34
- * rather than imported: a host that can define collections (the console's
35
- * CollectionBuilder) supplies this; a host that only consumes them (a site
36
- * shell) omits it and the "New collection" affordance is simply not offered.
37
- * Nothing is faked either way.
40
+ * Schema AUTHORING injected. The library ships a real `CollectionBuilder`, so a
41
+ * host that omits this still gets one; a host that wants its own (or wants the
42
+ * affordance withheld from a read-only surface) supplies or suppresses it.
38
43
  */
39
44
  renderBuilder?: (p: {
40
45
  onSaved: (doctype: string) => void;
41
46
  onCancel: () => void;
42
47
  }) => ReactNode;
48
+ /** Lane icon (defaults to the generic collection glyph). */
49
+ icon?: IconLike;
43
50
  }
44
- export declare function CollectionsBrowser({ client, module, label, subtitle, onOpen, setupDescription, setupBullets, renderBuilder }: CollectionsBrowserProps): import("react/jsx-runtime").JSX.Element;
51
+ export declare function CollectionsBrowser({ client, module, label, subtitle, onOpen, setupDescription, setupBullets, renderBuilder, icon, }: CollectionsBrowserProps): import("react/jsx-runtime").JSX.Element;
@@ -1,11 +1,23 @@
1
1
  /**
2
- * DocTypeRecords — the Twenty-grade records surface for ONE framework DocType,
3
- * driven ENTIRELY by DocType metadata. It loads the schema (→ @hanzo/data
2
+ * DocTypeRecords — the documents surface for ONE framework DocType, driven
3
+ * ENTIRELY by DocType metadata. It loads the schema (→ @hanzo/data
4
4
  * `FieldDefinition[]` via `docTypeToFields`), the documents, and each relation's
5
- * candidate records, then renders through @hanzo/data's `RecordsView` the shared
6
- * table board view with filter/sort/group, inline cell editing, and a detail
7
- * hand-off. Every field type shows the right Display/Input with ZERO per-doctype
8
- * code, so a CMS Page, an ERP Invoice, or a Helpdesk Ticket all render here.
5
+ * candidate records, then renders them in the shape the CONTAINER can actually
6
+ * hold. Every field type shows the right Display/Input with ZERO per-doctype code,
7
+ * so a CMS Page, an ERP Invoice, or a Helpdesk Ticket all render here.
8
+ *
9
+ * MOBILE FIRST — the layout is a decision, not a media query afterthought:
10
+ * · phone → `RecordCards`, a stacked card per record. A table of 12 columns on
11
+ * a 390px screen is a sideways drag; a card is a read.
12
+ * · desktop → @hanzo/data's `RecordsView` (table ⇆ board, filter/sort/group,
13
+ * inline cell editing).
14
+ * The first paint is ALWAYS the card list (see `responsive.ts`), and the table is
15
+ * the enhancement applied once the box measures wide enough.
16
+ *
17
+ * The table also finally honors the DocType's OWN column projection
18
+ * (`listHiddenFields` → `inListView`): a Sales Order that declares 4 list columns
19
+ * out of 12 fields now renders 4. That projection was implemented and tested but
20
+ * never passed to a view — which is exactly why the grid was unusable narrow.
9
21
  *
10
22
  * Persistence is REAL: an inline cell edit sends the FULL record (the engine
11
23
  * validates the whole document on update) via `savePayload`, and reflects the
@@ -15,6 +27,7 @@
15
27
  import { type ReactNode } from 'react';
16
28
  import type { FrameworkClient } from './client';
17
29
  import type { DocType, FrameworkDoc } from './types';
30
+ import type { MediaUploader } from './media';
18
31
  export interface DocTypeRecordsProps {
19
32
  client: FrameworkClient;
20
33
  /** DocType name to render. */
@@ -27,10 +40,15 @@ export interface DocTypeRecordsProps {
27
40
  project?: string;
28
41
  title?: ReactNode;
29
42
  /**
30
- * An asset library (a DocType whose required field is an Attach) is better
31
- * shown as a gallery than a table, but uploading bytes needs the HOST's object
32
- * store — not something this layer can know. So the gallery is injected: a host
33
- * with a DAM supplies it, a host without one gets the honest generic table.
43
+ * Object storage for a media/asset collection. Without it a media DocType still
44
+ * renders as records (honest) the DAM gallery needs a real place to put bytes,
45
+ * and this layer refuses to invent one.
46
+ */
47
+ media?: MediaUploader;
48
+ /**
49
+ * A host that would rather draw the asset gallery itself supplies this; it wins
50
+ * over the built-in `MediaGrid`. Both paths need somewhere real to put bytes, so
51
+ * a host with neither gets the honest generic table — nothing is faked.
34
52
  */
35
53
  renderMedia?: (p: {
36
54
  dt: DocType;
@@ -39,5 +57,7 @@ export interface DocTypeRecordsProps {
39
57
  onChanged: () => void;
40
58
  toolbarExtra: ReactNode;
41
59
  }) => ReactNode;
60
+ /** How many documents to fetch (the engine caps its own page). */
61
+ limit?: number;
42
62
  }
43
- export declare function DocTypeRecords({ client, doctype, onOpen, onCreate, project, title, renderMedia }: DocTypeRecordsProps): import("react/jsx-runtime").JSX.Element;
63
+ export declare function DocTypeRecords({ client, doctype, onOpen, onCreate, project, title, media, renderMedia, limit, }: DocTypeRecordsProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * MediaGrid — the DAM (media library) for a media DocType (an Attach-backed
3
+ * collection). A REAL asset manager: drag/drop or pick files → they upload through
4
+ * the injected `MediaUploader` (the host's own per-org object store) → a document
5
+ * is created from the DocType's OWN schema → the grid shows a live thumbnail
6
+ * (presigned on view, since object URLs are short-lived). Delete removes the
7
+ * document AND the object.
8
+ *
9
+ * The document body is `mediaDocPayload(dt, facts)` — metadata-driven. This used
10
+ * to write a hardcoded `{title, file, mime, size, width, height}`, which is the
11
+ * CMS Media fixture's schema: a lane whose media type labels its rows `caption`
12
+ * got untitled rows, silently, because the engine drops unknown keys. Now the
13
+ * filename lands on the DocType's declared title field and each fact is written
14
+ * only if that DocType has somewhere to put it.
15
+ *
16
+ * MOBILE FIRST: tiles are flex-basis, not a fixed 200px — two-up on a phone,
17
+ * filling whatever the box gives on a desktop. The upload control is a full-width
18
+ * tap target on a phone, and the per-tile delete is a 44px control rather than a
19
+ * 13px glyph.
20
+ *
21
+ * Web-first by nature (file picker + drag/drop + `<img>` thumbnails), like the
22
+ * other file-touching components in this layer.
23
+ */
24
+ import { type ReactNode } from 'react';
25
+ import type { FrameworkClient } from './client';
26
+ import type { DocType, FrameworkDoc } from './types';
27
+ import { type MediaUploader } from './media';
28
+ export interface MediaGridProps {
29
+ client: FrameworkClient;
30
+ dt: DocType;
31
+ docs: FrameworkDoc[];
32
+ /** The host's object store, bound to this lane's bucket. */
33
+ media: MediaUploader;
34
+ /** Open a media document's detail (edit alt text, replace, etc.). */
35
+ onOpen: (name: string) => void;
36
+ /** Re-fetch the media list after an upload/delete. */
37
+ onChanged: () => void;
38
+ toolbarExtra?: ReactNode;
39
+ /** Accepted file types for the picker. */
40
+ accept?: string;
41
+ }
42
+ export declare function MediaGrid({ client, dt, docs, media, onOpen, onChanged, toolbarExtra, accept, }: MediaGridProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * RecordCards — the PHONE view of a DocType's documents: one stacked card per
3
+ * record instead of a horizontally-scrolling grid.
4
+ *
5
+ * A 12-column ERP Sales Order in a table on a 390px screen is a wall you drag
6
+ * sideways to read one row; the same record as a card is a title plus its declared
7
+ * list columns as label → value pairs, reading top to bottom in the width you
8
+ * have. Nothing is hidden that the table would have shown — the card renders the
9
+ * DocType's OWN `inListView` projection (`cardFields`), and the full record is one
10
+ * tap away.
11
+ *
12
+ * The whole card is the tap target (far past the 44px floor), values render
13
+ * through the SAME `FieldDisplay` router the table uses, so every field type
14
+ * behaves identically in both, and search is the pure `searchRecords` — no second
15
+ * filtering model.
16
+ */
17
+ import { type ReactNode } from 'react';
18
+ import { type FieldDefinition } from '@hanzo/data';
19
+ export interface RecordCardsProps {
20
+ /** The field shown as the card's headline (the DocType's titleField). */
21
+ titleField?: string;
22
+ /** All fields (used for search); `cardFields` decides what a card shows. */
23
+ fields: FieldDefinition[];
24
+ /** The subset rendered as label → value rows on each card. */
25
+ cardFields: FieldDefinition[];
26
+ records: Record<string, unknown>[];
27
+ loading?: boolean;
28
+ empty?: ReactNode;
29
+ onOpen?: (record: Record<string, unknown>) => void;
30
+ onCreate?: () => void;
31
+ createLabel?: string;
32
+ /** Extra toolbar control (e.g. Refresh). */
33
+ toolbarExtra?: ReactNode;
34
+ }
35
+ export declare function RecordCards({ titleField, fields, cardFields, records, loading, empty, onOpen, onCreate, createLabel, toolbarExtra, }: RecordCardsProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Pure logic for the content-type builder — the model + validation + DocType
3
+ * projection for defining a collection's fields on-page. No React, no I/O, so it
4
+ * is unit-tested in plain Node. The builder UI is a thin shell over these.
5
+ *
6
+ * A "collection" is a framework DocType tagged with the lane module; a "field" is
7
+ * a DocField. The builder offers exactly the engine's fieldtypes (mapped to
8
+ * friendly labels), lets the author add/remove/reorder/require fields, and
9
+ * projects the whole thing to a valid `DocType` create body. Names are slugified
10
+ * to field-name rules so a save never 400s on a bad identifier.
11
+ */
12
+ import type { DocType, DocField, Fieldtype } from './types';
13
+ /**
14
+ * The builder's field-type vocabulary — every framework fieldtype, with a friendly
15
+ * label + hint. This is the ONE list the "add field" type picker renders, so the
16
+ * builder always offers exactly what the engine accepts (no drift). Order is the
17
+ * author-facing order (common types first).
18
+ */
19
+ export declare const BUILDER_FIELD_TYPES: {
20
+ type: Fieldtype;
21
+ label: string;
22
+ hint: string;
23
+ }[];
24
+ /** The friendly label for a fieldtype (falls back to the raw type). */
25
+ export declare function fieldTypeLabel(t: Fieldtype): string;
26
+ /** True when a fieldtype needs `options` (Select choices, Link/Table target). */
27
+ export declare function fieldNeedsOptions(t: Fieldtype): boolean;
28
+ /** One editable row in the builder — a superset of DocField the UI mutates. */
29
+ export interface BuilderField {
30
+ /** Stable client key for React (not persisted). */
31
+ key: string;
32
+ /** The field's label (author-facing); the fieldname is derived from it. */
33
+ label: string;
34
+ type: Fieldtype;
35
+ required: boolean;
36
+ inListView: boolean;
37
+ /** Select → newline-joined choices; Link/Table → target collection name. */
38
+ options: string;
39
+ }
40
+ /** A fresh, unique client key. */
41
+ export declare function newKey(): string;
42
+ /** A blank field row of a given type. */
43
+ export declare function blankField(type?: Fieldtype): BuilderField;
44
+ /**
45
+ * The default starter fields for a NEW content collection: a required Title, a URL
46
+ * Slug (the autoname key), a rich Body, and a Draft/Published Status. The author
47
+ * edits from here. Mirrors the cloud CMS seed so a hand-built collection matches a
48
+ * seeded one.
49
+ */
50
+ export declare function starterFields(): BuilderField[];
51
+ /** An existing DocType → editable builder rows (drops engine-managed fields). */
52
+ export declare function fieldsFromDocType(dt: DocType): BuilderField[];
53
+ /** The fieldname derived from a label (slug with underscores; identifier-safe). */
54
+ export declare function fieldNameFromLabel(label: string): string;
55
+ export interface BuilderValidation {
56
+ ok: boolean;
57
+ /** Per-row error keyed by BuilderField.key. */
58
+ fieldErrors: Record<string, string>;
59
+ /** A form-level error (name / no-fields). */
60
+ formError: string | null;
61
+ }
62
+ /**
63
+ * Validate the whole builder: the collection name, at least one field, unique
64
+ * non-empty field labels/names, and options present where required. Pure.
65
+ */
66
+ export declare function validateBuilder(name: string, fields: BuilderField[]): BuilderValidation;
67
+ /** A builder row → a framework DocField (drops UI-only bits, coerces options). */
68
+ export declare function toDocField(f: BuilderField): DocField;
69
+ /** Move a row within the ordered field list (a no-op at either end). Pure. */
70
+ export declare function moveField(fields: BuilderField[], key: string, dir: -1 | 1): BuilderField[];
71
+ /**
72
+ * The builder → a `DocType` create body. Auto-derives `autoname`/`titleField` from
73
+ * conventional fields when present (a `slug` field → `field:slug` naming; a `title`
74
+ * field → the title). Tags the module so the collection joins the lane.
75
+ */
76
+ export declare function toDocType(name: string, module: string, fields: BuilderField[]): DocType;
@@ -1,36 +1,39 @@
1
1
  /**
2
- * FrameworkApi — the ONE generic client for the Hanzo Framework DocType engine
3
- * (cloud `clients/framework`, live at /v1/framework/*). It is metadata-driven and
4
- * doctype-agnostic: the SAME `records` CRUD serves a CMS Page, an ERP Invoice, or
5
- * a Helpdesk Ticket — an app lane is just a `module` filter over `doctypes`.
2
+ * The generic client for the Hanzo Framework DocType engine (`/v1/framework/*`).
3
+ * It is metadata-driven and doctype-agnostic: the SAME `records` CRUD serves a CMS
4
+ * Page, an ERP Invoice, or a Helpdesk Ticket an app lane is just a `module`
5
+ * filter over `doctypes`.
6
6
  *
7
- * TRANSPORT IS INJECTED. This layer knows the framework's *shape*, never how a
8
- * given host reaches it: the console proxies through its own user-bearer BFF, the
9
- * site shells (erp/crm/cms/help.<brand>) call the API origin with the IAM bearer
10
- * directly. Both hand a `Transport` to `createFrameworkClient` and get the SAME
11
- * client, so there is exactly one definition of the wire surface.
7
+ * THE HOST OWNS TRANSPORT. This module never picks an origin, a credential, or a
8
+ * proxy: it takes a `FrameworkTransport` and builds paths RELATIVE to the framework
9
+ * root (`doctypes`, `Article/hello`, `modules/cms/install`). The console maps those
10
+ * onto its own user-bearer `/v1/framework/...` proxy; a native app or a test maps
11
+ * them somewhere else. One client, any host.
12
12
  *
13
- * Names (DocType + document) are used verbatim in the URL path, so a host keeps
14
- * them slug-style (no spaces/`%`) and this encodes each segment. Payloads are read
15
- * defensively from `{ data }` (or a bare array), so a shape drift degrades a list
16
- * rather than throwing.
13
+ * Names (DocType + document) go verbatim into the path, so they are
14
+ * `encodeURIComponent`-escaped here and kept slug-style on create (`slugify`).
15
+ * Payloads are read defensively from `{ data }` (or a bare array), so a shape drift
16
+ * degrades a list rather than throwing.
17
17
  */
18
- import type { DocType, FrameworkDoc, ListQuery, ModuleInfo, InstallResult } from './types';
18
+ import type { DocType, FrameworkDoc, InstallResult, ListQuery, ModuleInfo } from './types';
19
19
  /**
20
- * The four verbs the framework surface needs, relative to the caller's
21
- * `/v1/framework` root. A host implements this over whatever credential path it
22
- * already owns — this layer never touches auth, cookies, or origins.
20
+ * The ONE seam a host injects. Paths are relative to the framework root and never
21
+ * start with a slash; the host prefixes its own base (and adds credentials).
23
22
  */
24
- export interface Transport {
25
- get: <T>(path: string) => Promise<T>;
26
- post: <T>(path: string, body?: unknown) => Promise<T>;
27
- put: <T>(path: string, body?: unknown) => Promise<T>;
23
+ export interface FrameworkTransport {
24
+ get: (path: string) => Promise<unknown>;
25
+ post: (path: string, body?: unknown) => Promise<unknown>;
26
+ put: (path: string, body?: unknown) => Promise<unknown>;
28
27
  del: (path: string) => Promise<void>;
29
28
  }
30
- /** Build the generic document list querystring from a typed ListQuery. */
29
+ /** The name 8.0.24 shipped for the same seam. One concept, one shape, two spellings. */
30
+ export type Transport = FrameworkTransport;
31
+ /** Pull the first array under a common envelope key (the engine uses `data`). */
32
+ export declare function rows(payload: unknown): Record<string, unknown>[];
33
+ /** Build the generic document list querystring from a typed ListQuery. Pure. */
31
34
  export declare function listQuery(q?: ListQuery): string;
32
- /** Build the framework client over a host's transport. */
33
- export declare function createFrameworkClient(t: Transport): {
35
+ /** The engine surface, as consumed by every DocType view. */
36
+ export interface FrameworkClient {
34
37
  /** The DocType registry (schemas) — the collection definitions of every app lane. */
35
38
  doctypes: {
36
39
  list: () => Promise<DocType[]>;
@@ -64,24 +67,8 @@ export declare function createFrameworkClient(t: Transport): {
64
67
  assign: (user: string, role: string) => Promise<void>;
65
68
  revoke: (user: string, role: string) => Promise<void>;
66
69
  };
67
- };
68
- export type FrameworkClient = ReturnType<typeof createFrameworkClient>;
69
- /** An HTTP failure carrying the status, so `classifyBackend` can tell 401 from 403 from 404. */
70
- export declare class FrameworkHttpError extends Error {
71
- readonly status: number;
72
- constructor(status: number, message: string);
70
+ /** Engine health/reach probe — `GET /summary`. */
71
+ summary: () => Promise<Record<string, unknown>>;
73
72
  }
74
- export interface FetchTransportOptions {
75
- /** Absolute `/v1/framework` root, e.g. `https://api.hanzo.ai/v1/framework`. */
76
- baseUrl: string;
77
- /** Called per request — a bearer token, or null when signed out. */
78
- token?: () => string | null | undefined;
79
- /** Injectable for tests; defaults to the global `fetch`. */
80
- fetchImpl?: typeof fetch;
81
- }
82
- /**
83
- * The bearer transport every site shell uses: the caller's IAM access token on
84
- * the Authorization header, straight to the API origin. No cookie, no second
85
- * credential path — the engine resolves the org from the token's owner claim.
86
- */
87
- export declare function fetchTransport(opts: FetchTransportOptions): Transport;
73
+ /** Bind the framework surface to a host's transport. */
74
+ export declare function createFrameworkClient(t: FrameworkTransport): FrameworkClient;