Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions examples/hello-world-solid/babel.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"presets": [
[
"@babel/preset-typescript",
{
"allExtensions": true,
"isTSX": true
}
],
[
"babel-preset-solid",
{
"generate": "universal",
"moduleName": "@clack/ui-solid"
}
]
]
}
24 changes: 24 additions & 0 deletions examples/hello-world-solid/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@examples/hello-world-solid",
"private": true,
"type": "module",
"scripts": {
"build": "babel src --extensions .ts,.tsx --out-dir dist --keep-file-extension --delete-dir-on-start",
"prestart": "pnpm run build",
"start": "tsx --conditions=browser dist/index.tsx"
},
"dependencies": {
"@bomb.sh/tty": "^0.7.0",
"@clack/ui": "workspace:*",
"@clack/ui-solid": "workspace:*",
"solid-js": "1.9.14"
},
"devDependencies": {
"@babel/cli": "7.29.7",
"@babel/core": "7.29.7",
"@babel/preset-typescript": "7.29.7",
"@types/node": "^22.20.0",
"babel-preset-solid": "1.9.12",
"tsx": "^4.19.0"
}
}
44 changes: 44 additions & 0 deletions examples/hello-world-solid/src/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// oxlint-disable bombshell-dev/exported-function-async -- Solid components render synchronously.
import { fixed, grow, percent } from '@bomb.sh/tty';
import { type JSXElement, rgba } from '@clack/ui-solid';
import { createSignal } from 'solid-js';

/** Render the interactive hello-world example. */
export function HelloWorld(): JSXElement {
const [say, setSay] = createSignal('Hello');
const [to, setTo] = createSignal('World');

return (
<box
layout={{
direction: 'ttb',
gap: 1,
padding: { top: 1, bottom: 1, left: 2, right: 2 },
width: fixed(40),
}}
border={{ color: blue, top: 1, right: 1, bottom: 1, left: 1 }}
>
<text color={cyan}>
{say()}, {to()}!
</text>
<box layout={{ direction: 'ttb', width: grow() }}>
<box layout={{ direction: 'ltr', gap: 1, width: grow() }}>
<box layout={{ width: percent(0.3) }}>
<text color={gray}>say:</text>
</box>
<box layout={{ width: percent(0.3) }}>
<text color={gray}>to:</text>
</box>
</box>
<box layout={{ direction: 'ltr', gap: 1, width: grow() }}>
<input onInput={(event) => setSay(event.value)} />
<input onInput={(event) => setTo(event.value)} />
</box>
</box>
</box>
);
}

const blue = rgba(0, 0, 238);
const cyan = rgba(0, 205, 205);
const gray = rgba(127, 127, 127);
9 changes: 9 additions & 0 deletions examples/hello-world-solid/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { stdin, stdout } from 'node:process';
import { createUI } from '@clack/ui';
import { createRoot } from '@clack/ui-solid';
import { HelloWorld } from './app.tsx';

await using ui = await createUI({ input: stdin, output: stdout });
const root = createRoot(ui.host.element);
root.render(() => <HelloWorld />);
await ui.main();
13 changes: 13 additions & 0 deletions examples/hello-world-solid/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "@bomb.sh/tools/tsconfig.json",
"compilerOptions": {
"composite": false,
"declaration": false,
"declarationMap": false,
"jsx": "preserve",
"jsxImportSource": "@clack/ui-solid",
"noEmit": true,
"types": ["node"]
},
"exclude": ["dist"]
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"format": "bsh format",
"format:check": "bsh format --check",
"lint": "bsh lint .",
"test": "bsh test"
"test": "pnpm --filter @clack/ui build && bsh test"
},
"devDependencies": {
"@bomb.sh/tools": "^0.6.1",
Expand Down
79 changes: 79 additions & 0 deletions packages/ui-solid/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{
"name": "@clack/ui-solid",
"version": "0.0.0",
"keywords": [
"bombshell",
"cli",
"solid",
"solid-js"
],
"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": "vitest run"
},
"dependencies": {
"@clack/ui": "workspace:*"
},
"devDependencies": {
"@bomb.sh/tools": "^0.5.4",
"@types/node": "^22.20.0",
"solid-js": "1.9.14",
"vitest": "^4.1.9"
},
"peerDependencies": {
"solid-js": "^1.9.14"
},
"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",
"src/*.test.ts"
]
}
}
20 changes: 20 additions & 0 deletions packages/ui-solid/src/components.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// oxlint-disable bombshell-dev/exported-function-async -- Solid components synchronously return Host elements.
import type { HostElement } from '@clack/ui/elements';
import type { BoxProps, TextProps } from './elements.ts';
import { createElement, spread } from './renderer.ts';

export type { BoxProps, TextProps } from './elements.ts';

/** A layout container backed by the Host's `box` element. */
export function Box(props: BoxProps): HostElement {
const element = createElement('box') as HostElement;
spread(element, props);
return element;
}

/** A styled text scope backed by the Host's `text` element. */
export function Text(props: TextProps): HostElement {
const element = createElement('text') as HostElement;
spread(element, props);
return element;
}
49 changes: 49 additions & 0 deletions packages/ui-solid/src/elements.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { HostElements as CoreHostElements, InputEvent } from '@clack/ui';
import { describe, expectTypeOf, test } from 'vitest';
import type { JSX } from './jsx-runtime.ts';
import type { JSXElement } from './types.ts';

interface SolidProbeEvent {
readonly value: number;
}

interface SolidProbeProps {
count: number;
onprobe?: (event: SolidProbeEvent) => void;
}

declare module '@clack/ui/elements' {
interface HostElements {
'@test/solid-probe': SolidProbeProps;
}
}

describe('Solid host elements', () => {
test('maps box and text properties from the core registry', () => {
expectTypeOf<JSX.IntrinsicElements['box']['layout']>().toEqualTypeOf<
CoreHostElements['box']['layout']
>();
expectTypeOf<JSX.IntrinsicElements['text']['color']>().toEqualTypeOf<
CoreHostElements['text']['color']
>();
expectTypeOf<JSX.IntrinsicElements['box']['children']>().toEqualTypeOf<
JSXElement | undefined
>();
});

test('maps lowercase host event properties to Solid casing', () => {
expectTypeOf<NonNullable<JSX.IntrinsicElements['input']['onInput']>>()
.parameter(0)
.toEqualTypeOf<InputEvent>();
expectTypeOf<
'oninput' extends keyof JSX.IntrinsicElements['input'] ? true : false
>().toEqualTypeOf<false>();
});

test('derives custom elements from core registry augmentation', () => {
expectTypeOf<JSX.IntrinsicElements['@test/solid-probe']['count']>().toEqualTypeOf<number>();
expectTypeOf<NonNullable<JSX.IntrinsicElements['@test/solid-probe']['onProbe']>>()
.parameter(0)
.toEqualTypeOf<SolidProbeEvent>();
});
});
21 changes: 21 additions & 0 deletions packages/ui-solid/src/elements.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { HostElements as CoreHostElements } from '@clack/ui';
import type { HostRef, JSXElement } from './types.ts';

type SolidProperties<Properties> = {
[Name in keyof Properties as Name extends string
? Name extends `on${infer Type}`
? `on${Capitalize<Type>}`
: Name
: Name]: Properties[Name];
} & {
children?: JSXElement;
ref?: HostRef;
};

export type HostElements = {
[Name in keyof CoreHostElements]: SolidProperties<CoreHostElements[Name]>;
};

export type BoxProps = HostElements['box'];
export type InputProps = HostElements['input'];
export type TextProps = HostElements['text'];
19 changes: 19 additions & 0 deletions packages/ui-solid/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export { rgba } from '@clack/ui';
export { Box, Text } from './components.ts';
export type { BoxProps, HostElements, InputProps, TextProps } from './elements.ts';
export {
createRoot,
type Root,
effect,
memo,
createComponent,
createElement,
createTextNode,
insertNode,
insert,
spread,
setProp,
mergeProps,
use,
} from './renderer.ts';
export type { HostRef, JSXElement } from './types.ts';
10 changes: 10 additions & 0 deletions packages/ui-solid/src/jsx-dev-runtime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { HostElements } from './index.ts';
import type { JSXElement } from './types.ts';

export declare namespace JSX {
type Element = JSXElement;
interface ElementChildrenAttribute {
children: unknown;
}
interface IntrinsicElements extends HostElements {}
}
10 changes: 10 additions & 0 deletions packages/ui-solid/src/jsx-runtime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { HostElements } from './index.ts';
import type { JSXElement } from './types.ts';

export declare namespace JSX {
type Element = JSXElement;
interface ElementChildrenAttribute {
children: unknown;
}
type IntrinsicElements = HostElements;
}
Loading
Loading