import { z } from "zod/v4"; const uiActionSchema = z.object({ type: z.string().trim().min(1).max(120), payload: z.record(z.string(), z.unknown()).optional(), }); export type ColineUiAction = z.infer; const uiTextNodeSchema = z.object({ type: z.literal("Text"), props: z.object({ value: z.string(), tone: z.enum(["default", "muted", "positive", "warning", "danger"]).optional(), }), }); const uiHeadingNodeSchema = z.object({ type: z.literal("Heading"), props: z.object({ value: z.string().min(1).max(500), level: z.int().min(1).max(4).default(2), }), }); const uiBadgeNodeSchema = z.object({ type: z.literal("Badge"), props: z.object({ value: z.string().min(1).max(120), tone: z.enum(["default", "muted", "positive", "warning", "danger"]).default("default"), }), }); const uiButtonNodeSchema = z.object({ type: z.literal("Button"), props: z.object({ label: z.string().min(1).max(120), variant: z.enum(["primary", "secondary", "ghost", "danger"]).default("primary"), action: uiActionSchema, }), }); const uiDividerNodeSchema = z.object({ type: z.literal("Divider"), props: z.object({}).default({}), }); const uiFileCardNodeSchema = z.object({ type: z.literal("FileCard"), props: z.object({ title: z.string().min(1).max(200), subtitle: z.string().max(500).optional(), fileId: z.string().trim().min(1).max(200).optional(), action: uiActionSchema.optional(), }), }); const uiUserChipNodeSchema = z.object({ type: z.literal("UserChip"), props: z.object({ label: z.string().min(1).max(120), userId: z.string().trim().min(1).max(200).optional(), }), }); const uiEmptyStateNodeSchema = z.object({ type: z.literal("EmptyState"), props: z.object({ title: z.string().min(1).max(160), description: z.string().max(1000).optional(), action: uiActionSchema.optional(), }), }); type ColineUiNodeBase = | z.infer | z.infer | z.infer | z.infer | z.infer | z.infer | z.infer | z.infer; export type ColineUiNode = ColineUiNodeBase | ColineUiStackNode; export interface ColineUiStackNode { type: "Stack"; props: { direction?: "vertical" | "horizontal"; gap?: "xs" | "sm" | "md" | "lg"; children: ColineUiNode[]; }; } const uiNodeLazySchema: z.ZodType = z.lazy(() => z.union([ uiTextNodeSchema, uiHeadingNodeSchema, uiBadgeNodeSchema, uiButtonNodeSchema, uiDividerNodeSchema, uiFileCardNodeSchema, uiUserChipNodeSchema, uiEmptyStateNodeSchema, z.object({ type: z.literal("Stack"), props: z.object({ direction: z.enum(["vertical", "horizontal"]).default("vertical"), gap: z.enum(["xs", "sm", "md", "lg"]).default("md"), children: z.array(uiNodeLazySchema), }), }), ]), ); export const colineUiNodeSchema = uiNodeLazySchema; export const colineUiActionSchema = uiActionSchema; export const actions = { openFile(fileId: string, payload: Record = {}): ColineUiAction { return colineUiActionSchema.parse({ type: "open_file", payload: { fileId, ...payload, }, }); }, openAppHome(payload: Record = {}): ColineUiAction { return colineUiActionSchema.parse({ type: "open_app_home", payload, }); }, navigate(href: string, payload: Record = {}): ColineUiAction { return colineUiActionSchema.parse({ type: "navigate", payload: { href, ...payload, }, }); }, createFile(input: { name: string; typeKey: string; driveId?: string; parentId?: string | null; document?: Record; }): ColineUiAction { const payload: Record = { name: input.name, typeKey: input.typeKey, }; if (input.driveId) { payload["driveId"] = input.driveId; } if (input.parentId !== undefined) { payload["parentId"] = input.parentId; } if (input.document) { payload["document"] = input.document; } return colineUiActionSchema.parse({ type: "create_file", payload, }); }, custom(type: string, payload: Record = {}): ColineUiAction { return colineUiActionSchema.parse({ type, payload, }); }, }; export const ui = { text( value: string, options: { tone?: "default" | "muted" | "positive" | "warning" | "danger"; } = {}, ): ColineUiNode { return colineUiNodeSchema.parse({ type: "Text", props: { value, ...(options.tone ? { tone: options.tone } : {}), }, }); }, heading( value: string, options: { level?: 1 | 2 | 3 | 4; } = {}, ): ColineUiNode { return colineUiNodeSchema.parse({ type: "Heading", props: { value, ...(options.level ? { level: options.level } : {}), }, }); }, badge( value: string, options: { tone?: "default" | "muted" | "positive" | "warning" | "danger"; } = {}, ): ColineUiNode { return colineUiNodeSchema.parse({ type: "Badge", props: { value, ...(options.tone ? { tone: options.tone } : {}), }, }); }, button( label: string, options: { variant?: "primary" | "secondary" | "ghost" | "danger"; action: ColineUiAction; }, ): ColineUiNode { return colineUiNodeSchema.parse({ type: "Button", props: { label, action: options.action, ...(options.variant ? { variant: options.variant } : {}), }, }); }, divider(): ColineUiNode { return colineUiNodeSchema.parse({ type: "Divider", props: {}, }); }, fileCard(input: { title: string; subtitle?: string; fileId?: string; action?: ColineUiAction; }): ColineUiNode { return colineUiNodeSchema.parse({ type: "FileCard", props: { title: input.title, ...(input.subtitle ? { subtitle: input.subtitle } : {}), ...(input.fileId ? { fileId: input.fileId } : {}), ...(input.action ? { action: input.action } : {}), }, }); }, userChip(input: { label: string; userId?: string; }): ColineUiNode { return colineUiNodeSchema.parse({ type: "UserChip", props: { label: input.label, ...(input.userId ? { userId: input.userId } : {}), }, }); }, emptyState(input: { title: string; description?: string; action?: ColineUiAction; }): ColineUiNode { return colineUiNodeSchema.parse({ type: "EmptyState", props: { title: input.title, ...(input.description ? { description: input.description } : {}), ...(input.action ? { action: input.action } : {}), }, }); }, stack( children: ColineUiNode[], options: { direction?: "vertical" | "horizontal"; gap?: "xs" | "sm" | "md" | "lg"; } = {}, ): ColineUiNode { return colineUiNodeSchema.parse({ type: "Stack", props: { children, ...(options.direction ? { direction: options.direction } : {}), ...(options.gap ? { gap: options.gap } : {}), }, }); }, };