@dealdeploy/skl 0.1.7 → 0.2.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/.agents/skills/opentui/SKILL.md +198 -0
- package/.agents/skills/opentui/references/animation/REFERENCE.md +431 -0
- package/.agents/skills/opentui/references/components/REFERENCE.md +143 -0
- package/.agents/skills/opentui/references/components/code-diff.md +496 -0
- package/.agents/skills/opentui/references/components/containers.md +412 -0
- package/.agents/skills/opentui/references/components/inputs.md +531 -0
- package/.agents/skills/opentui/references/components/text-display.md +384 -0
- package/.agents/skills/opentui/references/core/REFERENCE.md +145 -0
- package/.agents/skills/opentui/references/core/api.md +506 -0
- package/.agents/skills/opentui/references/core/configuration.md +166 -0
- package/.agents/skills/opentui/references/core/gotchas.md +393 -0
- package/.agents/skills/opentui/references/core/patterns.md +448 -0
- package/.agents/skills/opentui/references/keyboard/REFERENCE.md +511 -0
- package/.agents/skills/opentui/references/layout/REFERENCE.md +337 -0
- package/.agents/skills/opentui/references/layout/patterns.md +444 -0
- package/.agents/skills/opentui/references/react/REFERENCE.md +174 -0
- package/.agents/skills/opentui/references/react/api.md +435 -0
- package/.agents/skills/opentui/references/react/configuration.md +301 -0
- package/.agents/skills/opentui/references/react/gotchas.md +443 -0
- package/.agents/skills/opentui/references/react/patterns.md +501 -0
- package/.agents/skills/opentui/references/solid/REFERENCE.md +201 -0
- package/.agents/skills/opentui/references/solid/api.md +543 -0
- package/.agents/skills/opentui/references/solid/configuration.md +315 -0
- package/.agents/skills/opentui/references/solid/gotchas.md +415 -0
- package/.agents/skills/opentui/references/solid/patterns.md +558 -0
- package/.agents/skills/opentui/references/testing/REFERENCE.md +614 -0
- package/.claude/skills/opentui/SKILL.md +198 -0
- package/.claude/skills/opentui/references/animation/REFERENCE.md +431 -0
- package/.claude/skills/opentui/references/components/REFERENCE.md +143 -0
- package/.claude/skills/opentui/references/components/code-diff.md +496 -0
- package/.claude/skills/opentui/references/components/containers.md +412 -0
- package/.claude/skills/opentui/references/components/inputs.md +531 -0
- package/.claude/skills/opentui/references/components/text-display.md +384 -0
- package/.claude/skills/opentui/references/core/REFERENCE.md +145 -0
- package/.claude/skills/opentui/references/core/api.md +506 -0
- package/.claude/skills/opentui/references/core/configuration.md +166 -0
- package/.claude/skills/opentui/references/core/gotchas.md +393 -0
- package/.claude/skills/opentui/references/core/patterns.md +448 -0
- package/.claude/skills/opentui/references/keyboard/REFERENCE.md +511 -0
- package/.claude/skills/opentui/references/layout/REFERENCE.md +337 -0
- package/.claude/skills/opentui/references/layout/patterns.md +444 -0
- package/.claude/skills/opentui/references/react/REFERENCE.md +174 -0
- package/.claude/skills/opentui/references/react/api.md +435 -0
- package/.claude/skills/opentui/references/react/configuration.md +301 -0
- package/.claude/skills/opentui/references/react/gotchas.md +443 -0
- package/.claude/skills/opentui/references/react/patterns.md +501 -0
- package/.claude/skills/opentui/references/solid/REFERENCE.md +201 -0
- package/.claude/skills/opentui/references/solid/api.md +543 -0
- package/.claude/skills/opentui/references/solid/configuration.md +315 -0
- package/.claude/skills/opentui/references/solid/gotchas.md +415 -0
- package/.claude/skills/opentui/references/solid/patterns.md +558 -0
- package/.claude/skills/opentui/references/testing/REFERENCE.md +614 -0
- package/index.ts +169 -38
- package/package.json +1 -1
- package/update.ts +87 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
# Solid Configuration
|
|
2
|
+
|
|
3
|
+
## Project Setup
|
|
4
|
+
|
|
5
|
+
### Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bunx create-tui@latest -t solid my-app
|
|
9
|
+
cd my-app && bun install
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The CLI creates the `my-app` directory for you - it must **not already exist**.
|
|
13
|
+
|
|
14
|
+
Options: `--no-git` (skip git init), `--no-install` (skip bun install)
|
|
15
|
+
|
|
16
|
+
### Manual Setup
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
mkdir my-tui && cd my-tui
|
|
20
|
+
bun init
|
|
21
|
+
bun install @opentui/solid @opentui/core solid-js
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## TypeScript Configuration
|
|
25
|
+
|
|
26
|
+
### tsconfig.json
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"compilerOptions": {
|
|
31
|
+
"lib": ["ESNext"],
|
|
32
|
+
"target": "ESNext",
|
|
33
|
+
"module": "ESNext",
|
|
34
|
+
"moduleResolution": "bundler",
|
|
35
|
+
|
|
36
|
+
"jsx": "preserve",
|
|
37
|
+
"jsxImportSource": "@opentui/solid",
|
|
38
|
+
|
|
39
|
+
"strict": true,
|
|
40
|
+
"skipLibCheck": true,
|
|
41
|
+
"noEmit": true,
|
|
42
|
+
"types": ["bun-types"]
|
|
43
|
+
},
|
|
44
|
+
"include": ["src/**/*"]
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Critical settings:**
|
|
49
|
+
- `jsx: "preserve"` - Let Solid's compiler handle JSX
|
|
50
|
+
- `jsxImportSource: "@opentui/solid"` - Import JSX runtime from OpenTUI Solid
|
|
51
|
+
|
|
52
|
+
## Bun Configuration
|
|
53
|
+
|
|
54
|
+
### bunfig.toml
|
|
55
|
+
|
|
56
|
+
**Required** for the Solid compiler:
|
|
57
|
+
|
|
58
|
+
```toml
|
|
59
|
+
preload = ["@opentui/solid/preload"]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
This loads the Solid JSX transform before your code runs.
|
|
63
|
+
|
|
64
|
+
## Package Configuration
|
|
65
|
+
|
|
66
|
+
### package.json
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"name": "my-tui-app",
|
|
71
|
+
"type": "module",
|
|
72
|
+
"scripts": {
|
|
73
|
+
"start": "bun run src/index.tsx",
|
|
74
|
+
"dev": "bun --watch run src/index.tsx",
|
|
75
|
+
"test": "bun test",
|
|
76
|
+
"build": "bun run build.ts"
|
|
77
|
+
},
|
|
78
|
+
"dependencies": {
|
|
79
|
+
"@opentui/core": "latest",
|
|
80
|
+
"@opentui/solid": "latest",
|
|
81
|
+
"solid-js": "latest"
|
|
82
|
+
},
|
|
83
|
+
"devDependencies": {
|
|
84
|
+
"@types/bun": "latest",
|
|
85
|
+
"typescript": "latest"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Project Structure
|
|
91
|
+
|
|
92
|
+
Recommended structure:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
my-tui-app/
|
|
96
|
+
├── src/
|
|
97
|
+
│ ├── components/
|
|
98
|
+
│ │ ├── Header.tsx
|
|
99
|
+
│ │ ├── Sidebar.tsx
|
|
100
|
+
│ │ └── MainContent.tsx
|
|
101
|
+
│ ├── stores/
|
|
102
|
+
│ │ └── appStore.ts
|
|
103
|
+
│ ├── App.tsx
|
|
104
|
+
│ └── index.tsx
|
|
105
|
+
├── bunfig.toml # Required!
|
|
106
|
+
├── package.json
|
|
107
|
+
└── tsconfig.json
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Entry Point (src/index.tsx)
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
import { render } from "@opentui/solid"
|
|
114
|
+
import { App } from "./App"
|
|
115
|
+
|
|
116
|
+
render(() => <App />)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### App Component (src/App.tsx)
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
import { Header } from "./components/Header"
|
|
123
|
+
import { Sidebar } from "./components/Sidebar"
|
|
124
|
+
import { MainContent } from "./components/MainContent"
|
|
125
|
+
|
|
126
|
+
export function App() {
|
|
127
|
+
return (
|
|
128
|
+
<box flexDirection="column" width="100%" height="100%">
|
|
129
|
+
<Header />
|
|
130
|
+
<box flexDirection="row" flexGrow={1}>
|
|
131
|
+
<Sidebar />
|
|
132
|
+
<MainContent />
|
|
133
|
+
</box>
|
|
134
|
+
</box>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Renderer Configuration
|
|
140
|
+
|
|
141
|
+
### render() Options
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
import { render } from "@opentui/solid"
|
|
145
|
+
import { ConsolePosition } from "@opentui/core"
|
|
146
|
+
|
|
147
|
+
render(() => <App />, {
|
|
148
|
+
// Rendering
|
|
149
|
+
targetFPS: 60,
|
|
150
|
+
|
|
151
|
+
// Behavior
|
|
152
|
+
exitOnCtrlC: true,
|
|
153
|
+
autoFocus: true, // Auto-focus elements on click (default: true)
|
|
154
|
+
useMouse: true, // Enable mouse support (default: true)
|
|
155
|
+
|
|
156
|
+
// Debug console
|
|
157
|
+
consoleOptions: {
|
|
158
|
+
position: ConsolePosition.BOTTOM,
|
|
159
|
+
sizePercent: 30,
|
|
160
|
+
startInDebugMode: false,
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
// Cleanup
|
|
164
|
+
onDestroy: () => {
|
|
165
|
+
// Cleanup code
|
|
166
|
+
},
|
|
167
|
+
})
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Using Existing Renderer
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
import { render } from "@opentui/solid"
|
|
174
|
+
import { createCliRenderer } from "@opentui/core"
|
|
175
|
+
|
|
176
|
+
const renderer = await createCliRenderer({
|
|
177
|
+
exitOnCtrlC: false,
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
render(() => <App />, renderer)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Building for Distribution
|
|
184
|
+
|
|
185
|
+
### Build Script (build.ts)
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import solidPlugin from "@opentui/solid/bun-plugin"
|
|
189
|
+
|
|
190
|
+
await Bun.build({
|
|
191
|
+
entrypoints: ["./src/index.tsx"],
|
|
192
|
+
outdir: "./dist",
|
|
193
|
+
target: "bun",
|
|
194
|
+
minify: true,
|
|
195
|
+
plugins: [solidPlugin],
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
console.log("Build complete!")
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Run: `bun run build.ts`
|
|
202
|
+
|
|
203
|
+
### Creating Executables
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
import solidPlugin from "@opentui/solid/bun-plugin"
|
|
207
|
+
|
|
208
|
+
await Bun.build({
|
|
209
|
+
entrypoints: ["./src/index.tsx"],
|
|
210
|
+
target: "bun",
|
|
211
|
+
plugins: [solidPlugin],
|
|
212
|
+
compile: {
|
|
213
|
+
target: "bun-darwin-arm64", // or bun-linux-x64, etc.
|
|
214
|
+
outfile: "my-app",
|
|
215
|
+
},
|
|
216
|
+
})
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Available targets:**
|
|
220
|
+
- `bun-darwin-arm64` - macOS Apple Silicon
|
|
221
|
+
- `bun-darwin-x64` - macOS Intel
|
|
222
|
+
- `bun-linux-x64` - Linux x64
|
|
223
|
+
- `bun-linux-arm64` - Linux ARM64
|
|
224
|
+
- `bun-windows-x64` - Windows x64
|
|
225
|
+
|
|
226
|
+
## Environment Variables
|
|
227
|
+
|
|
228
|
+
Create `.env` for development:
|
|
229
|
+
|
|
230
|
+
```env
|
|
231
|
+
# Debug settings
|
|
232
|
+
OTUI_SHOW_STATS=false
|
|
233
|
+
SHOW_CONSOLE=false
|
|
234
|
+
|
|
235
|
+
# App settings
|
|
236
|
+
API_URL=https://api.example.com
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Bun auto-loads `.env` files:
|
|
240
|
+
|
|
241
|
+
```tsx
|
|
242
|
+
const apiUrl = process.env.API_URL
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Testing Configuration
|
|
246
|
+
|
|
247
|
+
### Test Setup
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
// src/test-utils.tsx
|
|
251
|
+
import { testRender } from "@opentui/solid"
|
|
252
|
+
|
|
253
|
+
export async function renderForTest(
|
|
254
|
+
Component: () => JSX.Element,
|
|
255
|
+
options = { width: 80, height: 24 }
|
|
256
|
+
) {
|
|
257
|
+
return await testRender(Component, options)
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Test Example
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
// src/components/Counter.test.tsx
|
|
265
|
+
import { test, expect } from "bun:test"
|
|
266
|
+
import { renderForTest } from "../test-utils"
|
|
267
|
+
import { Counter } from "./Counter"
|
|
268
|
+
|
|
269
|
+
test("Counter renders initial value", async () => {
|
|
270
|
+
const { snapshot } = await renderForTest(() => <Counter initialValue={5} />)
|
|
271
|
+
expect(snapshot()).toContain("Count: 5")
|
|
272
|
+
})
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Common Configuration Issues
|
|
276
|
+
|
|
277
|
+
### Missing bunfig.toml
|
|
278
|
+
|
|
279
|
+
**Symptom**: JSX not transformed, syntax errors
|
|
280
|
+
|
|
281
|
+
**Fix**: Create `bunfig.toml` with preload:
|
|
282
|
+
|
|
283
|
+
```toml
|
|
284
|
+
preload = ["@opentui/solid/preload"]
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Wrong JSX Settings
|
|
288
|
+
|
|
289
|
+
**Symptom**: JSX compiles to React calls
|
|
290
|
+
|
|
291
|
+
**Fix**: Ensure tsconfig has:
|
|
292
|
+
|
|
293
|
+
```json
|
|
294
|
+
{
|
|
295
|
+
"compilerOptions": {
|
|
296
|
+
"jsx": "preserve",
|
|
297
|
+
"jsxImportSource": "@opentui/solid"
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Build Missing Plugin
|
|
303
|
+
|
|
304
|
+
**Symptom**: Built output has untransformed JSX
|
|
305
|
+
|
|
306
|
+
**Fix**: Add Solid plugin to build:
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
import solidPlugin from "@opentui/solid/bun-plugin"
|
|
310
|
+
|
|
311
|
+
await Bun.build({
|
|
312
|
+
// ...
|
|
313
|
+
plugins: [solidPlugin],
|
|
314
|
+
})
|
|
315
|
+
```
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
# Solid Gotchas
|
|
2
|
+
|
|
3
|
+
## Critical
|
|
4
|
+
|
|
5
|
+
### Never use `process.exit()` directly
|
|
6
|
+
|
|
7
|
+
**This is the most common mistake.** Using `process.exit()` leaves the terminal in a broken state (cursor hidden, raw mode, alternate screen).
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
// WRONG - Terminal left in broken state
|
|
11
|
+
process.exit(0)
|
|
12
|
+
|
|
13
|
+
// CORRECT - Use renderer.destroy()
|
|
14
|
+
import { useRenderer } from "@opentui/solid"
|
|
15
|
+
|
|
16
|
+
function App() {
|
|
17
|
+
const renderer = useRenderer()
|
|
18
|
+
|
|
19
|
+
const handleExit = () => {
|
|
20
|
+
renderer.destroy() // Cleans up and exits properly
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`renderer.destroy()` restores the terminal (exits alternate screen, restores cursor, etc.) before exiting.
|
|
26
|
+
|
|
27
|
+
## Configuration Issues
|
|
28
|
+
|
|
29
|
+
### Missing bunfig.toml
|
|
30
|
+
|
|
31
|
+
**Symptom**: JSX syntax errors, components not rendering
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
SyntaxError: Unexpected token '<'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Fix**: Create `bunfig.toml` in project root:
|
|
38
|
+
|
|
39
|
+
```toml
|
|
40
|
+
preload = ["@opentui/solid/preload"]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Wrong JSX Settings
|
|
44
|
+
|
|
45
|
+
**Symptom**: JSX compiles to React, errors about React not found
|
|
46
|
+
|
|
47
|
+
**Fix**: Ensure tsconfig.json has:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"compilerOptions": {
|
|
52
|
+
"jsx": "preserve",
|
|
53
|
+
"jsxImportSource": "@opentui/solid"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Build Without Plugin
|
|
59
|
+
|
|
60
|
+
**Symptom**: Built bundle has raw JSX
|
|
61
|
+
|
|
62
|
+
**Fix**: Add Solid plugin to build:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import solidPlugin from "@opentui/solid/bun-plugin"
|
|
66
|
+
|
|
67
|
+
await Bun.build({
|
|
68
|
+
// ...
|
|
69
|
+
plugins: [solidPlugin],
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Reactivity Issues
|
|
74
|
+
|
|
75
|
+
### Accessing Signals Without Calling
|
|
76
|
+
|
|
77
|
+
**Symptom**: Value never updates, shows `[Function]`
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
// WRONG - Missing ()
|
|
81
|
+
const [count, setCount] = createSignal(0)
|
|
82
|
+
<text>Count: {count}</text> // Shows [Function]
|
|
83
|
+
|
|
84
|
+
// CORRECT
|
|
85
|
+
<text>Count: {count()}</text>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Breaking Reactivity with Destructuring
|
|
89
|
+
|
|
90
|
+
**Symptom**: Props stop being reactive
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
// WRONG - Breaks reactivity
|
|
94
|
+
function Component(props: { value: number }) {
|
|
95
|
+
const { value } = props // Destructured once, never updates!
|
|
96
|
+
return <text>{value}</text>
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// CORRECT - Keep props reactive
|
|
100
|
+
function Component(props: { value: number }) {
|
|
101
|
+
return <text>{props.value}</text>
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// OR use splitProps
|
|
105
|
+
function Component(props: { value: number; other: string }) {
|
|
106
|
+
const [local, rest] = splitProps(props, ["value"])
|
|
107
|
+
return <text>{local.value}</text>
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Effects Not Running
|
|
112
|
+
|
|
113
|
+
**Symptom**: createEffect doesn't trigger
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
// WRONG - Signal not accessed in effect
|
|
117
|
+
const [count, setCount] = createSignal(0)
|
|
118
|
+
|
|
119
|
+
createEffect(() => {
|
|
120
|
+
console.log("Count changed") // Never runs after initial!
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
// CORRECT - Access the signal
|
|
124
|
+
createEffect(() => {
|
|
125
|
+
console.log("Count:", count()) // Runs when count changes
|
|
126
|
+
})
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Component Naming
|
|
130
|
+
|
|
131
|
+
### Underscore vs Hyphen
|
|
132
|
+
|
|
133
|
+
Solid uses underscores for multi-word component names:
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
// WRONG - React-style naming
|
|
137
|
+
<tab-select /> // Error!
|
|
138
|
+
<ascii-font /> // Error!
|
|
139
|
+
<line-number /> // Error!
|
|
140
|
+
|
|
141
|
+
// CORRECT - Solid naming
|
|
142
|
+
<tab_select />
|
|
143
|
+
<ascii_font />
|
|
144
|
+
<line_number />
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Component mapping:**
|
|
148
|
+
| Concept | React | Solid |
|
|
149
|
+
|---------|-------|-------|
|
|
150
|
+
| Tab Select | `<tab-select>` | `<tab_select>` |
|
|
151
|
+
| ASCII Font | `<ascii-font>` | `<ascii_font>` |
|
|
152
|
+
| Line Number | `<line-number>` | `<line_number>` |
|
|
153
|
+
|
|
154
|
+
## Focus Issues
|
|
155
|
+
|
|
156
|
+
### Focus Not Working
|
|
157
|
+
|
|
158
|
+
Components need explicit focus:
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
// WRONG
|
|
162
|
+
<input placeholder="Type here..." />
|
|
163
|
+
|
|
164
|
+
// CORRECT
|
|
165
|
+
<input placeholder="Type here..." focused />
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Select Not Responding
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
// WRONG
|
|
172
|
+
<select options={["a", "b"]} />
|
|
173
|
+
|
|
174
|
+
// CORRECT
|
|
175
|
+
<select
|
|
176
|
+
options={[
|
|
177
|
+
{ name: "A", description: "Option A", value: "a" },
|
|
178
|
+
{ name: "B", description: "Option B", value: "b" },
|
|
179
|
+
]}
|
|
180
|
+
onSelect={(index, option) => {
|
|
181
|
+
// Called when Enter is pressed
|
|
182
|
+
console.log("Selected:", option.name)
|
|
183
|
+
}}
|
|
184
|
+
focused
|
|
185
|
+
/>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Select Events Confusion
|
|
189
|
+
|
|
190
|
+
Remember: `onSelect` fires on Enter (selection confirmed), `onChange` fires on navigation:
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
// WRONG - expecting onChange to fire on Enter
|
|
194
|
+
<select
|
|
195
|
+
options={options()}
|
|
196
|
+
onChange={(i, opt) => submitForm(opt)} // This fires on arrow keys!
|
|
197
|
+
/>
|
|
198
|
+
|
|
199
|
+
// CORRECT
|
|
200
|
+
<select
|
|
201
|
+
options={options()}
|
|
202
|
+
onSelect={(i, opt) => submitForm(opt)} // Enter pressed - submit
|
|
203
|
+
onChange={(i, opt) => showPreview(opt)} // Arrow keys - preview
|
|
204
|
+
/>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Control Flow Issues
|
|
208
|
+
|
|
209
|
+
### For vs Index
|
|
210
|
+
|
|
211
|
+
Use `For` for arrays of objects, `Index` for primitives:
|
|
212
|
+
|
|
213
|
+
```tsx
|
|
214
|
+
// For objects - item is reactive
|
|
215
|
+
<For each={objects()}>
|
|
216
|
+
{(obj) => <text>{obj.name}</text>}
|
|
217
|
+
</For>
|
|
218
|
+
|
|
219
|
+
// For primitives - use Index, item() is reactive
|
|
220
|
+
<Index each={strings()}>
|
|
221
|
+
{(str, index) => <text>{index}: {str()}</text>}
|
|
222
|
+
</Index>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Missing Fallback
|
|
226
|
+
|
|
227
|
+
Show requires fallback for proper rendering:
|
|
228
|
+
|
|
229
|
+
```tsx
|
|
230
|
+
// May cause issues
|
|
231
|
+
<Show when={data()}>
|
|
232
|
+
<Component />
|
|
233
|
+
</Show>
|
|
234
|
+
|
|
235
|
+
// Better - explicit fallback
|
|
236
|
+
<Show when={data()} fallback={<text>Loading...</text>}>
|
|
237
|
+
<Component />
|
|
238
|
+
</Show>
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Cleanup Issues
|
|
242
|
+
|
|
243
|
+
### Forgetting onCleanup
|
|
244
|
+
|
|
245
|
+
**Symptom**: Memory leaks, multiple intervals running
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
// WRONG - Interval never cleared
|
|
249
|
+
function Timer() {
|
|
250
|
+
const [time, setTime] = createSignal(0)
|
|
251
|
+
|
|
252
|
+
setInterval(() => setTime(t => t + 1), 1000)
|
|
253
|
+
|
|
254
|
+
return <text>{time()}</text>
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// CORRECT
|
|
258
|
+
function Timer() {
|
|
259
|
+
const [time, setTime] = createSignal(0)
|
|
260
|
+
|
|
261
|
+
const interval = setInterval(() => setTime(t => t + 1), 1000)
|
|
262
|
+
onCleanup(() => clearInterval(interval))
|
|
263
|
+
|
|
264
|
+
return <text>{time()}</text>
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Effect Cleanup
|
|
269
|
+
|
|
270
|
+
```tsx
|
|
271
|
+
createEffect(() => {
|
|
272
|
+
const subscription = subscribe(data())
|
|
273
|
+
|
|
274
|
+
// WRONG - No cleanup
|
|
275
|
+
// subscription stays active
|
|
276
|
+
|
|
277
|
+
// CORRECT
|
|
278
|
+
onCleanup(() => subscription.unsubscribe())
|
|
279
|
+
})
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Store Issues
|
|
283
|
+
|
|
284
|
+
### Mutating Store Directly
|
|
285
|
+
|
|
286
|
+
**Symptom**: Changes don't trigger updates
|
|
287
|
+
|
|
288
|
+
```tsx
|
|
289
|
+
const [state, setState] = createStore({ items: [] })
|
|
290
|
+
|
|
291
|
+
// WRONG - Direct mutation
|
|
292
|
+
state.items.push(newItem) // Won't trigger updates!
|
|
293
|
+
|
|
294
|
+
// CORRECT - Use setState
|
|
295
|
+
setState("items", items => [...items, newItem])
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Nested Updates
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
const [state, setState] = createStore({
|
|
302
|
+
user: { profile: { name: "John" } }
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
// WRONG
|
|
306
|
+
state.user.profile.name = "Jane"
|
|
307
|
+
|
|
308
|
+
// CORRECT
|
|
309
|
+
setState("user", "profile", "name", "Jane")
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Debugging
|
|
313
|
+
|
|
314
|
+
### Console Not Visible
|
|
315
|
+
|
|
316
|
+
OpenTUI captures console output:
|
|
317
|
+
|
|
318
|
+
```tsx
|
|
319
|
+
import { useRenderer } from "@opentui/solid"
|
|
320
|
+
import { onMount } from "solid-js"
|
|
321
|
+
|
|
322
|
+
function App() {
|
|
323
|
+
const renderer = useRenderer()
|
|
324
|
+
|
|
325
|
+
onMount(() => {
|
|
326
|
+
renderer.console.show()
|
|
327
|
+
console.log("Now visible!")
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
return <box>{/* ... */}</box>
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Tracking Reactivity
|
|
335
|
+
|
|
336
|
+
Use `createEffect` to debug:
|
|
337
|
+
|
|
338
|
+
```tsx
|
|
339
|
+
createEffect(() => {
|
|
340
|
+
console.log("State:", {
|
|
341
|
+
count: count(),
|
|
342
|
+
items: items(),
|
|
343
|
+
})
|
|
344
|
+
})
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Runtime Issues
|
|
348
|
+
|
|
349
|
+
### Use Bun
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
# WRONG
|
|
353
|
+
node src/index.tsx
|
|
354
|
+
npm run start
|
|
355
|
+
|
|
356
|
+
# CORRECT
|
|
357
|
+
bun run src/index.tsx
|
|
358
|
+
bun run start
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Async render()
|
|
362
|
+
|
|
363
|
+
The render function is async when creating a renderer:
|
|
364
|
+
|
|
365
|
+
```tsx
|
|
366
|
+
// This is fine - Bun supports top-level await
|
|
367
|
+
render(() => <App />)
|
|
368
|
+
|
|
369
|
+
// If you need the renderer
|
|
370
|
+
import { createCliRenderer } from "@opentui/core"
|
|
371
|
+
import { render } from "@opentui/solid"
|
|
372
|
+
|
|
373
|
+
const renderer = await createCliRenderer()
|
|
374
|
+
render(() => <App />, renderer)
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## Common Error Messages
|
|
378
|
+
|
|
379
|
+
### "Cannot read properties of undefined"
|
|
380
|
+
|
|
381
|
+
Usually a missing reactive access:
|
|
382
|
+
|
|
383
|
+
```tsx
|
|
384
|
+
// Check if signal is being called
|
|
385
|
+
<text>{count()}</text> // Note the ()
|
|
386
|
+
|
|
387
|
+
// Check if props are being accessed correctly
|
|
388
|
+
<text>{props.value}</text> // Not destructured
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### "JSX element has no corresponding closing tag"
|
|
392
|
+
|
|
393
|
+
Check component naming:
|
|
394
|
+
|
|
395
|
+
```tsx
|
|
396
|
+
// Wrong
|
|
397
|
+
<tab-select></tab-select>
|
|
398
|
+
|
|
399
|
+
// Correct
|
|
400
|
+
<tab_select></tab_select>
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### "store is not a function"
|
|
404
|
+
|
|
405
|
+
Stores aren't called like signals:
|
|
406
|
+
|
|
407
|
+
```tsx
|
|
408
|
+
const [store, setStore] = createStore({ count: 0 })
|
|
409
|
+
|
|
410
|
+
// WRONG
|
|
411
|
+
<text>{store().count}</text>
|
|
412
|
+
|
|
413
|
+
// CORRECT
|
|
414
|
+
<text>{store.count}</text>
|
|
415
|
+
```
|