semajsx 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/{computed-BpjqvQu1.mjs → computed-BidG06Lt.mjs} +2 -2
  2. package/dist/{computed-BpjqvQu1.mjs.map → computed-BidG06Lt.mjs.map} +1 -1
  3. package/dist/{document-OGuk9jhK.mjs → document-BOJDaiBc.mjs} +19 -4
  4. package/dist/document-BOJDaiBc.mjs.map +1 -0
  5. package/dist/{document-DFsOtfef.mjs → document-CwHVG_PJ.mjs} +2 -2
  6. package/dist/dom/index.d.mts +68 -3
  7. package/dist/dom/index.d.mts.map +1 -1
  8. package/dist/dom/index.mjs +4 -4
  9. package/dist/dom/jsx-dev-runtime.d.mts +4 -4
  10. package/dist/dom/jsx-dev-runtime.mjs +1 -1
  11. package/dist/dom/jsx-runtime.d.mts +4 -4
  12. package/dist/dom/jsx-runtime.mjs +2 -2
  13. package/dist/{helpers-DrifjCXb.d.mts → helpers-CfRDJgcP.d.mts} +3 -3
  14. package/dist/{helpers-DrifjCXb.d.mts.map → helpers-CfRDJgcP.d.mts.map} +1 -1
  15. package/dist/{index-DS5X3Pvb.d.mts → index-B1pjI-Su.d.mts} +2 -2
  16. package/dist/{index-DS5X3Pvb.d.mts.map → index-B1pjI-Su.d.mts.map} +1 -1
  17. package/dist/index-DC3tthWf.d.mts +81 -0
  18. package/dist/index-DC3tthWf.d.mts.map +1 -0
  19. package/dist/index.d.mts +7 -7
  20. package/dist/index.mjs +5 -5
  21. package/dist/{island-marker-hZdmHMvx.d.mts → island-marker-BJIO07Vj.d.mts} +1 -1
  22. package/dist/island-marker-BJIO07Vj.d.mts.map +1 -0
  23. package/dist/{jsx-CGW4OyqA.d.mts → jsx-fNlLjLou.d.mts} +2 -2
  24. package/dist/{jsx-CGW4OyqA.d.mts.map → jsx-fNlLjLou.d.mts.map} +1 -1
  25. package/dist/{jsx-runtime-DU8DRISG.d.mts → jsx-runtime-BFuFPDzn.d.mts} +3 -3
  26. package/dist/{jsx-runtime-DU8DRISG.d.mts.map → jsx-runtime-BFuFPDzn.d.mts.map} +1 -1
  27. package/dist/jsx-runtime-D9ZNjMJ2.mjs.map +1 -1
  28. package/dist/{jsx-runtime-mBpL8czJ.d.mts → jsx-runtime-DZx2Yv-t.d.mts} +28 -6
  29. package/dist/{jsx-runtime-mBpL8czJ.d.mts.map → jsx-runtime-DZx2Yv-t.d.mts.map} +1 -1
  30. package/dist/lucide-CVtHepGM.mjs +126 -0
  31. package/dist/lucide-CVtHepGM.mjs.map +1 -0
  32. package/dist/{resource-B1IudM8_.d.mts → resource-BQI6AeJ0.d.mts} +23 -3
  33. package/dist/resource-BQI6AeJ0.d.mts.map +1 -0
  34. package/dist/{resource-BjsDAkbG.mjs → resource-DSlXDZZi.mjs} +2 -2
  35. package/dist/{resource-BjsDAkbG.mjs.map → resource-DSlXDZZi.mjs.map} +1 -1
  36. package/dist/signal/index.d.mts +2 -2
  37. package/dist/signal/index.mjs +3 -3
  38. package/dist/{signal-4PgGfydw.mjs → signal-BN8vHXDb.mjs} +1 -1
  39. package/dist/{signal-4PgGfydw.mjs.map → signal-BN8vHXDb.mjs.map} +1 -1
  40. package/dist/{signal-CLsaPA7c.d.mts → signal-BwxUlXKs.d.mts} +1 -1
  41. package/dist/{signal-CLsaPA7c.d.mts.map → signal-BwxUlXKs.d.mts.map} +1 -1
  42. package/dist/{src-CRi0xsNK.mjs → src-BqX3sryB.mjs} +68 -5
  43. package/dist/src-BqX3sryB.mjs.map +1 -0
  44. package/dist/src-DR-EWgVP.mjs +868 -0
  45. package/dist/src-DR-EWgVP.mjs.map +1 -0
  46. package/dist/{src-DEoBG1zB.mjs → src-DUpFNNM_.mjs} +4 -4
  47. package/dist/{src-DEoBG1zB.mjs.map → src-DUpFNNM_.mjs.map} +1 -1
  48. package/dist/{src-BlS3Hc-L.mjs → src-DW3tIczg.mjs} +75 -5
  49. package/dist/src-DW3tIczg.mjs.map +1 -0
  50. package/dist/{src-Jbt_w0hc.mjs → src-Ds9vl42d.mjs} +37 -35
  51. package/dist/src-Ds9vl42d.mjs.map +1 -0
  52. package/dist/{src-iC-NFwTy.mjs → src-DuSN6go_.mjs} +79 -22
  53. package/dist/src-DuSN6go_.mjs.map +1 -0
  54. package/dist/ssg/index.d.mts +4 -182
  55. package/dist/ssg/index.d.mts.map +1 -1
  56. package/dist/ssg/index.mjs +7 -756
  57. package/dist/ssg/index.mjs.map +1 -1
  58. package/dist/ssg/plugins/docs-theme.d.mts +180 -0
  59. package/dist/ssg/plugins/docs-theme.d.mts.map +1 -0
  60. package/dist/ssg/plugins/docs-theme.mjs +2042 -0
  61. package/dist/ssg/plugins/docs-theme.mjs.map +1 -0
  62. package/dist/ssg/plugins/lucide.d.mts +3 -0
  63. package/dist/ssg/plugins/lucide.mjs +7 -0
  64. package/dist/ssr/client.d.mts +3 -3
  65. package/dist/ssr/client.mjs +5 -5
  66. package/dist/ssr/index.d.mts +3 -3
  67. package/dist/ssr/index.d.mts.map +1 -1
  68. package/dist/ssr/index.mjs +6 -5
  69. package/dist/style/index.d.mts +2 -2
  70. package/dist/style/index.d.mts.map +1 -1
  71. package/dist/style/index.mjs +1 -1
  72. package/dist/style/react.d.mts +2 -2
  73. package/dist/style/react.mjs +2 -2
  74. package/dist/style/vue.d.mts +2 -2
  75. package/dist/style/vue.mjs +2 -2
  76. package/dist/terminal/index.d.mts +4 -4
  77. package/dist/terminal/index.mjs +2 -2
  78. package/dist/terminal/jsx-dev-runtime.d.mts +4 -4
  79. package/dist/terminal/jsx-dev-runtime.mjs +1 -1
  80. package/dist/terminal/jsx-runtime.d.mts +4 -4
  81. package/dist/terminal/jsx-runtime.mjs +1 -1
  82. package/dist/{types-DlNR9ZaJ.d.mts → types-BlaUrkq0.d.mts} +2 -2
  83. package/dist/{types-DlNR9ZaJ.d.mts.map → types-BlaUrkq0.d.mts.map} +1 -1
  84. package/dist/types-CGkRxnQB.d.mts +220 -0
  85. package/dist/types-CGkRxnQB.d.mts.map +1 -0
  86. package/dist/{types-Dgj6n-EE.d.mts → types-CZMcXQTW.d.mts} +9 -4
  87. package/dist/types-CZMcXQTW.d.mts.map +1 -0
  88. package/dist/{types-Bjx1Pp14.d.mts → types-D0jRO840.d.mts} +1 -1
  89. package/dist/{types-Bjx1Pp14.d.mts.map → types-D0jRO840.d.mts.map} +1 -1
  90. package/dist/{types-DEi0apQO.d.mts → types-DucvOZQ2.d.mts} +2 -2
  91. package/dist/{types-DEi0apQO.d.mts.map → types-DucvOZQ2.d.mts.map} +1 -1
  92. package/dist/{utils-BrGmTgfG.mjs → utils-DbTAs943.mjs} +1 -1
  93. package/dist/{utils-BrGmTgfG.mjs.map → utils-DbTAs943.mjs.map} +1 -1
  94. package/package.json +30 -2
  95. package/dist/document-OGuk9jhK.mjs.map +0 -1
  96. package/dist/island-marker-hZdmHMvx.d.mts.map +0 -1
  97. package/dist/jsx-runtime-D2B2BK8X.mjs +0 -4
  98. package/dist/resource-B1IudM8_.d.mts.map +0 -1
  99. package/dist/src-BlS3Hc-L.mjs.map +0 -1
  100. package/dist/src-CRi0xsNK.mjs.map +0 -1
  101. package/dist/src-Jbt_w0hc.mjs.map +0 -1
  102. package/dist/src-iC-NFwTy.mjs.map +0 -1
  103. package/dist/types-Dgj6n-EE.d.mts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src-DR-EWgVP.mjs","names":["fsWatch"],"sources":["../../ssg/src/document.tsx","../../ssg/src/types.ts","../../ssg/src/mdx/processor.ts","../../ssg/src/mdx/vite-plugin.ts","../../ssg/src/ssg.ts","../../ssg/src/collection/index.ts","../../ssg/src/sources/base.ts","../../ssg/src/sources/file.ts","../../ssg/src/sources/git.ts","../../ssg/src/sources/remote.ts","../../ssg/src/sources/custom.ts"],"sourcesContent":["/** @jsxImportSource @semajsx/dom */\n\nimport type { DocumentTemplate } from \"./types\";\n\n/**\n * Default HTML document template for SSG\n */\nexport const DefaultDocument: DocumentTemplate = ({\n children,\n title = \"SSG Page\",\n scripts,\n css,\n}) => (\n <html lang=\"en\">\n <head>\n <meta charSet=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>{title}</title>\n {css?.map((href) => (\n <link rel=\"stylesheet\" href={href} />\n ))}\n </head>\n <body>\n {children}\n {scripts}\n </body>\n </html>\n);\n","import type { Component, VNode } from \"@semajsx/core\";\nimport type { z } from \"zod\";\n\n// =============================================================================\n// Collection Registry (for type inference)\n// =============================================================================\n\n/**\n * Infer a registry type from an array of collections\n * Maps collection names to their data types\n */\nexport type InferCollections<T extends readonly Collection[]> = {\n [K in T[number] as K[\"name\"]]: K extends Collection<infer D> ? D : unknown;\n};\n\n// =============================================================================\n// Collection Entry\n// =============================================================================\n\nexport interface CollectionEntry<T = unknown> {\n /** Unique identifier within the collection */\n id: string;\n /** URL-friendly slug */\n slug: string;\n /** Validated frontmatter data */\n data: T;\n /** Raw content body (markdown/mdx) */\n body: string;\n /** Render the content to JSX */\n render: () => Promise<{ Content: () => VNode }>;\n}\n\n// =============================================================================\n// Collection Source\n// =============================================================================\n\nexport interface ChangeSet<T = unknown> {\n /** Cursor for next incremental fetch */\n cursor: string;\n /** Newly added entries */\n added: CollectionEntry<T>[];\n /** Updated entries */\n updated: CollectionEntry<T>[];\n /** Deleted entry IDs */\n deleted: string[];\n}\n\nexport type WatchCallback<T = unknown> = (changes: ChangeSet<T>) => void;\n\nexport interface CollectionSource<T = unknown> {\n /** Unique identifier for this source */\n id: string;\n\n /** Get all entries from this source */\n getEntries(): Promise<CollectionEntry<T>[]>;\n\n /** Get a single entry by ID */\n getEntry(id: string): Promise<CollectionEntry<T> | null>;\n\n /** Watch for changes (optional) */\n watch?(callback: WatchCallback<T>): () => void;\n\n /** Get incremental changes since cursor (optional) */\n getChanges?(since: string): Promise<ChangeSet<T>>;\n}\n\n// =============================================================================\n// Collection Definition\n// =============================================================================\n\nexport interface CollectionConfig<T extends z.ZodType = z.ZodType> {\n /** Collection name */\n name: string;\n /** Data source (returns raw entries, validated against schema later) */\n source: CollectionSource<unknown>;\n /** Zod schema for validation */\n schema: T;\n}\n\nexport interface Collection<T = unknown> {\n name: string;\n source: CollectionSource<unknown>;\n schema: z.ZodType;\n /** Type-only field for inference */\n _outputType?: T;\n}\n\n// =============================================================================\n// Route Configuration\n// =============================================================================\n\nexport interface StaticPath<P = Record<string, string>> {\n params: P;\n props?: Record<string, unknown>;\n}\n\nexport interface RouteConfig<TRegistry extends Record<string, unknown> = Record<string, unknown>> {\n /** Route path pattern (e.g., '/blog/:slug') */\n path: string;\n /** Component to render. Props are provided dynamically by the route config. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Route props are dynamic, typed via `props`/`getStaticPaths`\n component: (props: any) => VNode;\n /** Static props for the route */\n props?:\n | Record<string, unknown>\n | ((ssg: SSGInstance<TRegistry>) => Promise<Record<string, unknown>>);\n /** Generate static paths for dynamic routes */\n getStaticPaths?: (ssg: SSGInstance<TRegistry>) => Promise<StaticPath[]>;\n}\n\n// =============================================================================\n// MDX Configuration\n// =============================================================================\n\nexport interface MDXConfig {\n /** Remark plugins */\n remarkPlugins?: unknown[];\n /** Rehype plugins */\n rehypePlugins?: unknown[];\n /** Component mapping for MDX */\n components?: Record<string, Component>;\n}\n\n// =============================================================================\n// Document Template\n// =============================================================================\n\n/**\n * Raw HTML VNode that can be used directly in JSX or converted to string\n */\nexport class RawHTML {\n public readonly type = \"div\";\n public readonly props: { dangerouslySetInnerHTML: { __html: string } };\n public readonly children: never[] = [];\n\n constructor(public readonly html: string) {\n this.props = { dangerouslySetInnerHTML: { __html: html } };\n }\n\n toString(): string {\n return this.html;\n }\n}\n\nexport interface DocumentProps {\n /** Rendered page content (VNode with toString) */\n children: RawHTML;\n /** Page title */\n title?: string;\n /** Base URL path */\n base: string;\n /** Route path */\n path: string;\n /** Route props */\n props: Record<string, unknown>;\n /** Script tags for islands (as RawHTML) */\n scripts?: RawHTML;\n /** CSS stylesheet paths */\n css?: string[];\n}\n\nexport type DocumentTemplate = (props: DocumentProps) => VNode;\n\n// =============================================================================\n// Plugin System\n// =============================================================================\n\n/**\n * Partial SSG config that a plugin's config() hook can return.\n * Each field has a defined merge strategy:\n * - mdx: merge (concat arrays, merge component objects)\n * - document: override (last writer wins)\n * - routes: concat\n * - collections: concat\n */\nexport interface SSGPluginConfig {\n mdx?: Partial<MDXConfig>;\n document?: DocumentTemplate;\n routes?: RouteConfig[];\n collections?: readonly Collection[];\n}\n\nexport interface SSGPlugin {\n /** Plugin name for identification and debugging */\n name: string;\n\n /** Plugin ordering: 'pre' runs before normal plugins, 'post' runs after */\n enforce?: \"pre\" | \"post\";\n\n /**\n * Modify SSG config before it is resolved.\n * Return partial config to merge.\n * Called in plugin order: enforce:'pre' → normal → enforce:'post' → user config.\n */\n config?(config: SSGConfig): SSGPluginConfig | void;\n\n /** Called after config is fully resolved. Read-only inspection. */\n configResolved?(config: SSGConfig): void;\n\n /** Called before build starts */\n buildStart?(): void | Promise<void>;\n\n /** Called after build completes */\n buildEnd?(result: BuildResult): void | Promise<void>;\n}\n\n// =============================================================================\n// SSG Configuration\n// =============================================================================\n\nexport interface SSGConfig<\n TCollections extends readonly Collection[] = Collection[],\n TRegistry extends Record<string, unknown> = InferCollections<TCollections>,\n> {\n /** Output directory for built files */\n outDir: string;\n /** Root directory for resolving relative paths (defaults to script location) */\n rootDir?: string;\n /** Base URL path */\n base?: string;\n /** Collections to include */\n collections?: TCollections;\n /** Route definitions */\n routes?: RouteConfig<TRegistry>[];\n /** Plugins that contribute remark/rehype plugins and components.\n * Plugin factories may return arrays (Vite-style); nested arrays are flattened. */\n plugins?: (SSGPlugin | SSGPlugin[])[];\n /** MDX configuration (merged after plugins, takes precedence) */\n mdx?: MDXConfig;\n /** Custom document template */\n document?: DocumentTemplate;\n}\n\n// =============================================================================\n// Build State & Result\n// =============================================================================\n\nexport interface BuildState {\n /** Cursor for each collection */\n cursors: Record<string, string>;\n /** Content hash for each page */\n pageHashes: Record<string, string>;\n /** Last build timestamp */\n timestamp: number;\n}\n\nexport interface BuildResult {\n /** New build state for incremental builds */\n state: BuildState;\n /** Paths that were built */\n paths: string[];\n /** Build statistics */\n stats: {\n added: number;\n updated: number;\n deleted: number;\n unchanged: number;\n };\n}\n\nexport interface BuildOptions {\n /** Enable incremental build */\n incremental?: boolean;\n /** Previous build state */\n state?: BuildState;\n /** Only build specific collections */\n collections?: string[];\n}\n\n// =============================================================================\n// Watcher\n// =============================================================================\n\nexport interface WatchOptions {\n /** Callback when rebuild completes */\n onRebuild?: (result: BuildResult) => void;\n /** Callback on error */\n onError?: (error: Error) => void;\n}\n\nexport interface Watcher {\n /** Stop watching */\n close(): void;\n}\n\n// =============================================================================\n// SSG Instance\n// =============================================================================\n\nexport interface SSGInstance<TRegistry extends Record<string, unknown> = Record<string, unknown>> {\n /** Get the root directory for resolving paths */\n getRootDir(): string;\n\n /** Get all entries from a collection */\n getCollection<K extends keyof TRegistry & string>(\n name: K,\n ): Promise<CollectionEntry<TRegistry[K]>[]>;\n\n /** Get a single entry from a collection */\n getEntry<K extends keyof TRegistry & string>(\n name: K,\n id: string,\n ): Promise<CollectionEntry<TRegistry[K]> | null>;\n\n /** Build the static site */\n build(options?: BuildOptions): Promise<BuildResult>;\n\n /** Watch for changes and rebuild */\n watch(options?: WatchOptions): Watcher;\n}\n","import { compile } from \"@mdx-js/mdx\";\nimport type { Pluggable } from \"unified\";\nimport type { VNode } from \"@semajsx/core\";\nimport type { MDXConfig, MDXCompileResult, Heading } from \"./types\";\n\n/**\n * MDX Processor for compiling MDX content to JSX components\n */\nexport class MDXProcessor {\n private config: MDXConfig;\n\n constructor(config: MDXConfig = {}) {\n this.config = config;\n }\n\n /**\n * Compile MDX content to a JSX component\n */\n async compile(\n content: string,\n frontmatter: Record<string, unknown> = {},\n ): Promise<MDXCompileResult> {\n // Extract headings for table of contents\n const headings = this.extractHeadings(content);\n\n // Compile MDX to JavaScript\n const compiled = await compile(content, {\n outputFormat: \"function-body\",\n development: false,\n remarkPlugins: (this.config.remarkPlugins ?? []) as Pluggable[],\n rehypePlugins: (this.config.rehypePlugins ?? []) as Pluggable[],\n // Use SemaJSX runtime\n jsxImportSource: \"semajsx/dom\",\n });\n\n // Dynamic import of JSX runtime\n const jsxRuntime = await import(\"semajsx/dom/jsx-runtime\");\n\n // Create the Content component\n const Content = this.createComponent(\n String(compiled),\n this.config.components ?? {},\n jsxRuntime,\n );\n\n return {\n Content,\n frontmatter,\n headings,\n };\n }\n\n /**\n * Create a component from compiled MDX code\n */\n private createComponent(\n code: string,\n components: Record<string, (props: Record<string, unknown>) => VNode>,\n jsxRuntime: unknown,\n ): (props?: Record<string, unknown>) => VNode {\n // Import JSX runtime\n // This will be resolved at runtime\n return (props: Record<string, unknown> = {}) => {\n try {\n // Create a function from the MDX compiled code\n // MDX compiled code expects jsx runtime in arguments[0]\n // By using new Function() with no parameters, we can pass\n // the runtime via call() and it becomes arguments[0]\n // eslint-disable-next-line @typescript-eslint/no-implied-eval\n const fn = new Function(code + \"\\nreturn MDXContent;\");\n\n // Call with jsxRuntime as arguments[0]\n // MDX returns an object with default export\n const result = fn.call(null, jsxRuntime) as {\n default: (props: Record<string, unknown>) => VNode;\n };\n return result.default({ ...props, components });\n } catch (e) {\n // Fallback for when runtime is not available\n throw new Error(`MDX rendering failed: ${(e as Error).message}`);\n }\n };\n }\n\n /**\n * Extract headings from markdown content\n */\n private extractHeadings(content: string): Heading[] {\n const headings: Heading[] = [];\n const headingRegex = /^(#{1,6})\\s+(.+)$/gm;\n\n let match;\n while ((match = headingRegex.exec(content)) !== null) {\n const hashes = match[1];\n const rawText = match[2];\n if (!hashes || !rawText) continue;\n\n const depth = hashes.length;\n const text = rawText.trim();\n const slug = this.slugify(text);\n\n headings.push({ depth, text, slug });\n }\n\n return headings;\n }\n\n /**\n * Convert text to URL-friendly slug\n */\n private slugify(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .replace(/-+/g, \"-\")\n .trim();\n }\n}\n\n/**\n * Create an MDX processor instance\n */\nexport function createMDXProcessor(config?: MDXConfig): MDXProcessor {\n return new MDXProcessor(config);\n}\n","import { compile } from \"@mdx-js/mdx\";\nimport type { Plugin, PluginOption } from \"vite\";\nimport type { Pluggable } from \"unified\";\n\n/**\n * MDX plugin options\n */\nexport interface MDXPluginOptions {\n /** Remark plugins */\n remarkPlugins?: unknown[];\n /** Rehype plugins */\n rehypePlugins?: unknown[];\n}\n\n/**\n * Vite plugin for MDX support\n * Transforms .mdx files to JSX modules\n */\nexport function viteMDXPlugin(options: MDXPluginOptions = {}): PluginOption {\n const plugin: Plugin = {\n name: \"semajsx-mdx\",\n\n async transform(code: string, id: string) {\n // Only transform .mdx files\n if (!id.endsWith(\".mdx\")) {\n return null;\n }\n\n try {\n // Compile MDX to JavaScript (not JSX)\n const compiled = await compile(code, {\n jsxImportSource: \"semajsx/dom\",\n outputFormat: \"program\",\n development: false,\n remarkPlugins: (options.remarkPlugins ?? []) as Pluggable[],\n rehypePlugins: (options.rehypePlugins ?? []) as Pluggable[],\n });\n\n return {\n code: String(compiled),\n map: null,\n };\n } catch (error) {\n const err = error as Error;\n throw new Error(`MDX compilation failed for ${id}: ${err.message}`);\n }\n },\n };\n\n return plugin;\n}\n","import { mkdir, writeFile, rm } from \"fs/promises\";\nimport { join, dirname, resolve } from \"path\";\nimport { createApp, renderDocument } from \"@semajsx/ssr\";\nimport type { App, RouteContext } from \"@semajsx/ssr\";\nimport { DefaultDocument } from \"./document\";\nimport type { VNode } from \"@semajsx/core\";\nimport {\n RawHTML,\n type SSGConfig,\n type SSGPlugin,\n type SSGInstance,\n type MDXConfig,\n type RouteConfig,\n type DocumentTemplate,\n type Collection,\n type CollectionEntry,\n type BuildOptions,\n type BuildResult,\n type BuildState,\n type WatchOptions,\n type Watcher,\n type DocumentProps,\n type InferCollections,\n} from \"./types\";\nimport { viteMDXPlugin } from \"./mdx\";\n\n/**\n * Sort plugins by enforce order: 'pre' → normal → 'post'\n */\nfunction sortPlugins(plugins: SSGPlugin[]): SSGPlugin[] {\n const pre: SSGPlugin[] = [];\n const normal: SSGPlugin[] = [];\n const post: SSGPlugin[] = [];\n\n for (const plugin of plugins) {\n if (plugin.enforce === \"pre\") pre.push(plugin);\n else if (plugin.enforce === \"post\") post.push(plugin);\n else normal.push(plugin);\n }\n\n return [...pre, ...normal, ...post];\n}\n\n/**\n * Merge a partial MDX config into an existing one.\n */\nfunction mergeMdxConfig(base: MDXConfig, partial: Partial<MDXConfig>): MDXConfig {\n return {\n remarkPlugins: [...(base.remarkPlugins ?? []), ...(partial.remarkPlugins ?? [])],\n rehypePlugins: [...(base.rehypePlugins ?? []), ...(partial.rehypePlugins ?? [])],\n components: { ...base.components, ...partial.components },\n };\n}\n\n/**\n * Resolved config produced by running all plugin config hooks.\n */\ninterface ResolvedPluginConfig {\n mdx: MDXConfig;\n document?: DocumentTemplate;\n routes: RouteConfig[];\n collections: Collection[];\n}\n\n/**\n * Result of plugin resolution: merged config + flat list of all plugins.\n */\ninterface PluginResolutionResult {\n config: ResolvedPluginConfig;\n allPlugins: SSGPlugin[];\n}\n\n/**\n * Run config hooks on all plugins, then merge with user config.\n *\n * Plugin arrays are flattened Vite-style: plugin factories may return\n * `SSGPlugin | SSGPlugin[]`, and the top-level array is `.flat()`-ed\n * before sorting and processing.\n *\n * Merge strategies:\n * - mdx: merge (concat arrays, merge component objects)\n * - document: override (last writer wins, user config last)\n * - routes: concat (plugin routes first, then user routes)\n * - collections: concat (plugin collections first, then user collections)\n */\nfunction resolvePlugins(\n plugins: (SSGPlugin | SSGPlugin[])[],\n config: SSGConfig,\n): PluginResolutionResult {\n // Vite-style: flatten nested arrays from plugin factories\n const flat = plugins.flat();\n const sorted = sortPlugins(flat);\n\n let mdx: MDXConfig = { remarkPlugins: [], rehypePlugins: [], components: {} };\n let document: DocumentTemplate | undefined;\n const routes: RouteConfig[] = [];\n const collections: Collection[] = [];\n\n // Call config hooks in order\n for (const plugin of sorted) {\n if (!plugin.config) continue;\n const partial = plugin.config(config);\n if (!partial) continue;\n\n if (partial.mdx) mdx = mergeMdxConfig(mdx, partial.mdx);\n if (partial.document) document = partial.document;\n if (partial.routes) routes.push(...partial.routes);\n if (partial.collections) collections.push(...partial.collections);\n }\n\n // User config takes precedence (applied last)\n if (config.mdx) mdx = mergeMdxConfig(mdx, config.mdx);\n if (config.document) document = config.document;\n if (config.routes) routes.push(...config.routes);\n if (config.collections) collections.push(...config.collections);\n\n return { config: { mdx, document, routes, collections }, allPlugins: sorted };\n}\n\n/**\n * SSG (Static Site Generator) core class\n * Built on top of server's createApp\n */\nexport class SSG<\n TRegistry extends Record<string, unknown> = Record<string, unknown>,\n> implements SSGInstance<TRegistry> {\n private config: SSGConfig;\n private plugins: SSGPlugin[];\n private rootDir: string;\n private collections: Map<string, Collection>;\n private entriesCache: Map<string, CollectionEntry[]>;\n private app: App;\n private mdxModules: Map<string, string> = new Map();\n\n constructor(config: SSGConfig) {\n // Resolve rootDir\n this.rootDir = config.rootDir ?? process.cwd();\n\n // Run config hooks — merges plugins + user config, collects all plugins (including nested)\n const { config: resolved, allPlugins } = resolvePlugins(config.plugins ?? [], config);\n\n // Store all plugins (including sub-plugins) for lifecycle hooks\n this.plugins = allPlugins;\n\n this.config = {\n base: \"/\",\n ...config,\n mdx: resolved.mdx,\n document: resolved.document,\n routes: resolved.routes,\n collections: resolved.collections,\n outDir: resolve(this.rootDir, config.outDir),\n };\n\n // Call configResolved on all plugins\n for (const plugin of this.plugins) {\n plugin.configResolved?.(this.config);\n }\n this.collections = new Map();\n this.entriesCache = new Map();\n\n // Create App with MDX plugins\n this.app = createApp({\n root: this.rootDir,\n vite: {\n plugins: [\n // Virtual MDX content modules\n {\n name: \"ssg-virtual-mdx\",\n resolveId: (id: string) => {\n if (id.startsWith(\"virtual:mdx:\")) {\n return \"\\0\" + id;\n }\n },\n load: (id: string) => {\n if (id.startsWith(\"\\0virtual:mdx:\")) {\n const mdxId = id.replace(\"\\0virtual:mdx:\", \"\");\n return this.mdxModules.get(mdxId);\n }\n },\n },\n // MDX compiler plugin\n viteMDXPlugin(resolved.mdx),\n ],\n },\n });\n\n // Register collections (from resolved config — plugins + user)\n for (const collection of this.config.collections ?? []) {\n this.collections.set(collection.name, collection);\n }\n }\n\n getRootDir(): string {\n return this.rootDir;\n }\n\n async getCollection<K extends keyof TRegistry & string>(\n name: K,\n ): Promise<CollectionEntry<TRegistry[K]>[]> {\n const collection = this.collections.get(name);\n if (!collection) {\n throw new Error(`Collection \"${name}\" not found`);\n }\n\n if (this.entriesCache.has(name)) {\n return this.entriesCache.get(name) as CollectionEntry<TRegistry[K]>[];\n }\n\n const entries = await collection.source.getEntries();\n\n const validatedEntries = entries.map((entry) => {\n const result = collection.schema.safeParse(entry.data);\n if (!result.success) {\n throw new Error(`Validation error in ${name}/${entry.id}: ${result.error.message}`);\n }\n\n return {\n ...entry,\n data: result.data,\n render: async () => {\n // Get Vite server from app\n const vite = this.app.getViteServer();\n if (!vite) {\n throw new Error(\"Vite server not initialized\");\n }\n\n // Store MDX content as virtual module\n const mdxId = `${name}/${entry.id}.mdx`;\n this.mdxModules.set(mdxId, entry.body);\n\n try {\n // Load module through Vite SSR\n const moduleExports = (await vite.ssrLoadModule(`virtual:mdx:${mdxId}`)) as {\n default: (props: Record<string, unknown>) => unknown;\n };\n\n const Content = (props: Record<string, unknown> = {}): VNode =>\n moduleExports.default({\n ...props,\n components: this.config.mdx?.components ?? {},\n }) as VNode;\n\n return {\n Content,\n headings: this.extractHeadings(entry.body),\n };\n } finally {\n this.mdxModules.delete(mdxId);\n }\n },\n };\n });\n\n this.entriesCache.set(name, validatedEntries);\n return validatedEntries as CollectionEntry<TRegistry[K]>[];\n }\n\n async getEntry<K extends keyof TRegistry & string>(\n name: K,\n id: string,\n ): Promise<CollectionEntry<TRegistry[K]> | null> {\n const entries = await this.getCollection(name);\n return entries.find((e) => e.id === id || e.slug === id) ?? null;\n }\n\n async build(options: BuildOptions = {}): Promise<BuildResult> {\n const { incremental = false, state: prevState } = options;\n const outDir = this.config.outDir;\n\n // Plugin hook: buildStart\n for (const plugin of this.plugins) {\n await plugin.buildStart?.();\n }\n\n // Initialize App (starts Vite)\n await this.app.prepare();\n\n try {\n // Register routes with App\n await this.registerRoutes();\n\n // Build all pages\n const result = await this.buildPages(incremental, prevState, outDir);\n\n // Build islands for client-side hydration\n // Pass renderHtml so the Vite build uses the SSG's Document template\n // instead of a hardcoded minimal HTML template\n const documentTemplate = this.config.document ?? DefaultDocument;\n await this.app.build({\n outDir,\n mode: \"full\",\n minify: true,\n renderHtml: ({ html, css, scripts, title, path: routePath }) => {\n const scriptsHtml = scripts\n .map((s) => `<script type=\"module\" src=\"${s.src}\"></script>`)\n .join(\"\\n\");\n const documentProps: DocumentProps = {\n children: new RawHTML(html),\n title,\n base: this.config.base ?? \"/\",\n path: routePath,\n props: {},\n scripts: scriptsHtml ? new RawHTML(scriptsHtml) : undefined,\n css: css ?? [],\n };\n const documentVNode = documentTemplate(documentProps);\n return renderDocument(documentVNode);\n },\n vite: {\n build: {\n rollupOptions: {\n // Don't externalize for SSG - bundle everything\n external: [],\n },\n },\n },\n });\n\n // Plugin hook: buildEnd\n for (const plugin of this.plugins) {\n await plugin.buildEnd?.(result);\n }\n\n return result;\n } finally {\n await this.app.close();\n }\n }\n\n private async registerRoutes(): Promise<void> {\n const routes = this.config.routes ?? [];\n\n for (const route of routes) {\n if (route.getStaticPaths) {\n // Dynamic route - register each path\n const staticPaths = await route.getStaticPaths(this);\n for (const sp of staticPaths) {\n const path = this.applyParams(route.path, sp.params);\n const props: Record<string, unknown> = { ...sp.props, params: sp.params };\n\n this.app.route(\n path,\n (_context: RouteContext) => {\n return route.component(props);\n },\n { title: props.title as string | undefined },\n );\n }\n } else {\n // Static route\n let props: Record<string, unknown> = {};\n if (typeof route.props === \"function\") {\n props = await route.props(this);\n } else if (route.props) {\n props = route.props;\n }\n\n this.app.route(\n route.path,\n (_context: RouteContext) => {\n return route.component(props);\n },\n { title: props.title as string | undefined },\n );\n }\n }\n }\n\n private async buildPages(\n incremental: boolean,\n prevState: BuildState | undefined,\n outDir: string,\n ): Promise<BuildResult> {\n const state: BuildState = {\n cursors: {},\n pageHashes: {},\n timestamp: Date.now(),\n };\n\n const stats = { added: 0, updated: 0, deleted: 0, unchanged: 0 };\n const builtPaths: string[] = [];\n\n if (!incremental) {\n await rm(outDir, { recursive: true, force: true });\n }\n await mkdir(outDir, { recursive: true });\n\n this.entriesCache.clear();\n\n // Get all paths to build\n const allPaths = await this.generateAllPaths();\n const currentPaths = new Set(allPaths.map((p) => p.path));\n\n // Delete removed pages\n if (incremental && prevState) {\n for (const oldPath of Object.keys(prevState.pageHashes)) {\n if (!currentPaths.has(oldPath)) {\n const filePath = this.pathToFilePath(oldPath);\n try {\n await rm(join(outDir, filePath));\n stats.deleted++;\n } catch {\n // ignore\n }\n }\n }\n }\n\n // Build each page\n for (const { path, props } of allPaths) {\n const html = await this.renderPage(path, props);\n const hash = this.hashContent(html);\n\n const prevHash = prevState?.pageHashes[path];\n const needsWrite = !incremental || !prevHash || prevHash !== hash;\n\n if (needsWrite) {\n const filePath = this.pathToFilePath(path);\n const fullPath = join(outDir, filePath);\n\n await mkdir(dirname(fullPath), { recursive: true });\n await writeFile(fullPath, html);\n\n builtPaths.push(path);\n if (!prevHash) {\n stats.added++;\n } else {\n stats.updated++;\n }\n } else {\n stats.unchanged++;\n }\n\n state.pageHashes[path] = hash;\n }\n\n for (const [name, collection] of this.collections) {\n if (collection.source.getChanges) {\n state.cursors[name] = Date.now().toString();\n }\n }\n\n return { state, paths: builtPaths, stats };\n }\n\n private async renderPage(path: string, props: Record<string, unknown>): Promise<string> {\n // Use App to render the page\n const result = await this.app.render(path);\n\n // Wrap with document template\n const documentProps: DocumentProps = {\n children: new RawHTML(result.html),\n title: props.title as string | undefined,\n base: this.config.base ?? \"/\",\n path,\n props,\n scripts: result.scripts ? new RawHTML(result.scripts) : undefined,\n css: result.css ?? [],\n };\n\n const template = this.config.document ?? DefaultDocument;\n const documentVNode = template(documentProps);\n\n return renderDocument(documentVNode);\n }\n\n private async generateAllPaths(): Promise<\n Array<{ path: string; props: Record<string, unknown> }>\n > {\n const paths: Array<{ path: string; props: Record<string, unknown> }> = [];\n const routes = this.config.routes ?? [];\n\n for (const route of routes) {\n if (route.getStaticPaths) {\n const staticPaths = await route.getStaticPaths(this);\n for (const sp of staticPaths) {\n const path = this.applyParams(route.path, sp.params);\n paths.push({ path, props: { ...sp.props, params: sp.params } });\n }\n } else {\n let props: Record<string, unknown> = {};\n if (typeof route.props === \"function\") {\n props = await route.props(this);\n } else if (route.props) {\n props = route.props;\n }\n paths.push({ path: route.path, props });\n }\n }\n\n return paths;\n }\n\n watch(options: WatchOptions = {}): Watcher {\n const unsubscribers: (() => void)[] = [];\n\n for (const [name, collection] of this.collections) {\n if (collection.source.watch) {\n const unsubscribe = collection.source.watch(async () => {\n try {\n this.entriesCache.delete(name);\n const result = await this.build({ incremental: true });\n options.onRebuild?.(result);\n } catch (error) {\n options.onError?.(error as Error);\n }\n });\n unsubscribers.push(unsubscribe);\n }\n }\n\n return {\n close: () => unsubscribers.forEach((fn) => fn()),\n };\n }\n\n private pathToFilePath(urlPath: string): string {\n if (urlPath === \"/\") return \"index.html\";\n return `${urlPath.slice(1)}/index.html`;\n }\n\n private applyParams(pattern: string, params: Record<string, string>): string {\n let path = pattern;\n for (const [key, value] of Object.entries(params)) {\n path = path.replace(`:${key}`, value);\n }\n return path;\n }\n\n private hashContent(content: string): string {\n let hash = 0;\n for (let i = 0; i < content.length; i++) {\n const char = content.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash;\n }\n return hash.toString(16);\n }\n\n private extractHeadings(content: string): Array<{ depth: number; text: string; slug: string }> {\n const headings: Array<{ depth: number; text: string; slug: string }> = [];\n const regex = /^(#{1,6})\\s+(.+)$/gm;\n\n let match;\n while ((match = regex.exec(content)) !== null) {\n if (!match[1] || !match[2]) continue;\n const depth = match[1].length;\n const text = match[2].trim();\n const slug = text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\");\n headings.push({ depth, text, slug });\n }\n\n return headings;\n }\n}\n\nexport function createSSG<\n const TCollections extends readonly Collection[],\n TRegistry extends Record<string, unknown> = InferCollections<TCollections>,\n>(config: SSGConfig<TCollections, TRegistry>): SSGInstance<TRegistry> {\n return new SSG<TRegistry>(config as unknown as SSGConfig);\n}\n","import type { z } from \"zod\";\nimport type { Collection, CollectionConfig } from \"../types\";\n\n/**\n * Define a collection with schema validation\n */\nexport function defineCollection<T extends z.ZodType>(\n config: CollectionConfig<T>,\n): Collection<z.infer<T>> {\n return {\n name: config.name,\n source: config.source,\n schema: config.schema,\n };\n}\n\nexport type { Collection, CollectionConfig, CollectionEntry } from \"../types\";\n","import type { CollectionSource, CollectionEntry, ChangeSet, WatchCallback } from \"../types\";\n\n/**\n * Base class for collection sources\n */\nexport abstract class BaseSource<T = unknown> implements CollectionSource<T> {\n abstract id: string;\n\n abstract getEntries(): Promise<CollectionEntry<T>[]>;\n\n async getEntry(id: string): Promise<CollectionEntry<T> | null> {\n const entries = await this.getEntries();\n return entries.find((e) => e.id === id) ?? null;\n }\n\n watch?(callback: WatchCallback<T>): () => void;\n\n getChanges?(since: string): Promise<ChangeSet<T>>;\n}\n","import { glob } from \"glob\";\nimport matter from \"gray-matter\";\nimport { readFile, watch as fsWatch } from \"fs/promises\";\nimport { join, relative } from \"path\";\nimport type { CollectionEntry, ChangeSet, WatchCallback } from \"../types\";\nimport type { FileSourceOptions } from \"./types\";\nimport { BaseSource } from \"./base\";\n\n/**\n * File system source for collections\n */\nexport class FileSource<T = unknown> extends BaseSource<T> {\n id: string;\n private directory: string;\n private include: string;\n private shouldWatch: boolean;\n private contentRoot: string;\n\n constructor(options: FileSourceOptions, contentRoot: string = process.cwd()) {\n super();\n this.directory = options.directory;\n this.include = options.include ?? \"**/*.{md,mdx}\";\n this.shouldWatch = options.watch ?? false;\n this.contentRoot = contentRoot;\n this.id = `file:${this.directory}`;\n }\n\n async getEntries(): Promise<CollectionEntry<T>[]> {\n const dir = join(this.contentRoot, this.directory);\n const pattern = join(dir, this.include);\n const files = await glob(pattern);\n\n const entries = await Promise.all(\n files.map(async (filePath) => {\n const content = await readFile(filePath, \"utf-8\");\n const { data, content: body } = matter(content);\n\n const relativePath = relative(dir, filePath);\n const id = relativePath.replace(/\\.(md|mdx)$/, \"\");\n const slug = id.replace(/\\\\/g, \"/\");\n\n return {\n id,\n slug,\n data: data as T,\n body,\n render: async () => {\n // MDX compilation will be handled by the MDX processor\n return {\n Content: () => {\n throw new Error(\"MDX rendering not yet implemented\");\n },\n };\n },\n };\n }),\n );\n\n return entries;\n }\n\n override watch(callback: WatchCallback<T>): () => void {\n if (!this.shouldWatch) {\n return () => {};\n }\n\n const dir = join(this.contentRoot, this.directory);\n const abortController = new AbortController();\n\n const startWatching = async () => {\n try {\n const watcher = fsWatch(dir, {\n recursive: true,\n signal: abortController.signal,\n });\n\n for await (const _event of watcher) {\n // Debounce and collect changes\n const entries = await this.getEntries();\n callback({\n cursor: Date.now().toString(),\n added: [],\n updated: entries,\n deleted: [],\n });\n }\n } catch (err) {\n if ((err as Error).name !== \"AbortError\") {\n throw err;\n }\n }\n };\n\n startWatching();\n\n return () => {\n abortController.abort();\n };\n }\n\n override async getChanges(_since: string): Promise<ChangeSet<T>> {\n // For file source, we do a full reload\n // A more sophisticated implementation could track file mtimes\n const entries = await this.getEntries();\n return {\n cursor: Date.now().toString(),\n added: [],\n updated: entries,\n deleted: [],\n };\n }\n}\n\n/**\n * Create a file system source\n */\nexport function fileSource<T = unknown>(\n options: FileSourceOptions,\n contentRoot?: string,\n): FileSource<T> {\n return new FileSource<T>(options, contentRoot);\n}\n","import { exec } from \"child_process\";\nimport { promisify } from \"util\";\nimport type { CollectionEntry } from \"../types\";\nimport type { GitSourceOptions, GitCommitSourceOptions, GitTagSourceOptions } from \"./types\";\nimport { BaseSource } from \"./base\";\n\nconst execAsync = promisify(exec);\n\n/**\n * Git source for collections\n */\nexport class GitSource<T = unknown> extends BaseSource<T> {\n id: string;\n private options: GitSourceOptions;\n private cwd: string;\n\n constructor(options: GitSourceOptions, cwd: string = process.cwd()) {\n super();\n this.options = options;\n this.cwd = cwd;\n this.id = `git:${options.type}`;\n }\n\n async getEntries(): Promise<CollectionEntry<T>[]> {\n if (this.options.type === \"commits\") {\n return this.getCommits(this.options);\n } else {\n return this.getTags(this.options);\n }\n }\n\n private async getCommits(options: GitCommitSourceOptions): Promise<CollectionEntry<T>[]> {\n const args = [\"log\", \"--format=%H|%s|%an|%ae|%aI\"];\n\n if (options.filter?.since) {\n args.push(`--since=${options.filter.since}`);\n }\n if (options.filter?.until) {\n args.push(`--until=${options.filter.until}`);\n }\n if (options.filter?.author) {\n args.push(`--author=${options.filter.author}`);\n }\n if (options.limit) {\n args.push(`-n`, options.limit.toString());\n }\n if (options.filter?.paths?.length) {\n args.push(\"--\", ...options.filter.paths);\n }\n\n const { stdout } = await execAsync(`git ${args.join(\" \")}`, {\n cwd: this.cwd,\n });\n const lines = stdout.trim().split(\"\\n\").filter(Boolean);\n\n return lines.map((line) => {\n const parts = line.split(\"|\");\n const hash = parts[0] ?? \"\";\n const message = parts[1] ?? \"\";\n const author = parts[2] ?? \"\";\n const email = parts[3] ?? \"\";\n const dateStr = parts[4] ?? \"\";\n\n const data = {\n hash,\n message,\n author,\n email,\n date: new Date(dateStr),\n } as unknown as T;\n\n return {\n id: hash,\n slug: hash.slice(0, 7),\n data,\n body: message,\n render: async () => ({\n Content: () => {\n throw new Error(\"Git commits cannot be rendered as content\");\n },\n }),\n };\n });\n }\n\n private async getTags(options: GitTagSourceOptions): Promise<CollectionEntry<T>[]> {\n const pattern = options.pattern ?? \"*\";\n\n // Get tags with date and message\n const { stdout } = await execAsync(\n `git tag -l \"${pattern}\" --format=\"%(refname:short)|%(objectname:short)|%(creatordate:iso)|%(contents:subject)\"`,\n { cwd: this.cwd },\n );\n\n const lines = stdout.trim().split(\"\\n\").filter(Boolean);\n\n return lines.map((line) => {\n const parts = line.split(\"|\");\n const tag = parts[0] ?? \"\";\n const hash = parts[1] ?? \"\";\n const dateStr = parts[2] ?? \"\";\n const message = parts[3] ?? \"\";\n\n const data = {\n tag,\n hash,\n date: new Date(dateStr),\n message: message || undefined,\n } as unknown as T;\n\n return {\n id: tag,\n slug: tag,\n data,\n body: message,\n render: async () => ({\n Content: () => {\n throw new Error(\"Git tags cannot be rendered as content\");\n },\n }),\n };\n });\n }\n}\n\n/**\n * Create a Git source\n */\nexport function gitSource<T = unknown>(options: GitSourceOptions, cwd?: string): GitSource<T> {\n return new GitSource<T>(options, cwd);\n}\n","import type { CollectionEntry, ChangeSet, WatchCallback } from \"../types\";\nimport type { RemoteSourceOptions, WebhookConfig } from \"./types\";\nimport { BaseSource } from \"./base\";\n\n/**\n * Remote API source for collections\n */\nexport class RemoteSource<T = unknown> extends BaseSource<T> {\n id: string;\n private options: RemoteSourceOptions<T>;\n private pollTimer?: ReturnType<typeof setInterval>;\n\n constructor(options: RemoteSourceOptions<T>, id: string = \"remote\") {\n super();\n this.options = options;\n this.id = `remote:${id}`;\n }\n\n async getEntries(): Promise<CollectionEntry<T>[]> {\n const items = await this.options.fetch();\n\n return items.map((item, index) => {\n const id = (item as Record<string, unknown>).id?.toString() ?? index.toString();\n const slug = (item as Record<string, unknown>).slug?.toString() ?? id;\n\n return {\n id,\n slug,\n data: item,\n body: (item as Record<string, unknown>).content?.toString() ?? \"\",\n render: async () => ({\n Content: () => {\n throw new Error(\"Remote content rendering not yet implemented\");\n },\n }),\n };\n });\n }\n\n override async getEntry(id: string): Promise<CollectionEntry<T> | null> {\n if (this.options.fetchOne) {\n const item = await this.options.fetchOne(id);\n if (!item) return null;\n\n const slug = (item as Record<string, unknown>).slug?.toString() ?? id;\n\n return {\n id,\n slug,\n data: item,\n body: (item as Record<string, unknown>).content?.toString() ?? \"\",\n render: async () => ({\n Content: () => {\n throw new Error(\"Remote content rendering not yet implemented\");\n },\n }),\n };\n }\n\n return super.getEntry(id);\n }\n\n override watch(callback: WatchCallback<T>): () => void {\n if (this.options.pollInterval) {\n this.pollTimer = setInterval(async () => {\n const entries = await this.getEntries();\n callback({\n cursor: Date.now().toString(),\n added: [],\n updated: entries,\n deleted: [],\n });\n }, this.options.pollInterval);\n\n return () => {\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n }\n };\n }\n\n return () => {};\n }\n\n override async getChanges(since: string): Promise<ChangeSet<T>> {\n if (this.options.fetchChanges) {\n return this.options.fetchChanges(since);\n }\n\n // Fallback to full reload\n const entries = await this.getEntries();\n return {\n cursor: Date.now().toString(),\n added: [],\n updated: entries,\n deleted: [],\n };\n }\n\n /**\n * Get webhook configuration for external integration\n */\n getWebhookConfig(): WebhookConfig | undefined {\n return this.options.webhook;\n }\n}\n\n/**\n * Create a remote API source\n */\nexport function remoteSource<T = unknown>(\n options: RemoteSourceOptions<T>,\n id?: string,\n): RemoteSource<T> {\n return new RemoteSource<T>(options, id);\n}\n","import type { CollectionEntry, ChangeSet, WatchCallback } from \"../types\";\nimport type { CustomSourceOptions } from \"./types\";\nimport { BaseSource } from \"./base\";\n\n/**\n * Custom source for collections\n */\nexport class CustomSource<T = unknown> extends BaseSource<T> {\n id: string;\n private options: CustomSourceOptions<T>;\n\n constructor(options: CustomSourceOptions<T>) {\n super();\n this.options = options;\n this.id = options.id;\n }\n\n async getEntries(): Promise<CollectionEntry<T>[]> {\n const items = await this.options.getEntries();\n\n return items.map((item, index) => {\n const id = (item as Record<string, unknown>).id?.toString() ?? index.toString();\n const slug = (item as Record<string, unknown>).slug?.toString() ?? id;\n\n return {\n id,\n slug,\n data: item,\n body: (item as Record<string, unknown>).body?.toString() ?? \"\",\n render: async () => ({\n Content: () => {\n throw new Error(\"Custom content rendering not yet implemented\");\n },\n }),\n };\n });\n }\n\n override async getEntry(id: string): Promise<CollectionEntry<T> | null> {\n if (this.options.getEntry) {\n const item = await this.options.getEntry(id);\n if (!item) return null;\n\n const slug = (item as Record<string, unknown>).slug?.toString() ?? id;\n\n return {\n id,\n slug,\n data: item,\n body: (item as Record<string, unknown>).body?.toString() ?? \"\",\n render: async () => ({\n Content: () => {\n throw new Error(\"Custom content rendering not yet implemented\");\n },\n }),\n };\n }\n\n return super.getEntry(id);\n }\n\n override watch(callback: WatchCallback<T>): () => void {\n if (this.options.watch) {\n return this.options.watch(callback);\n }\n return () => {};\n }\n\n override async getChanges(since: string): Promise<ChangeSet<T>> {\n if (this.options.getChanges) {\n return this.options.getChanges(since);\n }\n\n const entries = await this.getEntries();\n return {\n cursor: Date.now().toString(),\n added: [],\n updated: entries,\n deleted: [],\n };\n }\n}\n\n/**\n * Create a custom source\n */\nexport function createSource<T = unknown>(options: CustomSourceOptions<T>): CustomSource<T> {\n return new CustomSource<T>(options);\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { t as signal } from "./signal-4PgGfydw.mjs";
2
- import { t as computed } from "./computed-BpjqvQu1.mjs";
3
- import { _ as Fragment, c as when, i as jsx, t as createRenderer } from "./src-BlS3Hc-L.mjs";
1
+ import { t as signal } from "./signal-BN8vHXDb.mjs";
2
+ import { t as computed } from "./computed-BidG06Lt.mjs";
3
+ import { c as when, i as jsx, t as createRenderer, v as Fragment } from "./src-DW3tIczg.mjs";
4
4
  import Yoga from "yoga-layout-prebuilt";
5
5
  import ansiEscapes from "ansi-escapes";
6
6
  import stringWidth from "string-width";
@@ -843,4 +843,4 @@ function BlankLine({ count = 1 }) {
843
843
 
844
844
  //#endregion
845
845
  export { renderBackground as C, getChalkColor as E, setText as S, getChalkBgColor as T, getParent as _, setProperty as a, removeChild as b, renderTextElement as c, applyStyle as d, collectText as f, getNextSibling as g, createTextNode as h, ExitHint as i, renderTextNode as l, createElement as m, print as n, setSignalProperty as o, createComment as p, render as r, TerminalRenderer as s, BlankLine as t, appendChild as u, insertBefore as v, renderBorder as w, replaceNode as x, markNodeAsDirty as y };
846
- //# sourceMappingURL=src-DEoBG1zB.mjs.map
846
+ //# sourceMappingURL=src-DUpFNNM_.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"src-DEoBG1zB.mjs","names":[],"sources":["../../terminal/src/utils/colors.ts","../../terminal/src/rendering/box.ts","../../terminal/src/operations.ts","../../terminal/src/rendering/text.ts","../../terminal/src/renderer.ts","../../terminal/src/properties.ts","../../terminal/src/components/ExitHint.tsx","../../terminal/src/render.ts","../../terminal/src/components/BlankLine.tsx"],"sourcesContent":["import chalk, { type ChalkInstance } from \"chalk\";\n\n/**\n * Get a chalk color function by name\n */\nexport function getChalkColor(colorName: string): ChalkInstance {\n const colors: Record<string, ChalkInstance> = {\n black: chalk.black,\n red: chalk.red,\n green: chalk.green,\n yellow: chalk.yellow,\n blue: chalk.blue,\n magenta: chalk.magenta,\n cyan: chalk.cyan,\n white: chalk.white,\n gray: chalk.gray,\n grey: chalk.grey,\n blackBright: chalk.blackBright,\n redBright: chalk.redBright,\n greenBright: chalk.greenBright,\n yellowBright: chalk.yellowBright,\n blueBright: chalk.blueBright,\n magentaBright: chalk.magentaBright,\n cyanBright: chalk.cyanBright,\n whiteBright: chalk.whiteBright,\n };\n return colors[colorName] || chalk;\n}\n\n/**\n * Get a chalk background color function by name\n */\nexport function getChalkBgColor(colorName: string): ChalkInstance {\n const bgColors: Record<string, ChalkInstance> = {\n black: chalk.bgBlack,\n red: chalk.bgRed,\n green: chalk.bgGreen,\n yellow: chalk.bgYellow,\n blue: chalk.bgBlue,\n magenta: chalk.bgMagenta,\n cyan: chalk.bgCyan,\n white: chalk.bgWhite,\n gray: chalk.bgGray,\n grey: chalk.bgGrey,\n blackBright: chalk.bgBlackBright,\n redBright: chalk.bgRedBright,\n greenBright: chalk.bgGreenBright,\n yellowBright: chalk.bgYellowBright,\n blueBright: chalk.bgBlueBright,\n magentaBright: chalk.bgMagentaBright,\n cyanBright: chalk.bgCyanBright,\n whiteBright: chalk.bgWhiteBright,\n };\n return bgColors[colorName] || chalk;\n}\n","import chalk from \"chalk\";\nimport cliBoxes from \"cli-boxes\";\nimport type { TerminalElement } from \"../types\";\nimport { getChalkColor, getChalkBgColor } from \"../utils/colors\";\n\n/**\n * Render a border around an element\n */\nexport function renderBorder(\n node: TerminalElement,\n writeAt: (x: number, y: number, text: string) => void,\n): void {\n const { style, x = 0, y = 0, width = 0, height = 0 } = node;\n const boxStyle = style.border || \"single\";\n\n if (boxStyle === \"none\") return;\n\n const box = cliBoxes[boxStyle] || cliBoxes.single;\n let borderChar = chalk;\n\n if (style.borderColor) {\n borderChar = getChalkColor(style.borderColor);\n }\n\n // Top border\n const topLine = borderChar(box.topLeft + box.top.repeat(Math.max(0, width - 2)) + box.topRight);\n writeAt(x, y, topLine);\n\n // Side borders with spacing\n for (let i = 1; i < height - 1; i++) {\n // Render left border, middle spaces, and right border as a single line\n const middleSpaces = \" \".repeat(Math.max(0, width - 2));\n const sideLine = borderChar(box.left) + middleSpaces + borderChar(box.right);\n writeAt(x, y + i, sideLine);\n }\n\n // Bottom border\n if (height > 1) {\n const bottomLine = borderChar(\n box.bottomLeft + box.bottom.repeat(Math.max(0, width - 2)) + box.bottomRight,\n );\n writeAt(x, y + height - 1, bottomLine);\n }\n}\n\n/**\n * Render background color\n */\nexport function renderBackground(\n node: TerminalElement,\n writeAt: (x: number, y: number, text: string) => void,\n): void {\n const { style, x = 0, y = 0, width = 0, height = 0 } = node;\n\n if (!style.backgroundColor) return;\n\n const bg = getChalkBgColor(style.backgroundColor);\n\n for (let i = 0; i < height; i++) {\n const line = bg(\" \".repeat(width));\n writeAt(x, y + i, line);\n }\n}\n","import Yoga from \"yoga-layout-prebuilt\";\nimport stringWidth from \"string-width\";\nimport wrapAnsi from \"wrap-ansi\";\nimport type { TerminalNode, TerminalElement, TerminalText, TerminalStyle } from \"./types\";\n\n/**\n * Create a terminal element\n */\nexport function createElement(tagName: string): TerminalElement {\n const yogaNode = Yoga.Node.create();\n\n const element: TerminalElement = {\n type: \"element\",\n tagName,\n style: {},\n props: {},\n yogaNode,\n parent: null,\n children: [],\n };\n\n // Set measure function for text containers\n // Text elements need to measure their text content\n if (tagName === \"text\") {\n yogaNode.setMeasureFunc(measureTextNode.bind(null, element));\n }\n\n return element;\n}\n\n/**\n * Create a terminal text node\n * Text nodes don't have yoga nodes - they are pure data containers\n */\nexport function createTextNode(text: string): TerminalText {\n return {\n type: \"text\",\n content: text,\n yogaNode: undefined,\n parent: null,\n children: [],\n };\n}\n\n/**\n * Create a comment (no-op in terminal, returns text node)\n */\nexport function createComment(_text: string): TerminalText {\n return createTextNode(\"\");\n}\n\n/**\n * Append a child to a parent node\n */\nexport function appendChild(parent: TerminalNode, child: TerminalNode): void {\n if (child.parent) {\n removeChild(child);\n }\n\n child.parent = parent;\n parent.children.push(child);\n\n if (parent.yogaNode && child.yogaNode) {\n parent.yogaNode.insertChild(child.yogaNode, parent.yogaNode.getChildCount());\n }\n}\n\n/**\n * Remove a child from its parent\n */\nexport function removeChild(node: TerminalNode): void {\n if (!node.parent) return;\n\n const parent = node.parent;\n const index = parent.children.indexOf(node);\n\n if (index !== -1) {\n parent.children.splice(index, 1);\n\n if (parent.yogaNode && node.yogaNode) {\n parent.yogaNode.removeChild(node.yogaNode);\n }\n }\n\n node.parent = null;\n}\n\n/**\n * Insert a node before another node\n */\nexport function insertBefore(\n parent: TerminalNode,\n newNode: TerminalNode,\n refNode: TerminalNode | null,\n): void {\n if (newNode.parent) {\n removeChild(newNode);\n }\n\n newNode.parent = parent;\n\n if (!refNode) {\n appendChild(parent, newNode);\n return;\n }\n\n const index = parent.children.indexOf(refNode);\n if (index !== -1) {\n parent.children.splice(index, 0, newNode);\n\n if (parent.yogaNode && newNode.yogaNode) {\n parent.yogaNode.insertChild(newNode.yogaNode, index);\n }\n }\n}\n\n/**\n * Replace a node with another node\n */\nexport function replaceNode(oldNode: TerminalNode, newNode: TerminalNode): void {\n const parent = oldNode.parent;\n if (!parent) return;\n\n insertBefore(parent, newNode, oldNode);\n removeChild(oldNode);\n}\n\n/**\n * Set text content of a text node\n */\nexport function setText(node: TerminalNode, text: string): void {\n if (node.type === \"text\") {\n if (node.content === text) {\n return;\n }\n\n node.content = text;\n\n // Text nodes don't have yoga nodes, mark parent as dirty\n if (node.parent) {\n markNodeAsDirty(node.parent);\n }\n }\n}\n\n/**\n * Apply yoga layout styles\n */\nexport function applyStyle(element: TerminalElement, style: Partial<TerminalStyle>): void {\n const { yogaNode } = element;\n if (!yogaNode) return;\n\n element.style = { ...element.style, ...style };\n\n // Flexbox\n if (style.flexDirection) {\n const direction = {\n row: Yoga.FLEX_DIRECTION_ROW,\n column: Yoga.FLEX_DIRECTION_COLUMN,\n \"row-reverse\": Yoga.FLEX_DIRECTION_ROW_REVERSE,\n \"column-reverse\": Yoga.FLEX_DIRECTION_COLUMN_REVERSE,\n }[style.flexDirection];\n yogaNode.setFlexDirection(direction);\n }\n\n if (style.justifyContent) {\n const justify = {\n \"flex-start\": Yoga.JUSTIFY_FLEX_START,\n center: Yoga.JUSTIFY_CENTER,\n \"flex-end\": Yoga.JUSTIFY_FLEX_END,\n \"space-between\": Yoga.JUSTIFY_SPACE_BETWEEN,\n \"space-around\": Yoga.JUSTIFY_SPACE_AROUND,\n }[style.justifyContent];\n yogaNode.setJustifyContent(justify);\n }\n\n if (style.alignItems) {\n const align = {\n \"flex-start\": Yoga.ALIGN_FLEX_START,\n center: Yoga.ALIGN_CENTER,\n \"flex-end\": Yoga.ALIGN_FLEX_END,\n stretch: Yoga.ALIGN_STRETCH,\n }[style.alignItems];\n yogaNode.setAlignItems(align);\n }\n\n // Flex grow/shrink\n if (style.flexGrow !== undefined) {\n yogaNode.setFlexGrow(style.flexGrow);\n }\n if (style.flexShrink !== undefined) {\n yogaNode.setFlexShrink(style.flexShrink);\n }\n\n // Dimensions\n if (style.width !== undefined) {\n if (typeof style.width === \"number\") {\n yogaNode.setWidth(style.width);\n } else if (style.width === \"auto\") {\n yogaNode.setWidthAuto();\n } else if (style.width.endsWith(\"%\")) {\n yogaNode.setWidthPercent(parseFloat(style.width));\n }\n }\n\n if (style.height !== undefined) {\n if (typeof style.height === \"number\") {\n yogaNode.setHeight(style.height);\n } else if (style.height === \"auto\") {\n yogaNode.setHeightAuto();\n } else if (style.height.endsWith(\"%\")) {\n yogaNode.setHeightPercent(parseFloat(style.height));\n }\n }\n\n // Min/Max dimensions\n if (style.minWidth !== undefined) yogaNode.setMinWidth(style.minWidth);\n if (style.minHeight !== undefined) yogaNode.setMinHeight(style.minHeight);\n if (style.maxWidth !== undefined) yogaNode.setMaxWidth(style.maxWidth);\n if (style.maxHeight !== undefined) yogaNode.setMaxHeight(style.maxHeight);\n\n // Margin\n if (style.margin !== undefined) {\n yogaNode.setMargin(Yoga.EDGE_ALL, style.margin);\n }\n // Logical properties (applied before specific edges so they can be overridden)\n if (style.marginInline !== undefined) {\n yogaNode.setMargin(Yoga.EDGE_LEFT, style.marginInline);\n yogaNode.setMargin(Yoga.EDGE_RIGHT, style.marginInline);\n }\n if (style.marginBlock !== undefined) {\n yogaNode.setMargin(Yoga.EDGE_TOP, style.marginBlock);\n yogaNode.setMargin(Yoga.EDGE_BOTTOM, style.marginBlock);\n }\n // Specific edges (override logical properties)\n if (style.marginLeft !== undefined) {\n yogaNode.setMargin(Yoga.EDGE_LEFT, style.marginLeft);\n }\n if (style.marginRight !== undefined) {\n yogaNode.setMargin(Yoga.EDGE_RIGHT, style.marginRight);\n }\n if (style.marginTop !== undefined) {\n yogaNode.setMargin(Yoga.EDGE_TOP, style.marginTop);\n }\n if (style.marginBottom !== undefined) {\n yogaNode.setMargin(Yoga.EDGE_BOTTOM, style.marginBottom);\n }\n\n // Padding\n if (style.padding !== undefined) {\n yogaNode.setPadding(Yoga.EDGE_ALL, style.padding);\n }\n // Logical properties (applied before specific edges so they can be overridden)\n if (style.paddingInline !== undefined) {\n yogaNode.setPadding(Yoga.EDGE_LEFT, style.paddingInline);\n yogaNode.setPadding(Yoga.EDGE_RIGHT, style.paddingInline);\n }\n if (style.paddingBlock !== undefined) {\n yogaNode.setPadding(Yoga.EDGE_TOP, style.paddingBlock);\n yogaNode.setPadding(Yoga.EDGE_BOTTOM, style.paddingBlock);\n }\n // Specific edges (override logical properties)\n if (style.paddingLeft !== undefined) {\n yogaNode.setPadding(Yoga.EDGE_LEFT, style.paddingLeft);\n }\n if (style.paddingRight !== undefined) {\n yogaNode.setPadding(Yoga.EDGE_RIGHT, style.paddingRight);\n }\n if (style.paddingTop !== undefined) {\n yogaNode.setPadding(Yoga.EDGE_TOP, style.paddingTop);\n }\n if (style.paddingBottom !== undefined) {\n yogaNode.setPadding(Yoga.EDGE_BOTTOM, style.paddingBottom);\n }\n}\n\n/**\n * Recursively collect all text content from a node's children\n * Similar to Ink's squashTextNodes\n */\nexport function collectText(node: TerminalNode): string {\n if (node.type === \"text\") {\n return node.content;\n }\n\n let text = \"\";\n for (const child of node.children) {\n text += collectText(child);\n }\n\n return text;\n}\n\n/**\n * Measure text node for Yoga layout\n * This is called by Yoga when calculating layout\n */\nfunction measureTextNode(node: TerminalElement, width: number): { width: number; height: number } {\n // Collect all text from children\n const text = collectText(node);\n\n if (text.length === 0) {\n return { width: 0, height: 0 };\n }\n\n const textWidth = stringWidth(text);\n const lines = text.split(\"\\n\");\n const height = lines.length;\n\n // Text fits within width, no wrapping needed\n if (textWidth <= width) {\n return { width: textWidth, height };\n }\n\n // Edge case: Yoga asking if we can fit in <1px\n if (textWidth >= 1 && width > 0 && width < 1) {\n return { width: textWidth, height };\n }\n\n // Wrap text if it exceeds width\n const wrappedText = wrapAnsi(text, Math.floor(width), {\n hard: true,\n trim: false,\n });\n const wrappedLines = wrappedText.split(\"\\n\");\n const wrappedWidth = Math.max(...wrappedLines.map((line) => stringWidth(line)));\n\n return { width: wrappedWidth, height: wrappedLines.length };\n}\n\n/**\n * Mark a node and its ancestors as dirty for relayout\n */\nexport function markNodeAsDirty(node: TerminalNode): void {\n if (node.yogaNode) {\n node.yogaNode.markDirty();\n return;\n }\n\n // Walk up to find a yoga node\n if (node.parent) {\n markNodeAsDirty(node.parent);\n }\n}\n\n/**\n * Get the parent node of a node\n */\nexport function getParent(node: TerminalNode): TerminalNode | null {\n return node.parent;\n}\n\n/**\n * Get the next sibling of a node\n */\nexport function getNextSibling(node: TerminalNode): TerminalNode | null {\n const parent = node.parent;\n if (!parent) return null;\n\n const index = parent.children.indexOf(node);\n if (index === -1 || index === parent.children.length - 1) {\n return null;\n }\n\n return parent.children[index + 1] || null;\n}\n","import chalk from \"chalk\";\nimport stringWidth from \"string-width\";\nimport sliceAnsi from \"slice-ansi\";\nimport type { TerminalElement, TerminalText } from \"../types\";\nimport { collectText } from \"../operations\";\nimport { getChalkColor } from \"../utils/colors\";\n\n/**\n * Render a text node\n */\nexport function renderTextNode(\n node: TerminalText,\n writeAt: (x: number, y: number, text: string) => void,\n terminalWidth: number,\n): void {\n const { content, x = 0, y = 0, width = 0 } = node;\n\n let text = content;\n const maxWidth = width || terminalWidth - x;\n\n // Truncate if needed\n if (stringWidth(text) > maxWidth) {\n text = sliceAnsi(text, 0, maxWidth);\n }\n\n // Write to buffer at position\n writeAt(x, y, text);\n}\n\n/**\n * Render a text element (collects and styles all text content)\n */\nexport function renderTextElement(\n node: TerminalElement,\n writeAt: (x: number, y: number, text: string) => void,\n): void {\n const { style, x = 0, y = 0 } = node;\n\n const text = collectText(node);\n if (!text) return;\n\n // Apply text styling\n let styledText = text;\n if (style.color) {\n styledText = getChalkColor(style.color)(styledText);\n }\n if (style.bold) {\n styledText = chalk.bold(styledText);\n }\n if (style.italic) {\n styledText = chalk.italic(styledText);\n }\n if (style.underline) {\n styledText = chalk.underline(styledText);\n }\n if (style.strikethrough) {\n styledText = chalk.strikethrough(styledText);\n }\n if (style.dim) {\n styledText = chalk.dim(styledText);\n }\n\n writeAt(x, y, styledText);\n}\n","import Yoga from \"yoga-layout-prebuilt\";\nimport ansiEscapes from \"ansi-escapes\";\nimport stringWidth from \"string-width\";\nimport sliceAnsi from \"slice-ansi\";\nimport type { TerminalNode, TerminalElement, TerminalText, TerminalRoot } from \"./types\";\nimport { renderBorder, renderBackground, renderTextNode, renderTextElement } from \"./rendering\";\n\n/**\n * Terminal renderer instance\n */\nexport class TerminalRenderer {\n private root: TerminalRoot;\n private buffer: string[] = [];\n private previousOutput: string = \"\";\n private lastOutputHeight: number = 0;\n private wasRawMode: boolean = false;\n\n constructor(stream: NodeJS.WriteStream = process.stdout) {\n this.root = {\n type: \"root\",\n stream,\n parent: null,\n children: [],\n yogaNode: Yoga.Node.create(),\n };\n\n // Set root dimensions to terminal size\n const { columns, rows } = stream;\n if (this.root.yogaNode) {\n this.root.yogaNode.setWidth(columns || 80);\n this.root.yogaNode.setHeight(rows || 24);\n // Set default flexbox properties to prevent children from stretching\n this.root.yogaNode.setFlexDirection(Yoga.FLEX_DIRECTION_COLUMN);\n this.root.yogaNode.setAlignItems(Yoga.ALIGN_FLEX_START);\n }\n\n // Enable raw mode to prevent ^C from being displayed\n // Save the previous state so we can restore it\n if (process.stdin.isTTY && process.stdin.setRawMode) {\n this.wasRawMode = process.stdin.isRaw || false;\n if (!this.wasRawMode) {\n process.stdin.setRawMode(true);\n }\n }\n\n // Hide cursor for cleaner rendering\n this.root.stream.write(ansiEscapes.cursorHide);\n }\n\n /**\n * Get the root node\n */\n getRoot(): TerminalRoot {\n return this.root;\n }\n\n /**\n * Render the tree to terminal\n */\n render(): void {\n // Calculate layout\n if (this.root.yogaNode) {\n this.root.yogaNode.calculateLayout(\n this.root.stream.columns || 80,\n this.root.stream.rows || 24,\n Yoga.DIRECTION_LTR,\n );\n }\n\n // Update positions\n this.updatePositions(this.root, 0, 0);\n\n // Render to buffer\n this.buffer = [];\n const height = this.root.stream.rows || 24;\n for (let i = 0; i < height; i++) {\n this.buffer[i] = \"\";\n }\n\n // Render children\n for (const child of this.root.children) {\n this.renderNode(child);\n }\n\n // Output to terminal\n this.output();\n }\n\n /**\n * Update positions of nodes based on yoga layout\n */\n private updatePositions(node: TerminalNode, parentX: number, parentY: number): void {\n if (node.yogaNode) {\n node.x = Math.round(parentX + node.yogaNode.getComputedLeft());\n node.y = Math.round(parentY + node.yogaNode.getComputedTop());\n node.width = Math.round(node.yogaNode.getComputedWidth());\n node.height = Math.round(node.yogaNode.getComputedHeight());\n\n // If this element has a border, add border size to width/height\n // and offset children by 1 to account for border thickness\n let childOffsetX = node.x || 0;\n let childOffsetY = node.y || 0;\n\n if (node.type === \"element\" && node.style.border && node.style.border !== \"none\") {\n // Expand width and height to include border (1 char on each side)\n node.width += 2;\n node.height += 2;\n // Offset children by 1 to leave room for border\n childOffsetX += 1;\n childOffsetY += 1;\n }\n\n for (const child of node.children) {\n this.updatePositions(child, childOffsetX, childOffsetY);\n }\n } else {\n // Text nodes don't have yoga nodes - inherit parent position\n node.x = parentX;\n node.y = parentY;\n\n for (const child of node.children) {\n this.updatePositions(child, parentX, parentY);\n }\n }\n }\n\n /**\n * Render a single node\n */\n private renderNode(node: TerminalNode): void {\n if (node.type === \"text\") {\n this.renderText(node);\n } else if (node.type === \"element\") {\n this.renderElement(node);\n }\n }\n\n /**\n * Render a text node\n */\n private renderText(node: TerminalText): void {\n const { y = 0, x = 0 } = node;\n\n if (y >= this.buffer.length || x < 0) return;\n\n renderTextNode(node, this.writeAt.bind(this), this.root.stream.columns || 80);\n }\n\n /**\n * Render an element node\n */\n private renderElement(node: TerminalElement): void {\n const { style, tagName } = node;\n\n // Render border if specified\n if (style.border && style.border !== \"none\") {\n renderBorder(node, this.writeAt.bind(this));\n }\n\n // Render background\n if (style.backgroundColor) {\n renderBackground(node, this.writeAt.bind(this));\n }\n\n // For text elements, collect and render all text content at once\n if (tagName === \"text\") {\n renderTextElement(node, this.writeAt.bind(this));\n } else {\n // For other elements, render children normally\n for (const child of node.children) {\n this.renderNode(child);\n }\n }\n }\n\n /**\n * Write text at a specific position in the buffer\n */\n private writeAt(x: number, y: number, text: string): void {\n if (y < 0 || y >= this.buffer.length) return;\n\n const row = this.buffer[y] || \"\";\n const width = stringWidth(text);\n const rowWidth = stringWidth(row);\n\n // Pad row if needed\n if (rowWidth < x) {\n this.buffer[y] = row + \" \".repeat(x - rowWidth) + text;\n } else {\n // Replace characters at position\n this.buffer[y] = sliceAnsi(row, 0, x) + text + sliceAnsi(row, x + width);\n }\n }\n\n /**\n * Output the buffer to terminal\n */\n private output(): void {\n // Remove trailing empty lines from buffer (only output actual content)\n let lastNonEmptyIndex = -1;\n for (let i = this.buffer.length - 1; i >= 0; i--) {\n const line = this.buffer[i];\n if (line && line.trim() !== \"\") {\n lastNonEmptyIndex = i;\n break;\n }\n }\n\n // Get only the lines with content\n const contentLines = lastNonEmptyIndex >= 0 ? this.buffer.slice(0, lastNonEmptyIndex + 1) : [];\n const output = contentLines.join(\"\\n\");\n\n // Only update if changed\n if (output !== this.previousOutput) {\n // Erase previous output if there was any\n // eraseLines moves cursor up and erases, so we're ready to write at the start position\n if (this.lastOutputHeight > 0) {\n this.root.stream.write(ansiEscapes.eraseLines(this.lastOutputHeight));\n }\n\n // Write new output (cursor is already at the right position after eraseLines)\n this.root.stream.write(output);\n\n // Calculate and store output height (number of actual lines rendered)\n this.lastOutputHeight = contentLines.length;\n this.previousOutput = output;\n }\n }\n\n /**\n * Clear the rendered output\n */\n clear(): void {\n if (this.lastOutputHeight > 0) {\n this.root.stream.write(ansiEscapes.eraseLines(this.lastOutputHeight));\n this.lastOutputHeight = 0;\n }\n this.previousOutput = \"\";\n }\n\n /**\n * Cleanup\n */\n destroy(): void {\n // Final render to ensure latest state is shown\n this.render();\n\n // Move cursor to line after output\n this.root.stream.write(\"\\n\");\n\n // Show cursor again on cleanup\n this.root.stream.write(ansiEscapes.cursorShow);\n\n // Restore raw mode to previous state\n if (process.stdin.isTTY && process.stdin.setRawMode && !this.wasRawMode) {\n process.stdin.setRawMode(false);\n }\n\n // Free yoga layout\n if (this.root.yogaNode) {\n this.root.yogaNode.freeRecursive();\n }\n }\n}\n","import type { Signal } from \"@semajsx/signal\";\nimport type { TerminalNode, TerminalStyle } from \"./types\";\nimport { applyStyle } from \"./operations\";\n\n/**\n * Set a property on a terminal node\n */\nexport function setProperty(node: TerminalNode, key: string, value: unknown): void {\n if (node.type !== \"element\") return;\n\n // Handle style properties\n if (isStyleProperty(key)) {\n const styleUpdate: Partial<TerminalStyle> = { [key]: value };\n applyStyle(node, styleUpdate);\n return;\n }\n\n // Store other props\n node.props[key] = value;\n}\n\n/**\n * Set a signal property on a terminal node\n */\nexport function setSignalProperty<T = unknown>(\n node: TerminalNode,\n key: string,\n signal: Signal<T>,\n): () => void {\n // Set initial value\n setProperty(node, key, signal.value);\n\n // Subscribe to changes\n return signal.subscribe((value: T) => {\n setProperty(node, key, value);\n });\n}\n\n/**\n * Check if a property is a style property\n */\nfunction isStyleProperty(key: string): boolean {\n const styleProps = new Set([\n \"flexDirection\",\n \"justifyContent\",\n \"alignItems\",\n \"flexGrow\",\n \"flexShrink\",\n \"flexBasis\",\n \"width\",\n \"height\",\n \"minWidth\",\n \"minHeight\",\n \"maxWidth\",\n \"maxHeight\",\n \"margin\",\n \"marginLeft\",\n \"marginRight\",\n \"marginTop\",\n \"marginBottom\",\n \"marginInline\",\n \"marginBlock\",\n \"padding\",\n \"paddingLeft\",\n \"paddingRight\",\n \"paddingTop\",\n \"paddingBottom\",\n \"paddingInline\",\n \"paddingBlock\",\n \"border\",\n \"borderColor\",\n \"color\",\n \"backgroundColor\",\n \"bold\",\n \"italic\",\n \"underline\",\n \"strikethrough\",\n \"dim\",\n ]);\n\n return styleProps.has(key);\n}\n","/** @jsxImportSource @semajsx/terminal */\nimport { computed, signal, type WritableSignal } from \"@semajsx/signal\";\nimport { when, type JSXNode } from \"@semajsx/core\";\n\n/**\n * Global exiting signal for terminal rendering\n * Set to true during unmount to hide exit hints in final render\n */\nconst globalExitingSignal = signal(false);\n\n/**\n * Get the global exiting signal\n * Used internally by render() to coordinate with ExitHint component\n */\nexport function getExitingSignal(): WritableSignal<boolean> {\n return globalExitingSignal;\n}\n\n/**\n * Reset the exiting signal (useful for testing or multiple render cycles)\n */\nexport function resetExitingSignal(): void {\n globalExitingSignal.value = false;\n}\n\nexport interface ExitHintProps {\n /**\n * Content to show during normal rendering, hidden during exit\n * After normalization: single VNode, array of VNodes, or undefined\n */\n children?: JSXNode;\n}\n\n/**\n * ExitHint component - hides its children during the final render before exit\n *\n * This is useful for hiding \"Press Ctrl+C to exit\" messages in the final\n * terminal output, keeping only the actual content visible.\n *\n * The component automatically detects when unmount() is called and reactively\n * hides its children during the final render using signal-based reactivity.\n *\n * @example\n * ```tsx\n * render(\n * <box flexDirection=\"column\" padding={1}>\n * <text bold>Counter: {count}</text>\n *\n * <ExitHint>\n * <text dim marginTop={1} color=\"yellow\">\n * Press Ctrl+C or ESC to exit\n * </text>\n * </ExitHint>\n * </box>\n * );\n * ```\n *\n * Result after exit:\n * - The counter remains visible\n * - The exit hint is hidden from final output\n */\nexport function ExitHint({ children }: ExitHintProps): JSXNode {\n // Create inverted signal: show when NOT exiting\n const shouldShow = computed(globalExitingSignal, (isExiting) => !isExiting);\n\n // Return signal directly - renderComponent now handles Signal<VNode>\n return when(shouldShow, children);\n}\n","import type { VNode } from \"@semajsx/core\";\nimport { setProperty, setSignalProperty } from \"./properties\";\nimport {\n appendChild,\n createElement,\n createTextNode,\n createComment,\n removeChild,\n replaceNode,\n insertBefore,\n getParent,\n getNextSibling,\n} from \"./operations\";\nimport { TerminalRenderer } from \"./renderer\";\nimport type { TerminalNode } from \"./types\";\nimport { getExitingSignal, resetExitingSignal } from \"./components/ExitHint\";\nimport { type ContextMap } from \"@semajsx/core\";\nimport { createRenderer, type RenderStrategy } from \"@semajsx/core\";\n\n/**\n * Terminal-specific render strategy (no reuse optimization needed)\n */\nconst terminalStrategy: RenderStrategy<TerminalNode> = {\n createTextNode,\n createComment,\n createElement,\n getParent,\n getNextSibling,\n insertBefore,\n appendChild,\n removeChild,\n replaceNode,\n setProperty,\n setSignalProperty,\n // Terminal doesn't need tryReuseNode optimization\n};\n\n// Create terminal renderer\nconst { renderNode, cleanupSubscriptions } = createRenderer(terminalStrategy);\n\n/**\n * Options for terminal rendering\n */\nexport interface RenderOptions {\n /**\n * Custom renderer instance. If not provided, one will be created automatically.\n */\n renderer?: TerminalRenderer;\n /**\n * Whether to automatically re-render on signal changes.\n * @default true\n */\n autoRender?: boolean;\n /**\n * Target frames per second for auto-rendering.\n * @default 60\n */\n fps?: number;\n /**\n * Output stream to render to. Only used if renderer is not provided.\n * @default process.stdout\n */\n stream?: NodeJS.WriteStream;\n}\n\n/**\n * Return type for render function\n */\nexport interface RenderResult {\n /**\n * Re-render the tree (useful for manual updates)\n */\n rerender: () => void;\n /**\n * Unmount and cleanup\n */\n unmount: () => void;\n /**\n * Wait for all pending async operations\n */\n waitUntilExit: () => Promise<void>;\n}\n\n/**\n * Options for print function\n */\nexport interface PrintOptions {\n /**\n * Output stream to print to.\n * @default process.stdout\n */\n stream?: NodeJS.WriteStream;\n}\n\n/**\n * Render a VNode tree to the terminal\n * Supports sync VNodes, async VNodes (Promise), and streaming VNodes (AsyncIterableIterator)\n *\n * @example\n * // Simple usage (ink-style)\n * const { unmount } = render(<App />);\n *\n * @example\n * // With custom stream\n * render(<App />, { stream: process.stderr });\n *\n * @example\n * // Disable auto-rendering\n * const { rerender } = render(<App />, { autoRender: false });\n * setInterval(rerender, 100);\n *\n * @example\n * // With custom renderer\n * const renderer = new TerminalRenderer(process.stderr);\n * render(<App />, { renderer });\n *\n * @example\n * // Custom FPS\n * render(<App />, { fps: 30 });\n */\nexport function render(element: VNode, options: RenderOptions = {}): RenderResult {\n const { renderer, autoRender = true, fps = 60, stream: outputStream = process.stdout } = options;\n\n // Reset exiting signal for new render\n resetExitingSignal();\n\n // Auto-create renderer if not provided (ink-style API)\n const autoCreated = !renderer;\n const actualRenderer = renderer || new TerminalRenderer(outputStream);\n\n const root = actualRenderer.getRoot();\n\n // Initialize empty context map for root render\n const initialContext: ContextMap = new Map();\n\n const rendered = renderNode(element, initialContext);\n\n if (rendered.node) {\n appendChild(root, rendered.node);\n } else if (rendered.children.length > 0) {\n // Fragment case - append all fragment children\n for (const child of rendered.children) {\n if (child.node) {\n appendChild(root, child.node);\n }\n }\n }\n\n // Initial render\n actualRenderer.render();\n\n // Rendering lock to prevent overlapping renders (race condition fix)\n let isRendering = false;\n const safeRender = () => {\n if (isRendering) return;\n isRendering = true;\n try {\n actualRenderer.render();\n } finally {\n isRendering = false;\n }\n };\n\n // Auto re-render on signal changes (like ink)\n let renderInterval: NodeJS.Timeout | null = null;\n if (autoRender) {\n const interval = Math.floor(1000 / fps);\n renderInterval = setInterval(() => {\n safeRender();\n }, interval);\n }\n\n // Promise that resolves on exit\n let exitResolver: (() => void) | null = null;\n const exitPromise = new Promise<void>((resolve) => {\n exitResolver = resolve;\n });\n\n // Track original terminal state for cleanup\n const originalRawMode = process.stdin.isTTY && process.stdin.isRaw;\n let handleExit: (() => void) | null = null;\n let handleKeypress: ((data: Buffer) => void) | null = null;\n\n // Cleanup function to restore terminal state\n const cleanup = () => {\n // Stop auto-rendering\n if (renderInterval) {\n clearInterval(renderInterval);\n renderInterval = null;\n }\n\n // Remove event listeners\n if (handleExit) {\n process.removeListener(\"SIGINT\", handleExit);\n process.removeListener(\"SIGTERM\", handleExit);\n }\n if (handleKeypress) {\n process.stdin.removeListener(\"data\", handleKeypress);\n }\n\n // Restore original terminal state\n if (process.stdin.isTTY && process.stdin.setRawMode) {\n try {\n process.stdin.setRawMode(originalRawMode || false);\n } catch {\n // Terminal may already be closed, ignore error\n }\n }\n\n // Clean up subscriptions only (preserve output on exit)\n cleanupSubscriptions(rendered);\n actualRenderer.destroy();\n\n if (exitResolver) {\n exitResolver();\n }\n };\n\n // Unmount function\n const unmount = () => {\n // Mark as exiting to hide ExitHint components\n getExitingSignal().value = true;\n\n // Trigger one final render to apply ExitHint changes\n // This removes exit prompts from the final output\n actualRenderer.render();\n\n cleanup();\n };\n\n // Handle Ctrl+C if auto-created\n if (autoCreated) {\n handleExit = () => {\n unmount();\n process.exit(0);\n };\n\n // Install signal handlers with error recovery\n try {\n process.on(\"SIGINT\", handleExit);\n process.on(\"SIGTERM\", handleExit);\n\n // Enable stdin for keyboard input\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n process.stdin.resume();\n\n handleKeypress = (data: Buffer) => {\n const key = data.toString();\n // Ctrl+C or ESC to exit\n if (key === \"\\u0003\" || key === \"\\u001b\") {\n if (handleExit) {\n handleExit();\n }\n }\n };\n\n process.stdin.on(\"data\", handleKeypress);\n }\n } catch (err) {\n // If setup fails, clean up and rethrow\n cleanup();\n throw err;\n }\n }\n\n return {\n rerender: safeRender,\n unmount,\n waitUntilExit: () => exitPromise,\n };\n}\n\n/**\n * Print a VNode tree to the terminal once (no auto-rendering).\n * Supports sync VNodes, async VNodes (Promise), and streaming VNodes (AsyncIterableIterator)\n *\n * This is a convenience function for one-time rendering scenarios like:\n * - Server startup messages\n * - CLI output\n * - Static terminal displays\n *\n * Unlike `render()`, this function:\n * - Does not subscribe to signal changes\n * - Does not auto-render on updates\n * - Immediately outputs and cleans up\n * - Does not capture keyboard input\n *\n * @example\n * // Simple server output\n * print(\n * <box border=\"round\" borderColor=\"green\" padding={1}>\n * <text bold color=\"green\">Server started!</text>\n * <text>URL: http://localhost:3000</text>\n * </box>\n * );\n *\n * @example\n * // Print to stderr\n * print(<text color=\"red\">Error occurred</text>, { stream: process.stderr });\n *\n * @example\n * // Print a blank line\n * print();\n */\nexport function print(element?: VNode, options: PrintOptions = {}): void {\n const { stream: outputStream = process.stdout } = options;\n\n // If no element provided, just print a blank line\n if (element === undefined) {\n outputStream.write(\"\\n\");\n return;\n }\n\n // Save raw mode state\n const wasRawMode = process.stdin.isTTY && process.stdin.isRaw;\n\n // Create renderer\n const renderer = new TerminalRenderer(outputStream);\n const root = renderer.getRoot();\n\n // Initialize empty context map for root render\n const initialContext: ContextMap = new Map();\n\n // Render the element\n const rendered = renderNode(element, initialContext);\n\n if (rendered.node) {\n appendChild(root, rendered.node);\n } else if (rendered.children.length > 0) {\n // Fragment case - append all fragment children\n for (const child of rendered.children) {\n if (child.node) {\n appendChild(root, child.node);\n }\n }\n }\n\n // Render once\n renderer.render();\n\n // Clean up subscriptions only (don't remove nodes from tree)\n // This keeps the output visible after destroy\n cleanupSubscriptions(rendered);\n\n // Destroy renderer (outputs final result and shows cursor)\n renderer.destroy();\n\n // Restore raw mode if it was enabled\n if (process.stdin.isTTY && process.stdin.setRawMode && wasRawMode) {\n process.stdin.setRawMode(true);\n }\n}\n","/** @jsxImportSource @semajsx/terminal */\nimport type { JSXNode } from \"@semajsx/core\";\n\nexport interface BlankLineProps {\n /**\n * Number of blank lines to render\n * @default 1\n */\n count?: number;\n}\n\n/**\n * BlankLine component - renders one or more empty lines\n *\n * Useful for adding vertical spacing between terminal UI elements.\n *\n * @example\n * ```tsx\n * <box flexDirection=\"column\">\n * <text>First line</text>\n * <BlankLine />\n * <text>After one blank line</text>\n * <BlankLine count={2} />\n * <text>After two blank lines</text>\n * </box>\n * ```\n */\nexport function BlankLine({ count = 1 }: BlankLineProps): JSXNode {\n const lines = Array.from({ length: count }, (_, i) => <text key={i}> </text>);\n\n return <>{lines}</>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"src-DUpFNNM_.mjs","names":[],"sources":["../../terminal/src/utils/colors.ts","../../terminal/src/rendering/box.ts","../../terminal/src/operations.ts","../../terminal/src/rendering/text.ts","../../terminal/src/renderer.ts","../../terminal/src/properties.ts","../../terminal/src/components/ExitHint.tsx","../../terminal/src/render.ts","../../terminal/src/components/BlankLine.tsx"],"sourcesContent":["import chalk, { type ChalkInstance } from \"chalk\";\n\n/**\n * Get a chalk color function by name\n */\nexport function getChalkColor(colorName: string): ChalkInstance {\n const colors: Record<string, ChalkInstance> = {\n black: chalk.black,\n red: chalk.red,\n green: chalk.green,\n yellow: chalk.yellow,\n blue: chalk.blue,\n magenta: chalk.magenta,\n cyan: chalk.cyan,\n white: chalk.white,\n gray: chalk.gray,\n grey: chalk.grey,\n blackBright: chalk.blackBright,\n redBright: chalk.redBright,\n greenBright: chalk.greenBright,\n yellowBright: chalk.yellowBright,\n blueBright: chalk.blueBright,\n magentaBright: chalk.magentaBright,\n cyanBright: chalk.cyanBright,\n whiteBright: chalk.whiteBright,\n };\n return colors[colorName] || chalk;\n}\n\n/**\n * Get a chalk background color function by name\n */\nexport function getChalkBgColor(colorName: string): ChalkInstance {\n const bgColors: Record<string, ChalkInstance> = {\n black: chalk.bgBlack,\n red: chalk.bgRed,\n green: chalk.bgGreen,\n yellow: chalk.bgYellow,\n blue: chalk.bgBlue,\n magenta: chalk.bgMagenta,\n cyan: chalk.bgCyan,\n white: chalk.bgWhite,\n gray: chalk.bgGray,\n grey: chalk.bgGrey,\n blackBright: chalk.bgBlackBright,\n redBright: chalk.bgRedBright,\n greenBright: chalk.bgGreenBright,\n yellowBright: chalk.bgYellowBright,\n blueBright: chalk.bgBlueBright,\n magentaBright: chalk.bgMagentaBright,\n cyanBright: chalk.bgCyanBright,\n whiteBright: chalk.bgWhiteBright,\n };\n return bgColors[colorName] || chalk;\n}\n","import chalk from \"chalk\";\nimport cliBoxes from \"cli-boxes\";\nimport type { TerminalElement } from \"../types\";\nimport { getChalkColor, getChalkBgColor } from \"../utils/colors\";\n\n/**\n * Render a border around an element\n */\nexport function renderBorder(\n node: TerminalElement,\n writeAt: (x: number, y: number, text: string) => void,\n): void {\n const { style, x = 0, y = 0, width = 0, height = 0 } = node;\n const boxStyle = style.border || \"single\";\n\n if (boxStyle === \"none\") return;\n\n const box = cliBoxes[boxStyle] || cliBoxes.single;\n let borderChar = chalk;\n\n if (style.borderColor) {\n borderChar = getChalkColor(style.borderColor);\n }\n\n // Top border\n const topLine = borderChar(box.topLeft + box.top.repeat(Math.max(0, width - 2)) + box.topRight);\n writeAt(x, y, topLine);\n\n // Side borders with spacing\n for (let i = 1; i < height - 1; i++) {\n // Render left border, middle spaces, and right border as a single line\n const middleSpaces = \" \".repeat(Math.max(0, width - 2));\n const sideLine = borderChar(box.left) + middleSpaces + borderChar(box.right);\n writeAt(x, y + i, sideLine);\n }\n\n // Bottom border\n if (height > 1) {\n const bottomLine = borderChar(\n box.bottomLeft + box.bottom.repeat(Math.max(0, width - 2)) + box.bottomRight,\n );\n writeAt(x, y + height - 1, bottomLine);\n }\n}\n\n/**\n * Render background color\n */\nexport function renderBackground(\n node: TerminalElement,\n writeAt: (x: number, y: number, text: string) => void,\n): void {\n const { style, x = 0, y = 0, width = 0, height = 0 } = node;\n\n if (!style.backgroundColor) return;\n\n const bg = getChalkBgColor(style.backgroundColor);\n\n for (let i = 0; i < height; i++) {\n const line = bg(\" \".repeat(width));\n writeAt(x, y + i, line);\n }\n}\n","import Yoga from \"yoga-layout-prebuilt\";\nimport stringWidth from \"string-width\";\nimport wrapAnsi from \"wrap-ansi\";\nimport type { TerminalNode, TerminalElement, TerminalText, TerminalStyle } from \"./types\";\n\n/**\n * Create a terminal element\n */\nexport function createElement(tagName: string): TerminalElement {\n const yogaNode = Yoga.Node.create();\n\n const element: TerminalElement = {\n type: \"element\",\n tagName,\n style: {},\n props: {},\n yogaNode,\n parent: null,\n children: [],\n };\n\n // Set measure function for text containers\n // Text elements need to measure their text content\n if (tagName === \"text\") {\n yogaNode.setMeasureFunc(measureTextNode.bind(null, element));\n }\n\n return element;\n}\n\n/**\n * Create a terminal text node\n * Text nodes don't have yoga nodes - they are pure data containers\n */\nexport function createTextNode(text: string): TerminalText {\n return {\n type: \"text\",\n content: text,\n yogaNode: undefined,\n parent: null,\n children: [],\n };\n}\n\n/**\n * Create a comment (no-op in terminal, returns text node)\n */\nexport function createComment(_text: string): TerminalText {\n return createTextNode(\"\");\n}\n\n/**\n * Append a child to a parent node\n */\nexport function appendChild(parent: TerminalNode, child: TerminalNode): void {\n if (child.parent) {\n removeChild(child);\n }\n\n child.parent = parent;\n parent.children.push(child);\n\n if (parent.yogaNode && child.yogaNode) {\n parent.yogaNode.insertChild(child.yogaNode, parent.yogaNode.getChildCount());\n }\n}\n\n/**\n * Remove a child from its parent\n */\nexport function removeChild(node: TerminalNode): void {\n if (!node.parent) return;\n\n const parent = node.parent;\n const index = parent.children.indexOf(node);\n\n if (index !== -1) {\n parent.children.splice(index, 1);\n\n if (parent.yogaNode && node.yogaNode) {\n parent.yogaNode.removeChild(node.yogaNode);\n }\n }\n\n node.parent = null;\n}\n\n/**\n * Insert a node before another node\n */\nexport function insertBefore(\n parent: TerminalNode,\n newNode: TerminalNode,\n refNode: TerminalNode | null,\n): void {\n if (newNode.parent) {\n removeChild(newNode);\n }\n\n newNode.parent = parent;\n\n if (!refNode) {\n appendChild(parent, newNode);\n return;\n }\n\n const index = parent.children.indexOf(refNode);\n if (index !== -1) {\n parent.children.splice(index, 0, newNode);\n\n if (parent.yogaNode && newNode.yogaNode) {\n parent.yogaNode.insertChild(newNode.yogaNode, index);\n }\n }\n}\n\n/**\n * Replace a node with another node\n */\nexport function replaceNode(oldNode: TerminalNode, newNode: TerminalNode): void {\n const parent = oldNode.parent;\n if (!parent) return;\n\n insertBefore(parent, newNode, oldNode);\n removeChild(oldNode);\n}\n\n/**\n * Set text content of a text node\n */\nexport function setText(node: TerminalNode, text: string): void {\n if (node.type === \"text\") {\n if (node.content === text) {\n return;\n }\n\n node.content = text;\n\n // Text nodes don't have yoga nodes, mark parent as dirty\n if (node.parent) {\n markNodeAsDirty(node.parent);\n }\n }\n}\n\n/**\n * Apply yoga layout styles\n */\nexport function applyStyle(element: TerminalElement, style: Partial<TerminalStyle>): void {\n const { yogaNode } = element;\n if (!yogaNode) return;\n\n element.style = { ...element.style, ...style };\n\n // Flexbox\n if (style.flexDirection) {\n const direction = {\n row: Yoga.FLEX_DIRECTION_ROW,\n column: Yoga.FLEX_DIRECTION_COLUMN,\n \"row-reverse\": Yoga.FLEX_DIRECTION_ROW_REVERSE,\n \"column-reverse\": Yoga.FLEX_DIRECTION_COLUMN_REVERSE,\n }[style.flexDirection];\n yogaNode.setFlexDirection(direction);\n }\n\n if (style.justifyContent) {\n const justify = {\n \"flex-start\": Yoga.JUSTIFY_FLEX_START,\n center: Yoga.JUSTIFY_CENTER,\n \"flex-end\": Yoga.JUSTIFY_FLEX_END,\n \"space-between\": Yoga.JUSTIFY_SPACE_BETWEEN,\n \"space-around\": Yoga.JUSTIFY_SPACE_AROUND,\n }[style.justifyContent];\n yogaNode.setJustifyContent(justify);\n }\n\n if (style.alignItems) {\n const align = {\n \"flex-start\": Yoga.ALIGN_FLEX_START,\n center: Yoga.ALIGN_CENTER,\n \"flex-end\": Yoga.ALIGN_FLEX_END,\n stretch: Yoga.ALIGN_STRETCH,\n }[style.alignItems];\n yogaNode.setAlignItems(align);\n }\n\n // Flex grow/shrink\n if (style.flexGrow !== undefined) {\n yogaNode.setFlexGrow(style.flexGrow);\n }\n if (style.flexShrink !== undefined) {\n yogaNode.setFlexShrink(style.flexShrink);\n }\n\n // Dimensions\n if (style.width !== undefined) {\n if (typeof style.width === \"number\") {\n yogaNode.setWidth(style.width);\n } else if (style.width === \"auto\") {\n yogaNode.setWidthAuto();\n } else if (style.width.endsWith(\"%\")) {\n yogaNode.setWidthPercent(parseFloat(style.width));\n }\n }\n\n if (style.height !== undefined) {\n if (typeof style.height === \"number\") {\n yogaNode.setHeight(style.height);\n } else if (style.height === \"auto\") {\n yogaNode.setHeightAuto();\n } else if (style.height.endsWith(\"%\")) {\n yogaNode.setHeightPercent(parseFloat(style.height));\n }\n }\n\n // Min/Max dimensions\n if (style.minWidth !== undefined) yogaNode.setMinWidth(style.minWidth);\n if (style.minHeight !== undefined) yogaNode.setMinHeight(style.minHeight);\n if (style.maxWidth !== undefined) yogaNode.setMaxWidth(style.maxWidth);\n if (style.maxHeight !== undefined) yogaNode.setMaxHeight(style.maxHeight);\n\n // Margin\n if (style.margin !== undefined) {\n yogaNode.setMargin(Yoga.EDGE_ALL, style.margin);\n }\n // Logical properties (applied before specific edges so they can be overridden)\n if (style.marginInline !== undefined) {\n yogaNode.setMargin(Yoga.EDGE_LEFT, style.marginInline);\n yogaNode.setMargin(Yoga.EDGE_RIGHT, style.marginInline);\n }\n if (style.marginBlock !== undefined) {\n yogaNode.setMargin(Yoga.EDGE_TOP, style.marginBlock);\n yogaNode.setMargin(Yoga.EDGE_BOTTOM, style.marginBlock);\n }\n // Specific edges (override logical properties)\n if (style.marginLeft !== undefined) {\n yogaNode.setMargin(Yoga.EDGE_LEFT, style.marginLeft);\n }\n if (style.marginRight !== undefined) {\n yogaNode.setMargin(Yoga.EDGE_RIGHT, style.marginRight);\n }\n if (style.marginTop !== undefined) {\n yogaNode.setMargin(Yoga.EDGE_TOP, style.marginTop);\n }\n if (style.marginBottom !== undefined) {\n yogaNode.setMargin(Yoga.EDGE_BOTTOM, style.marginBottom);\n }\n\n // Padding\n if (style.padding !== undefined) {\n yogaNode.setPadding(Yoga.EDGE_ALL, style.padding);\n }\n // Logical properties (applied before specific edges so they can be overridden)\n if (style.paddingInline !== undefined) {\n yogaNode.setPadding(Yoga.EDGE_LEFT, style.paddingInline);\n yogaNode.setPadding(Yoga.EDGE_RIGHT, style.paddingInline);\n }\n if (style.paddingBlock !== undefined) {\n yogaNode.setPadding(Yoga.EDGE_TOP, style.paddingBlock);\n yogaNode.setPadding(Yoga.EDGE_BOTTOM, style.paddingBlock);\n }\n // Specific edges (override logical properties)\n if (style.paddingLeft !== undefined) {\n yogaNode.setPadding(Yoga.EDGE_LEFT, style.paddingLeft);\n }\n if (style.paddingRight !== undefined) {\n yogaNode.setPadding(Yoga.EDGE_RIGHT, style.paddingRight);\n }\n if (style.paddingTop !== undefined) {\n yogaNode.setPadding(Yoga.EDGE_TOP, style.paddingTop);\n }\n if (style.paddingBottom !== undefined) {\n yogaNode.setPadding(Yoga.EDGE_BOTTOM, style.paddingBottom);\n }\n}\n\n/**\n * Recursively collect all text content from a node's children\n * Similar to Ink's squashTextNodes\n */\nexport function collectText(node: TerminalNode): string {\n if (node.type === \"text\") {\n return node.content;\n }\n\n let text = \"\";\n for (const child of node.children) {\n text += collectText(child);\n }\n\n return text;\n}\n\n/**\n * Measure text node for Yoga layout\n * This is called by Yoga when calculating layout\n */\nfunction measureTextNode(node: TerminalElement, width: number): { width: number; height: number } {\n // Collect all text from children\n const text = collectText(node);\n\n if (text.length === 0) {\n return { width: 0, height: 0 };\n }\n\n const textWidth = stringWidth(text);\n const lines = text.split(\"\\n\");\n const height = lines.length;\n\n // Text fits within width, no wrapping needed\n if (textWidth <= width) {\n return { width: textWidth, height };\n }\n\n // Edge case: Yoga asking if we can fit in <1px\n if (textWidth >= 1 && width > 0 && width < 1) {\n return { width: textWidth, height };\n }\n\n // Wrap text if it exceeds width\n const wrappedText = wrapAnsi(text, Math.floor(width), {\n hard: true,\n trim: false,\n });\n const wrappedLines = wrappedText.split(\"\\n\");\n const wrappedWidth = Math.max(...wrappedLines.map((line) => stringWidth(line)));\n\n return { width: wrappedWidth, height: wrappedLines.length };\n}\n\n/**\n * Mark a node and its ancestors as dirty for relayout\n */\nexport function markNodeAsDirty(node: TerminalNode): void {\n if (node.yogaNode) {\n node.yogaNode.markDirty();\n return;\n }\n\n // Walk up to find a yoga node\n if (node.parent) {\n markNodeAsDirty(node.parent);\n }\n}\n\n/**\n * Get the parent node of a node\n */\nexport function getParent(node: TerminalNode): TerminalNode | null {\n return node.parent;\n}\n\n/**\n * Get the next sibling of a node\n */\nexport function getNextSibling(node: TerminalNode): TerminalNode | null {\n const parent = node.parent;\n if (!parent) return null;\n\n const index = parent.children.indexOf(node);\n if (index === -1 || index === parent.children.length - 1) {\n return null;\n }\n\n return parent.children[index + 1] || null;\n}\n","import chalk from \"chalk\";\nimport stringWidth from \"string-width\";\nimport sliceAnsi from \"slice-ansi\";\nimport type { TerminalElement, TerminalText } from \"../types\";\nimport { collectText } from \"../operations\";\nimport { getChalkColor } from \"../utils/colors\";\n\n/**\n * Render a text node\n */\nexport function renderTextNode(\n node: TerminalText,\n writeAt: (x: number, y: number, text: string) => void,\n terminalWidth: number,\n): void {\n const { content, x = 0, y = 0, width = 0 } = node;\n\n let text = content;\n const maxWidth = width || terminalWidth - x;\n\n // Truncate if needed\n if (stringWidth(text) > maxWidth) {\n text = sliceAnsi(text, 0, maxWidth);\n }\n\n // Write to buffer at position\n writeAt(x, y, text);\n}\n\n/**\n * Render a text element (collects and styles all text content)\n */\nexport function renderTextElement(\n node: TerminalElement,\n writeAt: (x: number, y: number, text: string) => void,\n): void {\n const { style, x = 0, y = 0 } = node;\n\n const text = collectText(node);\n if (!text) return;\n\n // Apply text styling\n let styledText = text;\n if (style.color) {\n styledText = getChalkColor(style.color)(styledText);\n }\n if (style.bold) {\n styledText = chalk.bold(styledText);\n }\n if (style.italic) {\n styledText = chalk.italic(styledText);\n }\n if (style.underline) {\n styledText = chalk.underline(styledText);\n }\n if (style.strikethrough) {\n styledText = chalk.strikethrough(styledText);\n }\n if (style.dim) {\n styledText = chalk.dim(styledText);\n }\n\n writeAt(x, y, styledText);\n}\n","import Yoga from \"yoga-layout-prebuilt\";\nimport ansiEscapes from \"ansi-escapes\";\nimport stringWidth from \"string-width\";\nimport sliceAnsi from \"slice-ansi\";\nimport type { TerminalNode, TerminalElement, TerminalText, TerminalRoot } from \"./types\";\nimport { renderBorder, renderBackground, renderTextNode, renderTextElement } from \"./rendering\";\n\n/**\n * Terminal renderer instance\n */\nexport class TerminalRenderer {\n private root: TerminalRoot;\n private buffer: string[] = [];\n private previousOutput: string = \"\";\n private lastOutputHeight: number = 0;\n private wasRawMode: boolean = false;\n\n constructor(stream: NodeJS.WriteStream = process.stdout) {\n this.root = {\n type: \"root\",\n stream,\n parent: null,\n children: [],\n yogaNode: Yoga.Node.create(),\n };\n\n // Set root dimensions to terminal size\n const { columns, rows } = stream;\n if (this.root.yogaNode) {\n this.root.yogaNode.setWidth(columns || 80);\n this.root.yogaNode.setHeight(rows || 24);\n // Set default flexbox properties to prevent children from stretching\n this.root.yogaNode.setFlexDirection(Yoga.FLEX_DIRECTION_COLUMN);\n this.root.yogaNode.setAlignItems(Yoga.ALIGN_FLEX_START);\n }\n\n // Enable raw mode to prevent ^C from being displayed\n // Save the previous state so we can restore it\n if (process.stdin.isTTY && process.stdin.setRawMode) {\n this.wasRawMode = process.stdin.isRaw || false;\n if (!this.wasRawMode) {\n process.stdin.setRawMode(true);\n }\n }\n\n // Hide cursor for cleaner rendering\n this.root.stream.write(ansiEscapes.cursorHide);\n }\n\n /**\n * Get the root node\n */\n getRoot(): TerminalRoot {\n return this.root;\n }\n\n /**\n * Render the tree to terminal\n */\n render(): void {\n // Calculate layout\n if (this.root.yogaNode) {\n this.root.yogaNode.calculateLayout(\n this.root.stream.columns || 80,\n this.root.stream.rows || 24,\n Yoga.DIRECTION_LTR,\n );\n }\n\n // Update positions\n this.updatePositions(this.root, 0, 0);\n\n // Render to buffer\n this.buffer = [];\n const height = this.root.stream.rows || 24;\n for (let i = 0; i < height; i++) {\n this.buffer[i] = \"\";\n }\n\n // Render children\n for (const child of this.root.children) {\n this.renderNode(child);\n }\n\n // Output to terminal\n this.output();\n }\n\n /**\n * Update positions of nodes based on yoga layout\n */\n private updatePositions(node: TerminalNode, parentX: number, parentY: number): void {\n if (node.yogaNode) {\n node.x = Math.round(parentX + node.yogaNode.getComputedLeft());\n node.y = Math.round(parentY + node.yogaNode.getComputedTop());\n node.width = Math.round(node.yogaNode.getComputedWidth());\n node.height = Math.round(node.yogaNode.getComputedHeight());\n\n // If this element has a border, add border size to width/height\n // and offset children by 1 to account for border thickness\n let childOffsetX = node.x || 0;\n let childOffsetY = node.y || 0;\n\n if (node.type === \"element\" && node.style.border && node.style.border !== \"none\") {\n // Expand width and height to include border (1 char on each side)\n node.width += 2;\n node.height += 2;\n // Offset children by 1 to leave room for border\n childOffsetX += 1;\n childOffsetY += 1;\n }\n\n for (const child of node.children) {\n this.updatePositions(child, childOffsetX, childOffsetY);\n }\n } else {\n // Text nodes don't have yoga nodes - inherit parent position\n node.x = parentX;\n node.y = parentY;\n\n for (const child of node.children) {\n this.updatePositions(child, parentX, parentY);\n }\n }\n }\n\n /**\n * Render a single node\n */\n private renderNode(node: TerminalNode): void {\n if (node.type === \"text\") {\n this.renderText(node);\n } else if (node.type === \"element\") {\n this.renderElement(node);\n }\n }\n\n /**\n * Render a text node\n */\n private renderText(node: TerminalText): void {\n const { y = 0, x = 0 } = node;\n\n if (y >= this.buffer.length || x < 0) return;\n\n renderTextNode(node, this.writeAt.bind(this), this.root.stream.columns || 80);\n }\n\n /**\n * Render an element node\n */\n private renderElement(node: TerminalElement): void {\n const { style, tagName } = node;\n\n // Render border if specified\n if (style.border && style.border !== \"none\") {\n renderBorder(node, this.writeAt.bind(this));\n }\n\n // Render background\n if (style.backgroundColor) {\n renderBackground(node, this.writeAt.bind(this));\n }\n\n // For text elements, collect and render all text content at once\n if (tagName === \"text\") {\n renderTextElement(node, this.writeAt.bind(this));\n } else {\n // For other elements, render children normally\n for (const child of node.children) {\n this.renderNode(child);\n }\n }\n }\n\n /**\n * Write text at a specific position in the buffer\n */\n private writeAt(x: number, y: number, text: string): void {\n if (y < 0 || y >= this.buffer.length) return;\n\n const row = this.buffer[y] || \"\";\n const width = stringWidth(text);\n const rowWidth = stringWidth(row);\n\n // Pad row if needed\n if (rowWidth < x) {\n this.buffer[y] = row + \" \".repeat(x - rowWidth) + text;\n } else {\n // Replace characters at position\n this.buffer[y] = sliceAnsi(row, 0, x) + text + sliceAnsi(row, x + width);\n }\n }\n\n /**\n * Output the buffer to terminal\n */\n private output(): void {\n // Remove trailing empty lines from buffer (only output actual content)\n let lastNonEmptyIndex = -1;\n for (let i = this.buffer.length - 1; i >= 0; i--) {\n const line = this.buffer[i];\n if (line && line.trim() !== \"\") {\n lastNonEmptyIndex = i;\n break;\n }\n }\n\n // Get only the lines with content\n const contentLines = lastNonEmptyIndex >= 0 ? this.buffer.slice(0, lastNonEmptyIndex + 1) : [];\n const output = contentLines.join(\"\\n\");\n\n // Only update if changed\n if (output !== this.previousOutput) {\n // Erase previous output if there was any\n // eraseLines moves cursor up and erases, so we're ready to write at the start position\n if (this.lastOutputHeight > 0) {\n this.root.stream.write(ansiEscapes.eraseLines(this.lastOutputHeight));\n }\n\n // Write new output (cursor is already at the right position after eraseLines)\n this.root.stream.write(output);\n\n // Calculate and store output height (number of actual lines rendered)\n this.lastOutputHeight = contentLines.length;\n this.previousOutput = output;\n }\n }\n\n /**\n * Clear the rendered output\n */\n clear(): void {\n if (this.lastOutputHeight > 0) {\n this.root.stream.write(ansiEscapes.eraseLines(this.lastOutputHeight));\n this.lastOutputHeight = 0;\n }\n this.previousOutput = \"\";\n }\n\n /**\n * Cleanup\n */\n destroy(): void {\n // Final render to ensure latest state is shown\n this.render();\n\n // Move cursor to line after output\n this.root.stream.write(\"\\n\");\n\n // Show cursor again on cleanup\n this.root.stream.write(ansiEscapes.cursorShow);\n\n // Restore raw mode to previous state\n if (process.stdin.isTTY && process.stdin.setRawMode && !this.wasRawMode) {\n process.stdin.setRawMode(false);\n }\n\n // Free yoga layout\n if (this.root.yogaNode) {\n this.root.yogaNode.freeRecursive();\n }\n }\n}\n","import type { Signal } from \"@semajsx/signal\";\nimport type { TerminalNode, TerminalStyle } from \"./types\";\nimport { applyStyle } from \"./operations\";\n\n/**\n * Set a property on a terminal node\n */\nexport function setProperty(node: TerminalNode, key: string, value: unknown): void {\n if (node.type !== \"element\") return;\n\n // Handle style properties\n if (isStyleProperty(key)) {\n const styleUpdate: Partial<TerminalStyle> = { [key]: value };\n applyStyle(node, styleUpdate);\n return;\n }\n\n // Store other props\n node.props[key] = value;\n}\n\n/**\n * Set a signal property on a terminal node\n */\nexport function setSignalProperty<T = unknown>(\n node: TerminalNode,\n key: string,\n signal: Signal<T>,\n): () => void {\n // Set initial value\n setProperty(node, key, signal.value);\n\n // Subscribe to changes\n return signal.subscribe((value: T) => {\n setProperty(node, key, value);\n });\n}\n\n/**\n * Check if a property is a style property\n */\nfunction isStyleProperty(key: string): boolean {\n const styleProps = new Set([\n \"flexDirection\",\n \"justifyContent\",\n \"alignItems\",\n \"flexGrow\",\n \"flexShrink\",\n \"flexBasis\",\n \"width\",\n \"height\",\n \"minWidth\",\n \"minHeight\",\n \"maxWidth\",\n \"maxHeight\",\n \"margin\",\n \"marginLeft\",\n \"marginRight\",\n \"marginTop\",\n \"marginBottom\",\n \"marginInline\",\n \"marginBlock\",\n \"padding\",\n \"paddingLeft\",\n \"paddingRight\",\n \"paddingTop\",\n \"paddingBottom\",\n \"paddingInline\",\n \"paddingBlock\",\n \"border\",\n \"borderColor\",\n \"color\",\n \"backgroundColor\",\n \"bold\",\n \"italic\",\n \"underline\",\n \"strikethrough\",\n \"dim\",\n ]);\n\n return styleProps.has(key);\n}\n","/** @jsxImportSource @semajsx/terminal */\nimport { computed, signal, type WritableSignal } from \"@semajsx/signal\";\nimport { when, type JSXNode } from \"@semajsx/core\";\n\n/**\n * Global exiting signal for terminal rendering\n * Set to true during unmount to hide exit hints in final render\n */\nconst globalExitingSignal = signal(false);\n\n/**\n * Get the global exiting signal\n * Used internally by render() to coordinate with ExitHint component\n */\nexport function getExitingSignal(): WritableSignal<boolean> {\n return globalExitingSignal;\n}\n\n/**\n * Reset the exiting signal (useful for testing or multiple render cycles)\n */\nexport function resetExitingSignal(): void {\n globalExitingSignal.value = false;\n}\n\nexport interface ExitHintProps {\n /**\n * Content to show during normal rendering, hidden during exit\n * After normalization: single VNode, array of VNodes, or undefined\n */\n children?: JSXNode;\n}\n\n/**\n * ExitHint component - hides its children during the final render before exit\n *\n * This is useful for hiding \"Press Ctrl+C to exit\" messages in the final\n * terminal output, keeping only the actual content visible.\n *\n * The component automatically detects when unmount() is called and reactively\n * hides its children during the final render using signal-based reactivity.\n *\n * @example\n * ```tsx\n * render(\n * <box flexDirection=\"column\" padding={1}>\n * <text bold>Counter: {count}</text>\n *\n * <ExitHint>\n * <text dim marginTop={1} color=\"yellow\">\n * Press Ctrl+C or ESC to exit\n * </text>\n * </ExitHint>\n * </box>\n * );\n * ```\n *\n * Result after exit:\n * - The counter remains visible\n * - The exit hint is hidden from final output\n */\nexport function ExitHint({ children }: ExitHintProps): JSXNode {\n // Create inverted signal: show when NOT exiting\n const shouldShow = computed(globalExitingSignal, (isExiting) => !isExiting);\n\n // Return signal directly - renderComponent now handles Signal<VNode>\n return when(shouldShow, children);\n}\n","import type { VNode } from \"@semajsx/core\";\nimport { setProperty, setSignalProperty } from \"./properties\";\nimport {\n appendChild,\n createElement,\n createTextNode,\n createComment,\n removeChild,\n replaceNode,\n insertBefore,\n getParent,\n getNextSibling,\n} from \"./operations\";\nimport { TerminalRenderer } from \"./renderer\";\nimport type { TerminalNode } from \"./types\";\nimport { getExitingSignal, resetExitingSignal } from \"./components/ExitHint\";\nimport { type ContextMap } from \"@semajsx/core\";\nimport { createRenderer, type RenderStrategy } from \"@semajsx/core\";\n\n/**\n * Terminal-specific render strategy (no reuse optimization needed)\n */\nconst terminalStrategy: RenderStrategy<TerminalNode> = {\n createTextNode,\n createComment,\n createElement,\n getParent,\n getNextSibling,\n insertBefore,\n appendChild,\n removeChild,\n replaceNode,\n setProperty,\n setSignalProperty,\n // Terminal doesn't need tryReuseNode optimization\n};\n\n// Create terminal renderer\nconst { renderNode, cleanupSubscriptions } = createRenderer(terminalStrategy);\n\n/**\n * Options for terminal rendering\n */\nexport interface RenderOptions {\n /**\n * Custom renderer instance. If not provided, one will be created automatically.\n */\n renderer?: TerminalRenderer;\n /**\n * Whether to automatically re-render on signal changes.\n * @default true\n */\n autoRender?: boolean;\n /**\n * Target frames per second for auto-rendering.\n * @default 60\n */\n fps?: number;\n /**\n * Output stream to render to. Only used if renderer is not provided.\n * @default process.stdout\n */\n stream?: NodeJS.WriteStream;\n}\n\n/**\n * Return type for render function\n */\nexport interface RenderResult {\n /**\n * Re-render the tree (useful for manual updates)\n */\n rerender: () => void;\n /**\n * Unmount and cleanup\n */\n unmount: () => void;\n /**\n * Wait for all pending async operations\n */\n waitUntilExit: () => Promise<void>;\n}\n\n/**\n * Options for print function\n */\nexport interface PrintOptions {\n /**\n * Output stream to print to.\n * @default process.stdout\n */\n stream?: NodeJS.WriteStream;\n}\n\n/**\n * Render a VNode tree to the terminal\n * Supports sync VNodes, async VNodes (Promise), and streaming VNodes (AsyncIterableIterator)\n *\n * @example\n * // Simple usage (ink-style)\n * const { unmount } = render(<App />);\n *\n * @example\n * // With custom stream\n * render(<App />, { stream: process.stderr });\n *\n * @example\n * // Disable auto-rendering\n * const { rerender } = render(<App />, { autoRender: false });\n * setInterval(rerender, 100);\n *\n * @example\n * // With custom renderer\n * const renderer = new TerminalRenderer(process.stderr);\n * render(<App />, { renderer });\n *\n * @example\n * // Custom FPS\n * render(<App />, { fps: 30 });\n */\nexport function render(element: VNode, options: RenderOptions = {}): RenderResult {\n const { renderer, autoRender = true, fps = 60, stream: outputStream = process.stdout } = options;\n\n // Reset exiting signal for new render\n resetExitingSignal();\n\n // Auto-create renderer if not provided (ink-style API)\n const autoCreated = !renderer;\n const actualRenderer = renderer || new TerminalRenderer(outputStream);\n\n const root = actualRenderer.getRoot();\n\n // Initialize empty context map for root render\n const initialContext: ContextMap = new Map();\n\n const rendered = renderNode(element, initialContext);\n\n if (rendered.node) {\n appendChild(root, rendered.node);\n } else if (rendered.children.length > 0) {\n // Fragment case - append all fragment children\n for (const child of rendered.children) {\n if (child.node) {\n appendChild(root, child.node);\n }\n }\n }\n\n // Initial render\n actualRenderer.render();\n\n // Rendering lock to prevent overlapping renders (race condition fix)\n let isRendering = false;\n const safeRender = () => {\n if (isRendering) return;\n isRendering = true;\n try {\n actualRenderer.render();\n } finally {\n isRendering = false;\n }\n };\n\n // Auto re-render on signal changes (like ink)\n let renderInterval: NodeJS.Timeout | null = null;\n if (autoRender) {\n const interval = Math.floor(1000 / fps);\n renderInterval = setInterval(() => {\n safeRender();\n }, interval);\n }\n\n // Promise that resolves on exit\n let exitResolver: (() => void) | null = null;\n const exitPromise = new Promise<void>((resolve) => {\n exitResolver = resolve;\n });\n\n // Track original terminal state for cleanup\n const originalRawMode = process.stdin.isTTY && process.stdin.isRaw;\n let handleExit: (() => void) | null = null;\n let handleKeypress: ((data: Buffer) => void) | null = null;\n\n // Cleanup function to restore terminal state\n const cleanup = () => {\n // Stop auto-rendering\n if (renderInterval) {\n clearInterval(renderInterval);\n renderInterval = null;\n }\n\n // Remove event listeners\n if (handleExit) {\n process.removeListener(\"SIGINT\", handleExit);\n process.removeListener(\"SIGTERM\", handleExit);\n }\n if (handleKeypress) {\n process.stdin.removeListener(\"data\", handleKeypress);\n }\n\n // Restore original terminal state\n if (process.stdin.isTTY && process.stdin.setRawMode) {\n try {\n process.stdin.setRawMode(originalRawMode || false);\n } catch {\n // Terminal may already be closed, ignore error\n }\n }\n\n // Clean up subscriptions only (preserve output on exit)\n cleanupSubscriptions(rendered);\n actualRenderer.destroy();\n\n if (exitResolver) {\n exitResolver();\n }\n };\n\n // Unmount function\n const unmount = () => {\n // Mark as exiting to hide ExitHint components\n getExitingSignal().value = true;\n\n // Trigger one final render to apply ExitHint changes\n // This removes exit prompts from the final output\n actualRenderer.render();\n\n cleanup();\n };\n\n // Handle Ctrl+C if auto-created\n if (autoCreated) {\n handleExit = () => {\n unmount();\n process.exit(0);\n };\n\n // Install signal handlers with error recovery\n try {\n process.on(\"SIGINT\", handleExit);\n process.on(\"SIGTERM\", handleExit);\n\n // Enable stdin for keyboard input\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n process.stdin.resume();\n\n handleKeypress = (data: Buffer) => {\n const key = data.toString();\n // Ctrl+C or ESC to exit\n if (key === \"\\u0003\" || key === \"\\u001b\") {\n if (handleExit) {\n handleExit();\n }\n }\n };\n\n process.stdin.on(\"data\", handleKeypress);\n }\n } catch (err) {\n // If setup fails, clean up and rethrow\n cleanup();\n throw err;\n }\n }\n\n return {\n rerender: safeRender,\n unmount,\n waitUntilExit: () => exitPromise,\n };\n}\n\n/**\n * Print a VNode tree to the terminal once (no auto-rendering).\n * Supports sync VNodes, async VNodes (Promise), and streaming VNodes (AsyncIterableIterator)\n *\n * This is a convenience function for one-time rendering scenarios like:\n * - Server startup messages\n * - CLI output\n * - Static terminal displays\n *\n * Unlike `render()`, this function:\n * - Does not subscribe to signal changes\n * - Does not auto-render on updates\n * - Immediately outputs and cleans up\n * - Does not capture keyboard input\n *\n * @example\n * // Simple server output\n * print(\n * <box border=\"round\" borderColor=\"green\" padding={1}>\n * <text bold color=\"green\">Server started!</text>\n * <text>URL: http://localhost:3000</text>\n * </box>\n * );\n *\n * @example\n * // Print to stderr\n * print(<text color=\"red\">Error occurred</text>, { stream: process.stderr });\n *\n * @example\n * // Print a blank line\n * print();\n */\nexport function print(element?: VNode, options: PrintOptions = {}): void {\n const { stream: outputStream = process.stdout } = options;\n\n // If no element provided, just print a blank line\n if (element === undefined) {\n outputStream.write(\"\\n\");\n return;\n }\n\n // Save raw mode state\n const wasRawMode = process.stdin.isTTY && process.stdin.isRaw;\n\n // Create renderer\n const renderer = new TerminalRenderer(outputStream);\n const root = renderer.getRoot();\n\n // Initialize empty context map for root render\n const initialContext: ContextMap = new Map();\n\n // Render the element\n const rendered = renderNode(element, initialContext);\n\n if (rendered.node) {\n appendChild(root, rendered.node);\n } else if (rendered.children.length > 0) {\n // Fragment case - append all fragment children\n for (const child of rendered.children) {\n if (child.node) {\n appendChild(root, child.node);\n }\n }\n }\n\n // Render once\n renderer.render();\n\n // Clean up subscriptions only (don't remove nodes from tree)\n // This keeps the output visible after destroy\n cleanupSubscriptions(rendered);\n\n // Destroy renderer (outputs final result and shows cursor)\n renderer.destroy();\n\n // Restore raw mode if it was enabled\n if (process.stdin.isTTY && process.stdin.setRawMode && wasRawMode) {\n process.stdin.setRawMode(true);\n }\n}\n","/** @jsxImportSource @semajsx/terminal */\nimport type { JSXNode } from \"@semajsx/core\";\n\nexport interface BlankLineProps {\n /**\n * Number of blank lines to render\n * @default 1\n */\n count?: number;\n}\n\n/**\n * BlankLine component - renders one or more empty lines\n *\n * Useful for adding vertical spacing between terminal UI elements.\n *\n * @example\n * ```tsx\n * <box flexDirection=\"column\">\n * <text>First line</text>\n * <BlankLine />\n * <text>After one blank line</text>\n * <BlankLine count={2} />\n * <text>After two blank lines</text>\n * </box>\n * ```\n */\nexport function BlankLine({ count = 1 }: BlankLineProps): JSXNode {\n const lines = Array.from({ length: count }, (_, i) => <text key={i}> </text>);\n\n return <>{lines}</>;\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { t as signal } from "./signal-4PgGfydw.mjs";
2
- import { t as computed } from "./computed-BpjqvQu1.mjs";
3
- import { t as isSignal } from "./utils-BrGmTgfG.mjs";
1
+ import { t as signal } from "./signal-BN8vHXDb.mjs";
2
+ import { t as computed } from "./computed-BidG06Lt.mjs";
3
+ import { t as isSignal } from "./utils-DbTAs943.mjs";
4
4
 
5
5
  //#region ../core/src/types.ts
6
6
  /**
@@ -11,6 +11,11 @@ const Fragment = Symbol.for("semajsx.fragment");
11
11
  * Special VNode type for portal support
12
12
  */
13
13
  const Portal = Symbol.for("semajsx.portal");
14
+ /**
15
+ * Special VNode type for forward support
16
+ * Forward merges its props onto its single child element without producing a DOM node
17
+ */
18
+ const Forward = Symbol.for("semajsx.forward");
14
19
 
15
20
  //#endregion
16
21
  //#region ../core/src/vnode.ts
@@ -335,6 +340,8 @@ function createRenderer(strategy) {
335
340
  if (type === "#signal") return renderSignalNode(vnode, parentContext);
336
341
  if (type === Fragment) return renderFragment(vnode, parentContext);
337
342
  if (type === Portal) return renderPortal(vnode, parentContext);
343
+ if (type === Forward) return renderForward(vnode, parentContext);
344
+ if (type === "#native") return renderNativeNode(vnode);
338
345
  if (typeof type === "function") return renderComponent(vnode, parentContext);
339
346
  if (typeof type === "string") return renderElement(vnode, parentContext);
340
347
  throw new Error(`Unknown VNode type: ${String(type)}`);
@@ -443,6 +450,69 @@ function createRenderer(strategy) {
443
450
  };
444
451
  }
445
452
  /**
453
+ * Merge Forward's props into the child's props
454
+ * - class/className: concatenated (array)
455
+ * - style: merged objects (Forward overrides per-property)
456
+ * - on* events: chained (both handlers run)
457
+ * - other props: Forward overrides child
458
+ */
459
+ function mergeForwardProps(childProps, forwardProps) {
460
+ const merged = { ...childProps };
461
+ for (const [key, value] of Object.entries(forwardProps)) {
462
+ if (key === "key" || key === "children") continue;
463
+ if ((key === "class" || key === "className") && merged[key] != null) merged[key] = [merged[key], value];
464
+ else if (key === "style" && typeof merged[key] === "object" && typeof value === "object") merged[key] = {
465
+ ...merged[key],
466
+ ...value
467
+ };
468
+ else if (key.startsWith("on") && typeof value === "function" && typeof merged[key] === "function") {
469
+ const existing = merged[key];
470
+ merged[key] = (...args) => {
471
+ value(...args);
472
+ existing(...args);
473
+ };
474
+ } else merged[key] = value;
475
+ }
476
+ return merged;
477
+ }
478
+ /**
479
+ * Render a Forward node
480
+ * Forward merges its props onto its single child and renders it directly
481
+ */
482
+ function renderForward(vnode, parentContext) {
483
+ const child = vnode.children[0];
484
+ if (!child || vnode.children.length !== 1) throw new Error("Forward must have exactly one child element");
485
+ const forwardProps = vnode.props || {};
486
+ return renderNode({
487
+ ...child,
488
+ props: mergeForwardProps(child.props || {}, forwardProps)
489
+ }, parentContext);
490
+ }
491
+ /**
492
+ * Render a native node (pre-created element from external libraries)
493
+ * The element is used directly without creating a new one.
494
+ * Additional props are applied via the strategy's setProperty/setSignalProperty.
495
+ */
496
+ function renderNativeNode(vnode) {
497
+ const nativeNode = vnode.props?.__nativeNode;
498
+ if (!nativeNode) throw new Error("Native VNode must have an __nativeNode prop");
499
+ const subscriptions = [];
500
+ const props = vnode.props || {};
501
+ for (const [key, value] of Object.entries(props)) {
502
+ if (key === "__nativeNode" || key === "key" || key === "children") continue;
503
+ if (isSignal(value)) {
504
+ const unsub = strategy.setSignalProperty(nativeNode, key, value);
505
+ subscriptions.push(unsub);
506
+ } else strategy.setProperty(nativeNode, key, value);
507
+ }
508
+ return {
509
+ vnode,
510
+ node: nativeNode,
511
+ subscriptions,
512
+ children: []
513
+ };
514
+ }
515
+ /**
446
516
  * Render a component
447
517
  */
448
518
  function renderComponent(vnode, parentContext) {
@@ -583,5 +653,5 @@ function createRenderer(strategy) {
583
653
  }
584
654
 
585
655
  //#endregion
586
- export { Fragment as _, jsxs as a, when as c, createComponentAPI as d, ISLAND_MARKER as f, isVNode as g, h, jsx as i, Context as l, createTextVNode as m, isAsyncIterator as n, resource as o, createFragment as p, isPromise as r, stream as s, createRenderer as t, context as u, Portal as v };
587
- //# sourceMappingURL=src-BlS3Hc-L.mjs.map
656
+ export { Forward as _, jsxs as a, when as c, createComponentAPI as d, ISLAND_MARKER as f, isVNode as g, h, jsx as i, Context as l, createTextVNode as m, isAsyncIterator as n, resource as o, createFragment as p, isPromise as r, stream as s, createRenderer as t, context as u, Fragment as v, Portal as y };
657
+ //# sourceMappingURL=src-DW3tIczg.mjs.map