diff --git a/examples/hello-world-preact/package.json b/examples/hello-world-preact/package.json
new file mode 100644
index 0000000..1e7504c
--- /dev/null
+++ b/examples/hello-world-preact/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "@examples/hello-world-preact",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "start": "tsx src/index.tsx",
+ "dev": "tsx watch src/index.tsx"
+ },
+ "dependencies": {
+ "@bomb.sh/tty": "^0.7.0",
+ "@clack/ui": "workspace:*",
+ "@clack/ui-preact": "workspace:*",
+ "preact": "11.0.0-beta.2"
+ },
+ "devDependencies": {
+ "@types/node": "^22.20.0",
+ "tsx": "^4.19.0"
+ }
+}
diff --git a/examples/hello-world-preact/src/app.tsx b/examples/hello-world-preact/src/app.tsx
new file mode 100644
index 0000000..9e01941
--- /dev/null
+++ b/examples/hello-world-preact/src/app.tsx
@@ -0,0 +1,45 @@
+// oxlint-disable bombshell-dev/exported-function-async -- Preact components render synchronously.
+import { fixed, grow, percent } from '@bomb.sh/tty';
+import { rgba } from '@clack/ui-preact';
+import type { VNode } from 'preact';
+import { useState } from 'preact/hooks';
+
+/** Render the interactive hello-world example. */
+export function HelloWorld(): VNode {
+ const [say, setSay] = useState('Hello');
+ const [to, setTo] = useState('World');
+
+ return (
+
+
+ {say}, {to}!
+
+
+
+
+ say:
+
+
+ to:
+
+
+
+ setSay(event.value)} />
+ setTo(event.value)} />
+
+
+
+ );
+}
+
+const blue = rgba(0, 0, 238);
+const cyan = rgba(0, 205, 205);
+const gray = rgba(127, 127, 127);
diff --git a/examples/hello-world-preact/src/index.tsx b/examples/hello-world-preact/src/index.tsx
new file mode 100644
index 0000000..ad7a94b
--- /dev/null
+++ b/examples/hello-world-preact/src/index.tsx
@@ -0,0 +1,9 @@
+import { stdin, stdout } from 'node:process';
+import { createUI } from '@clack/ui';
+import { createRoot } from '@clack/ui-preact';
+import { HelloWorld } from './app.tsx';
+
+await using ui = await createUI({ input: stdin, output: stdout });
+const root = createRoot(ui.host.element);
+root.render();
+await ui.main();
diff --git a/examples/hello-world-preact/tsconfig.json b/examples/hello-world-preact/tsconfig.json
new file mode 100644
index 0000000..989f487
--- /dev/null
+++ b/examples/hello-world-preact/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "extends": "@bomb.sh/tools/tsconfig.json",
+ "compilerOptions": {
+ "composite": false,
+ "declaration": false,
+ "declarationMap": false,
+ "jsx": "react-jsx",
+ "jsxImportSource": "@clack/ui-preact",
+ "noEmit": true,
+ "types": ["node"]
+ }
+}
diff --git a/packages/ui-preact/package.json b/packages/ui-preact/package.json
new file mode 100644
index 0000000..085ca8d
--- /dev/null
+++ b/packages/ui-preact/package.json
@@ -0,0 +1,77 @@
+{
+ "name": "@clack/ui-preact",
+ "version": "0.0.0",
+ "keywords": [
+ "bombshell",
+ "cli",
+ "preact"
+ ],
+ "homepage": "https://bomb.sh/docs/ui",
+ "license": "MIT",
+ "author": {
+ "name": "Bombshell",
+ "email": "oss@bomb.sh",
+ "url": "https://bomb.sh"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/bombshell-dev/ui.git"
+ },
+ "type": "module",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.mts",
+ "import": "./dist/index.mjs"
+ },
+ "./jsx-runtime": {
+ "types": "./dist/jsx-runtime.d.mts",
+ "import": "./dist/jsx-runtime.mjs"
+ },
+ "./jsx-dev-runtime": {
+ "types": "./dist/jsx-dev-runtime.d.mts",
+ "import": "./dist/jsx-dev-runtime.mjs"
+ },
+ "./package.json": "./package.json"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "dev": "bsh dev",
+ "build": "bsh build --dts",
+ "format": "bsh format",
+ "lint": "bsh lint",
+ "test": "bsh test"
+ },
+ "dependencies": {
+ "@clack/ui": "workspace:*"
+ },
+ "devDependencies": {
+ "@bomb.sh/tools": "^0.5.4",
+ "@types/node": "^22.20.0",
+ "preact": "11.0.0-beta.2",
+ "vitest": "^4.1.9"
+ },
+ "peerDependencies": {
+ "preact": ">=11.0.0-beta.2 <12"
+ },
+ "devEngines": {
+ "packageManager": {
+ "name": "pnpm",
+ "version": "^10.7.0",
+ "onFail": "error"
+ },
+ "runtime": {
+ "name": "node",
+ "version": "^22.14.0",
+ "onFail": "error"
+ }
+ },
+ "knip": {
+ "entry": [
+ "src/index.ts",
+ "src/jsx-runtime.ts",
+ "src/jsx-dev-runtime.ts"
+ ]
+ }
+}
diff --git a/packages/ui-preact/src/components.ts b/packages/ui-preact/src/components.ts
new file mode 100644
index 0000000..6ac8a3e
--- /dev/null
+++ b/packages/ui-preact/src/components.ts
@@ -0,0 +1,10 @@
+import { type FunctionComponent, createElement } from 'preact';
+import type { BoxProps, TextProps } from './elements.ts';
+
+export type { BoxProps, TextProps } from './elements.ts';
+
+/** A layout container backed by the Host's `box` element. */
+export const Box: FunctionComponent = (props) => createElement('box', props);
+
+/** A styled text scope backed by the Host's `text` element. */
+export const Text: FunctionComponent = (props) => createElement('text', props);
diff --git a/packages/ui-preact/src/elements.test.ts b/packages/ui-preact/src/elements.test.ts
new file mode 100644
index 0000000..e8d1d2b
--- /dev/null
+++ b/packages/ui-preact/src/elements.test.ts
@@ -0,0 +1,47 @@
+import type { HostElements as CoreHostElements, InputEvent } from '@clack/ui';
+import type { ComponentChildren } from 'preact';
+import { describe, expectTypeOf, test } from 'vitest';
+import type { JSX } from './jsx-runtime.ts';
+
+interface PreactProbeEvent {
+ readonly value: number;
+}
+
+interface PreactProbeProps {
+ count: number;
+ onprobe?: (event: PreactProbeEvent) => void;
+}
+
+declare module '@clack/ui/elements' {
+ interface HostElements {
+ '@test/preact-probe': PreactProbeProps;
+ }
+}
+
+describe('Preact host elements', () => {
+ test('maps box and text properties from the core registry', () => {
+ expectTypeOf().toEqualTypeOf<
+ CoreHostElements['box']['layout']
+ >();
+ expectTypeOf().toEqualTypeOf<
+ CoreHostElements['text']['color']
+ >();
+ expectTypeOf().toEqualTypeOf();
+ });
+
+ test('maps lowercase host event properties to Preact casing', () => {
+ expectTypeOf>()
+ .parameter(0)
+ .toEqualTypeOf();
+ expectTypeOf<
+ 'oninput' extends keyof JSX.IntrinsicElements['input'] ? true : false
+ >().toEqualTypeOf();
+ });
+
+ test('derives custom elements from core registry augmentation', () => {
+ expectTypeOf().toEqualTypeOf();
+ expectTypeOf>()
+ .parameter(0)
+ .toEqualTypeOf();
+ });
+});
diff --git a/packages/ui-preact/src/elements.ts b/packages/ui-preact/src/elements.ts
new file mode 100644
index 0000000..46ab385
--- /dev/null
+++ b/packages/ui-preact/src/elements.ts
@@ -0,0 +1,23 @@
+import type { HostElements as CoreHostElements } from '@clack/ui';
+import type { ComponentChildren, Key, Ref } from 'preact';
+import type { ElementHandle } from './facade.ts';
+
+type PreactProperties = {
+ [Name in keyof Properties as Name extends string
+ ? Name extends `on${infer Type}`
+ ? `on${Capitalize}`
+ : Name
+ : Name]: Properties[Name];
+} & {
+ children?: ComponentChildren;
+ key?: Key | null;
+ ref?: Ref;
+};
+
+export type HostElements = {
+ [Name in keyof CoreHostElements]: PreactProperties;
+};
+
+export type BoxProps = HostElements['box'];
+export type InputProps = HostElements['input'];
+export type TextProps = HostElements['text'];
diff --git a/packages/ui-preact/src/facade.ts b/packages/ui-preact/src/facade.ts
new file mode 100644
index 0000000..a6da7a7
--- /dev/null
+++ b/packages/ui-preact/src/facade.ts
@@ -0,0 +1,161 @@
+// oxlint-disable bombshell-dev/exported-function-async -- This is an internal synchronous adapter factory.
+import type { Host } from '@clack/ui';
+import type { HostElement, HostElementChild, HostLiteral } from '@clack/ui/elements';
+import type { HostEventListener, HostEventType } from '@clack/ui/events';
+
+type PreactNode = PreactElementNode | PreactTextNode;
+
+interface HostAttribute {
+ readonly name: string;
+ readonly value: unknown;
+}
+
+export interface ElementHandle {
+ /** The framework-neutral element represented by this Preact host instance. */
+ readonly element: HostElement;
+}
+
+/** Create the DOM-shaped container Preact uses to mutate a Host. */
+export function createContainer(host: Host, element: HostElement): ElementHandle {
+ const document = new HostDocument(host);
+ return document.wrap(element) as PreactElementNode;
+}
+
+const XHTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
+const hostChild = Symbol('host child');
+
+class HostDocument {
+ readonly #instances = new WeakMap();
+ readonly host: Host;
+
+ constructor(host: Host) {
+ this.host = host;
+ }
+
+ createElementNS(_namespace: string | null, name: string): PreactElementNode {
+ return this.wrap(this.host.createElement(name)) as PreactElementNode;
+ }
+
+ createTextNode(content: string): PreactTextNode {
+ return this.wrap(this.host.createLiteral(String(content))) as PreactTextNode;
+ }
+
+ wrap(child: HostElementChild): PreactNode {
+ const existing = this.#instances.get(child);
+ if (existing) return existing;
+
+ const instance =
+ child.type === 'element'
+ ? new PreactElementNode(this, child)
+ : new PreactTextNode(this, child);
+ this.#instances.set(child, instance);
+ return instance;
+ }
+}
+
+abstract class PreactNodeBase {
+ abstract readonly nodeType: number;
+ readonly ownerDocument: HostDocument;
+ readonly [hostChild]: Child;
+
+ constructor(ownerDocument: HostDocument, child: Child) {
+ this.ownerDocument = ownerDocument;
+ this[hostChild] = child;
+ }
+
+ get parentNode(): PreactElementNode | null {
+ const parent = this[hostChild].parent;
+ return parent ? (this.ownerDocument.wrap(parent) as PreactElementNode) : null;
+ }
+
+ get nextSibling(): PreactNode | null {
+ const parent = this[hostChild].parent;
+ if (!parent) return null;
+
+ const index = parent.children.indexOf(this[hostChild]);
+ const sibling = parent.children[index + 1];
+ return sibling ? this.ownerDocument.wrap(sibling) : null;
+ }
+
+ remove(): void {
+ const parent = this[hostChild].parent;
+ if (parent) this.ownerDocument.host.removeChild(parent, this[hostChild]);
+ }
+}
+
+class PreactElementNode extends PreactNodeBase implements ElementHandle {
+ readonly #listeners = new WeakMap<(event: unknown) => void, HostEventListener>();
+ readonly nodeType = 1;
+ readonly namespaceURI = XHTML_NAMESPACE;
+ _children?: unknown;
+
+ get element(): HostElement {
+ return this[hostChild];
+ }
+
+ get localName(): string {
+ return this.element.name;
+ }
+
+ get attributes(): readonly HostAttribute[] {
+ return Object.entries(this.element.properties).map(([name, value]) => ({ name, value }));
+ }
+
+ get childNodes(): PreactNode[] {
+ return this.element.children.map((child) => this.ownerDocument.wrap(child));
+ }
+
+ get firstChild(): PreactNode | null {
+ const child = this.element.children[0];
+ return child ? this.ownerDocument.wrap(child) : null;
+ }
+
+ setAttribute(name: string, value: unknown): void {
+ this.ownerDocument.host.setProperty(this.element, name, value);
+ }
+
+ removeAttribute(name: string): void {
+ this.ownerDocument.host.setProperty(this.element, name, undefined);
+ }
+
+ insertBefore(child: PreactNode, anchor: PreactNode | null): PreactNode {
+ this.ownerDocument.host.insertBefore(this.element, child[hostChild], anchor?.[hostChild]);
+ return child;
+ }
+
+ appendChild(child: PreactNode): PreactNode {
+ return this.insertBefore(child, null);
+ }
+
+ removeChild(child: PreactNode): PreactNode {
+ this.ownerDocument.host.removeChild(this.element, child[hostChild]);
+ return child;
+ }
+
+ addEventListener(type: string, listener: (event: unknown) => void): void {
+ let wrapped = this.#listeners.get(listener);
+ if (!wrapped) {
+ wrapped = (event) => listener.call(this, event);
+ this.#listeners.set(listener, wrapped);
+ }
+ this.ownerDocument.host.addEventListener(this.element, type as HostEventType, wrapped);
+ }
+
+ removeEventListener(type: string, listener: (event: unknown) => void): void {
+ const wrapped = this.#listeners.get(listener);
+ if (!wrapped) return;
+ this.ownerDocument.host.removeEventListener(this.element, type as HostEventType, wrapped);
+ }
+}
+
+class PreactTextNode extends PreactNodeBase {
+ readonly nodeType = 3;
+
+ get data(): string {
+ return this[hostChild].content;
+ }
+
+ set data(content: string) {
+ this.ownerDocument.host.setText(this[hostChild], String(content));
+ }
+}
diff --git a/packages/ui-preact/src/index.ts b/packages/ui-preact/src/index.ts
new file mode 100644
index 0000000..ed85099
--- /dev/null
+++ b/packages/ui-preact/src/index.ts
@@ -0,0 +1,5 @@
+export { rgba } from '@clack/ui';
+export { Box, Text } from './components.ts';
+export type { BoxProps, HostElements, InputProps, TextProps } from './elements.ts';
+export type { ElementHandle } from './facade.ts';
+export { createRoot, type Root } from './root.ts';
diff --git a/packages/ui-preact/src/jsx-dev-runtime.ts b/packages/ui-preact/src/jsx-dev-runtime.ts
new file mode 100644
index 0000000..edb1528
--- /dev/null
+++ b/packages/ui-preact/src/jsx-dev-runtime.ts
@@ -0,0 +1,23 @@
+import type { Component, ComponentType, FunctionComponent, VNode } from 'preact';
+import type { JSX as PreactJSX } from 'preact/jsx-dev-runtime';
+import type { HostElements } from './index.ts';
+
+export { Fragment, jsxDEV } from 'preact/jsx-dev-runtime';
+
+export declare namespace JSX {
+ type LibraryManagedAttributes = PreactJSX.LibraryManagedAttributes<
+ ManagedComponent,
+ Props
+ >;
+ interface IntrinsicAttributes extends PreactJSX.IntrinsicAttributes {}
+ type ElementType = keyof HostElements | ComponentType;
+ interface Element extends VNode {}
+ type ElementClass = Component | FunctionComponent;
+ interface ElementAttributesProperty {
+ props: unknown;
+ }
+ interface ElementChildrenAttribute {
+ children: unknown;
+ }
+ interface IntrinsicElements extends HostElements {}
+}
diff --git a/packages/ui-preact/src/jsx-runtime.ts b/packages/ui-preact/src/jsx-runtime.ts
new file mode 100644
index 0000000..0d13c08
--- /dev/null
+++ b/packages/ui-preact/src/jsx-runtime.ts
@@ -0,0 +1,23 @@
+import type { Component, ComponentType, FunctionComponent, VNode } from 'preact';
+import type { JSX as PreactJSX } from 'preact/jsx-runtime';
+import type { HostElements } from './index.ts';
+
+export { Fragment, jsx, jsxs } from 'preact/jsx-runtime';
+
+export declare namespace JSX {
+ type LibraryManagedAttributes = PreactJSX.LibraryManagedAttributes<
+ ManagedComponent,
+ Props
+ >;
+ interface IntrinsicAttributes extends PreactJSX.IntrinsicAttributes {}
+ type ElementType = keyof HostElements | ComponentType;
+ interface Element extends VNode {}
+ type ElementClass = Component | FunctionComponent;
+ interface ElementAttributesProperty {
+ props: unknown;
+ }
+ interface ElementChildrenAttribute {
+ children: unknown;
+ }
+ type IntrinsicElements = HostElements;
+}
diff --git a/packages/ui-preact/src/root.test.ts b/packages/ui-preact/src/root.test.ts
new file mode 100644
index 0000000..7370f5d
--- /dev/null
+++ b/packages/ui-preact/src/root.test.ts
@@ -0,0 +1,165 @@
+import { HostApi, createHost, type HostElement, type HostLiteral } from '@clack/ui';
+import type { InputEvent } from '@clack/ui';
+import type { HostEventListener } from '@clack/ui/events';
+import { createElement as h, type VNode } from 'preact';
+import { useEffect, useState } from 'preact/hooks';
+import { assert, describe, expect, test, vi } from 'vitest';
+import { Box, Text, createRoot } from './index.ts';
+
+function element(child: HostElement['children'][number]): HostElement {
+ assert(child.type === 'element');
+ return child;
+}
+
+function literal(child: HostElement['children'][number]): HostLiteral {
+ assert(child.type === 'literal');
+ return child;
+}
+
+function item(key: string): VNode<{ key: string; name: string }> {
+ return h('item', { key, name: key });
+}
+
+function Counter(): VNode {
+ const [count, setCount] = useState(0);
+ useEffect(() => setCount(1), []);
+ return h(Text, {}, String(count));
+}
+
+describe('createRoot', () => {
+ test('builds and attaches a Preact subtree through the Host', () => {
+ const host = createHost();
+ const root = createRoot(host.element);
+
+ root.render(h(Box, { layout: { direction: 'ttb' } }, h(Text, { color: 15 }, 'hello')));
+
+ const box = element(host.element.children[0]!);
+ const text = element(box.children[0]!);
+ expect(box.name).toBe('box');
+ expect(box.properties).toEqual({ layout: { direction: 'ttb' } });
+ expect(box.node).not.toBeNull();
+ expect(text.name).toBe('text');
+ expect(text.properties).toEqual({ color: 15 });
+ expect(text.node).not.toBeNull();
+ expect(literal(text.children[0]!)).toMatchObject({ content: 'hello', parent: text });
+ });
+
+ test('mounts within the supplied attached element', () => {
+ const host = createHost();
+ const container = host.createElement('container');
+ host.insertBefore(host.element, container);
+ const root = createRoot(container);
+
+ root.render(h('probe', null));
+
+ expect(root.element).toBe(container);
+ expect(host.element.children).toEqual([container]);
+ expect(element(container.children[0]!)).toMatchObject({ name: 'probe' });
+
+ root.unmount();
+
+ expect(container.children).toEqual([]);
+ expect(container.node).not.toBeNull();
+ });
+
+ test('updates and removes properties and updates text literals', () => {
+ const host = createHost();
+ const root = createRoot(host.element);
+
+ root.render(h('probe', { removable: 'yes', structured: { value: 1 } }, 'first'));
+ const probe = element(host.element.children[0]!);
+ const text = literal(probe.children[0]!);
+
+ root.render(h('probe', { added: 0, structured: { value: 2 } }, 'second'));
+
+ expect(element(host.element.children[0]!)).toBe(probe);
+ expect(probe.properties).toEqual({ added: 0, structured: { value: 2 } });
+ expect(literal(probe.children[0]!)).toBe(text);
+ expect(text.content).toBe('second');
+ });
+
+ test('moves keyed children within the same parent without recreating them', () => {
+ const host = createHost();
+ const root = createRoot(host.element);
+
+ root.render(h('list', {}, item('first'), item('second'), item('third')));
+ const list = element(host.element.children[0]!);
+ const [first, second, third] = list.children;
+
+ root.render(h('list', {}, item('third'), item('first'), item('second')));
+
+ expect(list.children).toEqual([third, first, second]);
+ });
+
+ test('routes element creation through Host middleware', () => {
+ const host = createHost();
+ const createElement = vi.fn();
+ HostApi.decorate(host.root, () => ({
+ createElement(name) {
+ createElement(name);
+ return super.createElement(name);
+ },
+ }));
+ const root = createRoot(host.element);
+
+ root.render(h('custom-element', null));
+
+ expect(createElement).toHaveBeenCalledWith('custom-element');
+ });
+
+ test('delivers Host events through Preact event properties', () => {
+ const host = createHost();
+ let listener: HostEventListener<'input'> | undefined;
+ HostApi.decorate(host.root, () => ({
+ addEventListener(target, type, current) {
+ if (type === 'input') listener = current as HostEventListener<'input'>;
+ return super.addEventListener(target, type, current);
+ },
+ removeEventListener(target, type, current) {
+ if (current === listener) listener = undefined;
+ return super.removeEventListener(target, type, current);
+ },
+ }));
+ const root = createRoot(host.element);
+ const first = vi.fn();
+ const second = vi.fn();
+
+ root.render(h('input', { onInput: first }));
+ const input = element(host.element.children[0]!);
+ const event: InputEvent = { type: 'input', target: input, value: 'hello' };
+ listener!(event);
+
+ expect(first).toHaveBeenCalledWith(event);
+ expect(input.properties).toEqual({});
+
+ root.render(h('input', { onInput: second }));
+ listener!(event);
+
+ expect(first).toHaveBeenCalledTimes(1);
+ expect(second).toHaveBeenCalledWith(event);
+ });
+
+ test('applies updates scheduled by Preact hooks', async () => {
+ const host = createHost();
+ const root = createRoot(host.element);
+
+ root.render(h(Counter, {}));
+
+ const text = element(host.element.children[0]!);
+ await vi.waitFor(() => expect(literal(text.children[0]!).content).toBe('1'));
+ });
+
+ test('unmounts and detaches the Preact subtree', () => {
+ const host = createHost();
+ const root = createRoot(host.element);
+ root.render(h(Box, {}, h(Text, {}, 'bye')));
+ const box = element(host.element.children[0]!);
+ const text = element(box.children[0]!);
+
+ root.unmount();
+
+ expect(host.element.children).toEqual([]);
+ expect(box.node).toBeNull();
+ expect(text.node).toBeNull();
+ });
+});
diff --git a/packages/ui-preact/src/root.ts b/packages/ui-preact/src/root.ts
new file mode 100644
index 0000000..549c8b7
--- /dev/null
+++ b/packages/ui-preact/src/root.ts
@@ -0,0 +1,31 @@
+// oxlint-disable bombshell-dev/exported-function-async -- Preact root creation is synchronous.
+import { HostApi, type Host } from '@clack/ui';
+import type { HostElement } from '@clack/ui/elements';
+import { type ComponentChild, render } from 'preact';
+import { createContainer } from './facade.ts';
+
+export interface Root {
+ readonly element: HostElement;
+ readonly host: Host;
+ /** Reconcile `node` into the Host synchronously. */
+ render(node: ComponentChild): void;
+ /** Remove the Preact tree from the Host synchronously. */
+ unmount(): void;
+}
+
+/** Create a Preact root which reconciles into an attached Host element. */
+export function createRoot(element: HostElement): Root {
+ const host = HostApi.at(element.node!).getHost();
+ const container = createContainer(host, element) as unknown as Element;
+
+ return {
+ element,
+ host,
+ render(node) {
+ render(node, container);
+ },
+ unmount() {
+ render(null, container);
+ },
+ };
+}
diff --git a/packages/ui-preact/tsconfig.json b/packages/ui-preact/tsconfig.json
new file mode 100644
index 0000000..a055ad0
--- /dev/null
+++ b/packages/ui-preact/tsconfig.json
@@ -0,0 +1,6 @@
+{
+ "extends": "@bomb.sh/tools/tsconfig.json",
+ "compilerOptions": {
+ "types": ["node"]
+ }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7e704e7..2995d24 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -56,6 +56,28 @@ importers:
specifier: ^4.19.0
version: 4.23.15
+ examples/hello-world-preact:
+ dependencies:
+ '@bomb.sh/tty':
+ specifier: ^0.7.0
+ version: 0.7.0
+ '@clack/ui':
+ specifier: workspace:*
+ version: link:../../packages/ui
+ '@clack/ui-preact':
+ specifier: workspace:*
+ version: link:../../packages/ui-preact
+ preact:
+ specifier: 11.0.0-beta.2
+ version: 11.0.0-beta.2
+ devDependencies:
+ '@types/node':
+ specifier: ^22.20.0
+ version: 22.20.4
+ tsx:
+ specifier: ^4.19.0
+ version: 4.23.15
+
examples/hello-world-solid:
dependencies:
'@bomb.sh/tty':
@@ -96,6 +118,25 @@ importers:
specifier: 'catalog:'
version: 0.7.0
+ packages/ui-preact:
+ dependencies:
+ '@clack/ui':
+ specifier: workspace:*
+ version: link:../ui
+ devDependencies:
+ '@bomb.sh/tools':
+ specifier: ^0.5.4
+ version: 0.5.6(@types/node@22.20.4)(oxc-resolver@11.24.2)(tsx@4.23.15)(vite@8.3.0(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1))
+ '@types/node':
+ specifier: ^22.20.0
+ version: 22.20.4
+ preact:
+ specifier: 11.0.0-beta.2
+ version: 11.0.0-beta.2
+ vitest:
+ specifier: ^4.1.9
+ version: 4.1.11(@types/node@22.20.4)(vite@8.3.0(@types/node@22.20.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1))
+
packages/ui-solid:
dependencies:
'@clack/ui':
@@ -1645,6 +1686,14 @@ packages:
resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==}
engines: {node: ^10 || ^12 || >=14}
+ preact@11.0.0-beta.2:
+ resolution: {integrity: sha512-7nIgtr4wtls48+fuazknYNuZ3tOuJoo9ZrT+dzXrMBWkcQH5xmOJiibJpmbU8sFmo2IKLFe3eMp84n0Lq0dCHg==}
+ peerDependencies:
+ preact-render-to-string: '>=5'
+ peerDependenciesMeta:
+ preact-render-to-string:
+ optional: true
+
publint@0.3.24:
resolution: {integrity: sha512-9zS56KrKBoqi5Qt8h92uMP8TTM9AYZSgnmCo4u2priMqkOZvQnTsziZ2p5LJ2ywbYkAjoCDp2jda9u4cgFefIw==}
engines: {node: '>=18'}
@@ -3274,6 +3323,8 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
+ preact@11.0.0-beta.2: {}
+
publint@0.3.24:
dependencies:
'@publint/pack': 0.1.7