@boba-cli/dsl 1.0.0-alpha.2 → 1.0.0-alpha.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.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/view/renderer.ts","../src/view/nodes.ts","../src/app-builder.ts","../src/components/code.ts","../src/components/filepicker.ts","../src/components/filetree.ts","../src/components/help.ts","../src/components/help-bubble.ts","../src/components/list.ts","../src/components/markdown.ts","../src/components/paginator.ts","../src/components/progress.ts","../src/components/spinner.ts","../src/components/statusbar.ts","../src/components/stopwatch.ts","../src/components/table.ts","../src/components/textarea.ts","../src/components/textinput.ts","../src/components/timer.ts","../src/components/viewport.ts"],"names":["separator","render","teaQuit","newComponentModels","cmds","Style"],"mappings":"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","file":"index.js","sourcesContent":["import { Style } from '@boba-cli/chapstick'\nimport type { ViewNode, TextNode, LayoutNode, ComponentView } from '../types.js'\n\n/**\n * Render a view node tree to a string.\n *\n * @remarks\n * Recursively renders a {@link ViewNode} tree into a terminal-ready string.\n * Handles text styling, layout stacking, and component views. This function\n * is typically called internally by the DSL, but can be used directly for\n * testing or custom rendering.\n *\n * @example\n * ```typescript\n * const tree = vstack(\n * text('Hello').bold(),\n * text('World').foreground('#ff79c6')\n * )\n * const output = render(tree)\n * console.log(output)\n * ```\n *\n * @param node - The view node tree to render\n * @returns A string ready to display in the terminal\n *\n * @public\n */\nexport function render(node: ViewNode): string {\n if (typeof node === 'string') {\n return node\n }\n\n if (isTextNode(node)) {\n return renderTextNode(node)\n }\n\n if (isLayoutNode(node)) {\n return renderLayoutNode(node)\n }\n\n if (isComponentView(node)) {\n return node.view\n }\n\n // Should not reach here, but TypeScript doesn't know that\n return ''\n}\n\nfunction isTextNode(node: ViewNode): node is TextNode {\n return typeof node === 'object' && '_type' in node && node._type === 'text'\n}\n\nfunction isLayoutNode(node: ViewNode): node is LayoutNode {\n return (\n typeof node === 'object' &&\n '_type' in node &&\n (node._type === 'vstack' || node._type === 'hstack')\n )\n}\n\nfunction isComponentView(node: ViewNode): node is ComponentView {\n return typeof node === 'object' && '_type' in node && node._type === 'component'\n}\n\nfunction renderTextNode(node: TextNode): string {\n let style = new Style()\n\n if (node._bold) {\n style = style.bold(true)\n }\n // Note: dim is rendered as a darker color when foreground is set\n // If dim and no foreground, use a gray color\n if (node._dim && !node._foreground) {\n style = style.foreground('#888888')\n }\n if (node._italic) {\n style = style.italic(true)\n }\n if (node._foreground) {\n style = style.foreground(node._foreground)\n }\n if (node._background) {\n style = style.background(node._background)\n }\n\n return style.render(node.content)\n}\n\nfunction renderLayoutNode(node: LayoutNode): string {\n const renderedChildren = node.children\n .map((child) => render(child))\n .filter((s) => s.length > 0)\n\n if (node._type === 'vstack') {\n const separator = node.spacing > 0 ? '\\n'.repeat(node.spacing + 1) : '\\n'\n return renderedChildren.join(separator)\n }\n\n // hstack\n const separator = node.spacing > 0 ? ' '.repeat(node.spacing) : ''\n return renderedChildren.join(separator)\n}\n","import type { TextNode, LayoutNode, ViewNode, ComponentView } from '../types.js'\n\n/**\n * Create a text node with chainable style methods.\n *\n * @remarks\n * Text nodes support fluent styling via methods like `bold()`,\n * `dim()`, `italic()`, `foreground()`, and `background()`.\n *\n * @example\n * ```typescript\n * text('Hello').bold().foreground('#ff79c6')\n * text('Warning').dim()\n * ```\n *\n * @param content - The text content to display\n * @returns A new {@link TextNode}\n *\n * @public\n */\nexport function text(content: string): TextNode {\n return createTextNode(content, false, false, false, undefined, undefined)\n}\n\nfunction createTextNode(\n content: string,\n bold: boolean,\n dim: boolean,\n italic: boolean,\n foreground: string | undefined,\n background: string | undefined,\n): TextNode {\n return {\n _type: 'text',\n content,\n _bold: bold,\n _dim: dim,\n _italic: italic,\n _foreground: foreground,\n _background: background,\n bold() {\n return createTextNode(content, true, dim, italic, foreground, background)\n },\n dim() {\n return createTextNode(content, bold, true, italic, foreground, background)\n },\n italic() {\n return createTextNode(content, bold, dim, true, foreground, background)\n },\n foreground(color: string) {\n return createTextNode(content, bold, dim, italic, color, background)\n },\n background(color: string) {\n return createTextNode(content, bold, dim, italic, foreground, color)\n },\n }\n}\n\n/**\n * Create a vertical stack layout.\n *\n * @remarks\n * Arranges child views vertically with newlines between them. Children are\n * rendered in order from top to bottom.\n *\n * @example\n * ```typescript\n * vstack(\n * text('Line 1'),\n * text('Line 2'),\n * text('Line 3')\n * )\n * ```\n *\n * @param children - View nodes to stack vertically\n * @returns A new {@link LayoutNode} with vertical stacking\n *\n * @public\n */\nexport function vstack(...children: ViewNode[]): LayoutNode {\n return {\n _type: 'vstack',\n children,\n spacing: 0,\n }\n}\n\n/**\n * Create a horizontal stack layout.\n *\n * @remarks\n * Arranges child views horizontally on the same line. Children are\n * rendered in order from left to right.\n *\n * @example\n * ```typescript\n * hstack(\n * text('Left'),\n * text(' | '),\n * text('Right')\n * )\n * ```\n *\n * @param children - View nodes to stack horizontally\n * @returns A new {@link LayoutNode} with horizontal stacking\n *\n * @public\n */\nexport function hstack(...children: ViewNode[]): LayoutNode {\n return {\n _type: 'hstack',\n children,\n spacing: 0,\n }\n}\n\n/**\n * Create empty vertical space.\n *\n * @remarks\n * Useful for adding vertical spacing between sections of your UI.\n *\n * @example\n * ```typescript\n * vstack(\n * text('Header'),\n * spacer(2),\n * text('Content')\n * )\n * ```\n *\n * @param height - Number of blank lines to insert (default: 1)\n * @returns A string containing the specified number of newlines\n *\n * @public\n */\nexport function spacer(height = 1): string {\n return '\\n'.repeat(height)\n}\n\n/**\n * Create a divider line.\n *\n * @remarks\n * Renders a horizontal line using a repeated character.\n *\n * @example\n * ```typescript\n * vstack(\n * text('Section 1'),\n * divider(),\n * text('Section 2'),\n * divider('=', 50)\n * )\n * ```\n *\n * @param char - Character to repeat (default: '─')\n * @param width - Number of times to repeat the character (default: 40)\n * @returns A string containing the divider line\n *\n * @public\n */\nexport function divider(char = '─', width = 40): string {\n return char.repeat(width)\n}\n\n/**\n * Conditionally render a node.\n *\n * @remarks\n * Returns the node if the condition is true, otherwise returns an empty string.\n *\n * @example\n * ```typescript\n * vstack(\n * text('Always visible'),\n * when(state.showHelp, text('Help text'))\n * )\n * ```\n *\n * @param condition - Boolean condition to test\n * @param node - View node to render if condition is true\n * @returns The node if condition is true, empty string otherwise\n *\n * @public\n */\nexport function when(condition: boolean, node: ViewNode): ViewNode {\n return condition ? node : ''\n}\n\n/**\n * Choose between two nodes based on condition.\n *\n * @remarks\n * Returns one node if the condition is true, another if false.\n *\n * @example\n * ```typescript\n * vstack(\n * choose(\n * state.isLoading,\n * text('Loading...').dim(),\n * text('Ready!').bold()\n * )\n * )\n * ```\n *\n * @param condition - Boolean condition to test\n * @param ifTrue - View node to render if condition is true\n * @param ifFalse - View node to render if condition is false\n * @returns Either ifTrue or ifFalse depending on condition\n *\n * @public\n */\nexport function choose(condition: boolean, ifTrue: ViewNode, ifFalse: ViewNode): ViewNode {\n return condition ? ifTrue : ifFalse\n}\n\n/**\n * Map items to view nodes.\n *\n * @remarks\n * Transforms an array of items into an array of view nodes. The render\n * function receives each item and its index.\n *\n * @example\n * ```typescript\n * vstack(\n * ...map(state.items, (item, index) =>\n * text(`${index + 1}. ${item.name}`)\n * )\n * )\n * ```\n *\n * @typeParam T - The type of items in the array\n * @param items - Array of items to map\n * @param render - Function to transform each item into a view node\n * @returns Array of view nodes\n *\n * @public\n */\nexport function map<T>(items: T[], render: (item: T, index: number) => ViewNode): ViewNode[] {\n return items.map(render)\n}\n\n/**\n * Create a component view wrapper.\n * @internal\n */\nexport function componentView(view: string): ComponentView {\n return {\n _type: 'component',\n view,\n }\n}\n","import {\n Program,\n KeyMsg,\n quit as teaQuit,\n batch,\n type Cmd,\n type Model,\n type Msg,\n} from '@boba-cli/tea'\nimport { newBinding, matches } from '@boba-cli/key'\nimport type {\n App,\n ComponentBuilder,\n EventContext,\n InitContext,\n InitHandler,\n KeyHandler,\n MessageContext,\n MessageHandler,\n ViewFunction,\n ComponentView,\n RunOptions,\n} from './types.js'\nimport { render } from './view/renderer.js'\nimport { componentView } from './view/nodes.js'\n\n/**\n * Internal key handler registration.\n * @internal\n */\ninterface KeyHandlerEntry<State, Components extends Record<string, unknown>> {\n keys: string[]\n handler: KeyHandler<State, Components>\n}\n\n/**\n * Internal message handler registration.\n * @internal\n */\ninterface MessageHandlerEntry<State, Components extends Record<string, unknown>> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n msgClass: new (...args: any[]) => Msg\n handler: MessageHandler<State, Components, Msg>\n}\n\n/**\n * Internal component registration.\n * @internal\n */\ninterface ComponentEntry {\n key: string\n builder: ComponentBuilder<unknown>\n}\n\n/**\n * Builder for creating declarative CLI applications.\n *\n * @example\n * ```typescript\n * const app = createApp()\n * .state({ count: 0 })\n * .component('spinner', spinner())\n * .onKey('q', ({ quit }) => quit())\n * .view(({ state, components }) => vstack(\n * text('Count: ' + state.count),\n * components.spinner\n * ))\n * .build()\n *\n * await app.run()\n * ```\n *\n * @public\n */\nexport class AppBuilder<\n State = undefined,\n Components extends Record<string, unknown> = Record<string, never>,\n> {\n readonly #initialState: State | undefined\n readonly #components: ComponentEntry[]\n readonly #keyHandlers: KeyHandlerEntry<State, Components>[]\n readonly #messageHandlers: MessageHandlerEntry<State, Components>[]\n readonly #initHandler: InitHandler<State, Components> | undefined\n readonly #viewFn: ViewFunction<State, Components> | undefined\n\n private constructor(\n initialState: State | undefined,\n components: ComponentEntry[],\n keyHandlers: KeyHandlerEntry<State, Components>[],\n messageHandlers: MessageHandlerEntry<State, Components>[],\n initHandler: InitHandler<State, Components> | undefined,\n viewFn: ViewFunction<State, Components> | undefined,\n ) {\n this.#initialState = initialState\n this.#components = components\n this.#keyHandlers = keyHandlers\n this.#messageHandlers = messageHandlers\n this.#initHandler = initHandler\n this.#viewFn = viewFn\n }\n\n /**\n * Create a new AppBuilder instance.\n * @internal\n */\n static create(): AppBuilder<undefined, Record<string, never>> {\n return new AppBuilder(undefined, [], [], [], undefined, undefined)\n }\n\n /**\n * Set the initial application state.\n *\n * @remarks\n * This should typically be called early in the builder chain. If called after\n * registering key handlers or a view function, those will be preserved but\n * their type information will be updated to reflect the new state type.\n *\n * @example\n * ```typescript\n * createApp()\n * .state({ count: 0, name: 'World' })\n * ```\n *\n * @typeParam S - The application state type\n * @param initial - The initial state object\n * @returns A new {@link AppBuilder} with the state type parameter set\n *\n * @public\n */\n state<S>(initial: S): AppBuilder<S, Components> {\n // Preserve existing handlers and view function with updated type\n // This is safe because the handlers/view will receive the new state type\n return new AppBuilder(\n initial,\n this.#components,\n this.#keyHandlers as unknown as KeyHandlerEntry<S, Components>[],\n this.#messageHandlers as unknown as MessageHandlerEntry<S, Components>[],\n this.#initHandler as unknown as InitHandler<S, Components> | undefined,\n this.#viewFn as unknown as ViewFunction<S, Components> | undefined,\n )\n }\n\n /**\n * Register a component with a unique key.\n *\n * @remarks\n * Components are TEA models wrapped in a {@link ComponentBuilder} that\n * manages their lifecycle. The component's rendered view is available in\n * the view function via `components[key]`.\n *\n * @example\n * ```typescript\n * createApp()\n * .component('loading', spinner())\n * .component('input', textInput())\n * ```\n *\n * @typeParam K - The component key (string literal type)\n * @typeParam M - The component model type\n * @param key - Unique identifier for this component\n * @param builder - Component builder implementing init/update/view\n * @returns A new {@link AppBuilder} with the component registered\n *\n * @public\n */\n component<K extends string, M>(\n key: K,\n builder: ComponentBuilder<M>,\n ): AppBuilder<State, Components & Record<K, M>> {\n const newComponents = [...this.#components, { key, builder: builder as ComponentBuilder<unknown> }]\n return new AppBuilder(\n this.#initialState,\n newComponents,\n this.#keyHandlers as KeyHandlerEntry<State, Components & Record<K, M>>[],\n this.#messageHandlers as MessageHandlerEntry<State, Components & Record<K, M>>[],\n this.#initHandler as InitHandler<State, Components & Record<K, M>> | undefined,\n this.#viewFn as ViewFunction<State, Components & Record<K, M>> | undefined,\n )\n }\n\n /**\n * Register a key handler.\n *\n * @remarks\n * Key handlers receive an {@link EventContext} with the current state and\n * components. Multiple keys can be bound to the same handler by passing an\n * array of key strings. Key strings support modifiers like 'ctrl+c', 'alt+enter'.\n *\n * @example\n * ```typescript\n * createApp()\n * .onKey('q', ({ quit }) => quit())\n * .onKey(['up', 'k'], ({ state, update }) => update({ index: state.index - 1 }))\n * .onKey('ctrl+c', ({ quit }) => quit())\n * ```\n *\n * @param keys - Single key string or array of key strings\n * @param handler - Function to call when any of the keys are pressed\n * @returns A new {@link AppBuilder} with the key handler registered\n *\n * @public\n */\n onKey(\n keys: string | string[],\n handler: KeyHandler<State, Components>,\n ): AppBuilder<State, Components> {\n const keyArray = Array.isArray(keys) ? keys : [keys]\n const newHandlers = [...this.#keyHandlers, { keys: keyArray, handler }]\n return new AppBuilder(\n this.#initialState,\n this.#components,\n newHandlers,\n this.#messageHandlers,\n this.#initHandler,\n this.#viewFn,\n )\n }\n\n /**\n * Set the initialization handler.\n *\n * @remarks\n * The init handler is called once when the application starts. Use it to\n * schedule initial async operations like fetching data or starting timers.\n * Calling this method multiple times will replace the previous handler.\n *\n * @example\n * ```typescript\n * createApp()\n * .onInit(({ schedule }) => {\n * schedule(tick(1000, () => new MyMessage()))\n * })\n * ```\n *\n * @param handler - Function to call on initialization\n * @returns A new {@link AppBuilder} with the init handler registered\n *\n * @public\n */\n onInit(handler: InitHandler<State, Components>): AppBuilder<State, Components> {\n return new AppBuilder(\n this.#initialState,\n this.#components,\n this.#keyHandlers,\n this.#messageHandlers,\n handler,\n this.#viewFn,\n )\n }\n\n /**\n * Register a message handler for a specific message type.\n *\n * @remarks\n * Message handlers are called when a message of the specified type is received.\n * Use this to handle async callbacks from commands scheduled via `onInit()`\n * or other message handlers.\n *\n * @example\n * ```typescript\n * class DownloadComplete {\n * constructor(public packageName: string) {}\n * }\n *\n * createApp()\n * .onMessage(DownloadComplete, ({ msg, update, schedule }) => {\n * update({ currentPackage: msg.packageName })\n * schedule(tick(500, () => new DownloadComplete('next-package')))\n * })\n * ```\n *\n * @param msgClass - The message class constructor to match\n * @param handler - Function to call when a message of this type is received\n * @returns A new {@link AppBuilder} with the message handler registered\n *\n * @public\n */\n onMessage<M extends Msg>(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n msgClass: new (...args: any[]) => M,\n handler: MessageHandler<State, Components, M>,\n ): AppBuilder<State, Components> {\n const newHandlers = [\n ...this.#messageHandlers,\n { msgClass, handler: handler as MessageHandler<State, Components, Msg> },\n ]\n return new AppBuilder(\n this.#initialState,\n this.#components,\n this.#keyHandlers,\n newHandlers,\n this.#initHandler,\n this.#viewFn,\n )\n }\n\n /**\n * Set the view function.\n *\n * @remarks\n * The view function is called on every render cycle and receives the current\n * state and component views. It should return a {@link ViewNode} tree\n * describing the UI to display.\n *\n * @example\n * ```typescript\n * createApp()\n * .view(({ state, components }) => vstack(\n * text('Hello ' + state.name),\n * components.spinner\n * ))\n * ```\n *\n * @param fn - Function that returns a {@link ViewNode} tree\n * @returns A new {@link AppBuilder} with the view function set\n *\n * @public\n */\n view(fn: ViewFunction<State, Components>): AppBuilder<State, Components> {\n return new AppBuilder(\n this.#initialState,\n this.#components,\n this.#keyHandlers,\n this.#messageHandlers,\n this.#initHandler,\n fn,\n )\n }\n\n /**\n * Build the application.\n *\n * @remarks\n * Finalizes the builder chain and creates an {@link App} instance ready\n * to run. This method must be called after setting a view function via\n * {@link AppBuilder.view}.\n *\n * @throws Error if no view function has been set\n *\n * @returns The built {@link App} ready to run\n *\n * @public\n */\n build(): App<State, Components> {\n if (this.#viewFn === undefined) {\n throw new Error('AppBuilder: view() must be called before build()')\n }\n\n const initialState = this.#initialState as State\n const components = this.#components\n const keyHandlers = this.#keyHandlers\n const messageHandlers = this.#messageHandlers\n const initHandler = this.#initHandler\n const viewFn = this.#viewFn\n\n // Create the generated model\n const model = new GeneratedModel(\n initialState,\n components,\n keyHandlers,\n messageHandlers,\n initHandler,\n viewFn,\n )\n\n return {\n async run(options: RunOptions) {\n const program = new Program(model, { platform: options.platform })\n const result = await program.run()\n return { state: result.model.getUserState() }\n },\n getModel() {\n return model\n },\n }\n }\n}\n\n/**\n * Create a new application builder.\n *\n * @example\n * ```typescript\n * const app = createApp()\n * .state({ count: 0 })\n * .onKey('q', ({ quit }) => quit())\n * .view(({ state }) => text('Count: ' + state.count))\n * .build()\n * ```\n *\n * @public\n */\nexport function createApp(): AppBuilder<undefined, Record<string, never>> {\n return AppBuilder.create()\n}\n\n/**\n * Generated TEA model from the builder configuration.\n * @internal\n */\nclass GeneratedModel<State, Components extends Record<string, unknown>>\n implements Model<Msg, GeneratedModel<State, Components>>\n{\n readonly #userState: State\n readonly #componentModels: Map<string, unknown>\n readonly #componentBuilders: Map<string, ComponentBuilder<unknown>>\n readonly #keyHandlers: KeyHandlerEntry<State, Components>[]\n readonly #messageHandlers: MessageHandlerEntry<State, Components>[]\n readonly #initHandler: InitHandler<State, Components> | undefined\n readonly #viewFn: ViewFunction<State, Components> | undefined\n\n constructor(\n userState: State,\n components: ComponentEntry[],\n keyHandlers: KeyHandlerEntry<State, Components>[],\n messageHandlers: MessageHandlerEntry<State, Components>[],\n initHandler: InitHandler<State, Components> | undefined,\n viewFn: ViewFunction<State, Components> | undefined,\n componentModels?: Map<string, unknown>,\n ) {\n this.#userState = userState\n this.#keyHandlers = keyHandlers\n this.#messageHandlers = messageHandlers\n this.#initHandler = initHandler\n this.#viewFn = viewFn\n\n // Build component builders map\n this.#componentBuilders = new Map()\n for (const { key, builder } of components) {\n this.#componentBuilders.set(key, builder)\n }\n\n // Use provided component models or empty map (init will populate)\n this.#componentModels = componentModels ?? new Map<string, unknown>()\n }\n\n getUserState(): State {\n return this.#userState\n }\n\n init(): Cmd<Msg> {\n const cmds: Cmd<Msg>[] = []\n\n // Initialize all components\n for (const [key, builder] of this.#componentBuilders) {\n const [model, cmd] = builder.init()\n this.#componentModels.set(key, model)\n if (cmd) {\n cmds.push(cmd)\n }\n }\n\n // Call init handler if present\n if (this.#initHandler) {\n const scheduledCmds: Cmd<Msg>[] = []\n const componentUpdates: Array<{\n key: string\n model: unknown\n cmd: Cmd<Msg>\n }> = []\n\n const ctx: InitContext<State, Components> = {\n state: this.#userState,\n schedule: (cmd) => {\n if (cmd) scheduledCmds.push(cmd)\n },\n sendToComponent: (key, fn) => {\n const currentModel = this.#componentModels.get(key as string)\n if (currentModel !== undefined) {\n const [nextModel, cmd] = fn(currentModel as Components[typeof key])\n componentUpdates.push({\n key: key as string,\n model: nextModel,\n cmd,\n })\n }\n },\n }\n\n this.#initHandler(ctx)\n\n // Apply component updates\n for (const { key, model, cmd } of componentUpdates) {\n this.#componentModels.set(key, model)\n if (cmd) cmds.push(cmd)\n }\n\n // Add scheduled commands\n cmds.push(...scheduledCmds)\n }\n\n return cmds.length > 0 ? batch(...cmds) : null\n }\n\n update(msg: Msg): [GeneratedModel<State, Components>, Cmd<Msg>] {\n // Check key handlers first\n if (msg instanceof KeyMsg) {\n for (const { keys, handler} of this.#keyHandlers) {\n const binding = newBinding({ keys })\n if (matches(msg, binding)) {\n // Create event context and call handler\n let nextUserState = this.#userState\n let shouldQuit = false\n const componentUpdates: Array<{\n key: string\n model: unknown\n cmd: Cmd<Msg>\n }> = []\n\n const ctx: EventContext<State, Components> = {\n state: this.#userState,\n components: this.#buildComponentViews(),\n update: (patch) => {\n nextUserState = { ...nextUserState, ...patch }\n },\n setState: (newState) => {\n nextUserState = newState\n },\n quit: () => {\n shouldQuit = true\n },\n sendToComponent: (key, fn) => {\n const currentModel = this.#componentModels.get(key as string)\n if (currentModel !== undefined) {\n const [nextModel, cmd] = fn(currentModel as Components[typeof key])\n componentUpdates.push({\n key: key as string,\n model: nextModel,\n cmd,\n })\n }\n },\n }\n\n handler(ctx)\n\n if (shouldQuit) {\n return [this, teaQuit()]\n }\n\n // Apply any component updates\n const stateChanged = nextUserState !== this.#userState\n const componentsChanged = componentUpdates.length > 0\n\n if (stateChanged || componentsChanged) {\n const newComponentModels = new Map(this.#componentModels)\n const cmds: Cmd<Msg>[] = []\n\n for (const { key, model, cmd } of componentUpdates) {\n newComponentModels.set(key, model)\n if (cmd) {\n cmds.push(cmd)\n }\n }\n\n const next = new GeneratedModel(\n nextUserState,\n Array.from(this.#componentBuilders.entries()).map(([key, builder]) => ({\n key,\n builder,\n })),\n this.#keyHandlers,\n this.#messageHandlers,\n this.#initHandler,\n this.#viewFn,\n newComponentModels,\n )\n\n return [next, cmds.length > 0 ? batch(...cmds) : null]\n }\n\n return [this, null]\n }\n }\n }\n\n // Check message handlers\n for (const { msgClass, handler } of this.#messageHandlers) {\n if (msg instanceof msgClass) {\n let nextUserState = this.#userState\n let shouldQuit = false\n const scheduledCmds: Cmd<Msg>[] = []\n const componentUpdates: Array<{\n key: string\n model: unknown\n cmd: Cmd<Msg>\n }> = []\n\n const ctx: MessageContext<State, Components, Msg> = {\n msg,\n state: this.#userState,\n components: this.#buildComponentViews(),\n update: (patch) => {\n nextUserState = { ...nextUserState, ...patch }\n },\n setState: (newState) => {\n nextUserState = newState\n },\n quit: () => {\n shouldQuit = true\n },\n schedule: (cmd) => {\n if (cmd) scheduledCmds.push(cmd)\n },\n sendToComponent: (key, fn) => {\n const currentModel = this.#componentModels.get(key as string)\n if (currentModel !== undefined) {\n const [nextModel, cmd] = fn(currentModel as Components[typeof key])\n componentUpdates.push({\n key: key as string,\n model: nextModel,\n cmd,\n })\n }\n },\n }\n\n handler(ctx)\n\n if (shouldQuit) {\n return [this, teaQuit()]\n }\n\n // Apply state and component updates\n const stateChanged = nextUserState !== this.#userState\n const componentsChanged = componentUpdates.length > 0\n const hasScheduled = scheduledCmds.length > 0\n\n if (stateChanged || componentsChanged || hasScheduled) {\n const newComponentModels = new Map(this.#componentModels)\n const cmds: Cmd<Msg>[] = [...scheduledCmds]\n\n for (const { key, model, cmd } of componentUpdates) {\n newComponentModels.set(key, model)\n if (cmd) {\n cmds.push(cmd)\n }\n }\n\n const next = new GeneratedModel(\n nextUserState,\n Array.from(this.#componentBuilders.entries()).map(([key, builder]) => ({\n key,\n builder,\n })),\n this.#keyHandlers,\n this.#messageHandlers,\n this.#initHandler,\n this.#viewFn,\n newComponentModels,\n )\n\n return [next, cmds.length > 0 ? batch(...cmds) : null]\n }\n\n return [this, null]\n }\n }\n\n // Route message to all components\n const cmds: Cmd<Msg>[] = []\n let anyComponentChanged = false\n const newComponentModels = new Map(this.#componentModels)\n\n for (const [key, builder] of this.#componentBuilders) {\n const currentModel = this.#componentModels.get(key)\n if (currentModel === undefined) {\n continue\n }\n\n const [nextModel, cmd] = builder.update(currentModel, msg)\n\n if (nextModel !== currentModel) {\n newComponentModels.set(key, nextModel)\n anyComponentChanged = true\n }\n\n if (cmd) {\n cmds.push(cmd)\n }\n }\n\n if (anyComponentChanged) {\n const next = new GeneratedModel(\n this.#userState,\n Array.from(this.#componentBuilders.entries()).map(([key, builder]) => ({\n key,\n builder,\n })),\n this.#keyHandlers,\n this.#messageHandlers,\n this.#initHandler,\n this.#viewFn,\n newComponentModels,\n )\n return [next, cmds.length > 0 ? batch(...cmds) : null]\n }\n\n return [this, cmds.length > 0 ? batch(...cmds) : null]\n }\n\n view(): string {\n if (!this.#viewFn) {\n return ''\n }\n\n const componentViews = this.#buildComponentViews()\n const node = this.#viewFn({\n state: this.#userState,\n components: componentViews,\n })\n\n return render(node)\n }\n\n #buildComponentViews(): { [K in keyof Components]: ComponentView } {\n const views: Record<string, ComponentView> = {}\n\n for (const [key, builder] of this.#componentBuilders) {\n const model = this.#componentModels.get(key)\n if (model !== undefined) {\n views[key] = componentView(builder.view(model))\n }\n }\n\n return views as { [K in keyof Components]: ComponentView }\n }\n}\n","import type { Cmd, Msg } from '@boba-cli/tea'\nimport type { FileSystemAdapter, PathAdapter } from '@boba-cli/machine'\nimport { CodeModel } from '@boba-cli/code'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the code component builder.\n *\n * @remarks\n * Configure the code viewer when creating a code component. The code component\n * displays syntax-highlighted source code with scrolling support.\n *\n * Note: The code component requires filesystem and path adapters to read and\n * display files. These must be provided in the options.\n *\n * @public\n */\nexport interface CodeBuilderOptions {\n /**\n * Filesystem adapter for file operations.\n *\n * @remarks\n * For Node.js environments, use `NodeFileSystemAdapter` from `@boba-cli/machine/node`.\n * For browser environments, provide a custom implementation or memory-based adapter.\n */\n filesystem: FileSystemAdapter\n\n /**\n * Path adapter for path operations.\n *\n * @remarks\n * For Node.js environments, use `NodePathAdapter` from `@boba-cli/machine/node`.\n * For browser environments, provide a custom implementation.\n */\n path: PathAdapter\n\n /**\n * Whether the component is active and receives keyboard input (default: `false`).\n *\n * @remarks\n * When active, the code viewer responds to keyboard navigation (arrow keys,\n * page up/down, etc.) for scrolling through the code.\n */\n active?: boolean\n\n /**\n * Syntax highlighting theme to use (default: `\"dracula\"`).\n *\n * @remarks\n * Common themes include `\"dracula\"`, `\"monokai\"`, `\"github-light\"`, `\"nord\"`.\n * Uses Shiki for syntax highlighting under the hood.\n */\n theme?: string\n\n /**\n * Width of the code viewer in characters.\n *\n * @remarks\n * If not specified, defaults to 0. Typically you'll want to update this\n * dynamically based on terminal size via the model's `setSize()` method.\n */\n width?: number\n\n /**\n * Height of the code viewer in lines.\n *\n * @remarks\n * If not specified, defaults to 0. Typically you'll want to update this\n * dynamically based on terminal size via the model's `setSize()` method.\n */\n height?: number\n}\n\n/**\n * Create a code viewer component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/code` package.\n * The code viewer displays syntax-highlighted source code from files with\n * scrolling support and keyboard navigation.\n *\n * The component requires filesystem and path adapters to read files. You must\n * provide these in the options. After creating the component, use the model's\n * `setFileName()` method to load and display a file.\n *\n * @example\n * Basic usage with Node.js adapters:\n * ```typescript\n * import { NodeFileSystemAdapter, NodePathAdapter } from '@boba-cli/machine/node'\n *\n * const app = createApp()\n * .component('code', code({\n * filesystem: new NodeFileSystemAdapter(),\n * path: new NodePathAdapter(),\n * active: true,\n * width: 80,\n * height: 24,\n * }))\n * .view(({ components }) => components.code)\n * .build()\n * ```\n *\n * @example\n * With custom theme:\n * ```typescript\n * const app = createApp()\n * .component('viewer', code({\n * filesystem: new NodeFileSystemAdapter(),\n * path: new NodePathAdapter(),\n * theme: 'monokai',\n * active: true,\n * }))\n * .view(({ components }) => vstack(\n * text('Source Code').bold(),\n * components.viewer\n * ))\n * .build()\n * ```\n *\n * @example\n * Loading a file (typically done in a key handler or init):\n * ```typescript\n * // In the raw TEA model, you would call:\n * const [nextCode, cmd] = codeModel.setFileName('src/example.ts')\n *\n * // Note: The DSL doesn't currently provide direct access to component models\n * // from event handlers, so you may need to manage file loading differently\n * // or use the raw TEA approach for now.\n * ```\n *\n * @param options - Configuration options for the code viewer (requires filesystem and path adapters)\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function code(options: CodeBuilderOptions): ComponentBuilder<CodeModel> {\n return {\n init(): [CodeModel, Cmd<Msg>] {\n const model = CodeModel.new({\n filesystem: options.filesystem,\n path: options.path,\n active: options.active ?? false,\n syntaxTheme: options.theme ?? 'dracula',\n width: options.width ?? 0,\n height: options.height ?? 0,\n })\n return [model, model.init()]\n },\n\n update(model: CodeModel, msg: Msg): [CodeModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: CodeModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport type { FileSystemAdapter, PathAdapter } from '@boba-cli/machine'\nimport { FilepickerModel, type FilepickerStyles } from '@boba-cli/filepicker'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the filepicker component builder.\n *\n * @remarks\n * Configure the file picker's behavior, appearance, and file system access.\n * The filesystem and path adapters are required to interact with the file system.\n *\n * @public\n */\nexport interface FilepickerBuilderOptions {\n /**\n * FileSystem adapter for file operations.\n *\n * @remarks\n * Required for reading directory contents and file metadata.\n * For Node.js, use `platform.filesystem` from `createNodePlatform()`.\n */\n filesystem: FileSystemAdapter\n\n /**\n * Path adapter for path operations.\n *\n * @remarks\n * Required for path manipulation (dirname, join, etc.).\n * For Node.js, use `platform.path` from `createNodePlatform()`.\n */\n path: PathAdapter\n\n /**\n * Initial directory to display (default: current working directory).\n *\n * @remarks\n * If not specified, uses the filesystem adapter's current working directory.\n */\n currentDirectory?: string\n\n /**\n * Maximum height for the file list (default: 0 = unlimited).\n *\n * @remarks\n * Limits the number of visible files. When 0, all files are displayed.\n */\n height?: number\n\n /**\n * Show hidden files (default: false).\n *\n * @remarks\n * Hidden files typically start with a dot (.) on Unix-like systems.\n */\n showHidden?: boolean\n\n /**\n * Filter files by allowed extensions (default: all files).\n *\n * @remarks\n * Provide an array of file extensions (e.g., `['.ts', '.js']`) to filter\n * the file list. Directories are always shown regardless of this setting.\n * Empty array or undefined means no filtering.\n *\n * @example\n * ```typescript\n * allowedExtensions: ['.ts', '.tsx', '.js', '.jsx']\n * ```\n */\n allowedExtensions?: string[]\n\n /**\n * Style hooks for customizing the file picker appearance.\n *\n * @remarks\n * Partial styles object - any omitted styles will use defaults.\n * Available style hooks:\n * - `directory`: Style for directory names\n * - `file`: Style for file names\n * - `hidden`: Style for hidden files\n * - `selected`: Style for the selected item\n * - `cursor`: Style for the cursor indicator\n * - `status`: Style for the status bar (current directory)\n */\n styles?: Partial<FilepickerStyles>\n}\n\n/**\n * Create a filepicker component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/filepicker` package.\n * The filepicker allows users to navigate the file system and select files.\n * It requires filesystem and path adapters from the platform.\n *\n * @example\n * Basic usage with Node.js platform:\n * ```typescript\n * import { createNodePlatform } from '@boba-cli/machine/node'\n *\n * const platform = createNodePlatform()\n *\n * const app = createApp()\n * .component('picker', filepicker({\n * filesystem: platform.filesystem,\n * path: platform.path\n * }))\n * .view(({ components }) => components.picker)\n * .build()\n * ```\n *\n * @example\n * With custom directory and file filtering:\n * ```typescript\n * const app = createApp()\n * .component('picker', filepicker({\n * filesystem: platform.filesystem,\n * path: platform.path,\n * currentDirectory: '/home/user/projects',\n * allowedExtensions: ['.ts', '.tsx', '.js', '.jsx'],\n * showHidden: true,\n * height: 20\n * }))\n * .view(({ components }) => vstack(\n * text('Select a TypeScript file:'),\n * components.picker\n * ))\n * .build()\n * ```\n *\n * @example\n * With custom styling:\n * ```typescript\n * const app = createApp()\n * .component('picker', filepicker({\n * filesystem: platform.filesystem,\n * path: platform.path,\n * styles: {\n * directory: new Style().foreground('#50fa7b').bold(true),\n * file: new Style().foreground('#f8f8f2'),\n * selected: new Style().background('#44475a').foreground('#ff79c6')\n * }\n * }))\n * .view(({ components }) => components.picker)\n * .build()\n * ```\n *\n * @param options - Configuration options for the filepicker (filesystem and path are required)\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function filepicker(\n options: FilepickerBuilderOptions,\n): ComponentBuilder<FilepickerModel> {\n return {\n init(): [FilepickerModel, Cmd<Msg>] {\n const [model, cmd] = FilepickerModel.new({\n filesystem: options.filesystem,\n path: options.path,\n currentDir: options.currentDirectory,\n height: options.height,\n showHidden: options.showHidden,\n allowedTypes: options.allowedExtensions,\n styles: options.styles,\n })\n return [model, cmd]\n },\n\n update(model: FilepickerModel, msg: Msg): [FilepickerModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: FilepickerModel): string {\n return model.view()\n },\n }\n}\n","import type { Cmd, Msg } from '@boba-cli/tea'\nimport {\n FiletreeModel,\n type FiletreeKeyMap,\n type FiletreeStyles,\n} from '@boba-cli/filetree'\nimport type { FileSystemAdapter, PathAdapter } from '@boba-cli/machine'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the filetree component builder.\n *\n * @remarks\n * Configure the filetree component with directory items, styling, and behavior\n * when creating a filetree component.\n *\n * @public\n */\nexport interface FiletreeBuilderOptions {\n /**\n * Filesystem adapter for file operations (required).\n *\n * @remarks\n * Provides filesystem operations like reading directories and checking file stats.\n * Use the adapter from `@boba-cli/machine`.\n */\n filesystem: FileSystemAdapter\n /**\n * Path adapter for path operations (required).\n *\n * @remarks\n * Provides path manipulation utilities like join, resolve, and basename.\n * Use the adapter from `@boba-cli/machine`.\n */\n path: PathAdapter\n /**\n * Initial directory to display.\n *\n * @remarks\n * If not provided, defaults to the current working directory from the filesystem adapter.\n */\n currentDirectory?: string\n /**\n * Whether to show hidden files (default: false).\n */\n showHidden?: boolean\n /**\n * Height of the filetree component in lines.\n *\n * @remarks\n * Controls the viewport size for scrolling. If not provided, defaults to 24.\n */\n height?: number\n /**\n * Width of the filetree component in characters.\n *\n * @remarks\n * Used for text wrapping and rendering. If not provided, defaults to 80.\n */\n width?: number\n /**\n * Custom styles for the filetree components.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting.\n * Partial styles are merged with defaults.\n */\n styles?: Partial<FiletreeStyles>\n /**\n * Custom key mappings for filetree navigation.\n *\n * @remarks\n * Provides control over which keys trigger navigation actions like moving up/down.\n * If not provided, uses default key bindings.\n */\n keyMap?: FiletreeKeyMap\n}\n\n/**\n * Create a filetree component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/filetree` package.\n * The filetree provides keyboard navigation through directory listings with\n * scrolling viewport support.\n *\n * @example\n * Basic usage:\n * ```typescript\n * import { filetree } from '@boba-cli/dsl'\n * import { filesystem, path } from '@boba-cli/machine'\n *\n * const app = createApp()\n * .component('files', filetree({\n * filesystem,\n * path,\n * }))\n * .view(({ components }) => components.files)\n * .build()\n * ```\n *\n * @example\n * With custom styling and directory:\n * ```typescript\n * import { filetree } from '@boba-cli/dsl'\n * import { filesystem, path } from '@boba-cli/machine'\n * import { Style } from '@boba-cli/chapstick'\n *\n * const app = createApp()\n * .component('files', filetree({\n * filesystem,\n * path,\n * currentDirectory: '/home/user/projects',\n * showHidden: true,\n * height: 20,\n * width: 80,\n * styles: {\n * selectedItem: new Style().foreground('#50fa7b').bold(),\n * normalItem: new Style().foreground('#f8f8f2'),\n * },\n * }))\n * .view(({ components }) => vstack(\n * text('File Browser').bold(),\n * components.files\n * ))\n * .build()\n * ```\n *\n * @param options - Configuration options for the filetree\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function filetree(\n options: FiletreeBuilderOptions,\n): ComponentBuilder<FiletreeModel> {\n return {\n init(): [FiletreeModel, Cmd<Msg>] {\n const model = FiletreeModel.new({\n filesystem: options.filesystem,\n path: options.path,\n currentDir: options.currentDirectory,\n showHidden: options.showHidden,\n height: options.height,\n width: options.width,\n styles: options.styles,\n keyMap: options.keyMap,\n })\n return [model, model.init()]\n },\n\n update(model: FiletreeModel, msg: Msg): [FiletreeModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: FiletreeModel): string {\n return model.view()\n },\n }\n}\n\n// Re-export DirectoryItem type for convenience\nexport type { DirectoryItem } from '@boba-cli/filetree'\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { HelpModel, type HelpOptions, type KeyMap } from '@boba-cli/help'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the help component builder.\n *\n * @remarks\n * Configure the help view appearance and behavior when creating a help component.\n *\n * @public\n */\nexport interface HelpBuilderOptions {\n /**\n * Key map providing help bindings (required).\n *\n * @remarks\n * The key map provides the bindings to display in the help view via\n * `shortHelp()` and `fullHelp()` methods.\n */\n keyMap: KeyMap\n\n /**\n * Maximum width for the help view (default: 0, unlimited).\n *\n * @remarks\n * When set, the help view will truncate content that exceeds this width\n * and append an ellipsis indicator.\n */\n width?: number\n\n /**\n * Show full help with all key bindings (default: false).\n *\n * @remarks\n * When false, displays a compact single-line view.\n * When true, displays a multi-column view with all available bindings.\n */\n showAll?: boolean\n\n /**\n * Separator string for short help mode (default: ' • ').\n *\n * @remarks\n * Appears between key bindings in the compact single-line view.\n */\n shortSeparator?: string\n\n /**\n * Separator string for full help mode (default: ' ').\n *\n * @remarks\n * Appears between columns in the multi-column full help view.\n */\n fullSeparator?: string\n\n /**\n * Ellipsis indicator when truncating content (default: '…').\n *\n * @remarks\n * Displayed when help content exceeds the configured width.\n */\n ellipsis?: string\n\n /**\n * Custom styles for help text rendering.\n *\n * @remarks\n * Partial object of `HelpStyles` to override default styling.\n * Available style properties: shortKey, shortDesc, shortSeparator,\n * fullKey, fullDesc, fullSeparator, ellipsis.\n */\n styles?: HelpOptions['styles']\n}\n\n/**\n * Create a help component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/help` package.\n * The help component renders keyboard shortcuts and their descriptions,\n * with support for both compact and full-screen modes.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const keyMap = {\n * shortHelp: () => [bindings.up, bindings.down, bindings.quit],\n * fullHelp: () => [[bindings.up, bindings.down], [bindings.quit]]\n * }\n *\n * const app = createApp()\n * .component('help', help({ keyMap }))\n * .view(({ components }) => vstack(\n * text('My App'),\n * components.help\n * ))\n * .build()\n * ```\n *\n * @example\n * With custom configuration:\n * ```typescript\n * const app = createApp()\n * .component('help', help({\n * keyMap,\n * width: 80,\n * showAll: false,\n * styles: {\n * shortKey: new Style().foreground('#50fa7b').bold(),\n * shortDesc: new Style().foreground('#f8f8f2')\n * }\n * }))\n * .view(({ components }) => components.help)\n * .build()\n * ```\n *\n * @param options - Configuration options for the help component (keyMap is required)\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function help(options: HelpBuilderOptions): ComponentBuilder<HelpModel> {\n const { keyMap } = options\n const helpOpts: HelpOptions = {\n width: options.width,\n showAll: options.showAll,\n shortSeparator: options.shortSeparator,\n fullSeparator: options.fullSeparator,\n ellipsis: options.ellipsis,\n styles: options.styles,\n }\n\n return {\n init(): [HelpModel, Cmd<Msg>] {\n const model = HelpModel.new(helpOpts)\n return [model, null]\n },\n\n update(model: HelpModel, msg: Msg): [HelpModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: HelpModel): string {\n return model.view(keyMap)\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { HelpBubble, type Entry, type TitleColor } from '@boba-cli/help'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the help bubble component builder.\n *\n * @remarks\n * Configure the help bubble's title, colors, and entries when creating the component.\n *\n * @public\n */\nexport interface HelpBubbleBuilderOptions {\n /**\n * Array of help entries to display (required).\n *\n * @remarks\n * Each entry contains a key binding and its description.\n */\n entries: Entry[]\n /**\n * Title text for the help screen (default: 'Help').\n *\n * @remarks\n * Displayed at the top of the help bubble with styled background and foreground colors.\n */\n title?: string\n /**\n * Color configuration for the title bar.\n *\n * @remarks\n * Supports adaptive colors for light and dark terminals.\n * Uses `ColorInput` from `@boba-cli/chapstick`.\n */\n titleColor?: TitleColor\n /**\n * Whether the help bubble starts in active state (default: false).\n *\n * @remarks\n * When active, the help bubble receives keyboard input for scrolling.\n * When inactive, it ignores input messages.\n */\n active?: boolean\n}\n\n/**\n * Create a help bubble component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/help` package.\n * The help bubble displays a scrollable list of key bindings with descriptions,\n * using a viewport for navigation.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const app = createApp()\n * .component('help', helpBubble({\n * entries: [\n * { key: 'q/esc', description: 'Quit' },\n * { key: 'j/down', description: 'Move down' },\n * { key: 'k/up', description: 'Move up' }\n * ]\n * }))\n * .view(({ components }) => components.help)\n * .build()\n * ```\n *\n * @example\n * With custom title and colors:\n * ```typescript\n * const app = createApp()\n * .component('help', helpBubble({\n * entries: [\n * { key: 'enter', description: 'Select item' },\n * { key: 'tab', description: 'Switch focus' }\n * ],\n * title: 'Keyboard Shortcuts',\n * titleColor: {\n * background: '#282a36',\n * foreground: '#f8f8f2'\n * },\n * active: true\n * }))\n * .view(({ components }) => components.help)\n * .build()\n * ```\n *\n * @param options - Configuration options for the help bubble\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function helpBubble(\n options: HelpBubbleBuilderOptions,\n): ComponentBuilder<HelpBubble> {\n const title = options.title ?? 'Help'\n const titleColor = options.titleColor ?? {\n background: { dark: '#5f5f87', light: '#d7d7ff' },\n foreground: { dark: '#ffffff', light: '#000000' },\n }\n const active = options.active ?? false\n\n return {\n init(): [HelpBubble, Cmd<Msg>] {\n const model = HelpBubble.new(active, title, titleColor, options.entries)\n return [model, null]\n },\n\n update(model: HelpBubble, msg: Msg): [HelpBubble, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: HelpBubble): string {\n return model.view()\n },\n }\n}\n\n/**\n * Re-export Entry type for convenience.\n *\n * @public\n */\nexport type { Entry }\n","import type { Cmd, Msg } from '@boba-cli/tea'\nimport {\n ListModel,\n type Item,\n type ItemDelegate,\n type ListKeyMap,\n type ListStyles,\n} from '@boba-cli/list'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the list component builder.\n *\n * @remarks\n * Configure the list component with items, styling, and behavior when creating a list component.\n *\n * @typeParam T - The item type, must extend `Item`\n *\n * @public\n */\nexport interface ListBuilderOptions<T extends Item> {\n /**\n * Items to display in the list (required).\n *\n * @remarks\n * Each item must implement the `Item` interface with `filterValue()`,\n * `title()`, and `description()` methods.\n */\n items: T[]\n /**\n * Custom delegate for rendering items.\n *\n * @remarks\n * Provides control over how items are rendered, including height, spacing,\n * and custom styling. If not provided, uses the default delegate.\n */\n delegate?: ItemDelegate<T>\n /**\n * Height of the list component in lines.\n *\n * @remarks\n * Used to calculate pagination. If not provided, defaults to 0.\n *\n * When set to 0 (the default), the height is calculated based on the item delegate's\n * height calculations with a default page size. When set to a positive value, pagination\n * is calculated to fit that many lines, creating a scrollable list if items exceed the height.\n */\n height?: number\n /**\n * Width of the list component in characters.\n *\n * @remarks\n * Used for help text wrapping. If not provided, defaults to 0 (no width limit).\n */\n width?: number\n /**\n * Title displayed at the top of the list.\n *\n * @remarks\n * Only shown if `showTitle` is true (default: true).\n */\n title?: string\n /**\n * Whether to show the title bar (default: true).\n */\n showTitle?: boolean\n /**\n * Whether to show the filter input (default: true).\n */\n showFilter?: boolean\n /**\n * Whether to show pagination controls (default: true).\n */\n showPagination?: boolean\n /**\n * Whether to show help text (default: true).\n */\n showHelp?: boolean\n /**\n * Whether to show the status bar (default: true).\n */\n showStatusBar?: boolean\n /**\n * Whether filtering is enabled (default: true).\n *\n * @remarks\n * When false, filter-related keybindings and UI are disabled.\n */\n filteringEnabled?: boolean\n /**\n * Custom styles for the list components.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting.\n * Partial styles are merged with defaults.\n */\n styles?: Partial<ListStyles>\n /**\n * Custom key mappings for list navigation and actions.\n *\n * @remarks\n * Provides control over which keys trigger list actions like navigation,\n * filtering, and help display. If not provided, uses default key bindings.\n */\n keyMap?: ListKeyMap\n}\n\n/**\n * Create a list component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/list` package.\n * The list provides filtering, pagination, keyboard navigation, and help display.\n *\n * @example\n * Basic usage:\n * ```typescript\n * import { list, type Item } from '@boba-cli/dsl'\n *\n * interface TodoItem extends Item {\n * filterValue: () => string\n * title: () => string\n * description: () => string\n * }\n *\n * const items: TodoItem[] = [\n * { filterValue: () => 'Buy milk', title: () => 'Buy milk', description: () => 'From the store' },\n * { filterValue: () => 'Walk dog', title: () => 'Walk dog', description: () => 'In the park' },\n * ]\n *\n * const app = createApp()\n * .component('todos', list({ items }))\n * .view(({ components }) => components.todos)\n * .build()\n * ```\n *\n * @example\n * With custom styling and options:\n * ```typescript\n * import { list } from '@boba-cli/dsl'\n * import { Style } from '@boba-cli/chapstick'\n *\n * const app = createApp()\n * .component('todos', list({\n * items: todoItems,\n * title: 'My Tasks',\n * height: 20,\n * width: 80,\n * styles: {\n * titleBar: new Style().foreground('#50fa7b').bold(),\n * filterPrompt: new Style().foreground('#8be9fd'),\n * },\n * showPagination: true,\n * filteringEnabled: true,\n * }))\n * .view(({ components }) => vstack(\n * text('Welcome to the Todo App').bold(),\n * components.todos\n * ))\n * .build()\n * ```\n *\n * @typeParam T - The item type, must extend `Item`\n * @param options - Configuration options for the list\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function list<T extends Item>(\n options: ListBuilderOptions<T>,\n): ComponentBuilder<ListModel<T>> {\n return {\n init(): [ListModel<T>, Cmd<Msg>] {\n const model = ListModel.new({\n items: options.items,\n delegate: options.delegate,\n height: options.height,\n width: options.width,\n title: options.title,\n showTitle: options.showTitle,\n showFilter: options.showFilter,\n showPagination: options.showPagination,\n showHelp: options.showHelp,\n showStatusBar: options.showStatusBar,\n filteringEnabled: options.filteringEnabled,\n styles: options.styles,\n keyMap: options.keyMap,\n })\n return [model, model.init()]\n },\n\n update(model: ListModel<T>, msg: Msg): [ListModel<T>, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: ListModel<T>): string {\n return model.view()\n },\n }\n}\n\n// Re-export Item type for convenience\nexport type { Item } from '@boba-cli/list'\n","import type { Cmd, Msg } from '@boba-cli/tea'\nimport { renderMarkdown } from '@boba-cli/markdown'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Simple model for rendering static markdown content.\n *\n * @remarks\n * This is a minimal TEA model that holds pre-rendered markdown content.\n * It does not handle file I/O or user interaction - it simply displays\n * the rendered markdown string.\n *\n * @internal\n */\nclass StaticMarkdownModel {\n constructor(private readonly renderedContent: string) {}\n\n /**\n * Tea init hook (no-op).\n */\n init(): Cmd<Msg> {\n return null\n }\n\n /**\n * Tea update hook (no-op - static content).\n */\n update(_msg: Msg): [StaticMarkdownModel, Cmd<Msg>] {\n return [this, null]\n }\n\n /**\n * Render the markdown content.\n */\n view(): string {\n return this.renderedContent\n }\n}\n\n/**\n * Options for the markdown component builder.\n *\n * @remarks\n * Configure markdown rendering when creating a markdown component.\n * The markdown is rendered once during initialization using the `marked`\n * library with terminal styling support.\n *\n * @public\n */\nexport interface MarkdownBuilderOptions {\n /**\n * Markdown content to render.\n *\n * @remarks\n * This should be a string containing valid markdown syntax.\n * The content will be rendered with terminal styling including\n * colors for headings, bold/italic text, code blocks, links, etc.\n */\n content: string\n\n /**\n * Width for word wrapping (default: 80).\n *\n * @remarks\n * The markdown renderer will wrap text to fit within this width.\n * This is useful for ensuring content fits within your terminal or\n * specific layout constraints.\n */\n width?: number\n}\n\n/**\n * Create a markdown component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` that renders static markdown content\n * with terminal styling. The markdown is rendered once during initialization\n * using the `marked` library with the `marked-terminal` plugin for terminal\n * color support.\n *\n * This component displays static content and does not handle user input or\n * file operations. For file-based markdown viewing with scrolling, use\n * the `MarkdownModel` from `@boba-cli/markdown` directly.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const app = createApp()\n * .component('help', markdown({\n * content: '# Welcome\\n\\nThis is **bold** text.'\n * }))\n * .view(({ components }) => components.help)\n * .build()\n * ```\n *\n * @example\n * With custom width:\n * ```typescript\n * const app = createApp()\n * .component('readme', markdown({\n * content: readFileSync('README.md', 'utf-8'),\n * width: 120\n * }))\n * .view(({ components }) => vstack(\n * text('Documentation:').bold(),\n * components.readme\n * ))\n * .build()\n * ```\n *\n * @param options - Configuration options for the markdown component\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function markdown(\n options: MarkdownBuilderOptions,\n): ComponentBuilder<StaticMarkdownModel> {\n return {\n init(): [StaticMarkdownModel, Cmd<Msg>] {\n const renderedContent = renderMarkdown(options.content, {\n width: options.width ?? 80,\n })\n const model = new StaticMarkdownModel(renderedContent)\n return [model, null]\n },\n\n update(model: StaticMarkdownModel, msg: Msg): [StaticMarkdownModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: StaticMarkdownModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { PaginatorModel, PaginatorType, type KeyMap } from '@boba-cli/paginator'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the paginator component builder.\n *\n * @remarks\n * Configure the paginator display style and behavior when creating a paginator component.\n *\n * @public\n */\nexport interface PaginatorBuilderOptions {\n /**\n * Pagination display style (required).\n *\n * @remarks\n * - `'dots'`: Renders dots representing each page (e.g., '• ○ ○')\n * - `'arabic'`: Renders page numbers (e.g., '2/5')\n */\n type: 'dots' | 'arabic'\n\n /**\n * Number of items per page (required).\n *\n * @remarks\n * Must be at least 1. Used with `totalItems` to calculate total pages.\n */\n perPage: number\n\n /**\n * Total number of items to paginate (required).\n *\n * @remarks\n * Combined with `perPage` to automatically calculate the total number of pages.\n * For example, 100 items with 10 per page results in 10 pages.\n */\n totalItems: number\n\n /**\n * Initial page index (default: 0).\n *\n * @remarks\n * Zero-indexed page number. Will be clamped to valid range [0, totalPages - 1].\n */\n page?: number\n\n /**\n * Character for the active page dot (default: '•').\n *\n * @remarks\n * Only used when `type` is `'dots'`.\n */\n activeDot?: string\n\n /**\n * Character for inactive page dots (default: '○').\n *\n * @remarks\n * Only used when `type` is `'dots'`.\n */\n inactiveDot?: string\n\n /**\n * Format string for arabic pagination (default: '%d/%d').\n *\n * @remarks\n * Only used when `type` is `'arabic'`. The first `%d` is replaced with\n * the current page number (1-indexed), and the second `%d` is replaced with\n * the total number of pages.\n *\n * @example\n * ```typescript\n * arabicFormat: '%d/%d' // '3/10'\n * arabicFormat: 'Page %d of %d' // 'Page 3 of 10'\n * ```\n */\n arabicFormat?: string\n\n /**\n * Custom key bindings for paginator navigation.\n *\n * @remarks\n * Override default key bindings for navigating between pages.\n * By default, the paginator responds to:\n * - Next page: `pgdown`, `right`, `l`\n * - Previous page: `pgup`, `left`, `h`\n */\n keyMap?: KeyMap\n}\n\n/**\n * Create a paginator component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/paginator` package.\n * The paginator handles keyboard navigation and renders page indicators. It automatically\n * calculates total pages from `totalItems` and `perPage`.\n *\n * Use `PaginatorModel.getSliceBounds()` to get the [start, end] indices for slicing\n * your data array for the current page.\n *\n * @example\n * Basic arabic pagination:\n * ```typescript\n * const app = createApp<{ items: string[] }>()\n * .state({ items: [...Array(100)].map((_, i) => `Item ${i}`) })\n * .component('pager', paginator({\n * type: 'arabic',\n * perPage: 10,\n * totalItems: 100\n * }))\n * .view(({ state, components }) => {\n * const [start, end] = components.pager.getSliceBounds(state.items.length)\n * const pageItems = state.items.slice(start, end)\n * return vstack(\n * ...pageItems.map(item => text(item)),\n * components.pager\n * )\n * })\n * .build()\n * ```\n *\n * @example\n * Dots-style pagination:\n * ```typescript\n * const app = createApp()\n * .component('pager', paginator({\n * type: 'dots',\n * perPage: 1,\n * totalItems: 5,\n * activeDot: '●',\n * inactiveDot: '○'\n * }))\n * .view(({ components }) => hstack(\n * text('Navigate: '),\n * components.pager\n * ))\n * .build()\n * ```\n *\n * @example\n * Custom arabic format:\n * ```typescript\n * const app = createApp()\n * .component('pager', paginator({\n * type: 'arabic',\n * perPage: 20,\n * totalItems: 200,\n * arabicFormat: 'Page %d of %d'\n * }))\n * .view(({ components }) => components.pager)\n * .build()\n * ```\n *\n * @param options - Configuration options for the paginator (type, perPage, and totalItems are required)\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function paginator(options: PaginatorBuilderOptions): ComponentBuilder<PaginatorModel> {\n return {\n init(): [PaginatorModel, Cmd<Msg>] {\n // Convert the 'dots' | 'arabic' string type to PaginatorType enum\n const paginatorType =\n options.type === 'dots' ? PaginatorType.Dots : PaginatorType.Arabic\n\n // Create the base model with provided options\n const model = PaginatorModel.new({\n type: paginatorType,\n page: options.page,\n perPage: options.perPage,\n activeDot: options.activeDot,\n inactiveDot: options.inactiveDot,\n arabicFormat: options.arabicFormat,\n keyMap: options.keyMap,\n }).setTotalPages(options.totalItems)\n\n // Paginator doesn't need any initialization commands\n return [model, null]\n },\n\n update(model: PaginatorModel, msg: Msg): [PaginatorModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: PaginatorModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { Style, type ColorInput } from '@boba-cli/chapstick'\nimport { ProgressModel } from '@boba-cli/progress'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the progress bar component builder.\n *\n * @remarks\n * Configure the progress bar appearance, animation, and behavior when creating\n * a progress component.\n *\n * @public\n */\nexport interface ProgressBuilderOptions {\n /**\n * Width of the progress bar in characters (default: 40).\n *\n * @remarks\n * If `showPercentage` is enabled, the percentage text width is subtracted\n * from this total width.\n *\n * When set to 0, the progress bar itself is not displayed, but the percentage\n * text (if enabled via `showPercentage`) will still be shown. This is rarely\n * useful - prefer setting a positive width to display the actual bar.\n */\n width?: number\n\n /**\n * Character to use for the filled portion (default: '█').\n *\n * @remarks\n * Only the first character is used if a multi-character string is provided.\n */\n full?: string\n\n /**\n * Character to use for the empty portion (default: '░').\n *\n * @remarks\n * Only the first character is used if a multi-character string is provided.\n */\n empty?: string\n\n /**\n * Color for the filled portion (default: '#7571F9').\n *\n * @remarks\n * Ignored if `gradient` is configured. Accepts hex colors or named colors.\n */\n fullColor?: ColorInput\n\n /**\n * Color for the empty portion (default: '#606060').\n *\n * @remarks\n * Accepts hex colors or named colors.\n */\n emptyColor?: ColorInput\n\n /**\n * Whether to display the percentage value (default: true).\n */\n showPercentage?: boolean\n\n /**\n * Format string for the percentage display (default: ' %3.0f%%').\n *\n * @remarks\n * Supports printf-style formatting: `%[width].[precision]f`.\n * Use `%%` to output a literal `%` character.\n *\n * @example\n * ```typescript\n * ' %3.0f%%' // ' 100%'\n * ' %5.1f%%' // ' 50.5%'\n * ```\n */\n percentFormat?: string\n\n /**\n * Gradient configuration for the filled portion.\n *\n * @remarks\n * When set, overrides `fullColor` and creates a smooth color gradient\n * from `start` to `end`. The `scaleGradientToProgress` option controls whether the\n * gradient spans the entire bar width or only the filled portion.\n */\n gradient?: {\n /**\n * Starting color of the gradient.\n */\n start: ColorInput\n /**\n * Ending color of the gradient.\n */\n end: ColorInput\n /**\n * Whether to scale gradient to filled portion only (default: false).\n *\n * @remarks\n * - If `false`, gradient spans the entire bar width (progress reveals gradient)\n * - If `true`, gradient spans only the filled portion (gradient shrinks/grows)\n */\n scaleGradientToProgress?: boolean\n }\n\n /**\n * Spring animation configuration.\n *\n * @remarks\n * Controls the spring physics for smooth easing when the progress bar updates.\n * Higher frequency makes the animation faster, higher damping reduces oscillation.\n */\n spring?: {\n /**\n * Spring frequency (default: 18).\n *\n * @remarks\n * Higher values make the animation respond faster.\n */\n frequency?: number\n /**\n * Spring damping (default: 1).\n *\n * @remarks\n * Higher values reduce oscillation. A value of 1 is critically damped.\n */\n damping?: number\n }\n\n /**\n * Style for rendering the percentage text.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting.\n */\n percentageStyle?: Style\n}\n\n/**\n * Create a progress bar component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/progress` package.\n * The progress bar animates smoothly with spring physics when updated via\n * `ProgressModel.setPercent()`. Animation is triggered by calling `setPercent()`\n * on the model, not automatically on initialization.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const app = createApp()\n * .component('progress', progress())\n * .view(({ components }) => components.progress)\n * .build()\n * ```\n *\n * @example\n * With gradient styling:\n * ```typescript\n * const app = createApp()\n * .component('progress', progress({\n * gradient: {\n * start: '#5A56E0',\n * end: '#EE6FF8',\n * scaleGradientToProgress: false\n * },\n * showPercentage: true\n * }))\n * .view(({ components }) => vstack(\n * text('Loading...'),\n * components.progress\n * ))\n * .build()\n * ```\n *\n * @example\n * With custom spring animation:\n * ```typescript\n * const app = createApp()\n * .component('progress', progress({\n * spring: {\n * frequency: 25,\n * damping: 0.8\n * }\n * }))\n * .view(({ components }) => components.progress)\n * .build()\n * ```\n *\n * @param options - Configuration options for the progress bar\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function progress(\n options: ProgressBuilderOptions = {},\n): ComponentBuilder<ProgressModel> {\n return {\n init(): [ProgressModel, Cmd<Msg>] {\n // Determine if we should use gradient or solid fill\n const hasGradient = options.gradient !== undefined\n\n const model = hasGradient\n ? ProgressModel.withGradient(\n options.gradient!.start,\n options.gradient!.end,\n {\n width: options.width,\n full: options.full,\n empty: options.empty,\n emptyColor: options.emptyColor,\n showPercentage: options.showPercentage,\n percentFormat: options.percentFormat,\n scaleGradient: options.gradient!.scaleGradientToProgress,\n springFrequency: options.spring?.frequency,\n springDamping: options.spring?.damping,\n percentageStyle: options.percentageStyle,\n },\n )\n : ProgressModel.new({\n width: options.width,\n full: options.full,\n empty: options.empty,\n fullColor: options.fullColor,\n emptyColor: options.emptyColor,\n showPercentage: options.showPercentage,\n percentFormat: options.percentFormat,\n springFrequency: options.spring?.frequency,\n springDamping: options.spring?.damping,\n percentageStyle: options.percentageStyle,\n })\n\n // Progress doesn't auto-animate; animation is triggered by setPercent()\n return [model, null]\n },\n\n update(model: ProgressModel, msg: Msg): [ProgressModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: ProgressModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { Style } from '@boba-cli/chapstick'\nimport { SpinnerModel, type Spinner, line, type SpinnerOptions } from '@boba-cli/spinner'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the spinner component builder.\n *\n * @remarks\n * Configure the spinner animation and styling when creating a spinner component.\n * Spinners are animated loading indicators that automatically tick and cycle through\n * animation frames.\n *\n * @public\n */\nexport interface SpinnerBuilderOptions {\n /**\n * Spinner animation to use (default: `line`).\n *\n * @remarks\n * Available spinners include:\n * - `line`: Simple line rotation animation (default)\n * - `dot`: Dots appearing sequentially\n * - `miniDot`: Compact dot animation\n * - `pulse`: Pulsing circle animation\n * - `points`: Loading dots animation\n * - `moon`: Moon phase animation\n * - `meter`: Progress meter style\n * - `ellipsis`: Bouncing ellipsis\n *\n * All spinners are re-exported from `@boba-cli/spinner` for convenience.\n */\n spinner?: Spinner\n\n /**\n * Style for rendering the spinner.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting.\n * The style is applied to all frames of the spinner animation.\n */\n style?: Style\n}\n\n/**\n * Create a spinner component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/spinner` package.\n * The spinner automatically animates by ticking through animation frames. It starts\n * animating immediately when the application initializes.\n *\n * Spinners are useful for indicating loading states, background processes, or\n * that the application is working on a task.\n *\n * @example\n * Basic usage with default spinner:\n * ```typescript\n * const app = createApp()\n * .component('loading', spinner())\n * .view(({ components }) => vstack(\n * text('Loading...'),\n * components.loading\n * ))\n * .build()\n * ```\n *\n * @example\n * With custom animation and styling:\n * ```typescript\n * import { spinner, moon } from '@boba-cli/dsl'\n * import { Style } from '@boba-cli/chapstick'\n *\n * const app = createApp()\n * .component('loading', spinner({\n * spinner: moon,\n * style: new Style().foreground('#50fa7b')\n * }))\n * .view(({ components }) => hstack(\n * components.loading,\n * text(' Processing...')\n * ))\n * .build()\n * ```\n *\n * @example\n * Multiple spinners with different styles:\n * ```typescript\n * const app = createApp()\n * .component('spinner1', spinner({\n * spinner: line,\n * style: new Style().foreground('#ff79c6')\n * }))\n * .component('spinner2', spinner({\n * spinner: pulse,\n * style: new Style().foreground('#8be9fd')\n * }))\n * .view(({ components }) => vstack(\n * hstack(components.spinner1, text(' Task 1')),\n * hstack(components.spinner2, text(' Task 2'))\n * ))\n * .build()\n * ```\n *\n * @param options - Configuration options for the spinner\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function spinner(options: SpinnerBuilderOptions = {}): ComponentBuilder<SpinnerModel> {\n const spinnerOpts: SpinnerOptions = {\n spinner: options.spinner ?? line,\n style: options.style ?? new Style(),\n }\n\n return {\n init(): [SpinnerModel, Cmd<Msg>] {\n const model = new SpinnerModel(spinnerOpts)\n return [model, model.tick() as Cmd<Msg>]\n },\n\n update(model: SpinnerModel, msg: Msg): [SpinnerModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: SpinnerModel): string {\n return model.view()\n },\n }\n}\n","import type { Cmd, Msg } from '@boba-cli/tea'\nimport type { ColorInput } from '@boba-cli/chapstick'\nimport { StatusbarModel, type ColorConfig } from '@boba-cli/statusbar'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the statusbar component builder.\n *\n * @remarks\n * Configure the color scheme for the four columns of the statusbar.\n * Each column can have independent foreground and background colors.\n *\n * The statusbar is typically displayed at the bottom of your application and\n * provides a 4-column layout for displaying status information.\n *\n * Column layout:\n * - First column: Left-aligned, max 30 characters (truncated with ellipsis if exceeded)\n * - Second column: Fills remaining space, truncates as needed\n * - Third column: Right-aligned, no truncation\n * - Fourth column: Right-most column, right-aligned, no truncation\n *\n * @public\n */\nexport interface StatusBarBuilderOptions {\n /**\n * Color configuration for the first column.\n *\n * @remarks\n * The first column is left-aligned and truncates if content exceeds\n * 30 characters. Typically used for app name or mode display.\n */\n first: {\n foreground: ColorInput\n background: ColorInput\n }\n\n /**\n * Color configuration for the second column.\n *\n * @remarks\n * The second column fills the remaining space between the first\n * and third columns, truncating content as needed. Typically used\n * for contextual information or current file/state.\n */\n second: {\n foreground: ColorInput\n background: ColorInput\n }\n\n /**\n * Color configuration for the third column.\n *\n * @remarks\n * The third column is right-aligned and does not truncate. Typically\n * used for brief status indicators or counts.\n */\n third: {\n foreground: ColorInput\n background: ColorInput\n }\n\n /**\n * Color configuration for the fourth column.\n *\n * @remarks\n * The fourth column is right-aligned at the edge and does not truncate.\n * Typically used for time, position, or resource usage display.\n */\n fourth: {\n foreground: ColorInput\n background: ColorInput\n }\n}\n\n/**\n * Create a statusbar component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/statusbar` package.\n * The statusbar displays a 4-column layout at the bottom of the screen with\n * customizable colors for each column.\n *\n * The statusbar automatically responds to window resize events and manages\n * column layout and truncation. To update statusbar content, use the\n * `sendToComponent()` method in event handlers with the model's `setContent()` method.\n *\n * @example\n * Basic usage with color configuration:\n * ```typescript\n * const app = createApp()\n * .component('status', statusbar({\n * first: { foreground: '#ffffff', background: '#5555ff' },\n * second: { foreground: '#ffffff', background: '#333333' },\n * third: { foreground: '#ffffff', background: '#555555' },\n * fourth: { foreground: '#ffffff', background: '#ff5555' }\n * }))\n * .view(({ components }) => components.status)\n * .build()\n * ```\n *\n * @example\n * With dynamic content updates via key handlers:\n * ```typescript\n * const app = createApp<{ count: number }>()\n * .state({ count: 0 })\n * .component('status', statusbar({\n * first: { foreground: '#ffffff', background: '#5555ff' },\n * second: { foreground: '#ffffff', background: '#333333' },\n * third: { foreground: '#ffffff', background: '#555555' },\n * fourth: { foreground: '#ffffff', background: '#ff5555' }\n * }))\n * .onKey('up', ({ state, sendToComponent }) => {\n * sendToComponent('status', (model) =>\n * model.setContent(\n * 'MyApp v1.0',\n * 'Ready',\n * `Count: ${state.count}`,\n * new Date().toLocaleTimeString()\n * )\n * )\n * })\n * .view(({ components }) => vstack(\n * text('Press up to update status'),\n * components.status\n * ))\n * .build()\n * ```\n *\n * @example\n * With custom colors from Dracula theme:\n * ```typescript\n * const app = createApp()\n * .component('status', statusbar({\n * first: { foreground: '#f8f8f2', background: '#bd93f9' },\n * second: { foreground: '#f8f8f2', background: '#44475a' },\n * third: { foreground: '#f8f8f2', background: '#6272a4' },\n * fourth: { foreground: '#282a36', background: '#50fa7b' }\n * }))\n * .onKey('space', ({ sendToComponent }) => {\n * sendToComponent('status', (model) =>\n * model.setContent('Editor', 'file.ts', 'Ln 42', '12:34 PM')\n * )\n * })\n * .view(({ components }) => components.status)\n * .build()\n * ```\n *\n * @param options - Color configuration for the four statusbar columns\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function statusBar(\n options: StatusBarBuilderOptions,\n): ComponentBuilder<StatusbarModel> {\n const firstColors: ColorConfig = {\n foreground: options.first.foreground,\n background: options.first.background,\n }\n const secondColors: ColorConfig = {\n foreground: options.second.foreground,\n background: options.second.background,\n }\n const thirdColors: ColorConfig = {\n foreground: options.third.foreground,\n background: options.third.background,\n }\n const fourthColors: ColorConfig = {\n foreground: options.fourth.foreground,\n background: options.fourth.background,\n }\n\n return {\n init(): [StatusbarModel, Cmd<Msg>] {\n const model = StatusbarModel.new(\n firstColors,\n secondColors,\n thirdColors,\n fourthColors,\n )\n return [model, null]\n },\n\n update(model: StatusbarModel, msg: Msg): [StatusbarModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: StatusbarModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { StopwatchModel, type StopwatchOptions } from '@boba-cli/stopwatch'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the stopwatch component builder.\n *\n * @remarks\n * Configure the stopwatch timing and automatic start behavior when creating a stopwatch component.\n *\n * @public\n */\nexport interface StopwatchBuilderOptions {\n /**\n * Tick interval in milliseconds (default: 1000).\n *\n * @remarks\n * Controls how frequently the stopwatch updates its elapsed time display.\n */\n interval?: number\n /**\n * Whether to automatically start the stopwatch on initialization (default: false).\n *\n * @remarks\n * When `true`, the stopwatch begins counting immediately.\n * When `false`, you must call `model.start()` to begin counting.\n */\n autoStart?: boolean\n}\n\n/**\n * Create a stopwatch component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/stopwatch` package.\n * The stopwatch tracks elapsed time and can be controlled via its model methods\n * (`start()`, `stop()`, `toggle()`, `reset()`).\n *\n * @example\n * Basic usage (manual start):\n * ```typescript\n * const app = createApp()\n * .component('timer', stopwatch())\n * .view(({ components }) => components.timer)\n * .build()\n * ```\n *\n * @example\n * Auto-start with custom interval:\n * ```typescript\n * const app = createApp()\n * .component('timer', stopwatch({\n * interval: 100,\n * autoStart: true\n * }))\n * .view(({ components }) => hstack(\n * text('Elapsed: '),\n * components.timer\n * ))\n * .build()\n * ```\n *\n * @example\n * With key bindings to control the stopwatch:\n * ```typescript\n * const app = createApp()\n * .component('timer', stopwatch({ interval: 1000 }))\n * .view(({ components }) => components.timer)\n * .build()\n *\n * // Access the model to control it:\n * // model.start() - start counting\n * // model.stop() - pause counting\n * // model.toggle() - toggle running state\n * // model.reset() - reset to 0\n * ```\n *\n * @param options - Configuration options for the stopwatch\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function stopwatch(\n options: StopwatchBuilderOptions = {},\n): ComponentBuilder<StopwatchModel> {\n const { interval, autoStart = false } = options\n\n const stopwatchOpts: StopwatchOptions = {\n interval,\n }\n\n return {\n init(): [StopwatchModel, Cmd<Msg>] {\n const model = StopwatchModel.new(stopwatchOpts)\n // Type cast is safe: model.start() returns Cmd<StopwatchMsg>, and StopwatchMsg extends Msg.\n // The cast widens the type to the more general Cmd<Msg> that the component builder requires.\n const cmd = autoStart ? (model.start() as Cmd<Msg>) : null\n return [model, cmd]\n },\n\n update(model: StopwatchModel, msg: Msg): [StopwatchModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: StopwatchModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport type { BorderStyle } from '@boba-cli/chapstick'\nimport {\n TableModel,\n type Column,\n type Row,\n type TableKeyMap,\n type TableStyles,\n type TableOptions,\n} from '@boba-cli/table'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the table component builder.\n *\n * @remarks\n * Configure the table's columns, data, dimensions, and behavior when creating a table component.\n *\n * @public\n */\nexport interface TableBuilderOptions {\n /**\n * Column definitions for the table (required).\n *\n * @remarks\n * Each column specifies a title and width. Columns define the structure of the table.\n */\n columns: Column[]\n /**\n * Row data for the table.\n *\n * @remarks\n * Each row is an array of strings corresponding to the columns.\n * If not provided, the table will be empty.\n */\n rows?: Row[]\n /**\n * Visible height in rows.\n *\n * @remarks\n * Controls how many rows are visible at once. The table will scroll if there are more rows.\n * If not specified, defaults to the number of rows.\n *\n * When explicitly set to 0, the table shows no rows (effectively hidden). This is rarely\n * desired - omit this option to show all rows, or set a positive value to create a scrollable\n * window showing that many rows at once.\n */\n height?: number\n /**\n * Total width of the table.\n *\n * @remarks\n * If not specified, the width is calculated from column widths.\n *\n * When explicitly set to 0, the table has no width constraint and uses the sum of column\n * widths. This is typically not useful since the default behavior already calculates width\n * from columns - prefer omitting this option to use auto-calculated width.\n */\n width?: number\n /**\n * Whether the table is focused and can accept keyboard input.\n *\n * @remarks\n * When focused, the table responds to navigation keys (arrow keys, page up/down, etc.).\n */\n focused?: boolean\n /**\n * Whether to render borders around the table.\n *\n * @remarks\n * When true, draws borders using the specified or default border style.\n */\n bordered?: boolean\n /**\n * Border style to use when bordered is true.\n *\n * @remarks\n * Uses `BorderStyle` from `@boba-cli/chapstick` to control border characters.\n */\n borderStyle?: BorderStyle\n /**\n * Styling for table elements (header, cells, selected row, border).\n *\n * @remarks\n * Customize the appearance of different table parts using `Style` objects.\n */\n styles?: Partial<TableStyles>\n /**\n * Keyboard navigation bindings.\n *\n * @remarks\n * Override default key bindings for navigation actions (up, down, page up/down, etc.).\n */\n keyMap?: Partial<TableKeyMap>\n}\n\n/**\n * Create a table component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/table` package.\n * The table supports scrolling, row selection, and keyboard navigation when focused.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const app = createApp()\n * .component('data', table({\n * columns: [\n * { title: 'Name', width: 20 },\n * { title: 'Age', width: 10 },\n * ],\n * rows: [\n * ['Alice', '30'],\n * ['Bob', '25'],\n * ],\n * }))\n * .view(({ components }) => components.data)\n * .build()\n * ```\n *\n * @example\n * With borders and focus:\n * ```typescript\n * const app = createApp()\n * .component('data', table({\n * columns: [\n * { title: 'Item', width: 15 },\n * { title: 'Status', width: 10 },\n * ],\n * rows: [\n * ['Task 1', 'Done'],\n * ['Task 2', 'Pending'],\n * ],\n * bordered: true,\n * focused: true,\n * height: 5,\n * }))\n * .view(({ components }) => components.data)\n * .build()\n * ```\n *\n * @param options - Configuration options for the table\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function table(options: TableBuilderOptions): ComponentBuilder<TableModel> {\n const tableOpts: TableOptions = {\n columns: options.columns,\n rows: options.rows,\n height: options.height,\n width: options.width,\n focused: options.focused,\n bordered: options.bordered,\n borderStyle: options.borderStyle,\n styles: options.styles,\n keyMap: options.keyMap,\n }\n\n return {\n init(): [TableModel, Cmd<Msg>] {\n const model = TableModel.new(tableOpts)\n return [model, model.init()]\n },\n\n update(model: TableModel, msg: Msg): [TableModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: TableModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { Style } from '@boba-cli/chapstick'\nimport {\n TextareaModel,\n CursorMode,\n type TextareaOptions,\n type ValidateFunc,\n type KeyMap,\n} from '@boba-cli/textarea'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the textArea component builder.\n *\n * @remarks\n * Configure the multi-line text area appearance, validation, and behavior when creating\n * a text area component.\n *\n * @public\n */\nexport interface TextAreaBuilderOptions {\n /**\n * Initial value (multi-line text).\n *\n * @remarks\n * The starting content of the textarea. Use `\\n` for line breaks.\n */\n value?: string\n\n /**\n * Placeholder text shown when textarea is empty.\n *\n * @remarks\n * Displayed in a dimmed style when the textarea has no content. Disappears once\n * the user starts typing. Uses `placeholderStyle` for styling.\n */\n placeholder?: string\n\n /**\n * Width constraint for the textarea.\n *\n * @remarks\n * When set to 0 or not specified, the textarea has no width constraint and grows with content.\n * When set to a positive value, lines longer than the width will wrap to the next line.\n */\n width?: number\n\n /**\n * Maximum visible height in lines.\n *\n * @remarks\n * When set to 0 (the default), there is no maximum height constraint - the textarea\n * will grow to accommodate all lines of content. When set to a positive value, the\n * textarea will not exceed that height and will become scrollable if content exceeds\n * the limit.\n */\n maxHeight?: number\n\n /**\n * Maximum width constraint.\n *\n * @remarks\n * When set, prevents lines from exceeding this width. Works together with `width`\n * to control horizontal sizing.\n */\n maxWidth?: number\n\n /**\n * Prompt string shown before each line.\n *\n * @remarks\n * Displayed at the start of each line in the textarea. Uses `promptStyle` for styling.\n * Common patterns include empty string (no prompt), `'| '`, or `' '` for indentation.\n */\n prompt?: string\n\n /**\n * Show line numbers on the left (default: false).\n *\n * @remarks\n * When enabled, displays line numbers in the left margin using `lineNumberStyle` for styling.\n * Useful for code editing or when line references are important.\n */\n showLineNumbers?: boolean\n\n /**\n * Cursor display mode.\n *\n * @remarks\n * Controls how the cursor is displayed:\n * - `CursorMode.Blink`: Cursor blinks on and off (default)\n * - `CursorMode.Static`: Cursor is always visible\n * - `CursorMode.Hidden`: Cursor is not displayed\n */\n cursorMode?: CursorMode\n\n /**\n * Validation function.\n *\n * @remarks\n * Called on every change to validate the current textarea content. Return `null` for\n * valid input, or an `Error` with a descriptive message for invalid input. The error\n * message can be displayed to the user in the UI.\n *\n * @example\n * ```typescript\n * validate: (value) => {\n * const lines = value.split('\\n')\n * if (lines.length > 10) return new Error('Maximum 10 lines allowed')\n * return null\n * }\n * ```\n */\n validate?: ValidateFunc\n\n /**\n * Style for the prompt.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting to the prompt.\n */\n promptStyle?: Style\n\n /**\n * Style for the text content.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting to typed text.\n */\n textStyle?: Style\n\n /**\n * Style for the placeholder text.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting to the placeholder.\n */\n placeholderStyle?: Style\n\n /**\n * Style for the cursor.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting to the cursor.\n */\n cursorStyle?: Style\n\n /**\n * Style for line numbers.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting to line numbers\n * when `showLineNumbers` is enabled.\n */\n lineNumberStyle?: Style\n\n /**\n * Custom key bindings.\n *\n * @remarks\n * Override default key bindings for navigation and editing actions. If not provided,\n * uses default Vi-like key bindings (arrow keys, j/k for up/down, etc.).\n */\n keyMap?: KeyMap\n}\n\n/**\n * Create a textArea component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/textarea` package.\n * The text area supports multi-line editing, scrolling, line numbers, validation,\n * and extensive styling options.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const app = createApp()\n * .component('editor', textArea({\n * placeholder: 'Enter your text...',\n * maxHeight: 10\n * }))\n * .view(({ components }) => components.editor)\n * .build()\n * ```\n *\n * @example\n * With line numbers and validation:\n * ```typescript\n * const app = createApp()\n * .component('editor', textArea({\n * placeholder: 'Type code here...',\n * maxHeight: 20,\n * width: 80,\n * showLineNumbers: true,\n * validate: (value) => {\n * const lines = value.split('\\n')\n * if (lines.length > 100) return new Error('Maximum 100 lines')\n * return null\n * }\n * }))\n * .view(({ components }) => components.editor)\n * .build()\n * ```\n *\n * @example\n * With custom styling and cursor mode:\n * ```typescript\n * import { textArea, CursorMode } from '@boba-cli/dsl'\n * import { Style } from '@boba-cli/chapstick'\n *\n * const app = createApp()\n * .component('styledEditor', textArea({\n * prompt: '| ',\n * showLineNumbers: true,\n * cursorMode: CursorMode.Static,\n * promptStyle: new Style().foreground('#6272a4'),\n * textStyle: new Style().foreground('#f8f8f2'),\n * lineNumberStyle: new Style().foreground('#44475a').dim(),\n * cursorStyle: new Style().foreground('#ff79c6').bold()\n * }))\n * .view(({ components }) => components.styledEditor)\n * .build()\n * ```\n *\n * @example\n * Multi-line input with initial value:\n * ```typescript\n * const app = createApp()\n * .component('notes', textArea({\n * value: 'Line 1\\nLine 2\\nLine 3',\n * placeholder: 'Enter notes...',\n * maxHeight: 15,\n * maxWidth: 60\n * }))\n * .view(({ components }) => components.notes)\n * .build()\n * ```\n *\n * @param options - Configuration options for the text area\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function textArea(\n options: TextAreaBuilderOptions = {},\n): ComponentBuilder<TextareaModel> {\n const textareaOpts: TextareaOptions = {\n value: options.value,\n placeholder: options.placeholder,\n width: options.width,\n maxHeight: options.maxHeight,\n maxWidth: options.maxWidth,\n prompt: options.prompt ?? '',\n showLineNumbers: options.showLineNumbers ?? false,\n cursorMode: options.cursorMode,\n validate: options.validate,\n promptStyle: options.promptStyle ?? new Style(),\n textStyle: options.textStyle ?? new Style(),\n placeholderStyle: options.placeholderStyle ?? new Style(),\n cursorStyle: options.cursorStyle ?? new Style(),\n lineNumberStyle: options.lineNumberStyle ?? new Style(),\n keyMap: options.keyMap,\n }\n\n return {\n init(): [TextareaModel, Cmd<Msg>] {\n const model = TextareaModel.new(textareaOpts)\n const [focused, cmd] = model.focus()\n return [focused, cmd]\n },\n\n update(model: TextareaModel, msg: Msg): [TextareaModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: TextareaModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { Style } from '@boba-cli/chapstick'\nimport {\n TextInputModel,\n EchoMode,\n type TextInputOptions,\n type ValidateFunc,\n} from '@boba-cli/textinput'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the textInput component builder.\n *\n * @remarks\n * Configure the text input's appearance, behavior, and validation when creating\n * a text input component.\n *\n * @public\n */\nexport interface TextInputBuilderOptions {\n /**\n * Placeholder text shown when input is empty.\n *\n * @remarks\n * Displayed in a dimmed style when the input has no content. Uses `placeholderStyle` for styling.\n */\n placeholder?: string\n\n /**\n * Width constraint for the input field.\n *\n * @remarks\n * When set to 0 or not specified, the input field has no width constraint.\n * When set to a positive value, the input will be constrained to that width.\n */\n width?: number\n\n /**\n * Echo mode for input display (default: `EchoMode.Normal`).\n *\n * @remarks\n * Controls how input characters are displayed:\n * - `EchoMode.Normal`: Characters are displayed as typed\n * - `EchoMode.Password`: Characters are hidden with asterisks or dots\n * - `EchoMode.None`: No characters are displayed\n */\n echoMode?: EchoMode\n\n /**\n * Character limit for input (default: 0 = unlimited).\n *\n * @remarks\n * When set to a positive value, prevents input beyond the specified character count.\n * When 0 or not specified, there is no character limit.\n */\n charLimit?: number\n\n /**\n * Prompt string shown before the input.\n *\n * @remarks\n * Displayed at the start of the input line. Uses `promptStyle` for styling.\n * Common examples: `'> '`, `'$ '`, or `'Name: '`.\n */\n prompt?: string\n\n /**\n * Style for the prompt.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting.\n */\n promptStyle?: Style\n\n /**\n * Style for the input text.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting to typed text.\n */\n textStyle?: Style\n\n /**\n * Style for the placeholder text.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting to the placeholder.\n */\n placeholderStyle?: Style\n\n /**\n * Validation function.\n *\n * @remarks\n * Called on every change to validate the current input value. Return `null` for\n * valid input, or an `Error` with a descriptive message for invalid input.\n *\n * @example\n * ```typescript\n * validate: (value) => {\n * if (value.length < 3) return new Error('Too short')\n * if (!/^[a-z]+$/.test(value)) return new Error('Lowercase letters only')\n * return null\n * }\n * ```\n */\n validate?: ValidateFunc\n}\n\n/**\n * Create a textInput component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/textinput` package.\n * The text input accepts user keyboard input and supports validation, placeholders,\n * password masking, and character limits. The component is automatically focused\n * on initialization.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const app = createApp()\n * .component('nameInput', textInput({\n * placeholder: 'Enter your name...',\n * width: 40\n * }))\n * .view(({ components }) => components.nameInput)\n * .build()\n * ```\n *\n * @example\n * With validation:\n * ```typescript\n * const app = createApp()\n * .component('emailInput', textInput({\n * placeholder: 'Email address',\n * validate: (value) => {\n * if (!value.includes('@')) return new Error('Invalid email')\n * return null\n * }\n * }))\n * .view(({ components }) => components.emailInput)\n * .build()\n * ```\n *\n * @example\n * Password input:\n * ```typescript\n * const app = createApp()\n * .component('password', textInput({\n * placeholder: 'Password',\n * echoMode: EchoMode.Password,\n * charLimit: 50\n * }))\n * .view(({ components }) => vstack(\n * text('Enter your password:'),\n * components.password\n * ))\n * .build()\n * ```\n *\n * @example\n * With custom styling:\n * ```typescript\n * import { textInput, EchoMode } from '@boba-cli/dsl'\n * import { Style } from '@boba-cli/chapstick'\n *\n * const app = createApp()\n * .component('styledInput', textInput({\n * prompt: '> ',\n * placeholder: 'Type something...',\n * promptStyle: new Style().foreground('#50fa7b').bold(),\n * textStyle: new Style().foreground('#f8f8f2'),\n * placeholderStyle: new Style().foreground('#6272a4').italic()\n * }))\n * .view(({ components }) => components.styledInput)\n * .build()\n * ```\n *\n * @param options - Configuration options for the text input\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function textInput(\n options: TextInputBuilderOptions = {},\n): ComponentBuilder<TextInputModel> {\n const inputOpts: TextInputOptions = {\n placeholder: options.placeholder ?? '',\n width: options.width,\n echoMode: options.echoMode ?? EchoMode.Normal,\n charLimit: options.charLimit ?? 0,\n prompt: options.prompt ?? '',\n promptStyle: options.promptStyle ?? new Style(),\n textStyle: options.textStyle ?? new Style(),\n placeholderStyle: options.placeholderStyle ?? new Style(),\n validate: options.validate,\n }\n\n return {\n init(): [TextInputModel, Cmd<Msg>] {\n const model = TextInputModel.new(inputOpts)\n const [focused, cmd] = model.focus()\n return [focused, cmd]\n },\n\n update(model: TextInputModel, msg: Msg): [TextInputModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: TextInputModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { TimerModel, type TimerOptions } from '@boba-cli/timer'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the timer component builder.\n *\n * @remarks\n * Configure the countdown timer when creating a timer component.\n *\n * @public\n */\nexport interface TimerBuilderOptions {\n /**\n * Milliseconds until the timer expires (required).\n */\n timeout: number\n /**\n * Tick interval in milliseconds (default: 1000).\n *\n * @remarks\n * Controls how frequently the timer updates. Smaller intervals\n * provide more granular countdown display at the cost of more updates.\n */\n interval?: number\n /**\n * Whether to automatically start the timer on initialization (default: true).\n *\n * @remarks\n * If false, the timer must be manually started using the `start()` method.\n */\n autoStart?: boolean\n}\n\n/**\n * Create a timer component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/timer` package.\n * The timer counts down from the specified timeout and can be started, stopped,\n * or toggled programmatically.\n *\n * @example\n * Basic usage with auto-start:\n * ```typescript\n * const app = createApp()\n * .component('countdown', timer({ timeout: 60000 })) // 1 minute\n * .view(({ components }) => hstack(\n * text('Time remaining: '),\n * components.countdown\n * ))\n * .build()\n * ```\n *\n * @example\n * With custom interval and manual start:\n * ```typescript\n * const app = createApp()\n * .component('countdown', timer({\n * timeout: 30000, // 30 seconds\n * interval: 100, // Update every 100ms\n * autoStart: false // Don't start immediately\n * }))\n * .view(({ components }) => components.countdown)\n * .build()\n * ```\n *\n * @param options - Configuration options for the timer\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function timer(options: TimerBuilderOptions): ComponentBuilder<TimerModel> {\n const timerOpts: TimerOptions = {\n timeout: options.timeout,\n interval: options.interval,\n }\n\n const autoStart = options.autoStart ?? true\n\n return {\n init(): [TimerModel, Cmd<Msg>] {\n const model = TimerModel.new(timerOpts)\n const startCmd = autoStart ? model.start() : null\n // Type cast is safe: model.start() returns Cmd<TimerMsg> | null, and TimerMsg extends Msg.\n // The cast widens the type to the more general Cmd<Msg> that the component builder requires.\n return [model, startCmd as Cmd<Msg>]\n },\n\n update(model: TimerModel, msg: Msg): [TimerModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: TimerModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { Style } from '@boba-cli/chapstick'\nimport { ViewportModel, type ViewportOptions } from '@boba-cli/viewport'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the viewport component builder.\n *\n * @remarks\n * Configure the viewport dimensions, scrolling behavior, and content when creating a viewport component.\n *\n * @public\n */\nexport interface ViewportBuilderOptions {\n /**\n * Viewport width in characters.\n *\n * @remarks\n * Defines the horizontal size of the viewport. Defaults to 0.\n *\n * When set to 0 (the default), the viewport has no width constraint and will use\n * the natural width of the content. When set to a positive value, content will be\n * clipped or wrapped to fit the specified width.\n */\n width?: number\n\n /**\n * Viewport height in lines.\n *\n * @remarks\n * Defines the vertical size (number of visible lines) of the viewport. Defaults to 0.\n *\n * When set to 0 (the default), the viewport shows no lines (effectively disabled).\n * Set this to a positive value to create a scrollable window showing that many lines\n * at once. If there are more content lines than the height, the viewport becomes scrollable.\n */\n height?: number\n\n /**\n * Enable or disable mouse wheel scrolling (default: true).\n *\n * @remarks\n * When enabled, the viewport responds to mouse wheel events for scrolling.\n */\n mouseWheelEnabled?: boolean\n\n /**\n * Use high performance rendering mode (default: false).\n *\n * @remarks\n * When enabled, optimizes rendering for better performance with large content.\n * This is a placeholder for future optimization features.\n */\n highPerformanceRendering?: boolean\n\n /**\n * Initial content to display in the viewport.\n *\n * @remarks\n * The content will be split into lines and displayed in the scrollable viewport.\n * Use newline characters (`\\n`) to separate lines.\n */\n content?: string\n\n /**\n * Style for rendering the viewport content.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting.\n */\n style?: Style\n}\n\n/**\n * Create a viewport component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/viewport` package.\n * The viewport provides a scrollable window onto text content with keyboard and\n * mouse wheel navigation support.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const app = createApp()\n * .component('logs', viewport({\n * width: 80,\n * height: 20,\n * content: 'Line 1\\nLine 2\\nLine 3'\n * }))\n * .view(({ components }) => components.logs)\n * .build()\n * ```\n *\n * @example\n * With custom styling:\n * ```typescript\n * const app = createApp()\n * .component('output', viewport({\n * width: 100,\n * height: 30,\n * content: generateLongContent(),\n * style: new Style().foreground('#8be9fd'),\n * mouseWheelEnabled: true\n * }))\n * .view(({ components }) => vstack(\n * text('Scrollable Output:'),\n * components.output\n * ))\n * .build()\n * ```\n *\n * @example\n * High performance mode for large content:\n * ```typescript\n * const app = createApp()\n * .component('largeLog', viewport({\n * width: 120,\n * height: 40,\n * content: fs.readFileSync('large-log.txt', 'utf-8'),\n * highPerformanceRendering: true\n * }))\n * .view(({ components }) => components.largeLog)\n * .build()\n * ```\n *\n * @param options - Configuration options for the viewport\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function viewport(options: ViewportBuilderOptions = {}): ComponentBuilder<ViewportModel> {\n const viewportOpts: ViewportOptions = {\n width: options.width,\n height: options.height,\n mouseWheelEnabled: options.mouseWheelEnabled,\n style: options.style,\n }\n\n return {\n init(): [ViewportModel, Cmd<Msg>] {\n let model = ViewportModel.new(viewportOpts)\n if (options.content !== undefined) {\n model = model.setContent(options.content)\n }\n return [model, model.init()]\n },\n\n update(model: ViewportModel, msg: Msg): [ViewportModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: ViewportModel): string {\n return model.view()\n },\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/view/renderer.ts","../src/view/nodes.ts","../src/app-builder.ts","../src/components/code.ts","../src/components/filepicker.ts","../src/components/filetree.ts","../src/components/help.ts","../src/components/help-bubble.ts","../src/components/list.ts","../src/components/markdown.ts","../src/components/paginator.ts","../src/components/progress.ts","../src/components/spinner.ts","../src/components/statusbar.ts","../src/components/stopwatch.ts","../src/components/table.ts","../src/components/textarea.ts","../src/components/textinput.ts","../src/components/timer.ts","../src/components/viewport.ts"],"names":["separator","render","teaQuit","newComponentModels","cmds","Style"],"mappings":"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","file":"index.js","sourcesContent":["import { Style } from '@boba-cli/chapstick'\nimport type { ViewNode, TextNode, LayoutNode, ComponentView } from '../types.js'\n\n/**\n * Render a view node tree to a string.\n *\n * @remarks\n * Recursively renders a {@link ViewNode} tree into a terminal-ready string.\n * Handles text styling, layout stacking, and component views. This function\n * is typically called internally by the DSL, but can be used directly for\n * testing or custom rendering.\n *\n * @example\n * ```typescript\n * const tree = vstack(\n * text('Hello').bold(),\n * text('World').foreground('#ff79c6')\n * )\n * const output = render(tree)\n * console.log(output)\n * ```\n *\n * @param node - The view node tree to render\n * @returns A string ready to display in the terminal\n *\n * @public\n */\nexport function render(node: ViewNode): string {\n if (typeof node === 'string') {\n return node\n }\n\n if (isTextNode(node)) {\n return renderTextNode(node)\n }\n\n if (isLayoutNode(node)) {\n return renderLayoutNode(node)\n }\n\n if (isComponentView(node)) {\n return node.view\n }\n\n // Should not reach here, but TypeScript doesn't know that\n return ''\n}\n\nfunction isTextNode(node: ViewNode): node is TextNode {\n return typeof node === 'object' && '_type' in node && node._type === 'text'\n}\n\nfunction isLayoutNode(node: ViewNode): node is LayoutNode {\n return (\n typeof node === 'object' &&\n '_type' in node &&\n (node._type === 'vstack' || node._type === 'hstack')\n )\n}\n\nfunction isComponentView(node: ViewNode): node is ComponentView {\n return typeof node === 'object' && '_type' in node && node._type === 'component'\n}\n\nfunction renderTextNode(node: TextNode): string {\n let style = new Style()\n\n if (node._bold) {\n style = style.bold(true)\n }\n // Note: dim is rendered as a darker color when foreground is set\n // If dim and no foreground, use a gray color\n if (node._dim && !node._foreground) {\n style = style.foreground('#888888')\n }\n if (node._italic) {\n style = style.italic(true)\n }\n if (node._foreground) {\n style = style.foreground(node._foreground)\n }\n if (node._background) {\n style = style.background(node._background)\n }\n\n return style.render(node.content)\n}\n\nfunction renderLayoutNode(node: LayoutNode): string {\n const renderedChildren = node.children\n .map((child) => render(child))\n .filter((s) => s.length > 0)\n\n if (node._type === 'vstack') {\n const separator = node.spacing > 0 ? '\\n'.repeat(node.spacing + 1) : '\\n'\n return renderedChildren.join(separator)\n }\n\n // hstack\n const separator = node.spacing > 0 ? ' '.repeat(node.spacing) : ''\n return renderedChildren.join(separator)\n}\n","import type { TextNode, LayoutNode, ViewNode, ComponentView } from '../types.js'\n\n/**\n * Create a text node with chainable style methods.\n *\n * @remarks\n * Text nodes support fluent styling via methods like `bold()`,\n * `dim()`, `italic()`, `foreground()`, and `background()`.\n *\n * @example\n * ```typescript\n * text('Hello').bold().foreground('#ff79c6')\n * text('Warning').dim()\n * ```\n *\n * @param content - The text content to display\n * @returns A new {@link TextNode}\n *\n * @public\n */\nexport function text(content: string): TextNode {\n return createTextNode(content, false, false, false, undefined, undefined)\n}\n\nfunction createTextNode(\n content: string,\n bold: boolean,\n dim: boolean,\n italic: boolean,\n foreground: string | undefined,\n background: string | undefined,\n): TextNode {\n return {\n _type: 'text',\n content,\n _bold: bold,\n _dim: dim,\n _italic: italic,\n _foreground: foreground,\n _background: background,\n bold() {\n return createTextNode(content, true, dim, italic, foreground, background)\n },\n dim() {\n return createTextNode(content, bold, true, italic, foreground, background)\n },\n italic() {\n return createTextNode(content, bold, dim, true, foreground, background)\n },\n foreground(color: string) {\n return createTextNode(content, bold, dim, italic, color, background)\n },\n background(color: string) {\n return createTextNode(content, bold, dim, italic, foreground, color)\n },\n }\n}\n\n/**\n * Create a vertical stack layout.\n *\n * @remarks\n * Arranges child views vertically with newlines between them. Children are\n * rendered in order from top to bottom.\n *\n * @example\n * ```typescript\n * vstack(\n * text('Line 1'),\n * text('Line 2'),\n * text('Line 3')\n * )\n * ```\n *\n * @param children - View nodes to stack vertically\n * @returns A new {@link LayoutNode} with vertical stacking\n *\n * @public\n */\nexport function vstack(...children: ViewNode[]): LayoutNode {\n return {\n _type: 'vstack',\n children,\n spacing: 0,\n }\n}\n\n/**\n * Create a horizontal stack layout.\n *\n * @remarks\n * Arranges child views horizontally on the same line. Children are\n * rendered in order from left to right.\n *\n * @example\n * ```typescript\n * hstack(\n * text('Left'),\n * text(' | '),\n * text('Right')\n * )\n * ```\n *\n * @param children - View nodes to stack horizontally\n * @returns A new {@link LayoutNode} with horizontal stacking\n *\n * @public\n */\nexport function hstack(...children: ViewNode[]): LayoutNode {\n return {\n _type: 'hstack',\n children,\n spacing: 0,\n }\n}\n\n/**\n * Create empty vertical space.\n *\n * @remarks\n * Useful for adding vertical spacing between sections of your UI.\n *\n * @example\n * ```typescript\n * vstack(\n * text('Header'),\n * spacer(2),\n * text('Content')\n * )\n * ```\n *\n * @param height - Number of blank lines to insert (default: 1)\n * @returns A string containing the specified number of newlines\n *\n * @public\n */\nexport function spacer(height = 1): string {\n return '\\n'.repeat(height)\n}\n\n/**\n * Create a divider line.\n *\n * @remarks\n * Renders a horizontal line using a repeated character.\n *\n * @example\n * ```typescript\n * vstack(\n * text('Section 1'),\n * divider(),\n * text('Section 2'),\n * divider('=', 50)\n * )\n * ```\n *\n * @param char - Character to repeat (default: '─')\n * @param width - Number of times to repeat the character (default: 40)\n * @returns A string containing the divider line\n *\n * @public\n */\nexport function divider(char = '─', width = 40): string {\n return char.repeat(width)\n}\n\n/**\n * Conditionally render a node.\n *\n * @remarks\n * Returns the node if the condition is true, otherwise returns an empty string.\n *\n * @example\n * ```typescript\n * vstack(\n * text('Always visible'),\n * when(state.showHelp, text('Help text'))\n * )\n * ```\n *\n * @param condition - Boolean condition to test\n * @param node - View node to render if condition is true\n * @returns The node if condition is true, empty string otherwise\n *\n * @public\n */\nexport function when(condition: boolean, node: ViewNode): ViewNode {\n return condition ? node : ''\n}\n\n/**\n * Choose between two nodes based on condition.\n *\n * @remarks\n * Returns one node if the condition is true, another if false.\n *\n * @example\n * ```typescript\n * vstack(\n * choose(\n * state.isLoading,\n * text('Loading...').dim(),\n * text('Ready!').bold()\n * )\n * )\n * ```\n *\n * @param condition - Boolean condition to test\n * @param ifTrue - View node to render if condition is true\n * @param ifFalse - View node to render if condition is false\n * @returns Either ifTrue or ifFalse depending on condition\n *\n * @public\n */\nexport function choose(condition: boolean, ifTrue: ViewNode, ifFalse: ViewNode): ViewNode {\n return condition ? ifTrue : ifFalse\n}\n\n/**\n * Map items to view nodes.\n *\n * @remarks\n * Transforms an array of items into an array of view nodes. The render\n * function receives each item and its index.\n *\n * @example\n * ```typescript\n * vstack(\n * ...map(state.items, (item, index) =>\n * text(`${index + 1}. ${item.name}`)\n * )\n * )\n * ```\n *\n * @typeParam T - The type of items in the array\n * @param items - Array of items to map\n * @param render - Function to transform each item into a view node\n * @returns Array of view nodes\n *\n * @public\n */\nexport function map<T>(items: T[], render: (item: T, index: number) => ViewNode): ViewNode[] {\n return items.map(render)\n}\n\n/**\n * Create a component view wrapper.\n * @internal\n */\nexport function componentView(view: string): ComponentView {\n return {\n _type: 'component',\n view,\n }\n}\n","import {\n Program,\n KeyMsg,\n quit as teaQuit,\n batch,\n type Cmd,\n type Model,\n type Msg,\n} from '@boba-cli/tea'\nimport { newBinding, matches } from '@boba-cli/key'\nimport type {\n App,\n ComponentBuilder,\n EventContext,\n InitContext,\n InitHandler,\n KeyHandler,\n MessageContext,\n MessageHandler,\n ViewFunction,\n ComponentView,\n RunOptions,\n} from './types.js'\nimport { render } from './view/renderer.js'\nimport { componentView } from './view/nodes.js'\n\n/**\n * Internal key handler registration.\n * @internal\n */\ninterface KeyHandlerEntry<State, Components extends Record<string, unknown>> {\n keys: string[]\n handler: KeyHandler<State, Components>\n}\n\n/**\n * Internal message handler registration.\n * @internal\n */\ninterface MessageHandlerEntry<State, Components extends Record<string, unknown>> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n msgClass: new (...args: any[]) => Msg\n handler: MessageHandler<State, Components, Msg>\n}\n\n/**\n * Internal component registration.\n * @internal\n */\ninterface ComponentEntry {\n key: string\n builder: ComponentBuilder<unknown>\n}\n\n/**\n * Builder for creating declarative CLI applications.\n *\n * @example\n * ```typescript\n * const app = createApp()\n * .state({ count: 0 })\n * .component('spinner', spinner())\n * .onKey('q', ({ quit }) => quit())\n * .view(({ state, components }) => vstack(\n * text('Count: ' + state.count),\n * components.spinner\n * ))\n * .build()\n *\n * await app.run()\n * ```\n *\n * @public\n */\nexport class AppBuilder<\n State = undefined,\n Components extends Record<string, unknown> = Record<string, never>,\n> {\n readonly #initialState: State | undefined\n readonly #components: ComponentEntry[]\n readonly #keyHandlers: KeyHandlerEntry<State, Components>[]\n readonly #messageHandlers: MessageHandlerEntry<State, Components>[]\n readonly #initHandler: InitHandler<State, Components> | undefined\n readonly #viewFn: ViewFunction<State, Components> | undefined\n\n private constructor(\n initialState: State | undefined,\n components: ComponentEntry[],\n keyHandlers: KeyHandlerEntry<State, Components>[],\n messageHandlers: MessageHandlerEntry<State, Components>[],\n initHandler: InitHandler<State, Components> | undefined,\n viewFn: ViewFunction<State, Components> | undefined,\n ) {\n this.#initialState = initialState\n this.#components = components\n this.#keyHandlers = keyHandlers\n this.#messageHandlers = messageHandlers\n this.#initHandler = initHandler\n this.#viewFn = viewFn\n }\n\n /**\n * Create a new AppBuilder instance.\n * @internal\n */\n static create(): AppBuilder<undefined, Record<string, never>> {\n return new AppBuilder(undefined, [], [], [], undefined, undefined)\n }\n\n /**\n * Set the initial application state.\n *\n * @remarks\n * This should typically be called early in the builder chain. If called after\n * registering key handlers or a view function, those will be preserved but\n * their type information will be updated to reflect the new state type.\n *\n * @example\n * ```typescript\n * createApp()\n * .state({ count: 0, name: 'World' })\n * ```\n *\n * @typeParam S - The application state type\n * @param initial - The initial state object\n * @returns A new {@link AppBuilder} with the state type parameter set\n *\n * @public\n */\n state<S>(initial: S): AppBuilder<S, Components> {\n // Preserve existing handlers and view function with updated type\n // This is safe because the handlers/view will receive the new state type\n return new AppBuilder(\n initial,\n this.#components,\n this.#keyHandlers as unknown as KeyHandlerEntry<S, Components>[],\n this.#messageHandlers as unknown as MessageHandlerEntry<S, Components>[],\n this.#initHandler as unknown as InitHandler<S, Components> | undefined,\n this.#viewFn as unknown as ViewFunction<S, Components> | undefined,\n )\n }\n\n /**\n * Register a component with a unique key.\n *\n * @remarks\n * Components are TEA models wrapped in a {@link ComponentBuilder} that\n * manages their lifecycle. The component's rendered view is available in\n * the view function via `components[key]`.\n *\n * @example\n * ```typescript\n * createApp()\n * .component('loading', spinner())\n * .component('input', textInput())\n * ```\n *\n * @typeParam K - The component key (string literal type)\n * @typeParam M - The component model type\n * @param key - Unique identifier for this component\n * @param builder - Component builder implementing init/update/view\n * @returns A new {@link AppBuilder} with the component registered\n *\n * @public\n */\n component<K extends string, M>(\n key: K,\n builder: ComponentBuilder<M>,\n ): AppBuilder<State, Components & Record<K, M>> {\n const newComponents = [...this.#components, { key, builder: builder as ComponentBuilder<unknown> }]\n return new AppBuilder(\n this.#initialState,\n newComponents,\n this.#keyHandlers as KeyHandlerEntry<State, Components & Record<K, M>>[],\n this.#messageHandlers as MessageHandlerEntry<State, Components & Record<K, M>>[],\n this.#initHandler as InitHandler<State, Components & Record<K, M>> | undefined,\n this.#viewFn as ViewFunction<State, Components & Record<K, M>> | undefined,\n )\n }\n\n /**\n * Register a key handler.\n *\n * @remarks\n * Key handlers receive an {@link EventContext} with the current state and\n * components. Multiple keys can be bound to the same handler by passing an\n * array of key strings. Key strings support modifiers like 'ctrl+c', 'alt+enter'.\n *\n * @example\n * ```typescript\n * createApp()\n * .onKey('q', ({ quit }) => quit())\n * .onKey(['up', 'k'], ({ state, update }) => update({ index: state.index - 1 }))\n * .onKey('ctrl+c', ({ quit }) => quit())\n * ```\n *\n * @param keys - Single key string or array of key strings\n * @param handler - Function to call when any of the keys are pressed\n * @returns A new {@link AppBuilder} with the key handler registered\n *\n * @public\n */\n onKey(\n keys: string | string[],\n handler: KeyHandler<State, Components>,\n ): AppBuilder<State, Components> {\n const keyArray = Array.isArray(keys) ? keys : [keys]\n const newHandlers = [...this.#keyHandlers, { keys: keyArray, handler }]\n return new AppBuilder(\n this.#initialState,\n this.#components,\n newHandlers,\n this.#messageHandlers,\n this.#initHandler,\n this.#viewFn,\n )\n }\n\n /**\n * Set the initialization handler.\n *\n * @remarks\n * The init handler is called once when the application starts. Use it to\n * schedule initial async operations like fetching data or starting timers.\n * Calling this method multiple times will replace the previous handler.\n *\n * @example\n * ```typescript\n * createApp()\n * .onInit(({ schedule }) => {\n * schedule(tick(1000, () => new MyMessage()))\n * })\n * ```\n *\n * @param handler - Function to call on initialization\n * @returns A new {@link AppBuilder} with the init handler registered\n *\n * @public\n */\n onInit(handler: InitHandler<State, Components>): AppBuilder<State, Components> {\n return new AppBuilder(\n this.#initialState,\n this.#components,\n this.#keyHandlers,\n this.#messageHandlers,\n handler,\n this.#viewFn,\n )\n }\n\n /**\n * Register a message handler for a specific message type.\n *\n * @remarks\n * Message handlers are called when a message of the specified type is received.\n * Use this to handle async callbacks from commands scheduled via `onInit()`\n * or other message handlers.\n *\n * @example\n * ```typescript\n * class DownloadComplete {\n * constructor(public packageName: string) {}\n * }\n *\n * createApp()\n * .onMessage(DownloadComplete, ({ msg, update, schedule }) => {\n * update({ currentPackage: msg.packageName })\n * schedule(tick(500, () => new DownloadComplete('next-package')))\n * })\n * ```\n *\n * @param msgClass - The message class constructor to match\n * @param handler - Function to call when a message of this type is received\n * @returns A new {@link AppBuilder} with the message handler registered\n *\n * @public\n */\n onMessage<M extends Msg>(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n msgClass: new (...args: any[]) => M,\n handler: MessageHandler<State, Components, M>,\n ): AppBuilder<State, Components> {\n const newHandlers = [\n ...this.#messageHandlers,\n { msgClass, handler: handler as MessageHandler<State, Components, Msg> },\n ]\n return new AppBuilder(\n this.#initialState,\n this.#components,\n this.#keyHandlers,\n newHandlers,\n this.#initHandler,\n this.#viewFn,\n )\n }\n\n /**\n * Set the view function.\n *\n * @remarks\n * The view function is called on every render cycle and receives the current\n * state and component views. It should return a {@link ViewNode} tree\n * describing the UI to display.\n *\n * @example\n * ```typescript\n * createApp()\n * .view(({ state, components }) => vstack(\n * text('Hello ' + state.name),\n * components.spinner\n * ))\n * ```\n *\n * @param fn - Function that returns a {@link ViewNode} tree\n * @returns A new {@link AppBuilder} with the view function set\n *\n * @public\n */\n view(fn: ViewFunction<State, Components>): AppBuilder<State, Components> {\n return new AppBuilder(\n this.#initialState,\n this.#components,\n this.#keyHandlers,\n this.#messageHandlers,\n this.#initHandler,\n fn,\n )\n }\n\n /**\n * Build the application.\n *\n * @remarks\n * Finalizes the builder chain and creates an {@link App} instance ready\n * to run. This method must be called after setting a view function via\n * {@link AppBuilder.view}.\n *\n * @throws Error if no view function has been set\n *\n * @returns The built {@link App} ready to run\n *\n * @public\n */\n build(): App<State, Components> {\n if (this.#viewFn === undefined) {\n throw new Error('AppBuilder: view() must be called before build()')\n }\n\n const initialState = this.#initialState as State\n const components = this.#components\n const keyHandlers = this.#keyHandlers\n const messageHandlers = this.#messageHandlers\n const initHandler = this.#initHandler\n const viewFn = this.#viewFn\n\n // Create the generated model\n const model = new GeneratedModel(\n initialState,\n components,\n keyHandlers,\n messageHandlers,\n initHandler,\n viewFn,\n )\n\n return {\n async run(options: RunOptions) {\n const program = new Program(model, { platform: options.platform })\n const result = await program.run()\n return { state: result.model.getUserState() }\n },\n getModel() {\n return model\n },\n }\n }\n}\n\n/**\n * Create a new application builder.\n *\n * @example\n * ```typescript\n * const app = createApp()\n * .state({ count: 0 })\n * .onKey('q', ({ quit }) => quit())\n * .view(({ state }) => text('Count: ' + state.count))\n * .build()\n * ```\n *\n * @public\n */\nexport function createApp(): AppBuilder<undefined, Record<string, never>> {\n return AppBuilder.create()\n}\n\n/**\n * Generated TEA model from the builder configuration.\n * @internal\n */\nclass GeneratedModel<State, Components extends Record<string, unknown>>\n implements Model<Msg, GeneratedModel<State, Components>>\n{\n readonly #userState: State\n readonly #componentModels: Map<string, unknown>\n readonly #componentBuilders: Map<string, ComponentBuilder<unknown>>\n readonly #keyHandlers: KeyHandlerEntry<State, Components>[]\n readonly #messageHandlers: MessageHandlerEntry<State, Components>[]\n readonly #initHandler: InitHandler<State, Components> | undefined\n readonly #viewFn: ViewFunction<State, Components> | undefined\n\n constructor(\n userState: State,\n components: ComponentEntry[],\n keyHandlers: KeyHandlerEntry<State, Components>[],\n messageHandlers: MessageHandlerEntry<State, Components>[],\n initHandler: InitHandler<State, Components> | undefined,\n viewFn: ViewFunction<State, Components> | undefined,\n componentModels?: Map<string, unknown>,\n ) {\n this.#userState = userState\n this.#keyHandlers = keyHandlers\n this.#messageHandlers = messageHandlers\n this.#initHandler = initHandler\n this.#viewFn = viewFn\n\n // Build component builders map\n this.#componentBuilders = new Map()\n for (const { key, builder } of components) {\n this.#componentBuilders.set(key, builder)\n }\n\n // Use provided component models or empty map (init will populate)\n this.#componentModels = componentModels ?? new Map<string, unknown>()\n }\n\n getUserState(): State {\n return this.#userState\n }\n\n init(): Cmd<Msg> {\n const cmds: Cmd<Msg>[] = []\n\n // Initialize all components\n for (const [key, builder] of this.#componentBuilders) {\n const [model, cmd] = builder.init()\n this.#componentModels.set(key, model)\n if (cmd) {\n cmds.push(cmd)\n }\n }\n\n // Call init handler if present\n if (this.#initHandler) {\n const scheduledCmds: Cmd<Msg>[] = []\n const componentUpdates: Array<{\n key: string\n model: unknown\n cmd: Cmd<Msg>\n }> = []\n\n const ctx: InitContext<State, Components> = {\n state: this.#userState,\n schedule: (cmd) => {\n if (cmd) scheduledCmds.push(cmd)\n },\n sendToComponent: (key, fn) => {\n const currentModel = this.#componentModels.get(key as string)\n if (currentModel !== undefined) {\n const [nextModel, cmd] = fn(currentModel as Components[typeof key])\n componentUpdates.push({\n key: key as string,\n model: nextModel,\n cmd,\n })\n }\n },\n }\n\n this.#initHandler(ctx)\n\n // Apply component updates\n for (const { key, model, cmd } of componentUpdates) {\n this.#componentModels.set(key, model)\n if (cmd) cmds.push(cmd)\n }\n\n // Add scheduled commands\n cmds.push(...scheduledCmds)\n }\n\n return cmds.length > 0 ? batch(...cmds) : null\n }\n\n update(msg: Msg): [GeneratedModel<State, Components>, Cmd<Msg>] {\n // Check key handlers first\n if (msg instanceof KeyMsg) {\n for (const { keys, handler} of this.#keyHandlers) {\n const binding = newBinding({ keys })\n if (matches(msg, binding)) {\n // Create event context and call handler\n let nextUserState = this.#userState\n let shouldQuit = false\n const componentUpdates: Array<{\n key: string\n model: unknown\n cmd: Cmd<Msg>\n }> = []\n\n const ctx: EventContext<State, Components> = {\n state: this.#userState,\n components: this.#buildComponentViews(),\n update: (patch) => {\n nextUserState = { ...nextUserState, ...patch }\n },\n setState: (newState) => {\n nextUserState = newState\n },\n quit: () => {\n shouldQuit = true\n },\n sendToComponent: (key, fn) => {\n const currentModel = this.#componentModels.get(key as string)\n if (currentModel !== undefined) {\n const [nextModel, cmd] = fn(currentModel as Components[typeof key])\n componentUpdates.push({\n key: key as string,\n model: nextModel,\n cmd,\n })\n }\n },\n }\n\n handler(ctx)\n\n if (shouldQuit) {\n return [this, teaQuit()]\n }\n\n // Apply any component updates\n const stateChanged = nextUserState !== this.#userState\n const componentsChanged = componentUpdates.length > 0\n\n if (stateChanged || componentsChanged) {\n const newComponentModels = new Map(this.#componentModels)\n const cmds: Cmd<Msg>[] = []\n\n for (const { key, model, cmd } of componentUpdates) {\n newComponentModels.set(key, model)\n if (cmd) {\n cmds.push(cmd)\n }\n }\n\n const next = new GeneratedModel(\n nextUserState,\n Array.from(this.#componentBuilders.entries()).map(([key, builder]) => ({\n key,\n builder,\n })),\n this.#keyHandlers,\n this.#messageHandlers,\n this.#initHandler,\n this.#viewFn,\n newComponentModels,\n )\n\n return [next, cmds.length > 0 ? batch(...cmds) : null]\n }\n\n return [this, null]\n }\n }\n }\n\n // Check message handlers\n for (const { msgClass, handler } of this.#messageHandlers) {\n if (msg instanceof msgClass) {\n let nextUserState = this.#userState\n let shouldQuit = false\n const scheduledCmds: Cmd<Msg>[] = []\n const componentUpdates: Array<{\n key: string\n model: unknown\n cmd: Cmd<Msg>\n }> = []\n\n const ctx: MessageContext<State, Components, Msg> = {\n msg,\n state: this.#userState,\n components: this.#buildComponentViews(),\n update: (patch) => {\n nextUserState = { ...nextUserState, ...patch }\n },\n setState: (newState) => {\n nextUserState = newState\n },\n quit: () => {\n shouldQuit = true\n },\n schedule: (cmd) => {\n if (cmd) scheduledCmds.push(cmd)\n },\n sendToComponent: (key, fn) => {\n const currentModel = this.#componentModels.get(key as string)\n if (currentModel !== undefined) {\n const [nextModel, cmd] = fn(currentModel as Components[typeof key])\n componentUpdates.push({\n key: key as string,\n model: nextModel,\n cmd,\n })\n }\n },\n }\n\n handler(ctx)\n\n if (shouldQuit) {\n return [this, teaQuit()]\n }\n\n // Apply state and component updates\n const stateChanged = nextUserState !== this.#userState\n const componentsChanged = componentUpdates.length > 0\n const hasScheduled = scheduledCmds.length > 0\n\n if (stateChanged || componentsChanged || hasScheduled) {\n const newComponentModels = new Map(this.#componentModels)\n const cmds: Cmd<Msg>[] = [...scheduledCmds]\n\n for (const { key, model, cmd } of componentUpdates) {\n newComponentModels.set(key, model)\n if (cmd) {\n cmds.push(cmd)\n }\n }\n\n const next = new GeneratedModel(\n nextUserState,\n Array.from(this.#componentBuilders.entries()).map(([key, builder]) => ({\n key,\n builder,\n })),\n this.#keyHandlers,\n this.#messageHandlers,\n this.#initHandler,\n this.#viewFn,\n newComponentModels,\n )\n\n return [next, cmds.length > 0 ? batch(...cmds) : null]\n }\n\n return [this, null]\n }\n }\n\n // Route message to all components\n const cmds: Cmd<Msg>[] = []\n let anyComponentChanged = false\n const newComponentModels = new Map(this.#componentModels)\n\n for (const [key, builder] of this.#componentBuilders) {\n const currentModel = this.#componentModels.get(key)\n if (currentModel === undefined) {\n continue\n }\n\n const [nextModel, cmd] = builder.update(currentModel, msg)\n\n if (nextModel !== currentModel) {\n newComponentModels.set(key, nextModel)\n anyComponentChanged = true\n }\n\n if (cmd) {\n cmds.push(cmd)\n }\n }\n\n if (anyComponentChanged) {\n const next = new GeneratedModel(\n this.#userState,\n Array.from(this.#componentBuilders.entries()).map(([key, builder]) => ({\n key,\n builder,\n })),\n this.#keyHandlers,\n this.#messageHandlers,\n this.#initHandler,\n this.#viewFn,\n newComponentModels,\n )\n return [next, cmds.length > 0 ? batch(...cmds) : null]\n }\n\n return [this, cmds.length > 0 ? batch(...cmds) : null]\n }\n\n view(): string {\n if (!this.#viewFn) {\n return ''\n }\n\n const componentViews = this.#buildComponentViews()\n const node = this.#viewFn({\n state: this.#userState,\n components: componentViews,\n })\n\n return render(node)\n }\n\n #buildComponentViews(): { [K in keyof Components]: ComponentView } {\n const views: Record<string, ComponentView> = {}\n\n for (const [key, builder] of this.#componentBuilders) {\n const model = this.#componentModels.get(key)\n if (model !== undefined) {\n views[key] = componentView(builder.view(model))\n }\n }\n\n return views as { [K in keyof Components]: ComponentView }\n }\n}\n","import type { Cmd, Msg } from '@boba-cli/tea'\nimport type { FileSystemAdapter, PathAdapter } from '@boba-cli/machine'\nimport { CodeModel } from '@boba-cli/code'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the code component builder.\n *\n * @remarks\n * Configure the code viewer when creating a code component. The code component\n * displays syntax-highlighted source code with scrolling support.\n *\n * Note: The code component requires filesystem and path adapters to read and\n * display files. These must be provided in the options.\n *\n * @public\n */\nexport interface CodeBuilderOptions {\n /**\n * Filesystem adapter for file operations.\n *\n * @remarks\n * For Node.js environments, use `NodeFileSystemAdapter` from `@boba-cli/machine/node`.\n * For browser environments, provide a custom implementation or memory-based adapter.\n */\n filesystem: FileSystemAdapter\n\n /**\n * Path adapter for path operations.\n *\n * @remarks\n * For Node.js environments, use `NodePathAdapter` from `@boba-cli/machine/node`.\n * For browser environments, provide a custom implementation.\n */\n path: PathAdapter\n\n /**\n * Whether the component is active and receives keyboard input (default: `false`).\n *\n * @remarks\n * When active, the code viewer responds to keyboard navigation (arrow keys,\n * page up/down, etc.) for scrolling through the code.\n */\n active?: boolean\n\n /**\n * Syntax highlighting theme to use (default: `\"dracula\"`).\n *\n * @remarks\n * Common themes include `\"dracula\"`, `\"monokai\"`, `\"github-light\"`, `\"nord\"`.\n * Uses Shiki for syntax highlighting under the hood.\n */\n theme?: string\n\n /**\n * Width of the code viewer in characters.\n *\n * @remarks\n * If not specified, defaults to 0. Typically you'll want to update this\n * dynamically based on terminal size via the model's `setSize()` method.\n */\n width?: number\n\n /**\n * Height of the code viewer in lines.\n *\n * @remarks\n * If not specified, defaults to 0. Typically you'll want to update this\n * dynamically based on terminal size via the model's `setSize()` method.\n */\n height?: number\n}\n\n/**\n * Create a code viewer component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/code` package.\n * The code viewer displays syntax-highlighted source code from files with\n * scrolling support and keyboard navigation.\n *\n * The component requires filesystem and path adapters to read files. You must\n * provide these in the options. After creating the component, use the model's\n * `setFileName()` method to load and display a file.\n *\n * @example\n * Basic usage with Node.js adapters:\n * ```typescript\n * import { NodeFileSystemAdapter, NodePathAdapter } from '@boba-cli/machine/node'\n *\n * const app = createApp()\n * .component('code', code({\n * filesystem: new NodeFileSystemAdapter(),\n * path: new NodePathAdapter(),\n * active: true,\n * width: 80,\n * height: 24,\n * }))\n * .view(({ components }) => components.code)\n * .build()\n * ```\n *\n * @example\n * With custom theme:\n * ```typescript\n * const app = createApp()\n * .component('viewer', code({\n * filesystem: new NodeFileSystemAdapter(),\n * path: new NodePathAdapter(),\n * theme: 'monokai',\n * active: true,\n * }))\n * .view(({ components }) => vstack(\n * text('Source Code').bold(),\n * components.viewer\n * ))\n * .build()\n * ```\n *\n * @example\n * Loading a file (typically done in a key handler or init):\n * ```typescript\n * // In the raw TEA model, you would call:\n * const [nextCode, cmd] = codeModel.setFileName('src/example.ts')\n *\n * // Note: The DSL doesn't currently provide direct access to component models\n * // from event handlers, so you may need to manage file loading differently\n * // or use the raw TEA approach for now.\n * ```\n *\n * @param options - Configuration options for the code viewer (requires filesystem and path adapters)\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function code(options: CodeBuilderOptions): ComponentBuilder<CodeModel> {\n return {\n init(): [CodeModel, Cmd<Msg>] {\n const model = CodeModel.new({\n filesystem: options.filesystem,\n path: options.path,\n active: options.active ?? false,\n syntaxTheme: options.theme ?? 'dracula',\n width: options.width ?? 0,\n height: options.height ?? 0,\n })\n return [model, model.init()]\n },\n\n update(model: CodeModel, msg: Msg): [CodeModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: CodeModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport type { FileSystemAdapter, PathAdapter } from '@boba-cli/machine'\nimport { FilepickerModel, type FilepickerStyles } from '@boba-cli/filepicker'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the filepicker component builder.\n *\n * @remarks\n * Configure the file picker's behavior, appearance, and file system access.\n * The filesystem and path adapters are required to interact with the file system.\n *\n * @public\n */\nexport interface FilepickerBuilderOptions {\n /**\n * FileSystem adapter for file operations.\n *\n * @remarks\n * Required for reading directory contents and file metadata.\n * For Node.js, use `platform.filesystem` from `createNodePlatform()`.\n */\n filesystem: FileSystemAdapter\n\n /**\n * Path adapter for path operations.\n *\n * @remarks\n * Required for path manipulation (dirname, join, etc.).\n * For Node.js, use `platform.path` from `createNodePlatform()`.\n */\n path: PathAdapter\n\n /**\n * Initial directory to display (default: current working directory).\n *\n * @remarks\n * If not specified, uses the filesystem adapter's current working directory.\n */\n currentDirectory?: string\n\n /**\n * Maximum height for the file list (default: 0 = unlimited).\n *\n * @remarks\n * Limits the number of visible files. When 0, all files are displayed.\n */\n height?: number\n\n /**\n * Show hidden files (default: false).\n *\n * @remarks\n * Hidden files typically start with a dot (.) on Unix-like systems.\n */\n showHidden?: boolean\n\n /**\n * Filter files by allowed extensions (default: all files).\n *\n * @remarks\n * Provide an array of file extensions (e.g., `['.ts', '.js']`) to filter\n * the file list. Directories are always shown regardless of this setting.\n * Empty array or undefined means no filtering.\n *\n * @example\n * ```typescript\n * allowedExtensions: ['.ts', '.tsx', '.js', '.jsx']\n * ```\n */\n allowedExtensions?: string[]\n\n /**\n * Style hooks for customizing the file picker appearance.\n *\n * @remarks\n * Partial styles object - any omitted styles will use defaults.\n * Available style hooks:\n * - `directory`: Style for directory names\n * - `file`: Style for file names\n * - `hidden`: Style for hidden files\n * - `selected`: Style for the selected item\n * - `cursor`: Style for the cursor indicator\n * - `status`: Style for the status bar (current directory)\n */\n styles?: Partial<FilepickerStyles>\n}\n\n/**\n * Create a filepicker component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/filepicker` package.\n * The filepicker allows users to navigate the file system and select files.\n * It requires filesystem and path adapters from the platform.\n *\n * @example\n * Basic usage with Node.js platform:\n * ```typescript\n * import { createNodePlatform } from '@boba-cli/machine/node'\n *\n * const platform = createNodePlatform()\n *\n * const app = createApp()\n * .component('picker', filepicker({\n * filesystem: platform.filesystem,\n * path: platform.path\n * }))\n * .view(({ components }) => components.picker)\n * .build()\n * ```\n *\n * @example\n * With custom directory and file filtering:\n * ```typescript\n * const app = createApp()\n * .component('picker', filepicker({\n * filesystem: platform.filesystem,\n * path: platform.path,\n * currentDirectory: '/home/user/projects',\n * allowedExtensions: ['.ts', '.tsx', '.js', '.jsx'],\n * showHidden: true,\n * height: 20\n * }))\n * .view(({ components }) => vstack(\n * text('Select a TypeScript file:'),\n * components.picker\n * ))\n * .build()\n * ```\n *\n * @example\n * With custom styling:\n * ```typescript\n * const app = createApp()\n * .component('picker', filepicker({\n * filesystem: platform.filesystem,\n * path: platform.path,\n * styles: {\n * directory: new Style().foreground('#50fa7b').bold(true),\n * file: new Style().foreground('#f8f8f2'),\n * selected: new Style().background('#44475a').foreground('#ff79c6')\n * }\n * }))\n * .view(({ components }) => components.picker)\n * .build()\n * ```\n *\n * @param options - Configuration options for the filepicker (filesystem and path are required)\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function filepicker(\n options: FilepickerBuilderOptions,\n): ComponentBuilder<FilepickerModel> {\n return {\n init(): [FilepickerModel, Cmd<Msg>] {\n const [model, cmd] = FilepickerModel.new({\n filesystem: options.filesystem,\n path: options.path,\n currentDir: options.currentDirectory,\n height: options.height,\n showHidden: options.showHidden,\n allowedTypes: options.allowedExtensions,\n styles: options.styles,\n })\n return [model, cmd]\n },\n\n update(model: FilepickerModel, msg: Msg): [FilepickerModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: FilepickerModel): string {\n return model.view()\n },\n }\n}\n","import type { Cmd, Msg } from '@boba-cli/tea'\nimport {\n FiletreeModel,\n type FiletreeKeyMap,\n type FiletreeStyles,\n} from '@boba-cli/filetree'\nimport type { FileSystemAdapter, PathAdapter } from '@boba-cli/machine'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the filetree component builder.\n *\n * @remarks\n * Configure the filetree component with directory items, styling, and behavior\n * when creating a filetree component.\n *\n * @public\n */\nexport interface FiletreeBuilderOptions {\n /**\n * Filesystem adapter for file operations (required).\n *\n * @remarks\n * Provides filesystem operations like reading directories and checking file stats.\n * Use the adapter from `@boba-cli/machine`.\n */\n filesystem: FileSystemAdapter\n /**\n * Path adapter for path operations (required).\n *\n * @remarks\n * Provides path manipulation utilities like join, resolve, and basename.\n * Use the adapter from `@boba-cli/machine`.\n */\n path: PathAdapter\n /**\n * Initial directory to display.\n *\n * @remarks\n * If not provided, defaults to the current working directory from the filesystem adapter.\n */\n currentDirectory?: string\n /**\n * Whether to show hidden files (default: false).\n */\n showHidden?: boolean\n /**\n * Height of the filetree component in lines.\n *\n * @remarks\n * Controls the viewport size for scrolling. If not provided, defaults to 24.\n */\n height?: number\n /**\n * Width of the filetree component in characters.\n *\n * @remarks\n * Used for text wrapping and rendering. If not provided, defaults to 80.\n */\n width?: number\n /**\n * Custom styles for the filetree components.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting.\n * Partial styles are merged with defaults.\n */\n styles?: Partial<FiletreeStyles>\n /**\n * Custom key mappings for filetree navigation.\n *\n * @remarks\n * Provides control over which keys trigger navigation actions like moving up/down.\n * If not provided, uses default key bindings.\n */\n keyMap?: FiletreeKeyMap\n}\n\n/**\n * Create a filetree component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/filetree` package.\n * The filetree provides keyboard navigation through directory listings with\n * scrolling viewport support.\n *\n * @example\n * Basic usage:\n * ```typescript\n * import { filetree } from '@boba-cli/dsl'\n * import { filesystem, path } from '@boba-cli/machine'\n *\n * const app = createApp()\n * .component('files', filetree({\n * filesystem,\n * path,\n * }))\n * .view(({ components }) => components.files)\n * .build()\n * ```\n *\n * @example\n * With custom styling and directory:\n * ```typescript\n * import { filetree } from '@boba-cli/dsl'\n * import { filesystem, path } from '@boba-cli/machine'\n * import { Style } from '@boba-cli/chapstick'\n *\n * const app = createApp()\n * .component('files', filetree({\n * filesystem,\n * path,\n * currentDirectory: '/home/user/projects',\n * showHidden: true,\n * height: 20,\n * width: 80,\n * styles: {\n * selectedItem: new Style().foreground('#50fa7b').bold(),\n * normalItem: new Style().foreground('#f8f8f2'),\n * },\n * }))\n * .view(({ components }) => vstack(\n * text('File Browser').bold(),\n * components.files\n * ))\n * .build()\n * ```\n *\n * @param options - Configuration options for the filetree\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function filetree(\n options: FiletreeBuilderOptions,\n): ComponentBuilder<FiletreeModel> {\n return {\n init(): [FiletreeModel, Cmd<Msg>] {\n const model = FiletreeModel.new({\n filesystem: options.filesystem,\n path: options.path,\n currentDir: options.currentDirectory,\n showHidden: options.showHidden,\n height: options.height,\n width: options.width,\n styles: options.styles,\n keyMap: options.keyMap,\n })\n return [model, model.init()]\n },\n\n update(model: FiletreeModel, msg: Msg): [FiletreeModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: FiletreeModel): string {\n return model.view()\n },\n }\n}\n\n// Re-export DirectoryItem type for convenience\nexport type { DirectoryItem } from '@boba-cli/filetree'\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { HelpModel, type HelpOptions, type KeyMap } from '@boba-cli/help'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the help component builder.\n *\n * @remarks\n * Configure the help view appearance and behavior when creating a help component.\n *\n * @public\n */\nexport interface HelpBuilderOptions {\n /**\n * Key map providing help bindings (required).\n *\n * @remarks\n * The key map provides the bindings to display in the help view via\n * `shortHelp()` and `fullHelp()` methods.\n */\n keyMap: KeyMap\n\n /**\n * Maximum width for the help view (default: 0, unlimited).\n *\n * @remarks\n * When set, the help view will truncate content that exceeds this width\n * and append an ellipsis indicator.\n */\n width?: number\n\n /**\n * Show full help with all key bindings (default: false).\n *\n * @remarks\n * When false, displays a compact single-line view.\n * When true, displays a multi-column view with all available bindings.\n */\n showAll?: boolean\n\n /**\n * Separator string for short help mode (default: ' • ').\n *\n * @remarks\n * Appears between key bindings in the compact single-line view.\n */\n shortSeparator?: string\n\n /**\n * Separator string for full help mode (default: ' ').\n *\n * @remarks\n * Appears between columns in the multi-column full help view.\n */\n fullSeparator?: string\n\n /**\n * Ellipsis indicator when truncating content (default: '…').\n *\n * @remarks\n * Displayed when help content exceeds the configured width.\n */\n ellipsis?: string\n\n /**\n * Custom styles for help text rendering.\n *\n * @remarks\n * Partial object of `HelpStyles` to override default styling.\n * Available style properties: shortKey, shortDesc, shortSeparator,\n * fullKey, fullDesc, fullSeparator, ellipsis.\n */\n styles?: HelpOptions['styles']\n}\n\n/**\n * Create a help component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/help` package.\n * The help component renders keyboard shortcuts and their descriptions,\n * with support for both compact and full-screen modes.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const keyMap = {\n * shortHelp: () => [bindings.up, bindings.down, bindings.quit],\n * fullHelp: () => [[bindings.up, bindings.down], [bindings.quit]]\n * }\n *\n * const app = createApp()\n * .component('help', help({ keyMap }))\n * .view(({ components }) => vstack(\n * text('My App'),\n * components.help\n * ))\n * .build()\n * ```\n *\n * @example\n * With custom configuration:\n * ```typescript\n * const app = createApp()\n * .component('help', help({\n * keyMap,\n * width: 80,\n * showAll: false,\n * styles: {\n * shortKey: new Style().foreground('#50fa7b').bold(),\n * shortDesc: new Style().foreground('#f8f8f2')\n * }\n * }))\n * .view(({ components }) => components.help)\n * .build()\n * ```\n *\n * @param options - Configuration options for the help component (keyMap is required)\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function help(options: HelpBuilderOptions): ComponentBuilder<HelpModel> {\n const { keyMap } = options\n const helpOpts: HelpOptions = {\n width: options.width,\n showAll: options.showAll,\n shortSeparator: options.shortSeparator,\n fullSeparator: options.fullSeparator,\n ellipsis: options.ellipsis,\n styles: options.styles,\n }\n\n return {\n init(): [HelpModel, Cmd<Msg>] {\n const model = HelpModel.new(helpOpts)\n return [model, null]\n },\n\n update(model: HelpModel, msg: Msg): [HelpModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: HelpModel): string {\n return model.view(keyMap)\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { HelpBubble, type Entry, type TitleColor } from '@boba-cli/help'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the help bubble component builder.\n *\n * @remarks\n * Configure the help bubble's title, colors, and entries when creating the component.\n *\n * @public\n */\nexport interface HelpBubbleBuilderOptions {\n /**\n * Array of help entries to display (required).\n *\n * @remarks\n * Each entry contains a key binding and its description.\n */\n entries: Entry[]\n /**\n * Title text for the help screen (default: 'Help').\n *\n * @remarks\n * Displayed at the top of the help bubble with styled background and foreground colors.\n */\n title?: string\n /**\n * Color configuration for the title bar.\n *\n * @remarks\n * Supports adaptive colors for light and dark terminals.\n * Uses `ColorInput` from `@boba-cli/chapstick`.\n */\n titleColor?: TitleColor\n /**\n * Whether the help bubble starts in active state (default: false).\n *\n * @remarks\n * When active, the help bubble receives keyboard input for scrolling.\n * When inactive, it ignores input messages.\n */\n active?: boolean\n}\n\n/**\n * Create a help bubble component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/help` package.\n * The help bubble displays a scrollable list of key bindings with descriptions,\n * using a viewport for navigation.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const app = createApp()\n * .component('help', helpBubble({\n * entries: [\n * { key: 'q/esc', description: 'Quit' },\n * { key: 'j/down', description: 'Move down' },\n * { key: 'k/up', description: 'Move up' }\n * ]\n * }))\n * .view(({ components }) => components.help)\n * .build()\n * ```\n *\n * @example\n * With custom title and colors:\n * ```typescript\n * const app = createApp()\n * .component('help', helpBubble({\n * entries: [\n * { key: 'enter', description: 'Select item' },\n * { key: 'tab', description: 'Switch focus' }\n * ],\n * title: 'Keyboard Shortcuts',\n * titleColor: {\n * background: '#282a36',\n * foreground: '#f8f8f2'\n * },\n * active: true\n * }))\n * .view(({ components }) => components.help)\n * .build()\n * ```\n *\n * @param options - Configuration options for the help bubble\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function helpBubble(\n options: HelpBubbleBuilderOptions,\n): ComponentBuilder<HelpBubble> {\n const title = options.title ?? 'Help'\n const titleColor = options.titleColor ?? {\n background: { dark: '#5f5f87', light: '#d7d7ff' },\n foreground: { dark: '#ffffff', light: '#000000' },\n }\n const active = options.active ?? false\n\n return {\n init(): [HelpBubble, Cmd<Msg>] {\n const model = HelpBubble.new(active, title, titleColor, options.entries)\n return [model, null]\n },\n\n update(model: HelpBubble, msg: Msg): [HelpBubble, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: HelpBubble): string {\n return model.view()\n },\n }\n}\n\n/**\n * Re-export Entry type for convenience.\n *\n * @public\n */\nexport type { Entry }\n","import type { Cmd, Msg } from '@boba-cli/tea'\nimport {\n ListModel,\n type Item,\n type ItemDelegate,\n type ListKeyMap,\n type ListStyles,\n} from '@boba-cli/list'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the list component builder.\n *\n * @remarks\n * Configure the list component with items, styling, and behavior when creating a list component.\n *\n * @typeParam T - The item type, must extend `Item`\n *\n * @public\n */\nexport interface ListBuilderOptions<T extends Item> {\n /**\n * Items to display in the list (required).\n *\n * @remarks\n * Each item must implement the `Item` interface with `filterValue()`,\n * `title()`, and `description()` methods.\n */\n items: T[]\n /**\n * Custom delegate for rendering items.\n *\n * @remarks\n * Provides control over how items are rendered, including height, spacing,\n * and custom styling. If not provided, uses the default delegate.\n */\n delegate?: ItemDelegate<T>\n /**\n * Height of the list component in lines.\n *\n * @remarks\n * Used to calculate pagination. If not provided, defaults to 0.\n *\n * When set to 0 (the default), the height is calculated based on the item delegate's\n * height calculations with a default page size. When set to a positive value, pagination\n * is calculated to fit that many lines, creating a scrollable list if items exceed the height.\n */\n height?: number\n /**\n * Width of the list component in characters.\n *\n * @remarks\n * Used for help text wrapping. If not provided, defaults to 0 (no width limit).\n */\n width?: number\n /**\n * Title displayed at the top of the list.\n *\n * @remarks\n * Only shown if `showTitle` is true (default: true).\n */\n title?: string\n /**\n * Whether to show the title bar (default: true).\n */\n showTitle?: boolean\n /**\n * Whether to show the filter input (default: true).\n */\n showFilter?: boolean\n /**\n * Whether to show pagination controls (default: true).\n */\n showPagination?: boolean\n /**\n * Whether to show help text (default: true).\n */\n showHelp?: boolean\n /**\n * Whether to show the status bar (default: true).\n */\n showStatusBar?: boolean\n /**\n * Whether filtering is enabled (default: true).\n *\n * @remarks\n * When false, filter-related keybindings and UI are disabled.\n */\n filteringEnabled?: boolean\n /**\n * Custom styles for the list components.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting.\n * Partial styles are merged with defaults.\n */\n styles?: Partial<ListStyles>\n /**\n * Custom key mappings for list navigation and actions.\n *\n * @remarks\n * Provides control over which keys trigger list actions like navigation,\n * filtering, and help display. If not provided, uses default key bindings.\n */\n keyMap?: ListKeyMap\n}\n\n/**\n * Create a list component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/list` package.\n * The list provides filtering, pagination, keyboard navigation, and help display.\n *\n * @example\n * Basic usage:\n * ```typescript\n * import { list, type Item } from '@boba-cli/dsl'\n *\n * interface TodoItem extends Item {\n * filterValue: () => string\n * title: () => string\n * description: () => string\n * }\n *\n * const items: TodoItem[] = [\n * { filterValue: () => 'Buy milk', title: () => 'Buy milk', description: () => 'From the store' },\n * { filterValue: () => 'Walk dog', title: () => 'Walk dog', description: () => 'In the park' },\n * ]\n *\n * const app = createApp()\n * .component('todos', list({ items }))\n * .view(({ components }) => components.todos)\n * .build()\n * ```\n *\n * @example\n * With custom styling and options:\n * ```typescript\n * import { list } from '@boba-cli/dsl'\n * import { Style } from '@boba-cli/chapstick'\n *\n * const app = createApp()\n * .component('todos', list({\n * items: todoItems,\n * title: 'My Tasks',\n * height: 20,\n * width: 80,\n * styles: {\n * titleBar: new Style().foreground('#50fa7b').bold(),\n * filterPrompt: new Style().foreground('#8be9fd'),\n * },\n * showPagination: true,\n * filteringEnabled: true,\n * }))\n * .view(({ components }) => vstack(\n * text('Welcome to the Todo App').bold(),\n * components.todos\n * ))\n * .build()\n * ```\n *\n * @typeParam T - The item type, must extend `Item`\n * @param options - Configuration options for the list\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function list<T extends Item>(\n options: ListBuilderOptions<T>,\n): ComponentBuilder<ListModel<T>> {\n return {\n init(): [ListModel<T>, Cmd<Msg>] {\n const model = ListModel.new({\n items: options.items,\n delegate: options.delegate,\n height: options.height,\n width: options.width,\n title: options.title,\n showTitle: options.showTitle,\n showFilter: options.showFilter,\n showPagination: options.showPagination,\n showHelp: options.showHelp,\n showStatusBar: options.showStatusBar,\n filteringEnabled: options.filteringEnabled,\n styles: options.styles,\n keyMap: options.keyMap,\n })\n return [model, model.init()]\n },\n\n update(model: ListModel<T>, msg: Msg): [ListModel<T>, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: ListModel<T>): string {\n return model.view()\n },\n }\n}\n\n// Re-export types and classes for convenience\nexport type { Item, ListStyles } from '@boba-cli/list'\nexport { DefaultItem, ListModel } from '@boba-cli/list'\n","import type { Cmd, Msg } from '@boba-cli/tea'\nimport { renderMarkdown } from '@boba-cli/markdown'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Simple model for rendering static markdown content.\n *\n * @remarks\n * This is a minimal TEA model that holds pre-rendered markdown content.\n * It does not handle file I/O or user interaction - it simply displays\n * the rendered markdown string.\n *\n * @internal\n */\nclass StaticMarkdownModel {\n constructor(private readonly renderedContent: string) {}\n\n /**\n * Tea init hook (no-op).\n */\n init(): Cmd<Msg> {\n return null\n }\n\n /**\n * Tea update hook (no-op - static content).\n */\n update(_msg: Msg): [StaticMarkdownModel, Cmd<Msg>] {\n return [this, null]\n }\n\n /**\n * Render the markdown content.\n */\n view(): string {\n return this.renderedContent\n }\n}\n\n/**\n * Options for the markdown component builder.\n *\n * @remarks\n * Configure markdown rendering when creating a markdown component.\n * The markdown is rendered once during initialization using the `marked`\n * library with terminal styling support.\n *\n * @public\n */\nexport interface MarkdownBuilderOptions {\n /**\n * Markdown content to render.\n *\n * @remarks\n * This should be a string containing valid markdown syntax.\n * The content will be rendered with terminal styling including\n * colors for headings, bold/italic text, code blocks, links, etc.\n */\n content: string\n\n /**\n * Width for word wrapping (default: 80).\n *\n * @remarks\n * The markdown renderer will wrap text to fit within this width.\n * This is useful for ensuring content fits within your terminal or\n * specific layout constraints.\n */\n width?: number\n}\n\n/**\n * Create a markdown component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` that renders static markdown content\n * with terminal styling. The markdown is rendered once during initialization\n * using the `marked` library with the `marked-terminal` plugin for terminal\n * color support.\n *\n * This component displays static content and does not handle user input or\n * file operations. For file-based markdown viewing with scrolling, use\n * the `MarkdownModel` from `@boba-cli/markdown` directly.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const app = createApp()\n * .component('help', markdown({\n * content: '# Welcome\\n\\nThis is **bold** text.'\n * }))\n * .view(({ components }) => components.help)\n * .build()\n * ```\n *\n * @example\n * With custom width:\n * ```typescript\n * const app = createApp()\n * .component('readme', markdown({\n * content: readFileSync('README.md', 'utf-8'),\n * width: 120\n * }))\n * .view(({ components }) => vstack(\n * text('Documentation:').bold(),\n * components.readme\n * ))\n * .build()\n * ```\n *\n * @param options - Configuration options for the markdown component\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function markdown(\n options: MarkdownBuilderOptions,\n): ComponentBuilder<StaticMarkdownModel> {\n return {\n init(): [StaticMarkdownModel, Cmd<Msg>] {\n const renderedContent = renderMarkdown(options.content, {\n width: options.width ?? 80,\n })\n const model = new StaticMarkdownModel(renderedContent)\n return [model, null]\n },\n\n update(model: StaticMarkdownModel, msg: Msg): [StaticMarkdownModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: StaticMarkdownModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { PaginatorModel, PaginatorType, type KeyMap } from '@boba-cli/paginator'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the paginator component builder.\n *\n * @remarks\n * Configure the paginator display style and behavior when creating a paginator component.\n *\n * @public\n */\nexport interface PaginatorBuilderOptions {\n /**\n * Pagination display style (required).\n *\n * @remarks\n * - `'dots'`: Renders dots representing each page (e.g., '• ○ ○')\n * - `'arabic'`: Renders page numbers (e.g., '2/5')\n */\n type: 'dots' | 'arabic'\n\n /**\n * Number of items per page (required).\n *\n * @remarks\n * Must be at least 1. Used with `totalItems` to calculate total pages.\n */\n perPage: number\n\n /**\n * Total number of items to paginate (required).\n *\n * @remarks\n * Combined with `perPage` to automatically calculate the total number of pages.\n * For example, 100 items with 10 per page results in 10 pages.\n */\n totalItems: number\n\n /**\n * Initial page index (default: 0).\n *\n * @remarks\n * Zero-indexed page number. Will be clamped to valid range [0, totalPages - 1].\n */\n page?: number\n\n /**\n * Character for the active page dot (default: '•').\n *\n * @remarks\n * Only used when `type` is `'dots'`.\n */\n activeDot?: string\n\n /**\n * Character for inactive page dots (default: '○').\n *\n * @remarks\n * Only used when `type` is `'dots'`.\n */\n inactiveDot?: string\n\n /**\n * Format string for arabic pagination (default: '%d/%d').\n *\n * @remarks\n * Only used when `type` is `'arabic'`. The first `%d` is replaced with\n * the current page number (1-indexed), and the second `%d` is replaced with\n * the total number of pages.\n *\n * @example\n * ```typescript\n * arabicFormat: '%d/%d' // '3/10'\n * arabicFormat: 'Page %d of %d' // 'Page 3 of 10'\n * ```\n */\n arabicFormat?: string\n\n /**\n * Custom key bindings for paginator navigation.\n *\n * @remarks\n * Override default key bindings for navigating between pages.\n * By default, the paginator responds to:\n * - Next page: `pgdown`, `right`, `l`\n * - Previous page: `pgup`, `left`, `h`\n */\n keyMap?: KeyMap\n}\n\n/**\n * Create a paginator component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/paginator` package.\n * The paginator handles keyboard navigation and renders page indicators. It automatically\n * calculates total pages from `totalItems` and `perPage`.\n *\n * Use `PaginatorModel.getSliceBounds()` to get the [start, end] indices for slicing\n * your data array for the current page.\n *\n * @example\n * Basic arabic pagination:\n * ```typescript\n * const app = createApp<{ items: string[] }>()\n * .state({ items: [...Array(100)].map((_, i) => `Item ${i}`) })\n * .component('pager', paginator({\n * type: 'arabic',\n * perPage: 10,\n * totalItems: 100\n * }))\n * .view(({ state, components }) => {\n * const [start, end] = components.pager.getSliceBounds(state.items.length)\n * const pageItems = state.items.slice(start, end)\n * return vstack(\n * ...pageItems.map(item => text(item)),\n * components.pager\n * )\n * })\n * .build()\n * ```\n *\n * @example\n * Dots-style pagination:\n * ```typescript\n * const app = createApp()\n * .component('pager', paginator({\n * type: 'dots',\n * perPage: 1,\n * totalItems: 5,\n * activeDot: '●',\n * inactiveDot: '○'\n * }))\n * .view(({ components }) => hstack(\n * text('Navigate: '),\n * components.pager\n * ))\n * .build()\n * ```\n *\n * @example\n * Custom arabic format:\n * ```typescript\n * const app = createApp()\n * .component('pager', paginator({\n * type: 'arabic',\n * perPage: 20,\n * totalItems: 200,\n * arabicFormat: 'Page %d of %d'\n * }))\n * .view(({ components }) => components.pager)\n * .build()\n * ```\n *\n * @param options - Configuration options for the paginator (type, perPage, and totalItems are required)\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function paginator(options: PaginatorBuilderOptions): ComponentBuilder<PaginatorModel> {\n return {\n init(): [PaginatorModel, Cmd<Msg>] {\n // Convert the 'dots' | 'arabic' string type to PaginatorType enum\n const paginatorType =\n options.type === 'dots' ? PaginatorType.Dots : PaginatorType.Arabic\n\n // Create the base model with provided options\n const model = PaginatorModel.new({\n type: paginatorType,\n page: options.page,\n perPage: options.perPage,\n activeDot: options.activeDot,\n inactiveDot: options.inactiveDot,\n arabicFormat: options.arabicFormat,\n keyMap: options.keyMap,\n }).setTotalPages(options.totalItems)\n\n // Paginator doesn't need any initialization commands\n return [model, null]\n },\n\n update(model: PaginatorModel, msg: Msg): [PaginatorModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: PaginatorModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { Style, type ColorInput } from '@boba-cli/chapstick'\nimport { ProgressModel } from '@boba-cli/progress'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the progress bar component builder.\n *\n * @remarks\n * Configure the progress bar appearance, animation, and behavior when creating\n * a progress component.\n *\n * @public\n */\nexport interface ProgressBuilderOptions {\n /**\n * Width of the progress bar in characters (default: 40).\n *\n * @remarks\n * If `showPercentage` is enabled, the percentage text width is subtracted\n * from this total width.\n *\n * When set to 0, the progress bar itself is not displayed, but the percentage\n * text (if enabled via `showPercentage`) will still be shown. This is rarely\n * useful - prefer setting a positive width to display the actual bar.\n */\n width?: number\n\n /**\n * Character to use for the filled portion (default: '█').\n *\n * @remarks\n * Only the first character is used if a multi-character string is provided.\n */\n full?: string\n\n /**\n * Character to use for the empty portion (default: '░').\n *\n * @remarks\n * Only the first character is used if a multi-character string is provided.\n */\n empty?: string\n\n /**\n * Color for the filled portion (default: '#7571F9').\n *\n * @remarks\n * Ignored if `gradient` is configured. Accepts hex colors or named colors.\n */\n fullColor?: ColorInput\n\n /**\n * Color for the empty portion (default: '#606060').\n *\n * @remarks\n * Accepts hex colors or named colors.\n */\n emptyColor?: ColorInput\n\n /**\n * Whether to display the percentage value (default: true).\n */\n showPercentage?: boolean\n\n /**\n * Format string for the percentage display (default: ' %3.0f%%').\n *\n * @remarks\n * Supports printf-style formatting: `%[width].[precision]f`.\n * Use `%%` to output a literal `%` character.\n *\n * @example\n * ```typescript\n * ' %3.0f%%' // ' 100%'\n * ' %5.1f%%' // ' 50.5%'\n * ```\n */\n percentFormat?: string\n\n /**\n * Gradient configuration for the filled portion.\n *\n * @remarks\n * When set, overrides `fullColor` and creates a smooth color gradient\n * from `start` to `end`. The `scaleGradientToProgress` option controls whether the\n * gradient spans the entire bar width or only the filled portion.\n */\n gradient?: {\n /**\n * Starting color of the gradient.\n */\n start: ColorInput\n /**\n * Ending color of the gradient.\n */\n end: ColorInput\n /**\n * Whether to scale gradient to filled portion only (default: false).\n *\n * @remarks\n * - If `false`, gradient spans the entire bar width (progress reveals gradient)\n * - If `true`, gradient spans only the filled portion (gradient shrinks/grows)\n */\n scaleGradientToProgress?: boolean\n }\n\n /**\n * Spring animation configuration.\n *\n * @remarks\n * Controls the spring physics for smooth easing when the progress bar updates.\n * Higher frequency makes the animation faster, higher damping reduces oscillation.\n */\n spring?: {\n /**\n * Spring frequency (default: 18).\n *\n * @remarks\n * Higher values make the animation respond faster.\n */\n frequency?: number\n /**\n * Spring damping (default: 1).\n *\n * @remarks\n * Higher values reduce oscillation. A value of 1 is critically damped.\n */\n damping?: number\n }\n\n /**\n * Style for rendering the percentage text.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting.\n */\n percentageStyle?: Style\n}\n\n/**\n * Create a progress bar component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/progress` package.\n * The progress bar animates smoothly with spring physics when updated via\n * `ProgressModel.setPercent()`. Animation is triggered by calling `setPercent()`\n * on the model, not automatically on initialization.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const app = createApp()\n * .component('progress', progress())\n * .view(({ components }) => components.progress)\n * .build()\n * ```\n *\n * @example\n * With gradient styling:\n * ```typescript\n * const app = createApp()\n * .component('progress', progress({\n * gradient: {\n * start: '#5A56E0',\n * end: '#EE6FF8',\n * scaleGradientToProgress: false\n * },\n * showPercentage: true\n * }))\n * .view(({ components }) => vstack(\n * text('Loading...'),\n * components.progress\n * ))\n * .build()\n * ```\n *\n * @example\n * With custom spring animation:\n * ```typescript\n * const app = createApp()\n * .component('progress', progress({\n * spring: {\n * frequency: 25,\n * damping: 0.8\n * }\n * }))\n * .view(({ components }) => components.progress)\n * .build()\n * ```\n *\n * @param options - Configuration options for the progress bar\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function progress(\n options: ProgressBuilderOptions = {},\n): ComponentBuilder<ProgressModel> {\n return {\n init(): [ProgressModel, Cmd<Msg>] {\n // Determine if we should use gradient or solid fill\n const hasGradient = options.gradient !== undefined\n\n const model = hasGradient\n ? ProgressModel.withGradient(\n options.gradient!.start,\n options.gradient!.end,\n {\n width: options.width,\n full: options.full,\n empty: options.empty,\n emptyColor: options.emptyColor,\n showPercentage: options.showPercentage,\n percentFormat: options.percentFormat,\n scaleGradient: options.gradient!.scaleGradientToProgress,\n springFrequency: options.spring?.frequency,\n springDamping: options.spring?.damping,\n percentageStyle: options.percentageStyle,\n },\n )\n : ProgressModel.new({\n width: options.width,\n full: options.full,\n empty: options.empty,\n fullColor: options.fullColor,\n emptyColor: options.emptyColor,\n showPercentage: options.showPercentage,\n percentFormat: options.percentFormat,\n springFrequency: options.spring?.frequency,\n springDamping: options.spring?.damping,\n percentageStyle: options.percentageStyle,\n })\n\n // Progress doesn't auto-animate; animation is triggered by setPercent()\n return [model, null]\n },\n\n update(model: ProgressModel, msg: Msg): [ProgressModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: ProgressModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { Style } from '@boba-cli/chapstick'\nimport { SpinnerModel, type Spinner, line, type SpinnerOptions } from '@boba-cli/spinner'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the spinner component builder.\n *\n * @remarks\n * Configure the spinner animation and styling when creating a spinner component.\n * Spinners are animated loading indicators that automatically tick and cycle through\n * animation frames.\n *\n * @public\n */\nexport interface SpinnerBuilderOptions {\n /**\n * Spinner animation to use (default: `line`).\n *\n * @remarks\n * Available spinners include:\n * - `line`: Simple line rotation animation (default)\n * - `dot`: Dots appearing sequentially\n * - `miniDot`: Compact dot animation\n * - `pulse`: Pulsing circle animation\n * - `points`: Loading dots animation\n * - `moon`: Moon phase animation\n * - `meter`: Progress meter style\n * - `ellipsis`: Bouncing ellipsis\n *\n * All spinners are re-exported from `@boba-cli/spinner` for convenience.\n */\n spinner?: Spinner\n\n /**\n * Style for rendering the spinner.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting.\n * The style is applied to all frames of the spinner animation.\n */\n style?: Style\n}\n\n/**\n * Create a spinner component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/spinner` package.\n * The spinner automatically animates by ticking through animation frames. It starts\n * animating immediately when the application initializes.\n *\n * Spinners are useful for indicating loading states, background processes, or\n * that the application is working on a task.\n *\n * @example\n * Basic usage with default spinner:\n * ```typescript\n * const app = createApp()\n * .component('loading', spinner())\n * .view(({ components }) => vstack(\n * text('Loading...'),\n * components.loading\n * ))\n * .build()\n * ```\n *\n * @example\n * With custom animation and styling:\n * ```typescript\n * import { spinner, moon } from '@boba-cli/dsl'\n * import { Style } from '@boba-cli/chapstick'\n *\n * const app = createApp()\n * .component('loading', spinner({\n * spinner: moon,\n * style: new Style().foreground('#50fa7b')\n * }))\n * .view(({ components }) => hstack(\n * components.loading,\n * text(' Processing...')\n * ))\n * .build()\n * ```\n *\n * @example\n * Multiple spinners with different styles:\n * ```typescript\n * const app = createApp()\n * .component('spinner1', spinner({\n * spinner: line,\n * style: new Style().foreground('#ff79c6')\n * }))\n * .component('spinner2', spinner({\n * spinner: pulse,\n * style: new Style().foreground('#8be9fd')\n * }))\n * .view(({ components }) => vstack(\n * hstack(components.spinner1, text(' Task 1')),\n * hstack(components.spinner2, text(' Task 2'))\n * ))\n * .build()\n * ```\n *\n * @param options - Configuration options for the spinner\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function spinner(options: SpinnerBuilderOptions = {}): ComponentBuilder<SpinnerModel> {\n const spinnerOpts: SpinnerOptions = {\n spinner: options.spinner ?? line,\n style: options.style ?? new Style(),\n }\n\n return {\n init(): [SpinnerModel, Cmd<Msg>] {\n const model = new SpinnerModel(spinnerOpts)\n return [model, model.tick() as Cmd<Msg>]\n },\n\n update(model: SpinnerModel, msg: Msg): [SpinnerModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: SpinnerModel): string {\n return model.view()\n },\n }\n}\n","import type { Cmd, Msg } from '@boba-cli/tea'\nimport type { ColorInput } from '@boba-cli/chapstick'\nimport { StatusbarModel, type ColorConfig } from '@boba-cli/statusbar'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the statusbar component builder.\n *\n * @remarks\n * Configure the color scheme for the four columns of the statusbar.\n * Each column can have independent foreground and background colors.\n *\n * The statusbar is typically displayed at the bottom of your application and\n * provides a 4-column layout for displaying status information.\n *\n * Column layout:\n * - First column: Left-aligned, max 30 characters (truncated with ellipsis if exceeded)\n * - Second column: Fills remaining space, truncates as needed\n * - Third column: Right-aligned, no truncation\n * - Fourth column: Right-most column, right-aligned, no truncation\n *\n * @public\n */\nexport interface StatusBarBuilderOptions {\n /**\n * Color configuration for the first column.\n *\n * @remarks\n * The first column is left-aligned and truncates if content exceeds\n * 30 characters. Typically used for app name or mode display.\n */\n first: {\n foreground: ColorInput\n background: ColorInput\n }\n\n /**\n * Color configuration for the second column.\n *\n * @remarks\n * The second column fills the remaining space between the first\n * and third columns, truncating content as needed. Typically used\n * for contextual information or current file/state.\n */\n second: {\n foreground: ColorInput\n background: ColorInput\n }\n\n /**\n * Color configuration for the third column.\n *\n * @remarks\n * The third column is right-aligned and does not truncate. Typically\n * used for brief status indicators or counts.\n */\n third: {\n foreground: ColorInput\n background: ColorInput\n }\n\n /**\n * Color configuration for the fourth column.\n *\n * @remarks\n * The fourth column is right-aligned at the edge and does not truncate.\n * Typically used for time, position, or resource usage display.\n */\n fourth: {\n foreground: ColorInput\n background: ColorInput\n }\n}\n\n/**\n * Create a statusbar component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/statusbar` package.\n * The statusbar displays a 4-column layout at the bottom of the screen with\n * customizable colors for each column.\n *\n * The statusbar automatically responds to window resize events and manages\n * column layout and truncation. To update statusbar content, use the\n * `sendToComponent()` method in event handlers with the model's `setContent()` method.\n *\n * @example\n * Basic usage with color configuration:\n * ```typescript\n * const app = createApp()\n * .component('status', statusbar({\n * first: { foreground: '#ffffff', background: '#5555ff' },\n * second: { foreground: '#ffffff', background: '#333333' },\n * third: { foreground: '#ffffff', background: '#555555' },\n * fourth: { foreground: '#ffffff', background: '#ff5555' }\n * }))\n * .view(({ components }) => components.status)\n * .build()\n * ```\n *\n * @example\n * With dynamic content updates via key handlers:\n * ```typescript\n * const app = createApp<{ count: number }>()\n * .state({ count: 0 })\n * .component('status', statusbar({\n * first: { foreground: '#ffffff', background: '#5555ff' },\n * second: { foreground: '#ffffff', background: '#333333' },\n * third: { foreground: '#ffffff', background: '#555555' },\n * fourth: { foreground: '#ffffff', background: '#ff5555' }\n * }))\n * .onKey('up', ({ state, sendToComponent }) => {\n * sendToComponent('status', (model) =>\n * model.setContent(\n * 'MyApp v1.0',\n * 'Ready',\n * `Count: ${state.count}`,\n * new Date().toLocaleTimeString()\n * )\n * )\n * })\n * .view(({ components }) => vstack(\n * text('Press up to update status'),\n * components.status\n * ))\n * .build()\n * ```\n *\n * @example\n * With custom colors from Dracula theme:\n * ```typescript\n * const app = createApp()\n * .component('status', statusbar({\n * first: { foreground: '#f8f8f2', background: '#bd93f9' },\n * second: { foreground: '#f8f8f2', background: '#44475a' },\n * third: { foreground: '#f8f8f2', background: '#6272a4' },\n * fourth: { foreground: '#282a36', background: '#50fa7b' }\n * }))\n * .onKey('space', ({ sendToComponent }) => {\n * sendToComponent('status', (model) =>\n * model.setContent('Editor', 'file.ts', 'Ln 42', '12:34 PM')\n * )\n * })\n * .view(({ components }) => components.status)\n * .build()\n * ```\n *\n * @param options - Color configuration for the four statusbar columns\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function statusBar(\n options: StatusBarBuilderOptions,\n): ComponentBuilder<StatusbarModel> {\n const firstColors: ColorConfig = {\n foreground: options.first.foreground,\n background: options.first.background,\n }\n const secondColors: ColorConfig = {\n foreground: options.second.foreground,\n background: options.second.background,\n }\n const thirdColors: ColorConfig = {\n foreground: options.third.foreground,\n background: options.third.background,\n }\n const fourthColors: ColorConfig = {\n foreground: options.fourth.foreground,\n background: options.fourth.background,\n }\n\n return {\n init(): [StatusbarModel, Cmd<Msg>] {\n const model = StatusbarModel.new(\n firstColors,\n secondColors,\n thirdColors,\n fourthColors,\n )\n return [model, null]\n },\n\n update(model: StatusbarModel, msg: Msg): [StatusbarModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: StatusbarModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { StopwatchModel, type StopwatchOptions } from '@boba-cli/stopwatch'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the stopwatch component builder.\n *\n * @remarks\n * Configure the stopwatch timing and automatic start behavior when creating a stopwatch component.\n *\n * @public\n */\nexport interface StopwatchBuilderOptions {\n /**\n * Tick interval in milliseconds (default: 1000).\n *\n * @remarks\n * Controls how frequently the stopwatch updates its elapsed time display.\n */\n interval?: number\n /**\n * Whether to automatically start the stopwatch on initialization (default: false).\n *\n * @remarks\n * When `true`, the stopwatch begins counting immediately.\n * When `false`, you must call `model.start()` to begin counting.\n */\n autoStart?: boolean\n}\n\n/**\n * Create a stopwatch component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/stopwatch` package.\n * The stopwatch tracks elapsed time and can be controlled via its model methods\n * (`start()`, `stop()`, `toggle()`, `reset()`).\n *\n * @example\n * Basic usage (manual start):\n * ```typescript\n * const app = createApp()\n * .component('timer', stopwatch())\n * .view(({ components }) => components.timer)\n * .build()\n * ```\n *\n * @example\n * Auto-start with custom interval:\n * ```typescript\n * const app = createApp()\n * .component('timer', stopwatch({\n * interval: 100,\n * autoStart: true\n * }))\n * .view(({ components }) => hstack(\n * text('Elapsed: '),\n * components.timer\n * ))\n * .build()\n * ```\n *\n * @example\n * With key bindings to control the stopwatch:\n * ```typescript\n * const app = createApp()\n * .component('timer', stopwatch({ interval: 1000 }))\n * .view(({ components }) => components.timer)\n * .build()\n *\n * // Access the model to control it:\n * // model.start() - start counting\n * // model.stop() - pause counting\n * // model.toggle() - toggle running state\n * // model.reset() - reset to 0\n * ```\n *\n * @param options - Configuration options for the stopwatch\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function stopwatch(\n options: StopwatchBuilderOptions = {},\n): ComponentBuilder<StopwatchModel> {\n const { interval, autoStart = false } = options\n\n const stopwatchOpts: StopwatchOptions = {\n interval,\n }\n\n return {\n init(): [StopwatchModel, Cmd<Msg>] {\n const model = StopwatchModel.new(stopwatchOpts)\n // Type cast is safe: model.start() returns Cmd<StopwatchMsg>, and StopwatchMsg extends Msg.\n // The cast widens the type to the more general Cmd<Msg> that the component builder requires.\n const cmd = autoStart ? (model.start() as Cmd<Msg>) : null\n return [model, cmd]\n },\n\n update(model: StopwatchModel, msg: Msg): [StopwatchModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: StopwatchModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport type { BorderStyle } from '@boba-cli/chapstick'\nimport {\n TableModel,\n type Column,\n type Row,\n type TableKeyMap,\n type TableStyles,\n type TableOptions,\n} from '@boba-cli/table'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the table component builder.\n *\n * @remarks\n * Configure the table's columns, data, dimensions, and behavior when creating a table component.\n *\n * @public\n */\nexport interface TableBuilderOptions {\n /**\n * Column definitions for the table (required).\n *\n * @remarks\n * Each column specifies a title and width. Columns define the structure of the table.\n */\n columns: Column[]\n /**\n * Row data for the table.\n *\n * @remarks\n * Each row is an array of strings corresponding to the columns.\n * If not provided, the table will be empty.\n */\n rows?: Row[]\n /**\n * Visible height in rows.\n *\n * @remarks\n * Controls how many rows are visible at once. The table will scroll if there are more rows.\n * If not specified, defaults to the number of rows.\n *\n * When explicitly set to 0, the table shows no rows (effectively hidden). This is rarely\n * desired - omit this option to show all rows, or set a positive value to create a scrollable\n * window showing that many rows at once.\n */\n height?: number\n /**\n * Total width of the table.\n *\n * @remarks\n * If not specified, the width is calculated from column widths.\n *\n * When explicitly set to 0, the table has no width constraint and uses the sum of column\n * widths. This is typically not useful since the default behavior already calculates width\n * from columns - prefer omitting this option to use auto-calculated width.\n */\n width?: number\n /**\n * Whether the table is focused and can accept keyboard input.\n *\n * @remarks\n * When focused, the table responds to navigation keys (arrow keys, page up/down, etc.).\n */\n focused?: boolean\n /**\n * Whether to render borders around the table.\n *\n * @remarks\n * When true, draws borders using the specified or default border style.\n */\n bordered?: boolean\n /**\n * Border style to use when bordered is true.\n *\n * @remarks\n * Uses `BorderStyle` from `@boba-cli/chapstick` to control border characters.\n */\n borderStyle?: BorderStyle\n /**\n * Styling for table elements (header, cells, selected row, border).\n *\n * @remarks\n * Customize the appearance of different table parts using `Style` objects.\n */\n styles?: Partial<TableStyles>\n /**\n * Keyboard navigation bindings.\n *\n * @remarks\n * Override default key bindings for navigation actions (up, down, page up/down, etc.).\n */\n keyMap?: Partial<TableKeyMap>\n}\n\n/**\n * Create a table component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/table` package.\n * The table supports scrolling, row selection, and keyboard navigation when focused.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const app = createApp()\n * .component('data', table({\n * columns: [\n * { title: 'Name', width: 20 },\n * { title: 'Age', width: 10 },\n * ],\n * rows: [\n * ['Alice', '30'],\n * ['Bob', '25'],\n * ],\n * }))\n * .view(({ components }) => components.data)\n * .build()\n * ```\n *\n * @example\n * With borders and focus:\n * ```typescript\n * const app = createApp()\n * .component('data', table({\n * columns: [\n * { title: 'Item', width: 15 },\n * { title: 'Status', width: 10 },\n * ],\n * rows: [\n * ['Task 1', 'Done'],\n * ['Task 2', 'Pending'],\n * ],\n * bordered: true,\n * focused: true,\n * height: 5,\n * }))\n * .view(({ components }) => components.data)\n * .build()\n * ```\n *\n * @param options - Configuration options for the table\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function table(options: TableBuilderOptions): ComponentBuilder<TableModel> {\n const tableOpts: TableOptions = {\n columns: options.columns,\n rows: options.rows,\n height: options.height,\n width: options.width,\n focused: options.focused,\n bordered: options.bordered,\n borderStyle: options.borderStyle,\n styles: options.styles,\n keyMap: options.keyMap,\n }\n\n return {\n init(): [TableModel, Cmd<Msg>] {\n const model = TableModel.new(tableOpts)\n return [model, model.init()]\n },\n\n update(model: TableModel, msg: Msg): [TableModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: TableModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { Style } from '@boba-cli/chapstick'\nimport {\n TextareaModel,\n CursorMode,\n type TextareaOptions,\n type ValidateFunc,\n type KeyMap,\n} from '@boba-cli/textarea'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the textArea component builder.\n *\n * @remarks\n * Configure the multi-line text area appearance, validation, and behavior when creating\n * a text area component.\n *\n * @public\n */\nexport interface TextAreaBuilderOptions {\n /**\n * Initial value (multi-line text).\n *\n * @remarks\n * The starting content of the textarea. Use `\\n` for line breaks.\n */\n value?: string\n\n /**\n * Placeholder text shown when textarea is empty.\n *\n * @remarks\n * Displayed in a dimmed style when the textarea has no content. Disappears once\n * the user starts typing. Uses `placeholderStyle` for styling.\n */\n placeholder?: string\n\n /**\n * Width constraint for the textarea.\n *\n * @remarks\n * When set to 0 or not specified, the textarea has no width constraint and grows with content.\n * When set to a positive value, lines longer than the width will wrap to the next line.\n */\n width?: number\n\n /**\n * Maximum visible height in lines.\n *\n * @remarks\n * When set to 0 (the default), there is no maximum height constraint - the textarea\n * will grow to accommodate all lines of content. When set to a positive value, the\n * textarea will not exceed that height and will become scrollable if content exceeds\n * the limit.\n */\n maxHeight?: number\n\n /**\n * Maximum width constraint.\n *\n * @remarks\n * When set, prevents lines from exceeding this width. Works together with `width`\n * to control horizontal sizing.\n */\n maxWidth?: number\n\n /**\n * Prompt string shown before each line.\n *\n * @remarks\n * Displayed at the start of each line in the textarea. Uses `promptStyle` for styling.\n * Common patterns include empty string (no prompt), `'| '`, or `' '` for indentation.\n */\n prompt?: string\n\n /**\n * Show line numbers on the left (default: false).\n *\n * @remarks\n * When enabled, displays line numbers in the left margin using `lineNumberStyle` for styling.\n * Useful for code editing or when line references are important.\n */\n showLineNumbers?: boolean\n\n /**\n * Cursor display mode.\n *\n * @remarks\n * Controls how the cursor is displayed:\n * - `CursorMode.Blink`: Cursor blinks on and off (default)\n * - `CursorMode.Static`: Cursor is always visible\n * - `CursorMode.Hidden`: Cursor is not displayed\n */\n cursorMode?: CursorMode\n\n /**\n * Validation function.\n *\n * @remarks\n * Called on every change to validate the current textarea content. Return `null` for\n * valid input, or an `Error` with a descriptive message for invalid input. The error\n * message can be displayed to the user in the UI.\n *\n * @example\n * ```typescript\n * validate: (value) => {\n * const lines = value.split('\\n')\n * if (lines.length > 10) return new Error('Maximum 10 lines allowed')\n * return null\n * }\n * ```\n */\n validate?: ValidateFunc\n\n /**\n * Style for the prompt.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting to the prompt.\n */\n promptStyle?: Style\n\n /**\n * Style for the text content.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting to typed text.\n */\n textStyle?: Style\n\n /**\n * Style for the placeholder text.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting to the placeholder.\n */\n placeholderStyle?: Style\n\n /**\n * Style for the cursor.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting to the cursor.\n */\n cursorStyle?: Style\n\n /**\n * Style for line numbers.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting to line numbers\n * when `showLineNumbers` is enabled.\n */\n lineNumberStyle?: Style\n\n /**\n * Custom key bindings.\n *\n * @remarks\n * Override default key bindings for navigation and editing actions. If not provided,\n * uses default Vi-like key bindings (arrow keys, j/k for up/down, etc.).\n */\n keyMap?: KeyMap\n}\n\n/**\n * Create a textArea component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/textarea` package.\n * The text area supports multi-line editing, scrolling, line numbers, validation,\n * and extensive styling options.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const app = createApp()\n * .component('editor', textArea({\n * placeholder: 'Enter your text...',\n * maxHeight: 10\n * }))\n * .view(({ components }) => components.editor)\n * .build()\n * ```\n *\n * @example\n * With line numbers and validation:\n * ```typescript\n * const app = createApp()\n * .component('editor', textArea({\n * placeholder: 'Type code here...',\n * maxHeight: 20,\n * width: 80,\n * showLineNumbers: true,\n * validate: (value) => {\n * const lines = value.split('\\n')\n * if (lines.length > 100) return new Error('Maximum 100 lines')\n * return null\n * }\n * }))\n * .view(({ components }) => components.editor)\n * .build()\n * ```\n *\n * @example\n * With custom styling and cursor mode:\n * ```typescript\n * import { textArea, CursorMode } from '@boba-cli/dsl'\n * import { Style } from '@boba-cli/chapstick'\n *\n * const app = createApp()\n * .component('styledEditor', textArea({\n * prompt: '| ',\n * showLineNumbers: true,\n * cursorMode: CursorMode.Static,\n * promptStyle: new Style().foreground('#6272a4'),\n * textStyle: new Style().foreground('#f8f8f2'),\n * lineNumberStyle: new Style().foreground('#44475a').dim(),\n * cursorStyle: new Style().foreground('#ff79c6').bold()\n * }))\n * .view(({ components }) => components.styledEditor)\n * .build()\n * ```\n *\n * @example\n * Multi-line input with initial value:\n * ```typescript\n * const app = createApp()\n * .component('notes', textArea({\n * value: 'Line 1\\nLine 2\\nLine 3',\n * placeholder: 'Enter notes...',\n * maxHeight: 15,\n * maxWidth: 60\n * }))\n * .view(({ components }) => components.notes)\n * .build()\n * ```\n *\n * @param options - Configuration options for the text area\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function textArea(\n options: TextAreaBuilderOptions = {},\n): ComponentBuilder<TextareaModel> {\n const textareaOpts: TextareaOptions = {\n value: options.value,\n placeholder: options.placeholder,\n width: options.width,\n maxHeight: options.maxHeight,\n maxWidth: options.maxWidth,\n prompt: options.prompt ?? '',\n showLineNumbers: options.showLineNumbers ?? false,\n cursorMode: options.cursorMode,\n validate: options.validate,\n promptStyle: options.promptStyle ?? new Style(),\n textStyle: options.textStyle ?? new Style(),\n placeholderStyle: options.placeholderStyle ?? new Style(),\n cursorStyle: options.cursorStyle ?? new Style(),\n lineNumberStyle: options.lineNumberStyle ?? new Style(),\n keyMap: options.keyMap,\n }\n\n return {\n init(): [TextareaModel, Cmd<Msg>] {\n const model = TextareaModel.new(textareaOpts)\n const [focused, cmd] = model.focus()\n return [focused, cmd]\n },\n\n update(model: TextareaModel, msg: Msg): [TextareaModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: TextareaModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { Style } from '@boba-cli/chapstick'\nimport {\n TextInputModel,\n EchoMode,\n type TextInputOptions,\n type ValidateFunc,\n} from '@boba-cli/textinput'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the textInput component builder.\n *\n * @remarks\n * Configure the text input's appearance, behavior, and validation when creating\n * a text input component.\n *\n * @public\n */\nexport interface TextInputBuilderOptions {\n /**\n * Placeholder text shown when input is empty.\n *\n * @remarks\n * Displayed in a dimmed style when the input has no content. Uses `placeholderStyle` for styling.\n */\n placeholder?: string\n\n /**\n * Width constraint for the input field.\n *\n * @remarks\n * When set to 0 or not specified, the input field has no width constraint.\n * When set to a positive value, the input will be constrained to that width.\n */\n width?: number\n\n /**\n * Echo mode for input display (default: `EchoMode.Normal`).\n *\n * @remarks\n * Controls how input characters are displayed:\n * - `EchoMode.Normal`: Characters are displayed as typed\n * - `EchoMode.Password`: Characters are hidden with asterisks or dots\n * - `EchoMode.None`: No characters are displayed\n */\n echoMode?: EchoMode\n\n /**\n * Character limit for input (default: 0 = unlimited).\n *\n * @remarks\n * When set to a positive value, prevents input beyond the specified character count.\n * When 0 or not specified, there is no character limit.\n */\n charLimit?: number\n\n /**\n * Prompt string shown before the input.\n *\n * @remarks\n * Displayed at the start of the input line. Uses `promptStyle` for styling.\n * Common examples: `'> '`, `'$ '`, or `'Name: '`.\n */\n prompt?: string\n\n /**\n * Style for the prompt.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting.\n */\n promptStyle?: Style\n\n /**\n * Style for the input text.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting to typed text.\n */\n textStyle?: Style\n\n /**\n * Style for the placeholder text.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting to the placeholder.\n */\n placeholderStyle?: Style\n\n /**\n * Validation function.\n *\n * @remarks\n * Called on every change to validate the current input value. Return `null` for\n * valid input, or an `Error` with a descriptive message for invalid input.\n *\n * @example\n * ```typescript\n * validate: (value) => {\n * if (value.length < 3) return new Error('Too short')\n * if (!/^[a-z]+$/.test(value)) return new Error('Lowercase letters only')\n * return null\n * }\n * ```\n */\n validate?: ValidateFunc\n}\n\n/**\n * Create a textInput component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/textinput` package.\n * The text input accepts user keyboard input and supports validation, placeholders,\n * password masking, and character limits. The component is automatically focused\n * on initialization.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const app = createApp()\n * .component('nameInput', textInput({\n * placeholder: 'Enter your name...',\n * width: 40\n * }))\n * .view(({ components }) => components.nameInput)\n * .build()\n * ```\n *\n * @example\n * With validation:\n * ```typescript\n * const app = createApp()\n * .component('emailInput', textInput({\n * placeholder: 'Email address',\n * validate: (value) => {\n * if (!value.includes('@')) return new Error('Invalid email')\n * return null\n * }\n * }))\n * .view(({ components }) => components.emailInput)\n * .build()\n * ```\n *\n * @example\n * Password input:\n * ```typescript\n * const app = createApp()\n * .component('password', textInput({\n * placeholder: 'Password',\n * echoMode: EchoMode.Password,\n * charLimit: 50\n * }))\n * .view(({ components }) => vstack(\n * text('Enter your password:'),\n * components.password\n * ))\n * .build()\n * ```\n *\n * @example\n * With custom styling:\n * ```typescript\n * import { textInput, EchoMode } from '@boba-cli/dsl'\n * import { Style } from '@boba-cli/chapstick'\n *\n * const app = createApp()\n * .component('styledInput', textInput({\n * prompt: '> ',\n * placeholder: 'Type something...',\n * promptStyle: new Style().foreground('#50fa7b').bold(),\n * textStyle: new Style().foreground('#f8f8f2'),\n * placeholderStyle: new Style().foreground('#6272a4').italic()\n * }))\n * .view(({ components }) => components.styledInput)\n * .build()\n * ```\n *\n * @param options - Configuration options for the text input\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function textInput(\n options: TextInputBuilderOptions = {},\n): ComponentBuilder<TextInputModel> {\n const inputOpts: TextInputOptions = {\n placeholder: options.placeholder ?? '',\n width: options.width,\n echoMode: options.echoMode ?? EchoMode.Normal,\n charLimit: options.charLimit ?? 0,\n prompt: options.prompt ?? '',\n promptStyle: options.promptStyle ?? new Style(),\n textStyle: options.textStyle ?? new Style(),\n placeholderStyle: options.placeholderStyle ?? new Style(),\n validate: options.validate,\n }\n\n return {\n init(): [TextInputModel, Cmd<Msg>] {\n const model = TextInputModel.new(inputOpts)\n const [focused, cmd] = model.focus()\n return [focused, cmd]\n },\n\n update(model: TextInputModel, msg: Msg): [TextInputModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: TextInputModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { TimerModel, type TimerOptions } from '@boba-cli/timer'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the timer component builder.\n *\n * @remarks\n * Configure the countdown timer when creating a timer component.\n *\n * @public\n */\nexport interface TimerBuilderOptions {\n /**\n * Milliseconds until the timer expires (required).\n */\n timeout: number\n /**\n * Tick interval in milliseconds (default: 1000).\n *\n * @remarks\n * Controls how frequently the timer updates. Smaller intervals\n * provide more granular countdown display at the cost of more updates.\n */\n interval?: number\n /**\n * Whether to automatically start the timer on initialization (default: true).\n *\n * @remarks\n * If false, the timer must be manually started using the `start()` method.\n */\n autoStart?: boolean\n}\n\n/**\n * Create a timer component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/timer` package.\n * The timer counts down from the specified timeout and can be started, stopped,\n * or toggled programmatically.\n *\n * @example\n * Basic usage with auto-start:\n * ```typescript\n * const app = createApp()\n * .component('countdown', timer({ timeout: 60000 })) // 1 minute\n * .view(({ components }) => hstack(\n * text('Time remaining: '),\n * components.countdown\n * ))\n * .build()\n * ```\n *\n * @example\n * With custom interval and manual start:\n * ```typescript\n * const app = createApp()\n * .component('countdown', timer({\n * timeout: 30000, // 30 seconds\n * interval: 100, // Update every 100ms\n * autoStart: false // Don't start immediately\n * }))\n * .view(({ components }) => components.countdown)\n * .build()\n * ```\n *\n * @param options - Configuration options for the timer\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function timer(options: TimerBuilderOptions): ComponentBuilder<TimerModel> {\n const timerOpts: TimerOptions = {\n timeout: options.timeout,\n interval: options.interval,\n }\n\n const autoStart = options.autoStart ?? true\n\n return {\n init(): [TimerModel, Cmd<Msg>] {\n const model = TimerModel.new(timerOpts)\n const startCmd = autoStart ? model.start() : null\n // Type cast is safe: model.start() returns Cmd<TimerMsg> | null, and TimerMsg extends Msg.\n // The cast widens the type to the more general Cmd<Msg> that the component builder requires.\n return [model, startCmd as Cmd<Msg>]\n },\n\n update(model: TimerModel, msg: Msg): [TimerModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: TimerModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { Style } from '@boba-cli/chapstick'\nimport { ViewportModel, type ViewportOptions } from '@boba-cli/viewport'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the viewport component builder.\n *\n * @remarks\n * Configure the viewport dimensions, scrolling behavior, and content when creating a viewport component.\n *\n * @public\n */\nexport interface ViewportBuilderOptions {\n /**\n * Viewport width in characters.\n *\n * @remarks\n * Defines the horizontal size of the viewport. Defaults to 0.\n *\n * When set to 0 (the default), the viewport has no width constraint and will use\n * the natural width of the content. When set to a positive value, content will be\n * clipped or wrapped to fit the specified width.\n */\n width?: number\n\n /**\n * Viewport height in lines.\n *\n * @remarks\n * Defines the vertical size (number of visible lines) of the viewport. Defaults to 0.\n *\n * When set to 0 (the default), the viewport shows no lines (effectively disabled).\n * Set this to a positive value to create a scrollable window showing that many lines\n * at once. If there are more content lines than the height, the viewport becomes scrollable.\n */\n height?: number\n\n /**\n * Enable or disable mouse wheel scrolling (default: true).\n *\n * @remarks\n * When enabled, the viewport responds to mouse wheel events for scrolling.\n */\n mouseWheelEnabled?: boolean\n\n /**\n * Use high performance rendering mode (default: false).\n *\n * @remarks\n * When enabled, optimizes rendering for better performance with large content.\n * This is a placeholder for future optimization features.\n */\n highPerformanceRendering?: boolean\n\n /**\n * Initial content to display in the viewport.\n *\n * @remarks\n * The content will be split into lines and displayed in the scrollable viewport.\n * Use newline characters (`\\n`) to separate lines.\n */\n content?: string\n\n /**\n * Style for rendering the viewport content.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting.\n */\n style?: Style\n}\n\n/**\n * Create a viewport component builder.\n *\n * @remarks\n * Creates a `ComponentBuilder` wrapping the `@boba-cli/viewport` package.\n * The viewport provides a scrollable window onto text content with keyboard and\n * mouse wheel navigation support.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const app = createApp()\n * .component('logs', viewport({\n * width: 80,\n * height: 20,\n * content: 'Line 1\\nLine 2\\nLine 3'\n * }))\n * .view(({ components }) => components.logs)\n * .build()\n * ```\n *\n * @example\n * With custom styling:\n * ```typescript\n * const app = createApp()\n * .component('output', viewport({\n * width: 100,\n * height: 30,\n * content: generateLongContent(),\n * style: new Style().foreground('#8be9fd'),\n * mouseWheelEnabled: true\n * }))\n * .view(({ components }) => vstack(\n * text('Scrollable Output:'),\n * components.output\n * ))\n * .build()\n * ```\n *\n * @example\n * High performance mode for large content:\n * ```typescript\n * const app = createApp()\n * .component('largeLog', viewport({\n * width: 120,\n * height: 40,\n * content: fs.readFileSync('large-log.txt', 'utf-8'),\n * highPerformanceRendering: true\n * }))\n * .view(({ components }) => components.largeLog)\n * .build()\n * ```\n *\n * @param options - Configuration options for the viewport\n * @returns A `ComponentBuilder` ready to use with `AppBuilder.component`\n *\n * @public\n */\nexport function viewport(options: ViewportBuilderOptions = {}): ComponentBuilder<ViewportModel> {\n const viewportOpts: ViewportOptions = {\n width: options.width,\n height: options.height,\n mouseWheelEnabled: options.mouseWheelEnabled,\n style: options.style,\n }\n\n return {\n init(): [ViewportModel, Cmd<Msg>] {\n let model = ViewportModel.new(viewportOpts)\n if (options.content !== undefined) {\n model = model.setContent(options.content)\n }\n return [model, model.init()]\n },\n\n update(model: ViewportModel, msg: Msg): [ViewportModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: ViewportModel): string {\n return model.view()\n },\n }\n}\n"]}