@alfakit/core 0.2.0 → 0.2.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"realtime.js","names":[],"sources":["../src/realtime.ts"],"sourcesContent":["import type { NotificationSchema } from './schema/notifications'\nimport type { ToastPosition } from './schema/panel'\nimport type { RealtimeSchema } from './schema/realtime'\n\n/** Broadcast type of a toast pushed with `Notification::broadcast()`. */\nexport const BROADCAST_NOTIFICATION = 'alfakit.notification'\n\n/** Broadcast type sent by `sendToDatabase(..., isEventDispatched: true)`. */\nexport const DATABASE_NOTIFICATIONS_SENT = 'alfakit.database-notifications.sent'\n\n/** Window event the notifications menu listens to, to reload itself. */\nexport const DATABASE_NOTIFICATIONS_EVENT = 'alfakit:database-notifications-sent'\n\ntype NotificationCallback = (payload: Record<string, unknown>) => void\ntype EventCallback = (payload: Record<string, unknown>) => void\n\n/** The part of a Laravel Echo private channel Alfakit uses. */\nexport interface EchoChannelLike {\n notification(callback: NotificationCallback): unknown\n stopListeningForNotification?(callback: NotificationCallback): unknown\n listen?(event: string, callback: EventCallback): unknown\n stopListening?(event: string, callback?: EventCallback): unknown\n}\n\n/** The part of a Laravel Echo presence channel Alfakit uses (`Echo.join()`). */\nexport interface EchoPresenceChannelLike extends EchoChannelLike {\n here(callback: (members: unknown[]) => void): unknown\n joining(callback: (member: unknown) => void): unknown\n leaving(callback: (member: unknown) => void): unknown\n whisper?(event: string, data: Record<string, unknown>): unknown\n listenForWhisper?(event: string, callback: (data: Record<string, unknown>) => void): unknown\n}\n\n/** The part of Laravel Echo Alfakit uses (`window.Echo`). */\nexport interface EchoLike {\n private(channel: string): EchoChannelLike\n join?(channel: string): EchoPresenceChannelLike\n leave?(channel: string): void\n socketId?(): string | undefined\n}\n\n/**\n * The app's Laravel Echo instance, when one is set up as `window.Echo` (or was created by\n * `resolveEcho()`). Always null on the server, so SSR never touches it.\n */\nexport function getEcho(): EchoLike | null {\n if (typeof window === 'undefined') {\n return null\n }\n\n const echo = (window as unknown as { Echo?: unknown }).Echo\n\n if (echo && typeof (echo as EchoLike).private === 'function') {\n return echo as EchoLike\n }\n\n return createdEcho\n}\n\n// ---------------------------------------------------------------- Connection\n\n/** The settings `configureRealtime()` takes (`PanelSchema.realtime`). */\nexport type RealtimeConfig = RealtimeSchema\n\n/** Options for creating Echo: the panel's Echo options plus its auth endpoint and CSRF token. */\nexport type EchoConnectionOptions = Record<string, unknown> & {\n authEndpoint?: string\n csrfToken?: string\n}\n\n/**\n * Creates the Echo instance when the app has no `window.Echo` (`createAlfakit({ echo })`),\n * e.g. `(options) => new Echo({ ...options, broadcaster: 'reverb', Pusher })`.\n */\nexport type EchoFactory = (\n options: EchoConnectionOptions,\n) => EchoLike | null | undefined | Promise<EchoLike | null | undefined>\n\ntype ModuleLoader = (specifier: string) => Promise<Record<string, unknown>>\n\nlet realtimeConfig: (RealtimeConfig & { csrfToken?: string }) | null = null\nlet echoFactory: EchoFactory | null = null\nlet createdEcho: EchoLike | null = null\nlet pendingEcho: Promise<EchoLike | null> | null = null\n\n// The packages are optional peers the app installs: the specifier is a variable so bundlers\n// don't require them, and a missing package just means \"no Echo\".\nlet loadModule: ModuleLoader = (specifier) =>\n import(/* @vite-ignore */ /* webpackIgnore: true */ specifier) as Promise<Record<string, unknown>>\n\n/**\n * Set the panel's realtime settings (renderers call this with `panel.realtime` and the\n * CSRF token) and, optionally, an Echo instance or factory from `createAlfakit({ echo })`.\n */\nexport function configureRealtime(\n config: RealtimeConfig | null | undefined,\n options: { echo?: EchoLike | EchoFactory | null; csrfToken?: string } = {},\n): void {\n realtimeConfig = config ? { ...config, csrfToken: options.csrfToken } : null\n\n if (options.echo !== undefined) {\n if (typeof options.echo === 'function') {\n echoFactory = options.echo\n } else {\n createdEcho = options.echo\n echoFactory = null\n }\n }\n}\n\n/** The settings given to `configureRealtime()`. */\nexport function realtimeSettings(): RealtimeConfig | null {\n return realtimeConfig\n}\n\n/** Whether `Panel::realtime()` is on for this page. */\nexport function isRealtimeEnabled(): boolean {\n return Boolean(realtimeConfig?.live)\n}\n\n/**\n * The Echo instance to use: `window.Echo`, one made by the `createAlfakit({ echo })`\n * factory, or (with `config('alfakit.broadcasting.echo')`) one made from `laravel-echo`\n * and `pusher-js` if the app installed them. Null when none is available or on the server.\n */\nexport function resolveEcho(): Promise<EchoLike | null> {\n const existing = getEcho()\n\n if (existing || typeof window === 'undefined') {\n return Promise.resolve(existing)\n }\n\n pendingEcho ??= createEcho().then((echo) => {\n createdEcho = echo ?? null\n\n // Try again on the next call when nothing could be created.\n if (!createdEcho) pendingEcho = null\n\n return createdEcho\n })\n\n return pendingEcho\n}\n\nasync function createEcho(): Promise<EchoLike | null> {\n const config = realtimeConfig\n const options: EchoConnectionOptions = {\n ...(config?.echo ?? {}),\n ...(config?.authEndpoint ? { authEndpoint: config.authEndpoint } : {}),\n ...(config?.csrfToken ? { csrfToken: config.csrfToken } : {}),\n }\n\n try {\n if (echoFactory) {\n return (await echoFactory(options)) ?? null\n }\n\n if (!config?.live || !config.echo) {\n return null\n }\n\n const [echoModule, pusherModule] = await Promise.all([\n loadModule('laravel-echo'),\n loadModule('pusher-js'),\n ])\n const Echo = (echoModule.default ?? echoModule.Echo) as\n (new (options: Record<string, unknown>) => EchoLike) | undefined\n const Pusher = pusherModule.default ?? pusherModule.Pusher\n\n if (typeof Echo !== 'function') {\n return null\n }\n\n ;(window as unknown as { Pusher?: unknown }).Pusher ??= Pusher\n\n return new Echo({ broadcaster: 'reverb', ...options, Pusher })\n } catch {\n return null\n }\n}\n\n/** The socket id of the current Echo connection, sent as `X-Socket-ID` so Laravel's `toOthers()` skips this tab. */\nexport function currentSocketId(): string | undefined {\n try {\n return getEcho()?.socketId?.() || undefined\n } catch {\n return undefined\n }\n}\n\n/** `X-Socket-ID` when connected (merged into the requests of the renderers). */\nexport function realtimeHeaders(): Record<string, string> {\n const id = currentSocketId()\n\n return id ? { 'X-Socket-ID': id } : {}\n}\n\n/** Forget the connection and settings (tests). */\nexport function resetRealtime(options: { loader?: ModuleLoader } = {}): void {\n realtimeConfig = null\n echoFactory = null\n createdEcho = null\n pendingEcho = null\n channelRefs.clear()\n\n if (options.loader) loadModule = options.loader\n}\n\n// ---------------------------------------------------------------- Shared channels\n\nconst channelRefs = new Map<string, number>()\n\n/**\n * Use a channel of `echo` until the returned release function is called. Echo shares a\n * channel object per name, so it is left only when its last user releases it (a record's\n * presence avatars and conflict banner share its presence channel).\n */\nexport function acquireChannel<T extends 'private' | 'presence'>(\n echo: EchoLike,\n kind: T,\n name: string,\n): {\n channel: T extends 'presence' ? EchoPresenceChannelLike : EchoChannelLike\n release: () => void\n} | null {\n const channel = kind === 'presence' ? echo.join?.(name) : echo.private(name)\n\n if (!channel) return null\n\n const key = name\n channelRefs.set(key, (channelRefs.get(key) ?? 0) + 1)\n let released = false\n\n return {\n channel: channel as T extends 'presence' ? EchoPresenceChannelLike : EchoChannelLike,\n release: () => {\n if (released) return\n released = true\n\n const count = (channelRefs.get(key) ?? 1) - 1\n\n if (count <= 0) {\n channelRefs.delete(key)\n echo.leave?.(name)\n } else {\n channelRefs.set(key, count)\n }\n },\n }\n}\n\n/**\n * Run `connect` with Echo as soon as it is available: right away with `window.Echo`, else\n * once `resolveEcho()` settles. `onUnavailable` runs when there is no Echo. Returns the\n * cleanup, which also cancels a pending connection.\n */\nexport function withEcho(\n connect: (echo: EchoLike) => (() => void) | void,\n options: { echo?: EchoLike | null; onUnavailable?: () => (() => void) | void } = {},\n): () => void {\n let cleanup: (() => void) | void\n let active = true\n\n const start = (echo: EchoLike | null) => {\n if (!active) return\n\n cleanup = echo ? connect(echo) : options.onUnavailable?.()\n }\n\n if (options.echo !== undefined) {\n start(options.echo)\n } else {\n const echo = getEcho()\n\n if (echo || typeof window === 'undefined') {\n start(echo)\n } else {\n void resolveEcho().then(start)\n }\n }\n\n return () => {\n active = false\n cleanup?.()\n }\n}\n\n// ---------------------------------------------------------------- Record changes\n\n/** Echo event of `RecordChanged` (`broadcastAs()` is `alfakit.record`). */\nexport const RECORD_CHANGED_EVENT = '.alfakit.record'\n\n/** A record was created, updated, deleted or restored. Keys only: refetch to see the data. */\nexport interface RecordChange {\n event: 'created' | 'updated' | 'deleted' | 'restored'\n /** The model class with `-` for `\\\\`. */\n model: string\n key: string | number\n /** The user who changed it, when known. */\n by?: string | number | null\n /** Milliseconds since the epoch. */\n at?: number\n}\n\nfunction asRecordChange(payload: Record<string, unknown>): RecordChange | null {\n const event = payload.event\n const key = payload.key\n\n if (\n (event !== 'created' && event !== 'updated' && event !== 'deleted' && event !== 'restored') ||\n (typeof key !== 'string' && typeof key !== 'number')\n ) {\n return null\n }\n\n return {\n event,\n key,\n model: typeof payload.model === 'string' ? payload.model : '',\n by: typeof payload.by === 'string' || typeof payload.by === 'number' ? payload.by : null,\n at: typeof payload.at === 'number' ? payload.at : Date.now(),\n }\n}\n\n/**\n * Listen for record changes on a channel (`private` for models, `presence` for a record).\n * Returns the cleanup (a no-op without Echo).\n */\nexport function listenForRecordChanges(\n channelName: string,\n callback: (change: RecordChange) => void,\n options: { kind?: 'private' | 'presence'; echo?: EchoLike | null } = {},\n): () => void {\n return withEcho(\n (echo) => {\n const acquired = acquireChannel(echo, options.kind ?? 'private', channelName)\n\n if (!acquired) return\n\n const listener: EventCallback = (payload) => {\n const change = asRecordChange(payload)\n\n if (change) callback(change)\n }\n\n acquired.channel.listen?.(RECORD_CHANGED_EVENT, listener)\n\n return () => {\n acquired.channel.stopListening?.(RECORD_CHANGED_EVENT, listener)\n acquired.release()\n }\n },\n { echo: options.echo },\n )\n}\n\n// ---------------------------------------------------------------- Notifications\n\nexport interface RealtimeSubscriptionOptions {\n /** The user's private channel (`PanelSchema.realtime.channel`). */\n channel: string\n /** Defaults to `window.Echo`, or the Echo `resolveEcho()` creates. */\n echo?: EchoLike | null\n /** A broadcast toast arrived. */\n onNotification: (notification: NotificationSchema) => void\n /** A database notification was stored: reload the bell. */\n onDatabaseNotificationsSent?: () => void\n}\n\n/**\n * Listen for the user's broadcast notifications. Returns the unsubscribe function (a no-op\n * when Echo is missing). Each toast is delivered once, even if Echo reconnects or the\n * subscription is made twice (React strict mode).\n */\nexport function subscribeToNotifications(options: RealtimeSubscriptionOptions): () => void {\n if (!options.channel) {\n return () => {}\n }\n\n const seen = new Set<string>()\n\n return withEcho(\n (echo) => {\n let active = true\n\n const callback: NotificationCallback = (payload) => {\n if (!active) return\n\n if (payload.type === DATABASE_NOTIFICATIONS_SENT) {\n options.onDatabaseNotificationsSent?.()\n\n return\n }\n\n if (payload.format !== 'alfakit') return\n\n const id = typeof payload.id === 'string' ? payload.id : ''\n\n if (id !== '' && seen.has(id)) return\n if (id !== '') seen.add(id)\n\n const { type: _type, ...notification } = payload\n\n options.onNotification(notification as unknown as NotificationSchema)\n }\n\n const channel = echo.private(options.channel)\n channel.notification(callback)\n\n return () => {\n active = false\n channel.stopListeningForNotification?.(callback)\n }\n },\n { echo: options.echo },\n )\n}\n\n/** Tell mounted notification menus to reload (`DATABASE_NOTIFICATIONS_EVENT`). */\nexport function announceDatabaseNotificationsSent(): void {\n if (typeof window !== 'undefined') {\n window.dispatchEvent(new CustomEvent(DATABASE_NOTIFICATIONS_EVENT))\n }\n}\n\n/** Run `callback` whenever a database notification is announced. Returns the cleanup. */\nexport function onDatabaseNotificationsSent(callback: () => void): () => void {\n if (typeof window === 'undefined') {\n return () => {}\n }\n\n window.addEventListener(DATABASE_NOTIFICATIONS_EVENT, callback)\n\n return () => window.removeEventListener(DATABASE_NOTIFICATIONS_EVENT, callback)\n}\n\n/**\n * Offset of the panel's toaster: toasts at the top start below the sticky top bar (3.5rem\n * high) instead of covering the breadcrumbs, search and switchers, on phones too (where they\n * span the screen). The other sides keep sonner's defaults.\n */\nexport const PANEL_TOAST_OFFSET: { top: string } = { top: '4.5rem' }\n\nexport type SonnerPosition =\n 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'\n\n/**\n * The direction overlays anchored to a screen edge (the toaster, the schema inspector's\n * sheet) are placed by: the panel's reading direction, except in starter kit mode, where\n * the host app owns `<html dir>` and its layout (the kit's sidebar stays on the left in a\n * right-to-left panel language). There they take the document's side, so \"top end\" never\n * lands on the kit's sidebar, while their content keeps the panel's direction (W-D-03).\n */\nexport function overlayPlacementDirection(\n direction: 'ltr' | 'rtl',\n starterKit: boolean | undefined,\n): 'ltr' | 'rtl' {\n if (starterKit !== true) return direction\n if (typeof document === 'undefined') return 'ltr'\n\n return document.documentElement.dir.toLowerCase() === 'rtl' ? 'rtl' : 'ltr'\n}\n\n/**\n * The toaster position for the panel's `toastPosition()` (logical: `start`/`end`) in the\n * current reading direction. Defaults to the top end.\n */\nexport function resolveToastPosition(\n position: ToastPosition | null | undefined,\n direction: 'ltr' | 'rtl',\n): SonnerPosition {\n const [vertical, horizontal] = (position ?? 'top-end').split('-') as [\n 'top' | 'bottom',\n 'start' | 'center' | 'end',\n ]\n\n if (horizontal === 'center') {\n return `${vertical}-center`\n }\n\n const isLeft = (horizontal === 'start') === (direction === 'ltr')\n\n return `${vertical}-${isLeft ? 'left' : 'right'}`\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"realtime.js","names":[],"sources":["../src/realtime.ts"],"sourcesContent":["import type { NotificationSchema } from './schema/notifications'\nimport type { ToastPosition } from './schema/panel'\nimport type { RealtimeSchema } from './schema/realtime'\n\n/** Broadcast type of a toast pushed with `Notification::broadcast()`. */\nexport const BROADCAST_NOTIFICATION = 'alfakit.notification'\n\n/** Broadcast type sent by `sendToDatabase(..., isEventDispatched: true)`. */\nexport const DATABASE_NOTIFICATIONS_SENT = 'alfakit.database-notifications.sent'\n\n/** Window event the notifications menu listens to, to reload itself. */\nexport const DATABASE_NOTIFICATIONS_EVENT = 'alfakit:database-notifications-sent'\n\ntype NotificationCallback = (payload: Record<string, unknown>) => void\ntype EventCallback = (payload: Record<string, unknown>) => void\n\n/** The part of a Laravel Echo private channel Alfakit uses. */\nexport interface EchoChannelLike {\n notification(callback: NotificationCallback): unknown\n stopListeningForNotification?(callback: NotificationCallback): unknown\n listen?(event: string, callback: EventCallback): unknown\n stopListening?(event: string, callback?: EventCallback): unknown\n}\n\n/** The part of a Laravel Echo presence channel Alfakit uses (`Echo.join()`). */\nexport interface EchoPresenceChannelLike extends EchoChannelLike {\n here(callback: (members: unknown[]) => void): unknown\n joining(callback: (member: unknown) => void): unknown\n leaving(callback: (member: unknown) => void): unknown\n whisper?(event: string, data: Record<string, unknown>): unknown\n listenForWhisper?(event: string, callback: (data: Record<string, unknown>) => void): unknown\n}\n\n/** The part of Laravel Echo Alfakit uses (`window.Echo`). */\nexport interface EchoLike {\n private(channel: string): EchoChannelLike\n join?(channel: string): EchoPresenceChannelLike\n leave?(channel: string): void\n socketId?(): string | undefined\n}\n\n/**\n * The app's Laravel Echo instance, when one is set up as `window.Echo` (or was created by\n * `resolveEcho()`). Always null on the server, so SSR never touches it.\n */\nexport function getEcho(): EchoLike | null {\n if (typeof window === 'undefined') {\n return null\n }\n\n const echo = (window as unknown as { Echo?: unknown }).Echo\n\n if (echo && typeof (echo as EchoLike).private === 'function') {\n return echo as EchoLike\n }\n\n return createdEcho\n}\n\n// ---------------------------------------------------------------- Connection\n\n/** The settings `configureRealtime()` takes (`PanelSchema.realtime`). */\nexport type RealtimeConfig = RealtimeSchema\n\n/** Options for creating Echo: the panel's Echo options plus its auth endpoint and CSRF token. */\nexport type EchoConnectionOptions = Record<string, unknown> & {\n authEndpoint?: string\n csrfToken?: string\n}\n\n/**\n * Creates the Echo instance when the app has no `window.Echo` (`createAlfakit({ echo })`),\n * e.g. `(options) => new Echo({ ...options, broadcaster: 'reverb', Pusher })`.\n */\nexport type EchoFactory = (\n options: EchoConnectionOptions,\n) => EchoLike | null | undefined | Promise<EchoLike | null | undefined>\n\ntype ModuleLoader = (specifier: string) => Promise<Record<string, unknown>>\n\nlet realtimeConfig: (RealtimeConfig & { csrfToken?: string }) | null = null\nlet echoFactory: EchoFactory | null = null\nlet createdEcho: EchoLike | null = null\nlet pendingEcho: Promise<EchoLike | null> | null = null\n\n// The packages are optional peers the app installs: the specifier is a variable so bundlers\n// don't require them, and a missing package just means \"no Echo\".\nlet loadModule: ModuleLoader = (specifier) =>\n import(/* @vite-ignore */ /* webpackIgnore: true */ specifier) as Promise<Record<string, unknown>>\n\n/**\n * Set the panel's realtime settings (renderers call this with `panel.realtime` and the\n * CSRF token) and, optionally, an Echo instance or factory from `createAlfakit({ echo })`.\n */\nexport function configureRealtime(\n config: RealtimeConfig | null | undefined,\n options: { echo?: EchoLike | EchoFactory | null; csrfToken?: string } = {},\n): void {\n realtimeConfig = config ? { ...config, csrfToken: options.csrfToken } : null\n\n if (options.echo !== undefined) {\n if (typeof options.echo === 'function') {\n echoFactory = options.echo\n } else {\n createdEcho = options.echo\n echoFactory = null\n }\n }\n}\n\n/** The settings given to `configureRealtime()`. */\nexport function realtimeSettings(): RealtimeConfig | null {\n return realtimeConfig\n}\n\n/** Whether `Panel::realtime()` is on for this page. */\nexport function isRealtimeEnabled(): boolean {\n return Boolean(realtimeConfig?.live)\n}\n\n/**\n * The Echo instance to use: `window.Echo`, one made by the `createAlfakit({ echo })`\n * factory, or (with `config('alfakit.broadcasting.echo')`) one made from `laravel-echo`\n * and `pusher-js` if the app installed them. Null when none is available or on the server.\n */\nexport function resolveEcho(): Promise<EchoLike | null> {\n const existing = getEcho()\n\n if (existing || typeof window === 'undefined') {\n return Promise.resolve(existing)\n }\n\n pendingEcho ??= createEcho().then((echo) => {\n createdEcho = echo ?? null\n\n // Try again on the next call when nothing could be created.\n if (!createdEcho) pendingEcho = null\n\n return createdEcho\n })\n\n return pendingEcho\n}\n\nasync function createEcho(): Promise<EchoLike | null> {\n const config = realtimeConfig\n const options: EchoConnectionOptions = {\n ...(config?.echo ?? {}),\n ...(config?.authEndpoint ? { authEndpoint: config.authEndpoint } : {}),\n ...(config?.csrfToken ? { csrfToken: config.csrfToken } : {}),\n }\n\n try {\n if (echoFactory) {\n return (await echoFactory(options)) ?? null\n }\n\n if (!config?.live || !config.echo) {\n return null\n }\n\n const [echoModule, pusherModule] = await Promise.all([\n loadModule('laravel-echo'),\n loadModule('pusher-js'),\n ])\n const Echo = (echoModule.default ?? echoModule.Echo) as\n (new (options: Record<string, unknown>) => EchoLike) | undefined\n const Pusher = pusherModule.default ?? pusherModule.Pusher\n\n if (typeof Echo !== 'function') {\n return null\n }\n\n ;(window as unknown as { Pusher?: unknown }).Pusher ??= Pusher\n\n return new Echo({ broadcaster: 'reverb', ...options, Pusher })\n } catch {\n return null\n }\n}\n\n/** The socket id of the current Echo connection, sent as `X-Socket-ID` so Laravel's `toOthers()` skips this tab. */\nexport function currentSocketId(): string | undefined {\n try {\n return getEcho()?.socketId?.() || undefined\n } catch {\n return undefined\n }\n}\n\n/** `X-Socket-ID` when connected (merged into the requests of the renderers). */\nexport function realtimeHeaders(): Record<string, string> {\n const id = currentSocketId()\n\n return id ? { 'X-Socket-ID': id } : {}\n}\n\n/** Forget the connection and settings (tests). */\nexport function resetRealtime(options: { loader?: ModuleLoader } = {}): void {\n realtimeConfig = null\n echoFactory = null\n createdEcho = null\n pendingEcho = null\n channelRefs.clear()\n\n if (options.loader) loadModule = options.loader\n}\n\n// ---------------------------------------------------------------- Shared channels\n\nconst channelRefs = new Map<string, number>()\n\n/**\n * Use a channel of `echo` until the returned release function is called. Echo shares a\n * channel object per name, so it is left only when its last user releases it (a record's\n * presence avatars and conflict banner share its presence channel).\n */\nexport function acquireChannel<T extends 'private' | 'presence'>(\n echo: EchoLike,\n kind: T,\n name: string,\n): {\n channel: T extends 'presence' ? EchoPresenceChannelLike : EchoChannelLike\n release: () => void\n} | null {\n const channel = kind === 'presence' ? echo.join?.(name) : echo.private(name)\n\n if (!channel) return null\n\n const key = name\n channelRefs.set(key, (channelRefs.get(key) ?? 0) + 1)\n let released = false\n\n return {\n channel: channel as T extends 'presence' ? EchoPresenceChannelLike : EchoChannelLike,\n release: () => {\n if (released) return\n released = true\n\n const count = (channelRefs.get(key) ?? 1) - 1\n\n if (count <= 0) {\n channelRefs.delete(key)\n echo.leave?.(name)\n } else {\n channelRefs.set(key, count)\n }\n },\n }\n}\n\n/**\n * Run `connect` with Echo as soon as it is available: right away with `window.Echo`, else\n * once `resolveEcho()` settles. `onUnavailable` runs when there is no Echo. Returns the\n * cleanup, which also cancels a pending connection.\n */\nexport function withEcho(\n connect: (echo: EchoLike) => (() => void) | void,\n options: { echo?: EchoLike | null; onUnavailable?: () => (() => void) | void } = {},\n): () => void {\n let cleanup: (() => void) | void\n let active = true\n\n const start = (echo: EchoLike | null) => {\n if (!active) return\n\n cleanup = echo ? connect(echo) : options.onUnavailable?.()\n }\n\n if (options.echo !== undefined) {\n start(options.echo)\n } else {\n const echo = getEcho()\n\n if (echo || typeof window === 'undefined') {\n start(echo)\n } else {\n void resolveEcho().then(start)\n }\n }\n\n return () => {\n active = false\n cleanup?.()\n }\n}\n\n// ---------------------------------------------------------------- Record changes\n\n/** Echo event of `RecordChanged` (`broadcastAs()` is `alfakit.record`). */\nexport const RECORD_CHANGED_EVENT = '.alfakit.record'\n\n/** A record was created, updated, deleted or restored. Keys only: refetch to see the data. */\nexport interface RecordChange {\n event: 'created' | 'updated' | 'deleted' | 'restored'\n /** The model class with `-` for `\\\\`. */\n model: string\n key: string | number\n /** The user who changed it, when known. */\n by?: string | number | null\n /** Milliseconds since the epoch. */\n at?: number\n}\n\nfunction asRecordChange(payload: Record<string, unknown>): RecordChange | null {\n const event = payload.event\n const key = payload.key\n\n if (\n (event !== 'created' && event !== 'updated' && event !== 'deleted' && event !== 'restored') ||\n (typeof key !== 'string' && typeof key !== 'number')\n ) {\n return null\n }\n\n return {\n event,\n key,\n model: typeof payload.model === 'string' ? payload.model : '',\n by: typeof payload.by === 'string' || typeof payload.by === 'number' ? payload.by : null,\n at: typeof payload.at === 'number' ? payload.at : Date.now(),\n }\n}\n\n/**\n * Listen for record changes on a channel (`private` for models, `presence` for a record).\n * Returns the cleanup (a no-op without Echo).\n */\nexport function listenForRecordChanges(\n channelName: string,\n callback: (change: RecordChange) => void,\n options: { kind?: 'private' | 'presence'; echo?: EchoLike | null } = {},\n): () => void {\n return withEcho(\n (echo) => {\n const acquired = acquireChannel(echo, options.kind ?? 'private', channelName)\n\n if (!acquired) return\n\n const listener: EventCallback = (payload) => {\n const change = asRecordChange(payload)\n\n if (change) callback(change)\n }\n\n acquired.channel.listen?.(RECORD_CHANGED_EVENT, listener)\n\n return () => {\n acquired.channel.stopListening?.(RECORD_CHANGED_EVENT, listener)\n acquired.release()\n }\n },\n { echo: options.echo },\n )\n}\n\n// ---------------------------------------------------------------- Notifications\n\nexport interface RealtimeSubscriptionOptions {\n /** The user's private channel (`PanelSchema.realtime.channel`). */\n channel: string\n /** Defaults to `window.Echo`, or the Echo `resolveEcho()` creates. */\n echo?: EchoLike | null\n /** A broadcast toast arrived. */\n onNotification: (notification: NotificationSchema) => void\n /** A database notification was stored: reload the bell. */\n onDatabaseNotificationsSent?: () => void\n}\n\n/**\n * Listen for the user's broadcast notifications. Returns the unsubscribe function (a no-op\n * when Echo is missing). Each toast is delivered once, even if Echo reconnects or the\n * subscription is made twice (React strict mode).\n */\nexport function subscribeToNotifications(options: RealtimeSubscriptionOptions): () => void {\n if (!options.channel) {\n return () => {}\n }\n\n const seen = new Set<string>()\n\n return withEcho(\n (echo) => {\n let active = true\n\n const callback: NotificationCallback = (payload) => {\n if (!active) return\n\n if (payload.type === DATABASE_NOTIFICATIONS_SENT) {\n options.onDatabaseNotificationsSent?.()\n\n return\n }\n\n if (payload.format !== 'alfakit') return\n\n const id = typeof payload.id === 'string' ? payload.id : ''\n\n if (id !== '' && seen.has(id)) return\n if (id !== '') seen.add(id)\n\n const { type: _type, ...notification } = payload\n\n options.onNotification(notification as unknown as NotificationSchema)\n }\n\n const channel = echo.private(options.channel)\n channel.notification(callback)\n\n return () => {\n active = false\n channel.stopListeningForNotification?.(callback)\n }\n },\n { echo: options.echo },\n )\n}\n\n/** Tell mounted notification menus to reload (`DATABASE_NOTIFICATIONS_EVENT`). */\nexport function announceDatabaseNotificationsSent(): void {\n if (typeof window !== 'undefined') {\n window.dispatchEvent(new CustomEvent(DATABASE_NOTIFICATIONS_EVENT))\n }\n}\n\n/** Run `callback` whenever a database notification is announced. Returns the cleanup. */\nexport function onDatabaseNotificationsSent(callback: () => void): () => void {\n if (typeof window === 'undefined') {\n return () => {}\n }\n\n window.addEventListener(DATABASE_NOTIFICATIONS_EVENT, callback)\n\n return () => window.removeEventListener(DATABASE_NOTIFICATIONS_EVENT, callback)\n}\n\n/**\n * Offset of the panel's toaster: toasts at the top start below the sticky top bar (3.5rem\n * high) instead of covering the breadcrumbs, search and switchers, on phones too (where they\n * span the screen). The other sides keep sonner's defaults.\n */\nexport const PANEL_TOAST_OFFSET: { top: string } = { top: '4.5rem' }\n\nexport type SonnerPosition =\n 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'\n\n/**\n * The direction overlays anchored to a screen edge (the toaster, the schema inspector's\n * sheet) are placed by: the panel's reading direction, except in starter kit mode, where\n * they take the document's side (`<html dir>`, from the shared `alfakitDocument`; an app\n * whose root view or sidebar was not updated may still be left to right), so \"top end\"\n * never lands on the kit's sidebar, while their content keeps the panel's direction (W-D-03).\n */\nexport function overlayPlacementDirection(\n direction: 'ltr' | 'rtl',\n starterKit: boolean | undefined,\n): 'ltr' | 'rtl' {\n if (starterKit !== true) return direction\n if (typeof document === 'undefined') return 'ltr'\n\n return document.documentElement.dir.toLowerCase() === 'rtl' ? 'rtl' : 'ltr'\n}\n\n/**\n * The toaster position for the panel's `toastPosition()` (logical: `start`/`end`) in the\n * current reading direction. Defaults to the top end.\n */\nexport function resolveToastPosition(\n position: ToastPosition | null | undefined,\n direction: 'ltr' | 'rtl',\n): SonnerPosition {\n const [vertical, horizontal] = (position ?? 'top-end').split('-') as [\n 'top' | 'bottom',\n 'start' | 'center' | 'end',\n ]\n\n if (horizontal === 'center') {\n return `${vertical}-center`\n }\n\n const isLeft = (horizontal === 'start') === (direction === 'ltr')\n\n return `${vertical}-${isLeft ? 'left' : 'right'}`\n}\n"],"mappings":"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"}
@@ -235,6 +235,17 @@ export interface CmsPayload {
235
235
  /** The renderers' strings (`cms.*`), in the page's language. */
236
236
  translations: Record<string, unknown>;
237
237
  }
238
+ /**
239
+ * `GET /api/cms/page` (`CmsPlugin::api()`): the page, as `cms.page`, and the site's
240
+ * **Site design** as `cms.site.design` (null when none is set or `siteDesign(false)`). The
241
+ * `customCode()` head and body snippets are never part of it.
242
+ */
243
+ export interface CmsPageApiResponse {
244
+ data: CmsPageSchema;
245
+ site: {
246
+ design: CmsSiteDesign | null;
247
+ };
248
+ }
238
249
  export interface CmsAdminBarSchema {
239
250
  editUrl: string;
240
251
  /** A signed preview of the unpublished changes, when there are some. */
@@ -1 +1 @@
1
- {"version":3,"file":"cms.d.ts","names":[],"sources":["../../src/schema/cms.ts"],"mappings":";;;;;;;;iBASiB;EACf;EACA;EACA;;;iBAIe;EACf;EACA;EACA;;;;iBAKe;EACf;EACA;EACA;;EAEA;;iBAGe,eAAe,QAAQ;EACtC;EACA;EACA,MAAM;EACN,QAAQ;;iBAGO;EACf;EACA;EACA;EACA,eAAe;EACf,iBAAiB;EACjB,OAAO;EACP;;iBAGe;;EAEf;EACA;;iBAGe;EACf,OAAO;EACP;EACA;;iBAGe;EACf;EACA;EACA,QAAQ;;iBAGO;EACf;EACA;EACA,eAAe;EACf,iBAAiB;EACjB;;iBAGe;;EAEf;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA,OAAO;;iBAGQ;EACf;EACA;IAAS;IAAkB;;;iBAGZ;EACf;EACA;EACA;EACA,QAAQ;;iBAGO;EACf;EACA,OAAO;;YAGG;iBAEK;EACf;IACE;;IAEA;IACA;;EAEF;EACA,aAAa;;iBAGE;EACf;EACA;IAAW,QAAQ;;;iBAGJ;EACf;EACA;;EAEA;;EAEA,SAAS;;iBAGM;;EAEf;;EAEA,QAAQ;;;iBAIO;EACf,MAAM;EACN,aAAa;EACb,OAAO;EACP,SAAS;EACT,kBAAkB;EAClB,UAAU;EACV,KAAK;EACL,cAAc;EACd,OAAO;EACP,SAAS;EACT,MAAM;EACN,SAAS;;iBAGM;EACf;EACA;EACA;EACA;EACA,WAAW;EACX,SAAS;EACT;IAAc;IAAkB;;;EAEhC;;iBAGe;EACf;EACA;;iBAGe;EACf;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;EACR,aAAa;EACb,YAAY;EACZ,KAAK;;iBAGU;EACf;EACA;;EAEA;EACA;;EAEA;;EAEA;EACA,UAAU;;iBAGK;EACf;EACA;EACA,OAAO;;iBAGQ;EACf;EACA;EACA;EACA;EACA;EACA;IAAW;IAAc;IAAe;IAAa;;;EAErD,SAAS;;;iBAIM;;EAEf;;EAEA;EACA;IAAQ;IAAa;;EACrB;;;iBAIe;EACf,MAAM;EACN,OAAO,eAAe;EACtB,MAAM;;EAEN;;EAEA,QAAQ;;EAER;;EAEA,cAAc;;iBAGC;EACf;;EAEA;EACA;EACA;;;;;;iBAOe;EACf;EACA;EACA;EACA;;EAEA;EACA;EACA;;EAEA,SAAS;;EAET,gBAAgB;;EAEhB,UAAU;;EAEV,iBAAiB;;;iBAIF;EACf;;EAEA;EACA;;;iBAIe;EACf;EACA;EACA;;EAEA;;iBAGe;EACf,KAAK"}
1
+ {"version":3,"file":"cms.d.ts","names":[],"sources":["../../src/schema/cms.ts"],"mappings":";;;;;;;;iBASiB;EACf;EACA;EACA;;;iBAIe;EACf;EACA;EACA;;;;iBAKe;EACf;EACA;EACA;;EAEA;;iBAGe,eAAe,QAAQ;EACtC;EACA;EACA,MAAM;EACN,QAAQ;;iBAGO;EACf;EACA;EACA;EACA,eAAe;EACf,iBAAiB;EACjB,OAAO;EACP;;iBAGe;;EAEf;EACA;;iBAGe;EACf,OAAO;EACP;EACA;;iBAGe;EACf;EACA;EACA,QAAQ;;iBAGO;EACf;EACA;EACA,eAAe;EACf,iBAAiB;EACjB;;iBAGe;;EAEf;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA,OAAO;;iBAGQ;EACf;EACA;IAAS;IAAkB;;;iBAGZ;EACf;EACA;EACA;EACA,QAAQ;;iBAGO;EACf;EACA,OAAO;;YAGG;iBAEK;EACf;IACE;;IAEA;IACA;;EAEF;EACA,aAAa;;iBAGE;EACf;EACA;IAAW,QAAQ;;;iBAGJ;EACf;EACA;;EAEA;;EAEA,SAAS;;iBAGM;;EAEf;;EAEA,QAAQ;;;iBAIO;EACf,MAAM;EACN,aAAa;EACb,OAAO;EACP,SAAS;EACT,kBAAkB;EAClB,UAAU;EACV,KAAK;EACL,cAAc;EACd,OAAO;EACP,SAAS;EACT,MAAM;EACN,SAAS;;iBAGM;EACf;EACA;EACA;EACA;EACA,WAAW;EACX,SAAS;EACT;IAAc;IAAkB;;;EAEhC;;iBAGe;EACf;EACA;;iBAGe;EACf;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;EACR,aAAa;EACb,YAAY;EACZ,KAAK;;iBAGU;EACf;EACA;;EAEA;EACA;;EAEA;;EAEA;EACA,UAAU;;iBAGK;EACf;EACA;EACA,OAAO;;iBAGQ;EACf;EACA;EACA;EACA;EACA;EACA;IAAW;IAAc;IAAe;IAAa;;;EAErD,SAAS;;;iBAIM;;EAEf;;EAEA;EACA;IAAQ;IAAa;;EACrB;;;iBAIe;EACf,MAAM;EACN,OAAO,eAAe;EACtB,MAAM;;EAEN;;EAEA,QAAQ;;EAER;;EAEA,cAAc;;;;;;;iBAQC;EACf,MAAM;EACN;IAAQ,QAAQ;;;iBAGD;EACf;;EAEA;EACA;EACA;;;;;;iBAOe;EACf;EACA;EACA;EACA;;EAEA;EACA;EACA;;EAEA,SAAS;;EAET,gBAAgB;;EAEhB,UAAU;;EAEV,iBAAiB;;;iBAIF;EACf;;EAEA;EACA;;;iBAIe;EACf;EACA;EACA;;EAEA;;iBAGe;EACf,KAAK"}
@@ -21,9 +21,9 @@ import { DebugClosure, DebugDeprecation, DebugNode, DebugSchema, DebugServerTimi
21
21
  import { DevToolsClass, DevToolsContext, DevToolsSchema } from "./dev-tools.js";
22
22
  import { PwaPushSchema, PwaSchema } from "./pwa.js";
23
23
  import { StandaloneComponentSchema, isStandaloneComponent } from "./standalone.js";
24
- import { AlfakitPageProps, BreadcrumbSchema, ContentBlock, DefaultActionSchema, ErrorNotificationEntrySchema, ErrorNotificationsSchema, ImpersonationSchema, LanguageSwitcherPosition, LocaleSchema, LocalizationSchema, MenuItemSchema, NavigationBadgeSchema, NavigationGroupSchema, NavigationItemSchema, NavigationStateSchema, PageLayout, PageSchema, PanelDesignSchema, PanelEndpoints, PanelSchema, PanelThemeSchema, PanelUserSchema, PolledComponents, SlotDataValue, SlotItemSchema, StarterKitDashboardWidgetsSchema, StarterKitNavigationSchema, StarterKitWidgetsSchema, SubNavigationSchema, TENANT_MENU_COMPONENT, TenantSchema, ToastPosition, USER_MENU_COMPONENT } from "./panel.js";
25
- import { CmsAdminBarSchema, CmsAlternateSchema, CmsAspectRatio, CmsAutosaveResult, CmsBlockSchema, CmsBlockStyle, CmsBreadcrumbSchema, CmsBuiltInBlocks, CmsCallToActionBlockData, CmsColumnsBlockData, CmsEditorStatusSchema, CmsEmbedBlockData, CmsFaqBlockData, CmsFeatureSchema, CmsFeaturesBlockData, CmsFormBlockData, CmsGalleryBlockData, CmsHeroBlockData, CmsImageBlockData, CmsImageSchema, CmsLinkSchema, CmsLivePreviewSchema, CmsMenuItemSchema, CmsMenuSchema, CmsPageProps, CmsPageSchema, CmsPayload, CmsRichTextBlockData, CmsSectionBlockData, CmsSeoSchema, CmsSiteDesign, CmsSiteSchema, CmsTestimonialSchema, CmsTestimonialsBlockData } from "./cms.js";
24
+ import { AlfakitPageProps, BreadcrumbSchema, ContentBlock, DefaultActionSchema, ErrorNotificationEntrySchema, ErrorNotificationsSchema, ImpersonationSchema, LanguageSwitcherPosition, LocaleSchema, LocalizationSchema, MenuItemSchema, NavigationBadgeSchema, NavigationGroupSchema, NavigationItemSchema, NavigationStateSchema, PageLayout, PageSchema, PanelDesignSchema, PanelEndpoints, PanelSchema, PanelThemeSchema, PanelUserSchema, PolledComponents, SlotDataValue, SlotItemSchema, StarterKitDashboardWidgetsSchema, StarterKitDocumentSchema, StarterKitNavigationSchema, StarterKitWidgetsSchema, SubNavigationSchema, TENANT_MENU_COMPONENT, TenantSchema, ToastPosition, USER_MENU_COMPONENT } from "./panel.js";
25
+ import { CmsAdminBarSchema, CmsAlternateSchema, CmsAspectRatio, CmsAutosaveResult, CmsBlockSchema, CmsBlockStyle, CmsBreadcrumbSchema, CmsBuiltInBlocks, CmsCallToActionBlockData, CmsColumnsBlockData, CmsEditorStatusSchema, CmsEmbedBlockData, CmsFaqBlockData, CmsFeatureSchema, CmsFeaturesBlockData, CmsFormBlockData, CmsGalleryBlockData, CmsHeroBlockData, CmsImageBlockData, CmsImageSchema, CmsLinkSchema, CmsLivePreviewSchema, CmsMenuItemSchema, CmsMenuSchema, CmsPageApiResponse, CmsPageProps, CmsPageSchema, CmsPayload, CmsRichTextBlockData, CmsSectionBlockData, CmsSeoSchema, CmsSiteDesign, CmsSiteSchema, CmsTestimonialSchema, CmsTestimonialsBlockData } from "./cms.js";
26
26
  import { HelpdeskActivity, HelpdeskAttachment, HelpdeskBadge, HelpdeskChatConfig, HelpdeskChatConversation, HelpdeskChatMessage, HelpdeskChatResponse, HelpdeskInboxList, HelpdeskInboxSchema, HelpdeskMessage, HelpdeskSla, HelpdeskTicket, HelpdeskTicketSummary, HelpdeskView } from "./helpdesk.js";
27
27
  import { TranslationCellSchema, TranslationFilter, TranslationHistoryEntry, TranslationLocaleSchema, TranslationManagerSchema, TranslationProgressSchema, TranslationRowSchema, TranslationRowsResult, TranslationSaveResult, TranslationSuggestionSchema } from "./translation-manager.js";
28
28
  import { RecordVersionChangeSchema, RecordVersionSchema, RecordVersionsSchema } from "./versions.js";
29
- export { ActionGroupSchema, ActionModalCancelledResponse, ActionModalSchema, ActionResultFlash, ActionSchema, ActionVariant, ActionsComponentSchema, Affixes, AiActionStatus, AiConversationSchema, AiCopilotSchema, AiMessageRole, AiMessageSchema, AiProposedActionSchema, AiSetupNoticeSchema, AiStreamSchema, AlfakitPageProps, Alignment, AnalyticsBreakdownRowSchema, AnalyticsBreakdownTabSchema, AnalyticsBreakdownWidgetSchema, AnyActionSchema, AnyPaginationSchema, AppendingPaginationSchema, Attributes, BaseColumnSchema, BaseComponent, BaseEntry, BaseField, BaseWidgetSchema, BreadcrumbSchema, Breakpoint, Breakpoints, BuilderBlockSchema, BuilderItemSchema, BuilderItemState, BuilderSchema, BulkToggleActionSchema, COMMANDS_COMPONENT, CalendarChangeResult, CalendarEventSchema, CalendarView, CalendarWidgetSchema, CalloutSchema, CellSchema, ChartEmptyStateSchema, ChartFiltersFormSchema, ChartWidgetSchema, CheckboxColumnSchema, CheckboxListSchema, CheckboxSchema, CmsAdminBarSchema, CmsAlternateSchema, CmsAspectRatio, CmsAutosaveResult, CmsBlockSchema, CmsBlockStyle, CmsBreadcrumbSchema, CmsBuiltInBlocks, CmsCallToActionBlockData, CmsColumnsBlockData, CmsEditorStatusSchema, CmsEmbedBlockData, CmsFaqBlockData, CmsFeatureSchema, CmsFeaturesBlockData, CmsFormBlockData, CmsGalleryBlockData, CmsHeroBlockData, CmsImageBlockData, CmsImageSchema, CmsLinkSchema, CmsLivePreviewSchema, CmsMenuItemSchema, CmsMenuSchema, CmsPageProps, CmsPageSchema, CmsPayload, CmsRichTextBlockData, CmsSectionBlockData, CmsSeoSchema, CmsSiteDesign, CmsSiteSchema, CmsTestimonialSchema, CmsTestimonialsBlockData, CodeEditorSchema, CodeEntrySchema, Color, ColorColumnSchema, ColorEntrySchema, ColorPickerSchema, ColumnGroupSchema, ColumnLayoutColumnRef, ColumnLayoutNode, ColumnLayoutSchema, ColumnSchema, ColumnSpanValue, ColumnSummariesSchema, CommandPaletteLinkSchema, CommandPaletteResponse, CommandPaletteSchema, CommandSchema, ConflictsSchema, ContainerBreakpoint, ContentBlock, ContentSlotItem, ContentSlotName, ContentSlotSchema, ContentSlots, CursorPaginationSchema, CustomWidgetSchema, DashboardCustomizeSchema, DashboardLayoutEntry, DashboardLibraryWidget, DashboardSpanOption, DatabaseNotificationsResponse, DatePickerSchema, DebugClosure, DebugDeprecation, DebugNode, DebugSchema, DebugServerTimings, DefaultActionSchema, DeferredBadgeSchema, DeferredTabBadges, DevToolsClass, DevToolsContext, DevToolsSchema, EditableColumnSchema, EmptyStateComponentSchema, EmptyStateSchema, EntrySchema, ErrorNotificationEntrySchema, ErrorNotificationsSchema, FIELD_TYPES, FieldActionResponse, FieldActions, FieldSchema, FieldsetSchema, FileManagerActionNames, FileManagerBreadcrumbSchema, FileManagerDetailsSchema, FileManagerEntrySchema, FileManagerFolderSchema, FileManagerKind, FileManagerLinkSchema, FileManagerListingSchema, FileManagerPermissionsSchema, FileManagerPickedFileSchema, FileManagerPickerSchema, FileManagerPreviewSchema, FileManagerResult, FileManagerSchema, FileUploadImageEditorSchema, FileUploadPositions, FileUploadSchema, FilterIndicator, FilterSchema, FiltersFormWidth, FlexSchema, FormBlock, FormPayload, FormattedStateProps, FusedGroupSchema, GlobalSearchGroupSchema, GlobalSearchResultSchema, GridLayoutSchema, GridSchema, GroupSchema, HelpdeskActivity, HelpdeskAttachment, HelpdeskBadge, HelpdeskChatConfig, HelpdeskChatConversation, HelpdeskChatMessage, HelpdeskChatResponse, HelpdeskInboxList, HelpdeskInboxSchema, HelpdeskMessage, HelpdeskSla, HelpdeskTicket, HelpdeskTicketSummary, HelpdeskView, HiddenSchema, HtmlSchema, IconCellItem, IconColumnSchema, IconEntrySchema, IconName, IconPickerOption, IconPickerResponse, IconPickerSchema, IconPickerSetSchema, IconPosition, IconSchema, ImageCellSchema, ImageColumnSchema, ImageEntrySchema, ImageSchema, ImageStackProps, ImpersonationSchema, InfolistBlock, ItemActionName, ItemActionSchema, ItemActionsItemProps, ItemActionsProps, ItemActionsSchema, KanbanAssigneeSchema, KanbanBadgeSchema, KanbanCardSchema, KanbanColumnSchema, KanbanFetchResult, KanbanFiltersSchema, KanbanLaneSchema, KanbanMoveResult, KanbanSchema, KanbanWidgetSchema, KeyValueEntrySchema, KeyValueSchema, KeyboardShortcutsSchema, LanguageSwitcherPosition, LayoutSchema, LazyRelationManagerSchema, LazyWidgetSchema, LinksBlock, ListTabSchema, ListTabsProps, LiveConfig, LiveUpdatesSchema, LocaleSchema, LocalizationSchema, MenuItemSchema, ModalTableSelectResponse, ModalTableSelectSchema, MoneyInputSchema, NavigationBadgeSchema, NavigationGroupSchema, NavigationItemSchema, NavigationStateSchema, NotificationActionSchema, NotificationSchema, NotificationUndoSchema, OneTimeCodeInputSchema, Operation, Option, OptionGroup, OptionOrGroup, OptionsGridDirection, PageLayout, PageSchema, PaginationSchema, PanelDesignSchema, PanelEndpoints, PanelLayoutSchema, PanelSchema, PanelThemeSchema, PanelUserSchema, PartialFormPayload, PlaceholderSchema, PolledComponents, PresenceSchema, PresenceState, PwaPushSchema, PwaSchema, QueryBuilderConstraintRule, QueryBuilderConstraintSchema, QueryBuilderGroup, QueryBuilderGroupRule, QueryBuilderInput, QueryBuilderOperatorSchema, QueryBuilderRule, QueryBuilderRulesSchema, RadioSchema, RealtimeSchema, RecordKey, RecordVersionChangeSchema, RecordVersionSchema, RecordVersionsSchema, RelationGroupSchema, RelationManagerSchema, RelationManagersBlock, RelationManagersContentTabSchema, RepeatableEntrySchema, RepeaterItemSchema, RepeaterSchema, RichEditorCustomBlockSchema, RichEditorMentionSchema, RichEditorMergeTagSchema, RichEditorSchema, RichEditorToolSchema, RichEditorToolbarGroupSchema, SchemaBlock, SchemaComponent, SchemaComponentContext, SchemaPatch, SchemaRefreshResponse, SectionSchema, SelectColumnSchema, SelectOptionsResponse, SelectSchema, SimplePaginationSchema, Size, SliderBehavior, SliderNonLinearPoint, SliderPipSchema, SliderSchema, SlotDataValue, SlotItemSchema, SplitLayoutSchema, StackLayoutSchema, StandaloneComponentSchema, StarterKitDashboardWidgetsSchema, StarterKitNavigationSchema, StarterKitWidgetsSchema, StatSchema, StatsOverviewWidgetSchema, StepSchema, StepValidationHaltedResponse, SubNavigationSchema, SummaryValueSchema, TENANT_MENU_COMPONENT, TabSchema, TableBlock, TableColumnSchema, TableDeferredTotalsSchema, TableGroupOptionSchema, TableGroupingSchema, TablePersistedStateKey, TableRecordGroupSchema, TableRecordSchema, TableSchema, TableSortSchema, TableSpreadsheetSchema, TableSummariesSchema, TableSummaryRowSchema, TableTotalsSchema, TableTriggerActionKey, TableTriggerActionSchema, TableTriggerModalSchema, TableViewItemSchema, TableViewsSchema, TableWidgetSchema, TabsSchema, TagsInputSchema, TenantSchema, TextColumnSchema, TextEntrySchema, TextInputColumnSchema, TextInputSchema, TextSchema, TextStyleProps, TextareaSchema, ToastPosition, ToggleButtonsSchema, ToggleColumnSchema, ToggleSchema, TranslatableLocaleSchema, TranslatableSchema, TranslationCellSchema, TranslationFilter, TranslationHistoryEntry, TranslationLocaleSchema, TranslationManagerSchema, TranslationProgressSchema, TranslationRowSchema, TranslationRowsResult, TranslationSaveResult, TranslationSuggestionSchema, TreeNodeSchema, TreeSchema, TreeStructureItem, TreeWidgetSchema, USER_MENU_COMPONENT, UnorderedListSchema, UploadedFile, VerticalAlignment, ViewColumnSchema, ViewEntrySchema, ViewSchema, VirtualizeSchema, WidgetSchema, WidgetsBlock, Width, WizardSchema, isActionGroup, isField, isOptionGroup, isPartialFormPayload, isStandaloneComponent };
29
+ export { ActionGroupSchema, ActionModalCancelledResponse, ActionModalSchema, ActionResultFlash, ActionSchema, ActionVariant, ActionsComponentSchema, Affixes, AiActionStatus, AiConversationSchema, AiCopilotSchema, AiMessageRole, AiMessageSchema, AiProposedActionSchema, AiSetupNoticeSchema, AiStreamSchema, AlfakitPageProps, Alignment, AnalyticsBreakdownRowSchema, AnalyticsBreakdownTabSchema, AnalyticsBreakdownWidgetSchema, AnyActionSchema, AnyPaginationSchema, AppendingPaginationSchema, Attributes, BaseColumnSchema, BaseComponent, BaseEntry, BaseField, BaseWidgetSchema, BreadcrumbSchema, Breakpoint, Breakpoints, BuilderBlockSchema, BuilderItemSchema, BuilderItemState, BuilderSchema, BulkToggleActionSchema, COMMANDS_COMPONENT, CalendarChangeResult, CalendarEventSchema, CalendarView, CalendarWidgetSchema, CalloutSchema, CellSchema, ChartEmptyStateSchema, ChartFiltersFormSchema, ChartWidgetSchema, CheckboxColumnSchema, CheckboxListSchema, CheckboxSchema, CmsAdminBarSchema, CmsAlternateSchema, CmsAspectRatio, CmsAutosaveResult, CmsBlockSchema, CmsBlockStyle, CmsBreadcrumbSchema, CmsBuiltInBlocks, CmsCallToActionBlockData, CmsColumnsBlockData, CmsEditorStatusSchema, CmsEmbedBlockData, CmsFaqBlockData, CmsFeatureSchema, CmsFeaturesBlockData, CmsFormBlockData, CmsGalleryBlockData, CmsHeroBlockData, CmsImageBlockData, CmsImageSchema, CmsLinkSchema, CmsLivePreviewSchema, CmsMenuItemSchema, CmsMenuSchema, CmsPageApiResponse, CmsPageProps, CmsPageSchema, CmsPayload, CmsRichTextBlockData, CmsSectionBlockData, CmsSeoSchema, CmsSiteDesign, CmsSiteSchema, CmsTestimonialSchema, CmsTestimonialsBlockData, CodeEditorSchema, CodeEntrySchema, Color, ColorColumnSchema, ColorEntrySchema, ColorPickerSchema, ColumnGroupSchema, ColumnLayoutColumnRef, ColumnLayoutNode, ColumnLayoutSchema, ColumnSchema, ColumnSpanValue, ColumnSummariesSchema, CommandPaletteLinkSchema, CommandPaletteResponse, CommandPaletteSchema, CommandSchema, ConflictsSchema, ContainerBreakpoint, ContentBlock, ContentSlotItem, ContentSlotName, ContentSlotSchema, ContentSlots, CursorPaginationSchema, CustomWidgetSchema, DashboardCustomizeSchema, DashboardLayoutEntry, DashboardLibraryWidget, DashboardSpanOption, DatabaseNotificationsResponse, DatePickerSchema, DebugClosure, DebugDeprecation, DebugNode, DebugSchema, DebugServerTimings, DefaultActionSchema, DeferredBadgeSchema, DeferredTabBadges, DevToolsClass, DevToolsContext, DevToolsSchema, EditableColumnSchema, EmptyStateComponentSchema, EmptyStateSchema, EntrySchema, ErrorNotificationEntrySchema, ErrorNotificationsSchema, FIELD_TYPES, FieldActionResponse, FieldActions, FieldSchema, FieldsetSchema, FileManagerActionNames, FileManagerBreadcrumbSchema, FileManagerDetailsSchema, FileManagerEntrySchema, FileManagerFolderSchema, FileManagerKind, FileManagerLinkSchema, FileManagerListingSchema, FileManagerPermissionsSchema, FileManagerPickedFileSchema, FileManagerPickerSchema, FileManagerPreviewSchema, FileManagerResult, FileManagerSchema, FileUploadImageEditorSchema, FileUploadPositions, FileUploadSchema, FilterIndicator, FilterSchema, FiltersFormWidth, FlexSchema, FormBlock, FormPayload, FormattedStateProps, FusedGroupSchema, GlobalSearchGroupSchema, GlobalSearchResultSchema, GridLayoutSchema, GridSchema, GroupSchema, HelpdeskActivity, HelpdeskAttachment, HelpdeskBadge, HelpdeskChatConfig, HelpdeskChatConversation, HelpdeskChatMessage, HelpdeskChatResponse, HelpdeskInboxList, HelpdeskInboxSchema, HelpdeskMessage, HelpdeskSla, HelpdeskTicket, HelpdeskTicketSummary, HelpdeskView, HiddenSchema, HtmlSchema, IconCellItem, IconColumnSchema, IconEntrySchema, IconName, IconPickerOption, IconPickerResponse, IconPickerSchema, IconPickerSetSchema, IconPosition, IconSchema, ImageCellSchema, ImageColumnSchema, ImageEntrySchema, ImageSchema, ImageStackProps, ImpersonationSchema, InfolistBlock, ItemActionName, ItemActionSchema, ItemActionsItemProps, ItemActionsProps, ItemActionsSchema, KanbanAssigneeSchema, KanbanBadgeSchema, KanbanCardSchema, KanbanColumnSchema, KanbanFetchResult, KanbanFiltersSchema, KanbanLaneSchema, KanbanMoveResult, KanbanSchema, KanbanWidgetSchema, KeyValueEntrySchema, KeyValueSchema, KeyboardShortcutsSchema, LanguageSwitcherPosition, LayoutSchema, LazyRelationManagerSchema, LazyWidgetSchema, LinksBlock, ListTabSchema, ListTabsProps, LiveConfig, LiveUpdatesSchema, LocaleSchema, LocalizationSchema, MenuItemSchema, ModalTableSelectResponse, ModalTableSelectSchema, MoneyInputSchema, NavigationBadgeSchema, NavigationGroupSchema, NavigationItemSchema, NavigationStateSchema, NotificationActionSchema, NotificationSchema, NotificationUndoSchema, OneTimeCodeInputSchema, Operation, Option, OptionGroup, OptionOrGroup, OptionsGridDirection, PageLayout, PageSchema, PaginationSchema, PanelDesignSchema, PanelEndpoints, PanelLayoutSchema, PanelSchema, PanelThemeSchema, PanelUserSchema, PartialFormPayload, PlaceholderSchema, PolledComponents, PresenceSchema, PresenceState, PwaPushSchema, PwaSchema, QueryBuilderConstraintRule, QueryBuilderConstraintSchema, QueryBuilderGroup, QueryBuilderGroupRule, QueryBuilderInput, QueryBuilderOperatorSchema, QueryBuilderRule, QueryBuilderRulesSchema, RadioSchema, RealtimeSchema, RecordKey, RecordVersionChangeSchema, RecordVersionSchema, RecordVersionsSchema, RelationGroupSchema, RelationManagerSchema, RelationManagersBlock, RelationManagersContentTabSchema, RepeatableEntrySchema, RepeaterItemSchema, RepeaterSchema, RichEditorCustomBlockSchema, RichEditorMentionSchema, RichEditorMergeTagSchema, RichEditorSchema, RichEditorToolSchema, RichEditorToolbarGroupSchema, SchemaBlock, SchemaComponent, SchemaComponentContext, SchemaPatch, SchemaRefreshResponse, SectionSchema, SelectColumnSchema, SelectOptionsResponse, SelectSchema, SimplePaginationSchema, Size, SliderBehavior, SliderNonLinearPoint, SliderPipSchema, SliderSchema, SlotDataValue, SlotItemSchema, SplitLayoutSchema, StackLayoutSchema, StandaloneComponentSchema, StarterKitDashboardWidgetsSchema, StarterKitDocumentSchema, StarterKitNavigationSchema, StarterKitWidgetsSchema, StatSchema, StatsOverviewWidgetSchema, StepSchema, StepValidationHaltedResponse, SubNavigationSchema, SummaryValueSchema, TENANT_MENU_COMPONENT, TabSchema, TableBlock, TableColumnSchema, TableDeferredTotalsSchema, TableGroupOptionSchema, TableGroupingSchema, TablePersistedStateKey, TableRecordGroupSchema, TableRecordSchema, TableSchema, TableSortSchema, TableSpreadsheetSchema, TableSummariesSchema, TableSummaryRowSchema, TableTotalsSchema, TableTriggerActionKey, TableTriggerActionSchema, TableTriggerModalSchema, TableViewItemSchema, TableViewsSchema, TableWidgetSchema, TabsSchema, TagsInputSchema, TenantSchema, TextColumnSchema, TextEntrySchema, TextInputColumnSchema, TextInputSchema, TextSchema, TextStyleProps, TextareaSchema, ToastPosition, ToggleButtonsSchema, ToggleColumnSchema, ToggleSchema, TranslatableLocaleSchema, TranslatableSchema, TranslationCellSchema, TranslationFilter, TranslationHistoryEntry, TranslationLocaleSchema, TranslationManagerSchema, TranslationProgressSchema, TranslationRowSchema, TranslationRowsResult, TranslationSaveResult, TranslationSuggestionSchema, TreeNodeSchema, TreeSchema, TreeStructureItem, TreeWidgetSchema, USER_MENU_COMPONENT, UnorderedListSchema, UploadedFile, VerticalAlignment, ViewColumnSchema, ViewEntrySchema, ViewSchema, VirtualizeSchema, WidgetSchema, WidgetsBlock, Width, WizardSchema, isActionGroup, isField, isOptionGroup, isPartialFormPayload, isStandaloneComponent };
@@ -73,6 +73,13 @@ export interface StarterKitDashboardWidgetsSchema {
73
73
  /** A standalone widgets payload bound to the panel (lazy loading and polling included). */
74
74
  component: StandaloneComponentSchema;
75
75
  }
76
+ /** `<html lang dir>` of a page in a starter kit app (`alfakitDocument`). */
77
+ export interface StarterKitDocumentSchema {
78
+ /** BCP 47 tag of the page's language (`ar`, `en-GB`). */
79
+ tag: string;
80
+ /** The direction of the language picked for the `starterKit()` panel. */
81
+ direction: 'ltr' | 'rtl';
82
+ }
76
83
  export interface StarterKitNavigationSchema {
77
84
  /** The panel id. */
78
85
  id: string;
@@ -597,6 +604,13 @@ export interface AlfakitPageProps {
597
604
  * page (the host's own pages too) for the host sidebar. Null for guests.
598
605
  */
599
606
  alfakitNavigation?: StarterKitNavigationSchema[] | null;
607
+ /**
608
+ * `<html lang dir>` while the app has a `starterKit()` panel, shared with every Inertia page
609
+ * (the host's own pages and guests too): the app's current language and the direction of
610
+ * the language picked for the panel, so the whole page flips in a right-to-left language.
611
+ * Null on pages of other panels, which set the document themselves.
612
+ */
613
+ alfakitDocument?: StarterKitDocumentSchema | null;
600
614
  /**
601
615
  * `starterKit()` panels: their dashboard widgets on the host's dashboard
602
616
  * (`starterKitDashboard()`), for `<AlfakitWidgets />`. Null on other pages, for guests
@@ -1 +1 @@
1
- {"version":3,"file":"panel.d.ts","names":[],"sources":["../../src/schema/panel.ts"],"mappings":"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"}
1
+ {"version":3,"file":"panel.d.ts","names":[],"sources":["../../src/schema/panel.ts"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"panel.js","names":[],"sources":["../../src/schema/panel.ts"],"sourcesContent":["import type { ActionSchema, AnyActionSchema } from './actions'\nimport type { CommandPaletteSchema, KeyboardShortcutsSchema } from './command-palette'\nimport type { Color, IconName, RecordKey, Width } from './common'\nimport type { SchemaComponent } from './components'\nimport type { DebugSchema } from './debug'\nimport type { DevToolsSchema } from './dev-tools'\nimport type { FormBlock, InfolistBlock, LinksBlock, SchemaBlock } from './form'\nimport type { NotificationSchema } from './notifications'\nimport type { PwaSchema } from './pwa'\nimport type { ConflictsSchema, PresenceSchema, RealtimeSchema } from './realtime'\nimport type { DeferredTabBadges } from './resources'\nimport type { StandaloneComponentSchema } from './standalone'\nimport type { RelationManagerSchema, RelationManagersBlock, TableBlock } from './table'\nimport type { WidgetSchema, WidgetsBlock } from './widgets'\n\nexport interface NavigationItemSchema {\n /** Text, or sanitised HTML when `labelHtml` is true (an `Htmlable` label). */\n label: string\n /** `label` is sanitised HTML: render it as markup (use `htmlLabelToText()` for text). */\n labelHtml?: boolean\n icon?: IconName\n /**\n * Split navigation (`alfakit.navigation` is an Inertia once prop): the icon of the item\n * while active, swapped in by `resolvePanelNavigation()`.\n */\n activeIcon?: IconName\n url?: string\n openInNewTab?: boolean\n active?: boolean\n badge?: string | number\n badgeColor?: Color\n badgeTooltip?: string\n /** `Panel::deferNavigationBadges()`: the item has a badge that loads after the page. */\n deferredBadge?: boolean\n /** Set by `resolvePanelNavigation()` while a deferred badge is loading (show a placeholder). */\n badgePending?: boolean\n /** `NavigationItem::extraAttributes()`: HTML attributes for the item's link (`class` merges). */\n extraAttributes?: Record<string, string | number | boolean | null>\n children?: NavigationItemSchema[]\n}\n\nexport interface NavigationGroupSchema {\n label?: string | null\n icon?: IconName\n collapsible: boolean\n collapsed?: boolean\n items: NavigationItemSchema[]\n /** `NavigationGroup::extraSidebarAttributes()`: HTML attributes for the group in the sidebar. */\n extraSidebarAttributes?: Record<string, string | number | boolean | null>\n /** `NavigationGroup::extraTopbarAttributes()`: HTML attributes for the group in the top navigation. */\n extraTopbarAttributes?: Record<string, string | number | boolean | null>\n}\n\n/** A navigation item's badge, from `navigationState` or the deferred `alfakitNavigationBadges` prop. */\nexport interface NavigationBadgeSchema {\n badge: string | number\n badgeColor?: Color\n badgeTooltip?: string\n}\n\n/**\n * The per-request part of the panel navigation, whose structure (`navigation`) is an\n * Inertia once prop. Items are addressed by position: `group.item[.child…]` (`0.2.1`).\n */\nexport interface NavigationStateSchema {\n /** The active items (an active child's ancestors included). */\n active?: string[]\n /** Badges, unless `Panel::deferNavigationBadges()` defers them. */\n badges?: Record<string, NavigationBadgeSchema>\n}\n\n/** One `starterKit()` panel's navigation, as shared with the host application's pages. */\n/** One `starterKit()` panel's dashboard widgets on a host page (`alfakitWidgets` prop). */\nexport interface StarterKitDashboardWidgetsSchema {\n /** The panel id. */\n id: string\n /** A standalone widgets payload bound to the panel (lazy loading and polling included). */\n component: StandaloneComponentSchema\n}\n\nexport interface StarterKitNavigationSchema {\n /** The panel id. */\n id: string\n /** The panel's home URL. */\n url: string\n brandName: string\n /** Empty for tenancy panels outside a tenant (their items link to tenant routes). */\n groups: NavigationGroupSchema[]\n /**\n * The panel's own user menu links (`userMenuItems()`, plugin entries such as Billing's),\n * for `<AlfakitUserMenuItems />` in the kit's user menu. Links only (entries with server\n * logic or a modal need an AlfaKit page); omitted when there are none, and for tenancy\n * panels outside a tenant.\n */\n userMenu?: MenuItemSchema[]\n /** What the drop-in header components render before they load. */\n widgets?: StarterKitWidgetsSchema\n}\n\n/**\n * The `widgets` of a `starterKit()` panel's shared navigation: what `<AlfakitSearch />`,\n * `<AlfakitNotifications />`, `<AlfakitTenantSwitcher />` and `<AlfakitLanguageSwitcher />`\n * show on any host page before their code and the panel data (`endpoint`) have loaded.\n */\nexport interface StarterKitWidgetsSchema {\n /** GET: the panel data the components render with (the `alfakit` prop of the panel's pages). */\n endpoint: string\n /** Global search or the command palette (tenancy panels: only inside a tenant). */\n search?: { keyBindings: string[]; placeholder: string }\n /** `databaseNotifications()`: the bell's label and the user's unread count. */\n notifications?: { label: string; unreadCount: number }\n /** Tenancy panels: the user's tenants; `current` is null on the host's own pages. */\n tenancy?: {\n current?: TenantSchema | null\n tenants: TenantSchema[]\n registrationUrl?: string\n /** The registration page's own label (`RegisterTenant::getLabel()`). */\n registrationLabel?: string\n searchable?: true\n }\n /**\n * `<AlfakitLanguageSwitcher />`: the button's label (\"Change language\") and the current\n * language, when the panel has a language switcher with two or more languages\n * (`locales()`). The languages and the locale endpoint come with the panel data.\n */\n language?: { label: string; current: LocaleSchema }\n /** While impersonating: the banner the header components show on the host's own pages. */\n impersonation?: ImpersonationSchema\n /**\n * `AiPlugin::copilot()` (alfasix/alfakit-ai, `$panel->starterKitWidget('copilot')`): the\n * label and shortcuts of `<AlfakitCopilot />` before its code loads. Tenancy panels: only\n * inside a tenant.\n */\n copilot?: { label: string; keyBindings: string[] }\n}\n\n/** A page's secondary navigation: cluster pages or a record's pages. */\nexport interface SubNavigationSchema {\n position: 'start' | 'end' | 'top'\n groups: NavigationGroupSchema[]\n}\n\n/**\n * An entry of the user or tenant menu: a link (`url`, `method: 'post'` for POST links) or\n * an `Action` with server logic or a modal (`action`), run through the action endpoint\n * with the component `userMenu` / `tenantMenu` and the page's token.\n */\nexport interface MenuItemSchema {\n label?: string\n icon?: IconName\n url?: string\n openInNewTab?: boolean\n color?: Color\n method?: 'post'\n action?: ActionSchema\n /** Group index (nested arrays in `userMenuItems()`); a separator divides groups. */\n group?: number\n}\n\n/** Action endpoint components of the menus (`Panel::USER_MENU_COMPONENT`, …). */\nexport const USER_MENU_COMPONENT = 'userMenu'\nexport const TENANT_MENU_COMPONENT = 'tenantMenu'\n\nexport interface PanelUserSchema {\n id: RecordKey\n name: string\n email?: string | null\n avatar?: string | null\n}\n\nexport interface TenantSchema {\n id: RecordKey\n /** Text, or sanitised HTML when `nameHtml` is true (an `Htmlable` `getAlfakitName()`). */\n name: string\n /** `name` is sanitised HTML: render it as markup (use `htmlLabelToText()` for text). */\n nameHtml?: boolean\n avatar?: string | null\n url: string\n /** `HasCurrentTenantLabel`: a caption shown with the current tenant (\"Active team\"). */\n label?: string | null\n}\n\n/** The panel's design, see `Panel::theme()`. */\nexport interface PanelThemeSchema {\n name: string\n /** Value of `data-alfakit-theme`, base theme first: `\"alfakit acme\"`. */\n attribute: string\n sidebar: 'inset' | 'floating' | 'flush'\n density: 'comfortable' | 'compact'\n radius?: string | null\n font?: string | null\n /** CSS value for `--alfakit-font`. */\n fontCss?: string | null\n /** CSS value for `--alfakit-heading-font`. */\n headingFontCss?: string | null\n /** Stylesheet that loads the font, if any. */\n fontUrl?: string | null\n /** Every font stylesheet to load (body and heading fonts). */\n fontUrls?: string[]\n /** Structure and component styles; mirrored as `data-*` attributes on `<html>`. */\n design: PanelDesignSchema\n /**\n * Icon aliases replaced with `Icons::register()` (`alfakit::panels.sidebar.collapse-button`\n * → a Lucide name, image URL or sanitised SVG). The chrome falls back to its own icon.\n */\n icons?: Record<string, string> | null\n}\n\nexport interface PanelDesignSchema {\n layout: 'sidebar' | 'topbar' | 'rail'\n nav: 'soft' | 'pill' | 'bar' | 'underline'\n header: 'plain' | 'banner' | 'band' | 'compact'\n surfaces: 'bordered' | 'elevated' | 'flat' | 'ring'\n inputs: 'outline' | 'filled' | 'underline'\n buttons: 'rounded' | 'pill' | 'square'\n tables: 'lined' | 'striped' | 'minimal'\n /**\n * Below the `md` breakpoint: `drawer` (default) or the `bottom` mobile preset (bottom\n * navigation bar, tables as stacked cards, slide-over modals, 44px touch targets and\n * sticky form actions). See `Panel::mobile()`.\n */\n mobile?: 'drawer' | 'bottom'\n}\n\n/**\n * One piece of slot content: server-rendered HTML or a schema component. `for` lists the\n * components (`relationManager:<class>`, `widget:<id>`) it is scoped to: such items only\n * render in a `<Slot scope=\"…\">` of a matching component. `when` (`Panel::slot(…, when:)`)\n * limits it to renders whose slot data matches: `{ column: 'title' }` only renders in the\n * `table.header-cell` slot of the `title` column; a list value matches any of its entries.\n */\nexport type SlotItemSchema = (\n { type: 'html'; html: string } | { type: 'component'; component: SchemaComponent }\n) & { for?: string[]; when?: Record<string, SlotDataValue | SlotDataValue[]> }\n\n/** A value in a slot's render data (`<Slot data={{ column: 'title' }}>`). */\nexport type SlotDataValue = string | number | boolean | null\n\n/** A language the panel can be displayed in. */\nexport interface LocaleSchema {\n /** Laravel-style code: `en`, `pt_BR`, `zh_Hant`. */\n code: string\n /** BCP 47 tag for `Intl` and the `lang` attribute: `pt-BR`. */\n tag: string\n /** The language's own name: `Deutsch`, `العربية`. */\n label: string\n direction: 'ltr' | 'rtl'\n}\n\n/** The current request's language, formats and the panel's language list. */\nexport interface LocalizationSchema extends LocaleSchema {\n fallbackLocale: string\n /** 0 = Sunday … 6 = Saturday. */\n firstDayOfWeek: number\n /** Default ISO 4217 currency. */\n currency: string\n /** IANA timezone dates are displayed in. */\n timezone: string\n /** Whether to show a language menu. */\n switcher: boolean\n /**\n * Where panel pages offer the menu (`Panel::languageSwitcher()`): the user menu (default\n * when absent), a top bar button, both, or neither. Auth pages keep their switcher.\n */\n switcherPosition?: LanguageSwitcherPosition\n /** Languages to offer in the menu (empty when the switcher is off). */\n locales: LocaleSchema[]\n}\n\nexport type LanguageSwitcherPosition = 'user-menu' | 'topbar' | 'both' | 'hidden'\n\nexport interface PanelEndpoints {\n action: string\n modal: string\n schemaRefresh: string\n schemaOptions: string\n /** Validates one wizard step's fields (`step` = the Step component's key). */\n schemaValidate?: string\n /** Loads `deferLoading()` children and `Tab::deferBadge()` badges (`{schema}::{key}`). */\n schemaDeferred?: string\n tableCell: string\n tableReorder: string\n /** Stores a user's column order (`reorderableColumns()`). */\n tableColumns?: string\n /** `Table::deferTotals()`: the record count and summaries, loaded after the rows. */\n tableTotals?: string\n /** `Table::savedViews()`: save, share, rename, delete and pick a default view. */\n tableViews?: string\n /** `Table::spreadsheet()`: batch save (or validate) grid edits. */\n tableCells?: string\n /** Fresh schemas of lazy and polled components by token (components outside panels). */\n components?: string\n locale: string\n /** Calls `#[Expose]` methods of custom pages (`usePageMethod()`). */\n pageCall?: string\n /** Edit pages: what changed since the form was loaded (`detectConflicts()`). */\n conflicts?: string\n upload: string\n uploadDestroy: string\n /** Starts a resumable chunked upload (`{uploadChunks}/{id}` takes chunks, `/complete` finishes). */\n uploadChunks?: string\n globalSearch: string\n /** Command palette: \"create\" links and recent records (GET). */\n commandPalette?: string\n /** Stores the user's customised keyboard shortcuts (POST `{shortcuts}`). */\n shortcuts?: string\n /** `Action::undoable()`: undoes an action from its toast (POST `{undo}`). */\n undo?: string\n /** `Dashboard::customizable()`: saves or resets the user's layout (POST `{token, layout}`). */\n dashboardLayout?: string\n notifications: string\n notificationsReadAll: string\n notificationsDestroyAll: string\n logout: string\n}\n\n/** Where toasts appear (`Panel::toastPosition()`); `start`/`end` follow the reading direction. */\nexport type ToastPosition =\n 'top-start' | 'top-center' | 'top-end' | 'bottom-start' | 'bottom-center' | 'bottom-end'\n\n/** Shown while impersonating another user (`ImpersonateAction`). */\nexport interface ImpersonationSchema {\n /** \"You are signed in as …\", translated on the server. */\n label: string\n leaveLabel: string\n /** POST here to return to the real user. */\n leaveUrl: string\n}\n\n/** The `alfakit` prop shared with every page of a panel. */\n/**\n * One `errorNotifications` entry: the text to show, or `hidden` (caught, nothing shown,\n * `hiddenErrorNotification()`) or `disabled` (default handling, `disabledErrorNotification()`).\n */\nexport interface ErrorNotificationEntrySchema {\n title?: string\n body?: string | null\n icon?: IconName\n hidden?: boolean\n disabled?: boolean\n}\n\n/** `errorNotifications`, keyed by HTTP status plus `default`. */\nexport type ErrorNotificationsSchema = Record<string, ErrorNotificationEntrySchema>\n\nexport interface PanelSchema {\n id: string\n /**\n * The schema contract version the server serialised (`SchemaContract::VERSION`). Renderers\n * compare its major part with `SCHEMA_CONTRACT_VERSION`; absent on servers before 1.0.\n */\n schemaContract?: string\n path: string\n url?: string\n /** Where \"G then D\" goes when it isn't `url`: the app's `/dashboard` for a starter kit panel without a dashboard page. */\n dashboardUrl?: string\n brandName: string\n brandLogo?: string\n darkModeBrandLogo?: string\n brandLogoHeight?: string\n favicon?: string\n /**\n * Colour custom properties without the `--alfakit-` prefix: `primary`, its readable\n * `primary-foreground` and, for palettes (`Color::Indigo`), `primary-50` … `primary-950`.\n */\n colors?: Record<string, string>\n /**\n * CSS for custom colour names (`colors(['brand' => …])`): `.alfakit-color-{name}` rules\n * that point the generic colour utilities of themes.css at `--alfakit-{name}`.\n */\n colorCss?: string\n darkMode: {\n enabled: boolean\n forced: boolean\n /** `defaultThemeMode()`; absent for `system`. */\n default?: 'light' | 'dark'\n /** `false` when `themeSwitcher(false)`: no switcher, always the default mode. */\n switcher?: false\n }\n /** `false` when `topbar(false)`: sidebar layouts render no top bar (a slim bar on phones). */\n topbar?: false\n sidebarCollapsibleOnDesktop: boolean\n sidebarFullyCollapsibleOnDesktop?: boolean\n topNavigation?: boolean\n maxContentWidth?: Width | string\n breadcrumbs: boolean\n unsavedChangesAlerts?: boolean\n /** `false` when `collapsibleNavigationGroups(false)`: groups never collapse. */\n collapsibleNavigationGroups?: false\n /** CSS lengths for the open and icon-only sidebar (`--sidebar-width`, `--sidebar-width-icon`). */\n sidebarWidth?: string\n collapsedSidebarWidth?: string\n /** Max width of `auth` layout pages (login, registration…). */\n simplePageMaxContentWidth?: Width | string\n /** `false` when `spa(false)`: every link loads a full page. */\n spa?: false\n /** URL patterns (`*` wildcard) that load with a full page request. */\n spaUrlExceptions?: string[]\n /**\n * Notifications shown instead of Inertia's error modal, keyed by HTTP status plus\n * `default`. Absent when disabled or in debug mode. A page's own `errorNotifications`\n * replaces it while that page is open.\n */\n errorNotifications?: ErrorNotificationsSchema\n theme: PanelThemeSchema\n /** Content inserted with `Panel::slot()`, keyed by slot name. */\n slots?: Record<string, SlotItemSchema[]>\n /**\n * Empty for `starterKit()` panels: their navigation is the `alfakitNavigation` page prop,\n * which every page of the host application carries (not only the panel's).\n */\n navigation: NavigationGroupSchema[]\n /**\n * The active items and badges of this request; `navigation` holds only the structure\n * (sent once). Read the navigation through `usePanel()`, which merges the two.\n */\n navigationState?: NavigationStateSchema\n /**\n * `Panel::starterKit()`: the panel runs inside a Laravel starter kit app. Its pages render\n * in the host's own layout (sidebar, header, appearance) and the host's shadcn tokens;\n * the panel's own chrome, theme attributes and colour scheme are not applied.\n */\n starterKit?: true\n user?: PanelUserSchema\n userMenu?: {\n items: MenuItemSchema[]\n profileUrl?: string\n logoutUrl: string\n /** The `profile` entry as customised in `userMenuItems()` (replaces the built-in link). */\n profile?: MenuItemSchema\n /** The customised `logout` entry, or `false` when it is hidden. */\n logout?: MenuItemSchema | false\n /**\n * `topbar` when `userMenu(position: UserMenuPosition::Topbar)`: sidebar layouts show the\n * menu in the top bar instead of the sidebar footer. Omitted for the sidebar.\n */\n position?: 'topbar'\n }\n tenancy?: {\n current?: TenantSchema | null\n tenants: TenantSchema[]\n registrationUrl?: string\n profileUrl?: string\n /** The registration page's own label (`RegisterTenant::getLabel()`); else \"New\". */\n registrationLabel?: string\n /** The profile page's own label (`EditTenantProfile::getLabel()`); else \"Settings\". */\n profileLabel?: string\n /** The customised `register` entry of `tenantMenuItems()`. */\n registration?: MenuItemSchema\n /** The customised `profile` entry of `tenantMenuItems()`. */\n profile?: MenuItemSchema\n menu?: MenuItemSchema[]\n /** `false` when `tenantSwitcher(false)`: `tenants` is empty and the menu lists no others. */\n switcher?: false\n /** `searchableTenantMenu()`, or more than 10 tenants: show a search field in the menu. */\n searchable?: boolean\n }\n databaseNotifications?: {\n pollingInterval?: string\n unreadCount: number\n /**\n * `sidebar` when `databaseNotifications(position: 'sidebar')` or `topbar(false)`: sidebar\n * layouts show the bell in the sidebar instead of the top bar. Omitted for the top bar.\n */\n position?: 'sidebar'\n }\n globalSearch?: {\n keyBindings: string[]\n placeholder: string\n /** `globalSearchDebounce()` in milliseconds; renderers default to 250. */\n debounce?: number\n /** `globalSearchFieldSuffix()`: text shown at the end of the field instead of the key hint. */\n suffix?: string\n /** `false` when `globalSearchFieldKeyBindingSuffix(false)`: no key binding hint. */\n keyBindingSuffix?: false\n /**\n * `sidebar` when `globalSearch(position: GlobalSearchPosition::Sidebar)` or\n * `topbar(false)`: sidebar layouts show the field in the sidebar header. Omitted for the top bar.\n */\n position?: 'sidebar'\n }\n endpoints: Partial<PanelEndpoints> & { logout: string }\n /** `alfakit.uploads.chunk_size` in bytes: larger files are uploaded in chunks. */\n uploadChunkSize?: number\n auth: { login?: string; register?: string; passwordReset?: string }\n flashKey: string\n /** Present while impersonating: render a banner with a way back. */\n impersonation?: ImpersonationSchema\n /**\n * `Panel::realtimeNotifications()`: the private channel to listen on with Laravel Echo;\n * with `Panel::realtime()` also the connection (`live`, `authEndpoint`, `echo`).\n */\n realtime?: RealtimeSchema\n /** Omitted for the default (`top-end`). */\n toastPosition?: ToastPosition\n /** The command palette's extras: page actions, panel commands, navigation, create links, recent records. */\n commandPalette?: CommandPaletteSchema\n /** Keyboard shortcuts (`false` with `keyboardShortcuts(false)`); absent for guests. */\n keyboardShortcuts?: KeyboardShortcutsSchema | false\n /** `Panel::pwa()`: service worker, install prompt and Web Push; absent when off. */\n pwa?: PwaSchema\n localization: LocalizationSchema\n csrf: string\n translations: Record<string, unknown>\n}\n\nexport interface BreadcrumbSchema {\n label: string\n url?: string\n}\n\nexport type ContentBlock =\n | FormBlock\n | InfolistBlock\n | SchemaBlock\n | TableBlock\n | RelationManagersBlock\n | WidgetsBlock\n | LinksBlock\n | { type: string; key: string; [key: string]: unknown }\n\nexport type PageLayout = 'app' | 'auth' | 'blank' | 'fullscreen'\n\n/** The `page` prop rendered by `Alfakit/Page`. */\nexport interface PageSchema {\n class: string\n layout: PageLayout\n title: string\n heading?: string\n subheading?: string\n breadcrumbs?: BreadcrumbSchema[]\n maxContentWidth?: Width | string\n /** `fullscreen` layout: whether to show the slim header, and where its exit link goes. */\n fullscreen?: { header: boolean; exitUrl?: string }\n /** Cluster or record sub-navigation. */\n subNavigation?: SubNavigationSchema\n headerActions?: AnyActionSchema[]\n /** `$headerActionsAlignment`; absent for `end` (the default). */\n headerActionsAlignment?: 'start' | 'center' | 'end' | 'between' | 'justify'\n /** `$extraBodyAttributes` / `getExtraBodyAttributes()`: set on `<body>` while the page is open. */\n extraBodyAttributes?: Record<string, string>\n content: ContentBlock[]\n /** Encrypted context token for internal endpoints. */\n token: string\n record?: RecordKey\n view?: { component: string; props: Record<string, unknown> }\n /**\n * An action whose modal the renderer opens once the page hydrated (`$defaultAction`,\n * `?defaultAction=`, `?tableAction=&tableActionRecord=`).\n */\n defaultAction?: DefaultActionSchema\n /**\n * Page override of the panel's `errorNotifications` (`$hasErrorNotifications`,\n * `setUpErrorNotifications()`): `false` turns them off on this page. Absent when the\n * page follows the panel.\n */\n errorNotifications?: ErrorNotificationsSchema | false\n /** Record pages with `Panel::realtime()`: who else is on the record. */\n presence?: PresenceSchema\n /** Edit pages with `detectConflicts()`: the version the form was loaded from. */\n conflicts?: ConflictsSchema\n}\n\n/** `PageSchema::defaultAction`: what to open and in which context. */\nexport interface DefaultActionSchema {\n action: ActionSchema\n /** Context component: `page`, `table`, `relationManager:…`. */\n component: string\n record?: RecordKey\n arguments?: Record<string, unknown>\n}\n\n/**\n * Fresh schemas returned by a poll, keyed by poll target: `table`, `relationManager:Class`\n * or `widget:<id or class>`.\n */\nexport type PolledComponents = Record<string, ContentBlock | WidgetSchema | RelationManagerSchema>\n\n/** Shape of `usePage().props` on an Alfakit page. */\nexport interface AlfakitPageProps {\n alfakit: PanelSchema\n page?: PageSchema\n /** Components refreshed by a poll (only present after one); merged into `page`. */\n alfakitPoll?: PolledComponents\n /**\n * Lazy widgets and relation managers (`Page::LAZY_PROP`), an Inertia deferred prop that\n * arrives right after the first response; `resolveLazyComponents()` puts them in `page`.\n */\n alfakitLazy?: PolledComponents\n /** Badges of `Tab::deferBadge()` list tabs, a deferred prop loaded after the page. */\n alfakitTabBadges?: DeferredTabBadges\n /**\n * `Panel::deferNavigationBadges()`: the navigation badges keyed by item id\n * (`navigationState`), a deferred prop loaded after the page.\n */\n alfakitNavigationBadges?: Record<string, NavigationBadgeSchema>\n alfakitAppearance?: 'light' | 'dark' | 'system'\n /** The stored sidebar state (`alfakit_sidebar_state` cookie); null when never toggled. */\n alfakitSidebarOpen?: boolean | null\n /**\n * The navigation of the application's `starterKit()` panels, shared with every Inertia\n * page (the host's own pages too) for the host sidebar. Null for guests.\n */\n alfakitNavigation?: StarterKitNavigationSchema[] | null\n /**\n * `starterKit()` panels: their dashboard widgets on the host's dashboard\n * (`starterKitDashboard()`), for `<AlfakitWidgets />`. Null on other pages, for guests\n * and users who may not open the panel.\n */\n alfakitWidgets?: StarterKitDashboardWidgetsSchema[] | null\n /**\n * The developer debug panel's data (`Page::DEBUG_PROP`): schema tree with visibility and\n * authorisation, evaluated closures and server timings. Only present while the panel's\n * debug panel is active; never in production.\n */\n alfakitDebug?: DebugSchema\n /**\n * The schema inspector's data (context identity, class links). Only present while the\n * panel's dev tools are on (`Panel::devTools()` with `app.debug`); never in production.\n */\n alfakitDevTools?: DevToolsSchema\n flash?: {\n alfakitNotifications?: NotificationSchema[]\n alfakitAction?: {\n name: string\n status: 'success' | 'halted' | 'cancelled'\n closeModal?: boolean\n deselectRecords?: boolean | null\n /** A file the handler returned: a signed, one-time link to save it from. */\n download?: string\n }\n [key: string]: unknown\n }\n errors?: Record<string, string>\n [key: string]: unknown\n}\n\nexport type { ActionSchema }\n"],"mappings":";;AAgKA,MAAa,sBAAsB;AACnC,MAAa,wBAAwB"}
1
+ {"version":3,"file":"panel.js","names":[],"sources":["../../src/schema/panel.ts"],"sourcesContent":["import type { ActionSchema, AnyActionSchema } from './actions'\nimport type { CommandPaletteSchema, KeyboardShortcutsSchema } from './command-palette'\nimport type { Color, IconName, RecordKey, Width } from './common'\nimport type { SchemaComponent } from './components'\nimport type { DebugSchema } from './debug'\nimport type { DevToolsSchema } from './dev-tools'\nimport type { FormBlock, InfolistBlock, LinksBlock, SchemaBlock } from './form'\nimport type { NotificationSchema } from './notifications'\nimport type { PwaSchema } from './pwa'\nimport type { ConflictsSchema, PresenceSchema, RealtimeSchema } from './realtime'\nimport type { DeferredTabBadges } from './resources'\nimport type { StandaloneComponentSchema } from './standalone'\nimport type { RelationManagerSchema, RelationManagersBlock, TableBlock } from './table'\nimport type { WidgetSchema, WidgetsBlock } from './widgets'\n\nexport interface NavigationItemSchema {\n /** Text, or sanitised HTML when `labelHtml` is true (an `Htmlable` label). */\n label: string\n /** `label` is sanitised HTML: render it as markup (use `htmlLabelToText()` for text). */\n labelHtml?: boolean\n icon?: IconName\n /**\n * Split navigation (`alfakit.navigation` is an Inertia once prop): the icon of the item\n * while active, swapped in by `resolvePanelNavigation()`.\n */\n activeIcon?: IconName\n url?: string\n openInNewTab?: boolean\n active?: boolean\n badge?: string | number\n badgeColor?: Color\n badgeTooltip?: string\n /** `Panel::deferNavigationBadges()`: the item has a badge that loads after the page. */\n deferredBadge?: boolean\n /** Set by `resolvePanelNavigation()` while a deferred badge is loading (show a placeholder). */\n badgePending?: boolean\n /** `NavigationItem::extraAttributes()`: HTML attributes for the item's link (`class` merges). */\n extraAttributes?: Record<string, string | number | boolean | null>\n children?: NavigationItemSchema[]\n}\n\nexport interface NavigationGroupSchema {\n label?: string | null\n icon?: IconName\n collapsible: boolean\n collapsed?: boolean\n items: NavigationItemSchema[]\n /** `NavigationGroup::extraSidebarAttributes()`: HTML attributes for the group in the sidebar. */\n extraSidebarAttributes?: Record<string, string | number | boolean | null>\n /** `NavigationGroup::extraTopbarAttributes()`: HTML attributes for the group in the top navigation. */\n extraTopbarAttributes?: Record<string, string | number | boolean | null>\n}\n\n/** A navigation item's badge, from `navigationState` or the deferred `alfakitNavigationBadges` prop. */\nexport interface NavigationBadgeSchema {\n badge: string | number\n badgeColor?: Color\n badgeTooltip?: string\n}\n\n/**\n * The per-request part of the panel navigation, whose structure (`navigation`) is an\n * Inertia once prop. Items are addressed by position: `group.item[.child…]` (`0.2.1`).\n */\nexport interface NavigationStateSchema {\n /** The active items (an active child's ancestors included). */\n active?: string[]\n /** Badges, unless `Panel::deferNavigationBadges()` defers them. */\n badges?: Record<string, NavigationBadgeSchema>\n}\n\n/** One `starterKit()` panel's navigation, as shared with the host application's pages. */\n/** One `starterKit()` panel's dashboard widgets on a host page (`alfakitWidgets` prop). */\nexport interface StarterKitDashboardWidgetsSchema {\n /** The panel id. */\n id: string\n /** A standalone widgets payload bound to the panel (lazy loading and polling included). */\n component: StandaloneComponentSchema\n}\n\n/** `<html lang dir>` of a page in a starter kit app (`alfakitDocument`). */\nexport interface StarterKitDocumentSchema {\n /** BCP 47 tag of the page's language (`ar`, `en-GB`). */\n tag: string\n /** The direction of the language picked for the `starterKit()` panel. */\n direction: 'ltr' | 'rtl'\n}\n\nexport interface StarterKitNavigationSchema {\n /** The panel id. */\n id: string\n /** The panel's home URL. */\n url: string\n brandName: string\n /** Empty for tenancy panels outside a tenant (their items link to tenant routes). */\n groups: NavigationGroupSchema[]\n /**\n * The panel's own user menu links (`userMenuItems()`, plugin entries such as Billing's),\n * for `<AlfakitUserMenuItems />` in the kit's user menu. Links only (entries with server\n * logic or a modal need an AlfaKit page); omitted when there are none, and for tenancy\n * panels outside a tenant.\n */\n userMenu?: MenuItemSchema[]\n /** What the drop-in header components render before they load. */\n widgets?: StarterKitWidgetsSchema\n}\n\n/**\n * The `widgets` of a `starterKit()` panel's shared navigation: what `<AlfakitSearch />`,\n * `<AlfakitNotifications />`, `<AlfakitTenantSwitcher />` and `<AlfakitLanguageSwitcher />`\n * show on any host page before their code and the panel data (`endpoint`) have loaded.\n */\nexport interface StarterKitWidgetsSchema {\n /** GET: the panel data the components render with (the `alfakit` prop of the panel's pages). */\n endpoint: string\n /** Global search or the command palette (tenancy panels: only inside a tenant). */\n search?: { keyBindings: string[]; placeholder: string }\n /** `databaseNotifications()`: the bell's label and the user's unread count. */\n notifications?: { label: string; unreadCount: number }\n /** Tenancy panels: the user's tenants; `current` is null on the host's own pages. */\n tenancy?: {\n current?: TenantSchema | null\n tenants: TenantSchema[]\n registrationUrl?: string\n /** The registration page's own label (`RegisterTenant::getLabel()`). */\n registrationLabel?: string\n searchable?: true\n }\n /**\n * `<AlfakitLanguageSwitcher />`: the button's label (\"Change language\") and the current\n * language, when the panel has a language switcher with two or more languages\n * (`locales()`). The languages and the locale endpoint come with the panel data.\n */\n language?: { label: string; current: LocaleSchema }\n /** While impersonating: the banner the header components show on the host's own pages. */\n impersonation?: ImpersonationSchema\n /**\n * `AiPlugin::copilot()` (alfasix/alfakit-ai, `$panel->starterKitWidget('copilot')`): the\n * label and shortcuts of `<AlfakitCopilot />` before its code loads. Tenancy panels: only\n * inside a tenant.\n */\n copilot?: { label: string; keyBindings: string[] }\n}\n\n/** A page's secondary navigation: cluster pages or a record's pages. */\nexport interface SubNavigationSchema {\n position: 'start' | 'end' | 'top'\n groups: NavigationGroupSchema[]\n}\n\n/**\n * An entry of the user or tenant menu: a link (`url`, `method: 'post'` for POST links) or\n * an `Action` with server logic or a modal (`action`), run through the action endpoint\n * with the component `userMenu` / `tenantMenu` and the page's token.\n */\nexport interface MenuItemSchema {\n label?: string\n icon?: IconName\n url?: string\n openInNewTab?: boolean\n color?: Color\n method?: 'post'\n action?: ActionSchema\n /** Group index (nested arrays in `userMenuItems()`); a separator divides groups. */\n group?: number\n}\n\n/** Action endpoint components of the menus (`Panel::USER_MENU_COMPONENT`, …). */\nexport const USER_MENU_COMPONENT = 'userMenu'\nexport const TENANT_MENU_COMPONENT = 'tenantMenu'\n\nexport interface PanelUserSchema {\n id: RecordKey\n name: string\n email?: string | null\n avatar?: string | null\n}\n\nexport interface TenantSchema {\n id: RecordKey\n /** Text, or sanitised HTML when `nameHtml` is true (an `Htmlable` `getAlfakitName()`). */\n name: string\n /** `name` is sanitised HTML: render it as markup (use `htmlLabelToText()` for text). */\n nameHtml?: boolean\n avatar?: string | null\n url: string\n /** `HasCurrentTenantLabel`: a caption shown with the current tenant (\"Active team\"). */\n label?: string | null\n}\n\n/** The panel's design, see `Panel::theme()`. */\nexport interface PanelThemeSchema {\n name: string\n /** Value of `data-alfakit-theme`, base theme first: `\"alfakit acme\"`. */\n attribute: string\n sidebar: 'inset' | 'floating' | 'flush'\n density: 'comfortable' | 'compact'\n radius?: string | null\n font?: string | null\n /** CSS value for `--alfakit-font`. */\n fontCss?: string | null\n /** CSS value for `--alfakit-heading-font`. */\n headingFontCss?: string | null\n /** Stylesheet that loads the font, if any. */\n fontUrl?: string | null\n /** Every font stylesheet to load (body and heading fonts). */\n fontUrls?: string[]\n /** Structure and component styles; mirrored as `data-*` attributes on `<html>`. */\n design: PanelDesignSchema\n /**\n * Icon aliases replaced with `Icons::register()` (`alfakit::panels.sidebar.collapse-button`\n * → a Lucide name, image URL or sanitised SVG). The chrome falls back to its own icon.\n */\n icons?: Record<string, string> | null\n}\n\nexport interface PanelDesignSchema {\n layout: 'sidebar' | 'topbar' | 'rail'\n nav: 'soft' | 'pill' | 'bar' | 'underline'\n header: 'plain' | 'banner' | 'band' | 'compact'\n surfaces: 'bordered' | 'elevated' | 'flat' | 'ring'\n inputs: 'outline' | 'filled' | 'underline'\n buttons: 'rounded' | 'pill' | 'square'\n tables: 'lined' | 'striped' | 'minimal'\n /**\n * Below the `md` breakpoint: `drawer` (default) or the `bottom` mobile preset (bottom\n * navigation bar, tables as stacked cards, slide-over modals, 44px touch targets and\n * sticky form actions). See `Panel::mobile()`.\n */\n mobile?: 'drawer' | 'bottom'\n}\n\n/**\n * One piece of slot content: server-rendered HTML or a schema component. `for` lists the\n * components (`relationManager:<class>`, `widget:<id>`) it is scoped to: such items only\n * render in a `<Slot scope=\"…\">` of a matching component. `when` (`Panel::slot(…, when:)`)\n * limits it to renders whose slot data matches: `{ column: 'title' }` only renders in the\n * `table.header-cell` slot of the `title` column; a list value matches any of its entries.\n */\nexport type SlotItemSchema = (\n { type: 'html'; html: string } | { type: 'component'; component: SchemaComponent }\n) & { for?: string[]; when?: Record<string, SlotDataValue | SlotDataValue[]> }\n\n/** A value in a slot's render data (`<Slot data={{ column: 'title' }}>`). */\nexport type SlotDataValue = string | number | boolean | null\n\n/** A language the panel can be displayed in. */\nexport interface LocaleSchema {\n /** Laravel-style code: `en`, `pt_BR`, `zh_Hant`. */\n code: string\n /** BCP 47 tag for `Intl` and the `lang` attribute: `pt-BR`. */\n tag: string\n /** The language's own name: `Deutsch`, `العربية`. */\n label: string\n direction: 'ltr' | 'rtl'\n}\n\n/** The current request's language, formats and the panel's language list. */\nexport interface LocalizationSchema extends LocaleSchema {\n fallbackLocale: string\n /** 0 = Sunday … 6 = Saturday. */\n firstDayOfWeek: number\n /** Default ISO 4217 currency. */\n currency: string\n /** IANA timezone dates are displayed in. */\n timezone: string\n /** Whether to show a language menu. */\n switcher: boolean\n /**\n * Where panel pages offer the menu (`Panel::languageSwitcher()`): the user menu (default\n * when absent), a top bar button, both, or neither. Auth pages keep their switcher.\n */\n switcherPosition?: LanguageSwitcherPosition\n /** Languages to offer in the menu (empty when the switcher is off). */\n locales: LocaleSchema[]\n}\n\nexport type LanguageSwitcherPosition = 'user-menu' | 'topbar' | 'both' | 'hidden'\n\nexport interface PanelEndpoints {\n action: string\n modal: string\n schemaRefresh: string\n schemaOptions: string\n /** Validates one wizard step's fields (`step` = the Step component's key). */\n schemaValidate?: string\n /** Loads `deferLoading()` children and `Tab::deferBadge()` badges (`{schema}::{key}`). */\n schemaDeferred?: string\n tableCell: string\n tableReorder: string\n /** Stores a user's column order (`reorderableColumns()`). */\n tableColumns?: string\n /** `Table::deferTotals()`: the record count and summaries, loaded after the rows. */\n tableTotals?: string\n /** `Table::savedViews()`: save, share, rename, delete and pick a default view. */\n tableViews?: string\n /** `Table::spreadsheet()`: batch save (or validate) grid edits. */\n tableCells?: string\n /** Fresh schemas of lazy and polled components by token (components outside panels). */\n components?: string\n locale: string\n /** Calls `#[Expose]` methods of custom pages (`usePageMethod()`). */\n pageCall?: string\n /** Edit pages: what changed since the form was loaded (`detectConflicts()`). */\n conflicts?: string\n upload: string\n uploadDestroy: string\n /** Starts a resumable chunked upload (`{uploadChunks}/{id}` takes chunks, `/complete` finishes). */\n uploadChunks?: string\n globalSearch: string\n /** Command palette: \"create\" links and recent records (GET). */\n commandPalette?: string\n /** Stores the user's customised keyboard shortcuts (POST `{shortcuts}`). */\n shortcuts?: string\n /** `Action::undoable()`: undoes an action from its toast (POST `{undo}`). */\n undo?: string\n /** `Dashboard::customizable()`: saves or resets the user's layout (POST `{token, layout}`). */\n dashboardLayout?: string\n notifications: string\n notificationsReadAll: string\n notificationsDestroyAll: string\n logout: string\n}\n\n/** Where toasts appear (`Panel::toastPosition()`); `start`/`end` follow the reading direction. */\nexport type ToastPosition =\n 'top-start' | 'top-center' | 'top-end' | 'bottom-start' | 'bottom-center' | 'bottom-end'\n\n/** Shown while impersonating another user (`ImpersonateAction`). */\nexport interface ImpersonationSchema {\n /** \"You are signed in as …\", translated on the server. */\n label: string\n leaveLabel: string\n /** POST here to return to the real user. */\n leaveUrl: string\n}\n\n/** The `alfakit` prop shared with every page of a panel. */\n/**\n * One `errorNotifications` entry: the text to show, or `hidden` (caught, nothing shown,\n * `hiddenErrorNotification()`) or `disabled` (default handling, `disabledErrorNotification()`).\n */\nexport interface ErrorNotificationEntrySchema {\n title?: string\n body?: string | null\n icon?: IconName\n hidden?: boolean\n disabled?: boolean\n}\n\n/** `errorNotifications`, keyed by HTTP status plus `default`. */\nexport type ErrorNotificationsSchema = Record<string, ErrorNotificationEntrySchema>\n\nexport interface PanelSchema {\n id: string\n /**\n * The schema contract version the server serialised (`SchemaContract::VERSION`). Renderers\n * compare its major part with `SCHEMA_CONTRACT_VERSION`; absent on servers before 1.0.\n */\n schemaContract?: string\n path: string\n url?: string\n /** Where \"G then D\" goes when it isn't `url`: the app's `/dashboard` for a starter kit panel without a dashboard page. */\n dashboardUrl?: string\n brandName: string\n brandLogo?: string\n darkModeBrandLogo?: string\n brandLogoHeight?: string\n favicon?: string\n /**\n * Colour custom properties without the `--alfakit-` prefix: `primary`, its readable\n * `primary-foreground` and, for palettes (`Color::Indigo`), `primary-50` … `primary-950`.\n */\n colors?: Record<string, string>\n /**\n * CSS for custom colour names (`colors(['brand' => …])`): `.alfakit-color-{name}` rules\n * that point the generic colour utilities of themes.css at `--alfakit-{name}`.\n */\n colorCss?: string\n darkMode: {\n enabled: boolean\n forced: boolean\n /** `defaultThemeMode()`; absent for `system`. */\n default?: 'light' | 'dark'\n /** `false` when `themeSwitcher(false)`: no switcher, always the default mode. */\n switcher?: false\n }\n /** `false` when `topbar(false)`: sidebar layouts render no top bar (a slim bar on phones). */\n topbar?: false\n sidebarCollapsibleOnDesktop: boolean\n sidebarFullyCollapsibleOnDesktop?: boolean\n topNavigation?: boolean\n maxContentWidth?: Width | string\n breadcrumbs: boolean\n unsavedChangesAlerts?: boolean\n /** `false` when `collapsibleNavigationGroups(false)`: groups never collapse. */\n collapsibleNavigationGroups?: false\n /** CSS lengths for the open and icon-only sidebar (`--sidebar-width`, `--sidebar-width-icon`). */\n sidebarWidth?: string\n collapsedSidebarWidth?: string\n /** Max width of `auth` layout pages (login, registration…). */\n simplePageMaxContentWidth?: Width | string\n /** `false` when `spa(false)`: every link loads a full page. */\n spa?: false\n /** URL patterns (`*` wildcard) that load with a full page request. */\n spaUrlExceptions?: string[]\n /**\n * Notifications shown instead of Inertia's error modal, keyed by HTTP status plus\n * `default`. Absent when disabled or in debug mode. A page's own `errorNotifications`\n * replaces it while that page is open.\n */\n errorNotifications?: ErrorNotificationsSchema\n theme: PanelThemeSchema\n /** Content inserted with `Panel::slot()`, keyed by slot name. */\n slots?: Record<string, SlotItemSchema[]>\n /**\n * Empty for `starterKit()` panels: their navigation is the `alfakitNavigation` page prop,\n * which every page of the host application carries (not only the panel's).\n */\n navigation: NavigationGroupSchema[]\n /**\n * The active items and badges of this request; `navigation` holds only the structure\n * (sent once). Read the navigation through `usePanel()`, which merges the two.\n */\n navigationState?: NavigationStateSchema\n /**\n * `Panel::starterKit()`: the panel runs inside a Laravel starter kit app. Its pages render\n * in the host's own layout (sidebar, header, appearance) and the host's shadcn tokens;\n * the panel's own chrome, theme attributes and colour scheme are not applied.\n */\n starterKit?: true\n user?: PanelUserSchema\n userMenu?: {\n items: MenuItemSchema[]\n profileUrl?: string\n logoutUrl: string\n /** The `profile` entry as customised in `userMenuItems()` (replaces the built-in link). */\n profile?: MenuItemSchema\n /** The customised `logout` entry, or `false` when it is hidden. */\n logout?: MenuItemSchema | false\n /**\n * `topbar` when `userMenu(position: UserMenuPosition::Topbar)`: sidebar layouts show the\n * menu in the top bar instead of the sidebar footer. Omitted for the sidebar.\n */\n position?: 'topbar'\n }\n tenancy?: {\n current?: TenantSchema | null\n tenants: TenantSchema[]\n registrationUrl?: string\n profileUrl?: string\n /** The registration page's own label (`RegisterTenant::getLabel()`); else \"New\". */\n registrationLabel?: string\n /** The profile page's own label (`EditTenantProfile::getLabel()`); else \"Settings\". */\n profileLabel?: string\n /** The customised `register` entry of `tenantMenuItems()`. */\n registration?: MenuItemSchema\n /** The customised `profile` entry of `tenantMenuItems()`. */\n profile?: MenuItemSchema\n menu?: MenuItemSchema[]\n /** `false` when `tenantSwitcher(false)`: `tenants` is empty and the menu lists no others. */\n switcher?: false\n /** `searchableTenantMenu()`, or more than 10 tenants: show a search field in the menu. */\n searchable?: boolean\n }\n databaseNotifications?: {\n pollingInterval?: string\n unreadCount: number\n /**\n * `sidebar` when `databaseNotifications(position: 'sidebar')` or `topbar(false)`: sidebar\n * layouts show the bell in the sidebar instead of the top bar. Omitted for the top bar.\n */\n position?: 'sidebar'\n }\n globalSearch?: {\n keyBindings: string[]\n placeholder: string\n /** `globalSearchDebounce()` in milliseconds; renderers default to 250. */\n debounce?: number\n /** `globalSearchFieldSuffix()`: text shown at the end of the field instead of the key hint. */\n suffix?: string\n /** `false` when `globalSearchFieldKeyBindingSuffix(false)`: no key binding hint. */\n keyBindingSuffix?: false\n /**\n * `sidebar` when `globalSearch(position: GlobalSearchPosition::Sidebar)` or\n * `topbar(false)`: sidebar layouts show the field in the sidebar header. Omitted for the top bar.\n */\n position?: 'sidebar'\n }\n endpoints: Partial<PanelEndpoints> & { logout: string }\n /** `alfakit.uploads.chunk_size` in bytes: larger files are uploaded in chunks. */\n uploadChunkSize?: number\n auth: { login?: string; register?: string; passwordReset?: string }\n flashKey: string\n /** Present while impersonating: render a banner with a way back. */\n impersonation?: ImpersonationSchema\n /**\n * `Panel::realtimeNotifications()`: the private channel to listen on with Laravel Echo;\n * with `Panel::realtime()` also the connection (`live`, `authEndpoint`, `echo`).\n */\n realtime?: RealtimeSchema\n /** Omitted for the default (`top-end`). */\n toastPosition?: ToastPosition\n /** The command palette's extras: page actions, panel commands, navigation, create links, recent records. */\n commandPalette?: CommandPaletteSchema\n /** Keyboard shortcuts (`false` with `keyboardShortcuts(false)`); absent for guests. */\n keyboardShortcuts?: KeyboardShortcutsSchema | false\n /** `Panel::pwa()`: service worker, install prompt and Web Push; absent when off. */\n pwa?: PwaSchema\n localization: LocalizationSchema\n csrf: string\n translations: Record<string, unknown>\n}\n\nexport interface BreadcrumbSchema {\n label: string\n url?: string\n}\n\nexport type ContentBlock =\n | FormBlock\n | InfolistBlock\n | SchemaBlock\n | TableBlock\n | RelationManagersBlock\n | WidgetsBlock\n | LinksBlock\n | { type: string; key: string; [key: string]: unknown }\n\nexport type PageLayout = 'app' | 'auth' | 'blank' | 'fullscreen'\n\n/** The `page` prop rendered by `Alfakit/Page`. */\nexport interface PageSchema {\n class: string\n layout: PageLayout\n title: string\n heading?: string\n subheading?: string\n breadcrumbs?: BreadcrumbSchema[]\n maxContentWidth?: Width | string\n /** `fullscreen` layout: whether to show the slim header, and where its exit link goes. */\n fullscreen?: { header: boolean; exitUrl?: string }\n /** Cluster or record sub-navigation. */\n subNavigation?: SubNavigationSchema\n headerActions?: AnyActionSchema[]\n /** `$headerActionsAlignment`; absent for `end` (the default). */\n headerActionsAlignment?: 'start' | 'center' | 'end' | 'between' | 'justify'\n /** `$extraBodyAttributes` / `getExtraBodyAttributes()`: set on `<body>` while the page is open. */\n extraBodyAttributes?: Record<string, string>\n content: ContentBlock[]\n /** Encrypted context token for internal endpoints. */\n token: string\n record?: RecordKey\n view?: { component: string; props: Record<string, unknown> }\n /**\n * An action whose modal the renderer opens once the page hydrated (`$defaultAction`,\n * `?defaultAction=`, `?tableAction=&tableActionRecord=`).\n */\n defaultAction?: DefaultActionSchema\n /**\n * Page override of the panel's `errorNotifications` (`$hasErrorNotifications`,\n * `setUpErrorNotifications()`): `false` turns them off on this page. Absent when the\n * page follows the panel.\n */\n errorNotifications?: ErrorNotificationsSchema | false\n /** Record pages with `Panel::realtime()`: who else is on the record. */\n presence?: PresenceSchema\n /** Edit pages with `detectConflicts()`: the version the form was loaded from. */\n conflicts?: ConflictsSchema\n}\n\n/** `PageSchema::defaultAction`: what to open and in which context. */\nexport interface DefaultActionSchema {\n action: ActionSchema\n /** Context component: `page`, `table`, `relationManager:…`. */\n component: string\n record?: RecordKey\n arguments?: Record<string, unknown>\n}\n\n/**\n * Fresh schemas returned by a poll, keyed by poll target: `table`, `relationManager:Class`\n * or `widget:<id or class>`.\n */\nexport type PolledComponents = Record<string, ContentBlock | WidgetSchema | RelationManagerSchema>\n\n/** Shape of `usePage().props` on an Alfakit page. */\nexport interface AlfakitPageProps {\n alfakit: PanelSchema\n page?: PageSchema\n /** Components refreshed by a poll (only present after one); merged into `page`. */\n alfakitPoll?: PolledComponents\n /**\n * Lazy widgets and relation managers (`Page::LAZY_PROP`), an Inertia deferred prop that\n * arrives right after the first response; `resolveLazyComponents()` puts them in `page`.\n */\n alfakitLazy?: PolledComponents\n /** Badges of `Tab::deferBadge()` list tabs, a deferred prop loaded after the page. */\n alfakitTabBadges?: DeferredTabBadges\n /**\n * `Panel::deferNavigationBadges()`: the navigation badges keyed by item id\n * (`navigationState`), a deferred prop loaded after the page.\n */\n alfakitNavigationBadges?: Record<string, NavigationBadgeSchema>\n alfakitAppearance?: 'light' | 'dark' | 'system'\n /** The stored sidebar state (`alfakit_sidebar_state` cookie); null when never toggled. */\n alfakitSidebarOpen?: boolean | null\n /**\n * The navigation of the application's `starterKit()` panels, shared with every Inertia\n * page (the host's own pages too) for the host sidebar. Null for guests.\n */\n alfakitNavigation?: StarterKitNavigationSchema[] | null\n /**\n * `<html lang dir>` while the app has a `starterKit()` panel, shared with every Inertia page\n * (the host's own pages and guests too): the app's current language and the direction of\n * the language picked for the panel, so the whole page flips in a right-to-left language.\n * Null on pages of other panels, which set the document themselves.\n */\n alfakitDocument?: StarterKitDocumentSchema | null\n /**\n * `starterKit()` panels: their dashboard widgets on the host's dashboard\n * (`starterKitDashboard()`), for `<AlfakitWidgets />`. Null on other pages, for guests\n * and users who may not open the panel.\n */\n alfakitWidgets?: StarterKitDashboardWidgetsSchema[] | null\n /**\n * The developer debug panel's data (`Page::DEBUG_PROP`): schema tree with visibility and\n * authorisation, evaluated closures and server timings. Only present while the panel's\n * debug panel is active; never in production.\n */\n alfakitDebug?: DebugSchema\n /**\n * The schema inspector's data (context identity, class links). Only present while the\n * panel's dev tools are on (`Panel::devTools()` with `app.debug`); never in production.\n */\n alfakitDevTools?: DevToolsSchema\n flash?: {\n alfakitNotifications?: NotificationSchema[]\n alfakitAction?: {\n name: string\n status: 'success' | 'halted' | 'cancelled'\n closeModal?: boolean\n deselectRecords?: boolean | null\n /** A file the handler returned: a signed, one-time link to save it from. */\n download?: string\n }\n [key: string]: unknown\n }\n errors?: Record<string, string>\n [key: string]: unknown\n}\n\nexport type { ActionSchema }\n"],"mappings":";;AAwKA,MAAa,sBAAsB;AACnC,MAAa,wBAAwB"}
@@ -0,0 +1,32 @@
1
+ import { StarterKitDocumentSchema } from "./schema/panel.js";
2
+ //#region src/starter-kit-document.d.ts
3
+ /**
4
+ * `<html lang dir>` in a starter kit app. The server shares `alfakitDocument` with every
5
+ * Inertia page while a `starterKit()` panel exists: in a right-to-left panel language the
6
+ * whole page (the kit's sidebar, header and own pages too) is laid out right to left. The
7
+ * kit's root view renders the same `dir` (`StarterKitDocument::direction()`), so these keep
8
+ * it in step after visits and language switches.
9
+ *
10
+ * Its own small module: the host's sidebar and header call it on every page, without the
11
+ * panel runtime.
12
+ */
13
+ /** The shared `alfakitDocument` prop of a page, or null (pages of other panels, older servers). */
14
+ export declare function starterKitDocument(props: unknown): StarterKitDocumentSchema | null;
15
+ /** The page's reading direction in a starter kit app: `rtl` in a right-to-left panel language. */
16
+ export declare function starterKitDirection(props: unknown): 'ltr' | 'rtl';
17
+ /**
18
+ * Which side the kit's shadcn `<Sidebar>` docks on: the end of the line, so the right in a
19
+ * right-to-left language (`side` is physical in shadcn).
20
+ */
21
+ export declare function starterKitSidebarSide(props: unknown): 'left' | 'right';
22
+ /** Put a page's `alfakitDocument` on `<html lang dir>`; nothing without it. */
23
+ export declare function applyStarterKitDocument(props: unknown): void;
24
+ /**
25
+ * Apply `props` (the current page's) to `<html lang dir>` and, once per page load, follow
26
+ * every later visit (a language switch is one: it redirects back). Called by the starter
27
+ * kit's entry points (the sidebar navigation and direction hooks, the header components, the
28
+ * dashboard widgets, AlfaKit pages), so whichever renders first starts it. No-op on the server.
29
+ */
30
+ export declare function installStarterKitDocument(props: unknown): void;
31
+ //#endregion
32
+ //# sourceMappingURL=starter-kit-document.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"starter-kit-document.d.ts","names":[],"sources":["../src/starter-kit-document.ts"],"mappings":";;;;;;;;;;;;;wBAgBgB,mBAAmB,iBAAiB;;wBAapC,oBAAoB;;;;;wBAQpB,sBAAsB;;wBAKtB,wBAAwB;;;;;;;wBAcxB,0BAA0B"}
@@ -0,0 +1,57 @@
1
+ import { applyDocumentLocale } from "./i18n.js";
2
+ import { router } from "@inertiajs/core";
3
+ //#region src/starter-kit-document.ts
4
+ /**
5
+ * `<html lang dir>` in a starter kit app. The server shares `alfakitDocument` with every
6
+ * Inertia page while a `starterKit()` panel exists: in a right-to-left panel language the
7
+ * whole page (the kit's sidebar, header and own pages too) is laid out right to left. The
8
+ * kit's root view renders the same `dir` (`StarterKitDocument::direction()`), so these keep
9
+ * it in step after visits and language switches.
10
+ *
11
+ * Its own small module: the host's sidebar and header call it on every page, without the
12
+ * panel runtime.
13
+ */
14
+ /** The shared `alfakitDocument` prop of a page, or null (pages of other panels, older servers). */
15
+ function starterKitDocument(props) {
16
+ const value = props?.alfakitDocument;
17
+ if (!value || typeof value !== "object") return null;
18
+ const { tag, direction } = value;
19
+ return typeof tag === "string" && (direction === "ltr" || direction === "rtl") ? {
20
+ tag,
21
+ direction
22
+ } : null;
23
+ }
24
+ /** The page's reading direction in a starter kit app: `rtl` in a right-to-left panel language. */
25
+ function starterKitDirection(props) {
26
+ return starterKitDocument(props)?.direction ?? "ltr";
27
+ }
28
+ /**
29
+ * Which side the kit's shadcn `<Sidebar>` docks on: the end of the line, so the right in a
30
+ * right-to-left language (`side` is physical in shadcn).
31
+ */
32
+ function starterKitSidebarSide(props) {
33
+ return starterKitDirection(props) === "rtl" ? "right" : "left";
34
+ }
35
+ /** Put a page's `alfakitDocument` on `<html lang dir>`; nothing without it. */
36
+ function applyStarterKitDocument(props) {
37
+ const document = starterKitDocument(props);
38
+ if (document) applyDocumentLocale(document);
39
+ }
40
+ let listening = false;
41
+ /**
42
+ * Apply `props` (the current page's) to `<html lang dir>` and, once per page load, follow
43
+ * every later visit (a language switch is one: it redirects back). Called by the starter
44
+ * kit's entry points (the sidebar navigation and direction hooks, the header components, the
45
+ * dashboard widgets, AlfaKit pages), so whichever renders first starts it. No-op on the server.
46
+ */
47
+ function installStarterKitDocument(props) {
48
+ if (typeof window === "undefined") return;
49
+ applyStarterKitDocument(props);
50
+ if (listening) return;
51
+ listening = true;
52
+ router.on("navigate", (event) => applyStarterKitDocument(event.detail.page.props));
53
+ }
54
+ //#endregion
55
+ export { applyStarterKitDocument, installStarterKitDocument, starterKitDirection, starterKitDocument, starterKitSidebarSide };
56
+
57
+ //# sourceMappingURL=starter-kit-document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"starter-kit-document.js","names":[],"sources":["../src/starter-kit-document.ts"],"sourcesContent":["import { router } from '@inertiajs/core'\nimport { applyDocumentLocale } from './i18n'\nimport type { StarterKitDocumentSchema } from './schema/panel'\n\n/**\n * `<html lang dir>` in a starter kit app. The server shares `alfakitDocument` with every\n * Inertia page while a `starterKit()` panel exists: in a right-to-left panel language the\n * whole page (the kit's sidebar, header and own pages too) is laid out right to left. The\n * kit's root view renders the same `dir` (`StarterKitDocument::direction()`), so these keep\n * it in step after visits and language switches.\n *\n * Its own small module: the host's sidebar and header call it on every page, without the\n * panel runtime.\n */\n\n/** The shared `alfakitDocument` prop of a page, or null (pages of other panels, older servers). */\nexport function starterKitDocument(props: unknown): StarterKitDocumentSchema | null {\n const value = (props as { alfakitDocument?: unknown } | null | undefined)?.alfakitDocument\n\n if (!value || typeof value !== 'object') return null\n\n const { tag, direction } = value as Partial<StarterKitDocumentSchema>\n\n return typeof tag === 'string' && (direction === 'ltr' || direction === 'rtl')\n ? { tag, direction }\n : null\n}\n\n/** The page's reading direction in a starter kit app: `rtl` in a right-to-left panel language. */\nexport function starterKitDirection(props: unknown): 'ltr' | 'rtl' {\n return starterKitDocument(props)?.direction ?? 'ltr'\n}\n\n/**\n * Which side the kit's shadcn `<Sidebar>` docks on: the end of the line, so the right in a\n * right-to-left language (`side` is physical in shadcn).\n */\nexport function starterKitSidebarSide(props: unknown): 'left' | 'right' {\n return starterKitDirection(props) === 'rtl' ? 'right' : 'left'\n}\n\n/** Put a page's `alfakitDocument` on `<html lang dir>`; nothing without it. */\nexport function applyStarterKitDocument(props: unknown): void {\n const document = starterKitDocument(props)\n\n if (document) applyDocumentLocale(document)\n}\n\nlet listening = false\n\n/**\n * Apply `props` (the current page's) to `<html lang dir>` and, once per page load, follow\n * every later visit (a language switch is one: it redirects back). Called by the starter\n * kit's entry points (the sidebar navigation and direction hooks, the header components, the\n * dashboard widgets, AlfaKit pages), so whichever renders first starts it. No-op on the server.\n */\nexport function installStarterKitDocument(props: unknown): void {\n if (typeof window === 'undefined') return\n\n applyStarterKitDocument(props)\n\n if (listening) return\n\n listening = true\n router.on('navigate', (event) => applyStarterKitDocument(event.detail.page.props))\n}\n"],"mappings":";;;;;;;;;;;;;;AAgBA,SAAgB,mBAAmB,OAAiD;CAClF,MAAM,QAAS,OAA4D;CAE3E,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU,OAAO;CAEhD,MAAM,EAAE,KAAK,cAAc;CAE3B,OAAO,OAAO,QAAQ,aAAa,cAAc,SAAS,cAAc,SACpE;EAAE;EAAK;CAAU,IACjB;AACN;;AAGA,SAAgB,oBAAoB,OAA+B;CACjE,OAAO,mBAAmB,KAAK,CAAC,EAAE,aAAa;AACjD;;;;;AAMA,SAAgB,sBAAsB,OAAkC;CACtE,OAAO,oBAAoB,KAAK,MAAM,QAAQ,UAAU;AAC1D;;AAGA,SAAgB,wBAAwB,OAAsB;CAC5D,MAAM,WAAW,mBAAmB,KAAK;CAEzC,IAAI,UAAU,oBAAoB,QAAQ;AAC5C;AAEA,IAAI,YAAY;;;;;;;AAQhB,SAAgB,0BAA0B,OAAsB;CAC9D,IAAI,OAAO,WAAW,aAAa;CAEnC,wBAAwB,KAAK;CAE7B,IAAI,WAAW;CAEf,YAAY;CACZ,OAAO,GAAG,aAAa,UAAU,wBAAwB,MAAM,OAAO,KAAK,KAAK,CAAC;AACnF"}
@@ -21,7 +21,10 @@ export declare function columnGroupHeaderRow(columns: ColumnSchema[], groups: Co
21
21
  export declare function isGroupedColumn(name: string, groups: ColumnGroupSchema[] | undefined): boolean;
22
22
  /** Column names referenced anywhere in a content layout, in order. */
23
23
  export declare function layoutColumnNames(nodes: ColumnLayoutNode[] | undefined): string[];
24
- /** Classes of a `Split`: side by side from its breakpoint, stacked below it. */
24
+ /**
25
+ * Classes of a `Split`: side by side once the record is wide enough for its `from`
26
+ * breakpoint, stacked below it. Needs the record's `@container` (`data-slot="table-record"`).
27
+ */
25
28
  export declare function splitClasses(layout: Pick<SplitLayoutSchema, 'from'>): string;
26
29
  /** Responsive visibility classes for `visibleFrom()` / `hiddenFrom()`. */
27
30
  export declare function layoutVisibilityClasses(layout: {
@@ -1 +1 @@
1
- {"version":3,"file":"table-layout.d.ts","names":[],"sources":["../src/table-layout.ts"],"mappings":";;;;;;;iBAmBiB;EACf;EACA;EACA,QAAQ;EACR,SAAS;;;;;;;wBAQK,qBACd,SAAS,gBACT,QAAQ,kCACP;;wBAgCa,gBAAgB,cAAc,QAAQ;;wBAKtC,kBAAkB,OAAO;;wBAwBzB,aAAa,QAAQ,KAAK;;wBAqB1B,wBAAwB;EACtC,cAAc;EACd,aAAa;;;;;;wBA8BC,wBACd,QAAQ,KAAK,iDACb;;wBAqBc,aAAa,QAAQ,KAAK;;wBAW1B,gBAAgB,MAAM,kBAAkB,SAAS;;wBASjD,qBAAqB,UAAU;;wBAK/B,gBAAgB,YAAY;;wBAS5B,sBACd,YAAY,yCACX,cAAc,QAAQ;EAAuB;;;;;;wBAQhC,cACd,YAAY,yCACX;;wBAoBa,kBACd,YAAY,yCACX;;wBAWa,WAAW,0BAA0B,cAAc;;wBAiBnD,aAAa,0BAA0B,cAAc;;;;;;iBAqBpD;EACf;EACA;EACA;;wBAsCc,sBACd,0BACA,cACA,kBAAkB,sBACjB;;;;;;wBAsBa,oBACd,0BACA,cACA,gBACA,kBAAkB;;wBAgCJ,eAAe;EAAY;GACzC,kBAAkB,KAClB,8CACC;;wBAaa,mBAAmB,4BAA4B;;;;;wBAQ/C,oBAAoB,QAAQ;;;;;;;wBA8BtB,gBACpB,kBACA,eACA,mBACA,mBACA;EAAW;IACV"}
1
+ {"version":3,"file":"table-layout.d.ts","names":[],"sources":["../src/table-layout.ts"],"mappings":";;;;;;;iBAmBiB;EACf;EACA;EACA,QAAQ;EACR,SAAS;;;;;;;wBAQK,qBACd,SAAS,gBACT,QAAQ,kCACP;;wBAgCa,gBAAgB,cAAc,QAAQ;;wBAKtC,kBAAkB,OAAO;;;;;wBAgCzB,aAAa,QAAQ,KAAK;;wBAqB1B,wBAAwB;EACtC,cAAc;EACd,aAAa;;;;;;wBA8BC,wBACd,QAAQ,KAAK,iDACb;;wBAqBc,aAAa,QAAQ,KAAK;;wBAW1B,gBAAgB,MAAM,kBAAkB,SAAS;;wBASjD,qBAAqB,UAAU;;wBAK/B,gBAAgB,YAAY;;wBAS5B,sBACd,YAAY,yCACX,cAAc,QAAQ;EAAuB;;;;;;wBAQhC,cACd,YAAY,yCACX;;wBAoBa,kBACd,YAAY,yCACX;;wBAWa,WAAW,0BAA0B,cAAc;;wBAiBnD,aAAa,0BAA0B,cAAc;;;;;;iBAqBpD;EACf;EACA;EACA;;wBAsCc,sBACd,0BACA,cACA,kBAAkB,sBACjB;;;;;;wBAsBa,oBACd,0BACA,cACA,gBACA,kBAAkB;;wBAgCJ,eAAe;EAAY;GACzC,kBAAkB,KAClB,8CACC;;wBAaa,mBAAmB,4BAA4B;;;;;wBAQ/C,oBAAoB,QAAQ;;;;;;;wBA8BtB,gBACpB,kBACA,eACA,mBACA,mBACA;EAAW;IACV"}
@@ -38,15 +38,23 @@ function layoutColumnNames(nodes) {
38
38
  else names.push(...layoutColumnNames(node.schema));
39
39
  return names;
40
40
  }
41
+ /**
42
+ * `Split::from()` follows the width of the record (its `@container`), not the viewport, at
43
+ * the sizes of `RECORD_CONTAINER_FROM`: in a narrow card a badge goes under the text
44
+ * instead of squeezing it.
45
+ */
41
46
  const SPLIT_FROM = {
42
47
  default: "flex-row items-center",
43
- sm: "flex-col sm:flex-row sm:items-center",
44
- md: "flex-col md:flex-row md:items-center",
45
- lg: "flex-col lg:flex-row lg:items-center",
46
- xl: "flex-col xl:flex-row xl:items-center",
47
- "2xl": "flex-col 2xl:flex-row 2xl:items-center"
48
+ sm: "flex-col @3xs:flex-row @3xs:items-center",
49
+ md: "flex-col @xs:flex-row @xs:items-center",
50
+ lg: "flex-col @md:flex-row @md:items-center",
51
+ xl: "flex-col @xl:flex-row @xl:items-center",
52
+ "2xl": "flex-col @3xl:flex-row @3xl:items-center"
48
53
  };
49
- /** Classes of a `Split`: side by side from its breakpoint, stacked below it. */
54
+ /**
55
+ * Classes of a `Split`: side by side once the record is wide enough for its `from`
56
+ * breakpoint, stacked below it. Needs the record's `@container` (`data-slot="table-record"`).
57
+ */
50
58
  function splitClasses(layout) {
51
59
  return `flex gap-3 ${SPLIT_FROM[layout.from ?? "default"]}`;
52
60
  }