1ch 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,280 @@
1
+ # 1ch
2
+
3
+ **Terminal UI components for React.** For those who'd rather be in the terminal.
4
+
5
+ Every UI framework treats the browser as a canvas of infinite resolution. This one doesn't. The smallest unit is one character cell. A button is `[ OK ]`. A progress bar is `████░░░░`. A table border is `│` and `─` and `┼`.
6
+
7
+ The web primitives that make it work are almost embarrassingly simple: a monospace font, `white-space: pre`, and `ch` units. That's the entire rendering engine.
8
+
9
+ ```tsx
10
+ import { TermUI, TBox, TVStack, TTable, TBar } from "1ch";
11
+ import "1ch/style.css";
12
+
13
+ function Dashboard() {
14
+ return (
15
+ <TermUI width={60}>
16
+ <TBox title="Server Status">
17
+ <TVStack gap={1}>
18
+ <TBar label="CPU" value={73} max={100} />
19
+ <TBar label="MEM" value={4.2} max={8} />
20
+ <TTable
21
+ columns={[
22
+ { key: "service", header: "Service", width: 20 },
23
+ { key: "status", header: "Status", width: 10 },
24
+ { key: "uptime", header: "Uptime", width: 12 },
25
+ ]}
26
+ data={[
27
+ { service: "api-gateway", status: "UP", uptime: "14d 3h" },
28
+ { service: "worker-pool", status: "UP", uptime: "14d 3h" },
29
+ { service: "cache", status: "WARN", uptime: "2h 41m" },
30
+ ]}
31
+ />
32
+ </TVStack>
33
+ </TBox>
34
+ </TermUI>
35
+ );
36
+ }
37
+ ```
38
+
39
+ ## Why
40
+
41
+ - **Dense.** Every character is content or structure. Nothing is decorative. You can always tell where one element ends and another begins because the grid enforces it.
42
+ - **React-native.** Not a wrapper around a terminal emulator. Custom React reconciler that turns a component tree into character grids. React handles lifecycle, hooks, state. The reconciler handles layout.
43
+ - **Responsive.** All layout is lazy - functions of width, not pre-computed blocks. Resize the container and everything reflows.
44
+ - **Themeable.** Full token system: palette, semantic, syntax, markdown, component colors. Light/dark/system. Runtime-switchable. End-user JSON theming with validation.
45
+ - **Three input formats.** Render React components, or feed it markdown, HTML, or JSON and get terminal layouts back.
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ npm install 1ch react react-dom
51
+ ```
52
+
53
+ ## Components
54
+
55
+ React components that render through a custom reconciler. No DOM elements - they produce `LayoutFn` internally.
56
+
57
+ ### Layout
58
+
59
+ | Component | What it does |
60
+ | ------------ | ---------------------------------------------------- |
61
+ | `TVStack` | Vertical stack with optional `gap` |
62
+ | `THStack` | Horizontal stack with optional `gap` and `widths` |
63
+ | `TBox` | Bordered box with optional `title` and `borderColor` |
64
+ | `TSeparator` | Horizontal line |
65
+ | `TBlank` | Empty line |
66
+ | `TLine` | Single-line container |
67
+ | `TSpan` | Inline styled text |
68
+
69
+ ### Data
70
+
71
+ | Component | What it does |
72
+ | --------- | ---------------------------------------------------------- |
73
+ | `TTable` | Bordered table with column widths, wrapping, header colors |
74
+ | `TTree` | Nested tree view with `├─` and `└─` branch characters |
75
+ | `TList` | Bulleted or numbered list |
76
+ | `TCode` | Syntax-highlighted code block in a box |
77
+ | `TDiff` | Colored diff output |
78
+ | `TBar` | Progress bar (`████░░░░`) |
79
+ | `TSpark` | Sparkline (`▁▂▃▄▅▆▇█`) |
80
+
81
+ ### Interactive
82
+
83
+ | Component | What it does |
84
+ | ------------ | ---------------------------------- |
85
+ | `TTabs` | Clickable tab bar with `onSelect` |
86
+ | `TButton` | Clickable styled button |
87
+ | `TStatusbar` | Footer bar with left/right content |
88
+
89
+ ### Documents
90
+
91
+ | Component | What it does |
92
+ | ----------- | ------------------------------------------ |
93
+ | `TMarkdown` | Renders markdown string as terminal layout |
94
+ | `THtml` | Renders HTML string as terminal layout |
95
+ | `TJson` | Renders structured JSON as terminal layout |
96
+
97
+ ## Imperative API
98
+
99
+ If you don't want React components, every layout primitive has a function equivalent that returns a `LayoutFn`:
100
+
101
+ ```typescript
102
+ import { box, vstack, table, code, bar, hstack } from "1ch";
103
+
104
+ const layout = vstack(
105
+ box(
106
+ vstack(
107
+ bar(73, 100, 40),
108
+ table(
109
+ [
110
+ { key: "name", header: "Name", width: 20 },
111
+ { key: "value", header: "Value", width: 10 },
112
+ ],
113
+ [
114
+ { name: "requests", value: "1.2k/s" },
115
+ { name: "errors", value: "0.03%" },
116
+ ]
117
+ )
118
+ ),
119
+ { title: "Metrics" }
120
+ ),
121
+ code(`console.log("hello")`, { language: "javascript" })
122
+ );
123
+
124
+ // Materialize at any width
125
+ const block: Block = layout(80);
126
+ ```
127
+
128
+ ### Available builders
129
+
130
+ **Layout:** `box`, `hstack`, `vstack`, `separator`, `blank`, `lines`
131
+
132
+ **Data:** `table`, `tree`, `list`, `code`, `diff`
133
+
134
+ **Widgets:** `tabs`, `statusbar`, `badge`, `bar`, `spark`
135
+
136
+ **Text:** `pad`, `hl`, `padLine`
137
+
138
+ ## Document Pipeline
139
+
140
+ Feed it markdown, HTML, or JSON. Get terminal layouts back.
141
+
142
+ ```tsx
143
+ import { TermDocument } from "1ch";
144
+
145
+ // Markdown
146
+ <TermDocument
147
+ source={{ format: "markdown", value: "# Hello\n\nSome **bold** text." }}
148
+ width={60}
149
+ />
150
+
151
+ // HTML
152
+ <TermDocument
153
+ source={{ format: "html", value: "<h1>Hello</h1><p>Some text.</p>" }}
154
+ width={60}
155
+ />
156
+
157
+ // Or use the functions directly
158
+ import { fromMarkdown, fromHtml, fromJson } from "1ch";
159
+
160
+ const layout = fromMarkdown("# Hello\n\n- one\n- two", { theme });
161
+ const block = layout(80);
162
+ ```
163
+
164
+ ### HTML support
165
+
166
+ Supported tags: `h1`-`h6`, `p`, `ul`, `ol`, `li`, `table` (with `thead`/`tbody`/`tr`/`th`/`td`), `blockquote`, `hr`, `pre`, `div`, `section`, `article`, `main`, `header`, `footer`, `nav`.
167
+
168
+ `<term>` acts as an optional root - if present, only its subtree renders.
169
+
170
+ Color attributes: `color`, `data-color`, `marker-color`, `header-color`, `border-color`, `text-color`. Values can be theme keys (`yellow`, `cyan`) or CSS colors (`#56b6c2`).
171
+
172
+ ## Theme System
173
+
174
+ Two built-in themes (dark and light), runtime-switchable, fully customizable via JSON.
175
+
176
+ ```tsx
177
+ import {
178
+ TermThemeProvider,
179
+ TermUI,
180
+ parseThemeSpec,
181
+ defaultThemeSpec,
182
+ } from "1ch";
183
+
184
+ const parsed = parseThemeSpec(userThemeJson);
185
+ const spec = parsed.ok ? parsed.theme : defaultThemeSpec;
186
+
187
+ function App() {
188
+ return (
189
+ <TermThemeProvider initialTheme={spec} initialMode="system">
190
+ <TermUI width={80}>{/* components inherit the theme */}</TermUI>
191
+ </TermThemeProvider>
192
+ );
193
+ }
194
+ ```
195
+
196
+ A `ThemeSpec` defines both light and dark modes. Each mode has:
197
+
198
+ - **Palette** - 10 ANSI-like colors (black, red, green, yellow, blue, magenta, cyan, white, brightBlack, brightWhite)
199
+ - **Semantic** - UI intent (border, info, success, warn, danger, frameBg, frameFg, etc.)
200
+ - **Syntax** - Code highlighting (keyword, string, number, comment, function, type, punctuation)
201
+ - **Markdown** - Document rendering (headings h1-h6, paragraph, lists, tables, quotes, code)
202
+ - **Components** - Widget colors (tabs, statusbar, badge, button)
203
+
204
+ Use `validateThemeSpec()` to check user-provided JSON before applying it.
205
+
206
+ ## Hooks
207
+
208
+ ```typescript
209
+ import { useSpinner, useTick, useTermWidth, useStreamingText } from "1ch";
210
+
211
+ // Animated braille spinner
212
+ const spinner = useSpinner(80);
213
+
214
+ // Interval counter for animations
215
+ const tick = useTick(500);
216
+
217
+ // Measure container width in character units (uses ResizeObserver)
218
+ const ref = useRef(null);
219
+ const width = useTermWidth(ref, 80);
220
+
221
+ // Streaming text with lerp (detects appends, doesn't restart)
222
+ const displayed = useStreamingText(streamingText, 0.08);
223
+ ```
224
+
225
+ ## Putting It Together
226
+
227
+ ```tsx
228
+ import { TermUI, TBox, TCode, TTabs, TTab, TTable, TBar, TVStack } from "1ch";
229
+ import "1ch/style.css";
230
+
231
+ function App() {
232
+ const [tab, setTab] = useState(0);
233
+
234
+ return (
235
+ <TermUI width={80}>
236
+ <TTabs active={tab} onSelect={setTab}>
237
+ <TTab name="Overview">
238
+ <TBox title="Status">
239
+ <TVStack>
240
+ <TBar label="CPU" value={73} max={100} />
241
+ <TBar label="MEM" value={4.2} max={8} />
242
+ </TVStack>
243
+ </TBox>
244
+ </TTab>
245
+
246
+ <TTab name="Logs">
247
+ <TTable
248
+ columns={[
249
+ { key: "time", header: "Time", width: 12 },
250
+ { key: "level", header: "Level", width: 8 },
251
+ { key: "message", header: "Message", width: 40 },
252
+ ]}
253
+ data={[
254
+ { time: "14:03:21", level: "INFO", message: "Server started on :3000" },
255
+ { time: "14:03:22", level: "INFO", message: "Connected to database" },
256
+ { time: "14:05:01", level: "WARN", message: "Slow query: 2.3s" },
257
+ ]}
258
+ />
259
+ </TTab>
260
+
261
+ <TTab name="Config">
262
+ <TCode
263
+ code={`export default {
264
+ port: 3000,
265
+ database: "postgres://localhost/app",
266
+ cache: { ttl: 300, max: 1000 },
267
+ };`}
268
+ language="typescript"
269
+ title="config.ts"
270
+ />
271
+ </TTab>
272
+ </TTabs>
273
+ </TermUI>
274
+ );
275
+ }
276
+ ```
277
+
278
+ ## License
279
+
280
+ MIT