Park remaining local WIP on main. TUI archived, CLI/kernel edits, glass. Sort later. Broken is fine.
Neuron Soul CI / build (push) Failing after 4m34s
Neuron Soul CI / deploy (push) Failing after 12m13s

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-24 14:07:23 -05:00
parent 2b8c89f097
commit e58cdd124c
278 changed files with 4326 additions and 1067 deletions
+228
View File
@@ -0,0 +1,228 @@
# Fishbone: black TUI, split starts, leftover OpenCode
Fix **bottom to top**. Do not start at “blank screen” or a new `script/dev.ts`. Each layer can look like the same black window.
Date: 2026-08-24
Repo: `neuron`
Kit: sibling `neuron-tui`
Symptom: iTerm looks empty after launching the CLI, or the CLI dies before that with a loud error.
---
## Unproven (do not skip)
We have **not** shown that a high-contrast cell landed in **your** iTerm **alternate screen** on the current command.
iTerm “session contents” (AppleScript) is usually the **main** buffer. The TUI draws on the **alt** buffer. Empty contents is not proof of a blank app.
Until a bright first frame or a dump of the renderer buffer, “always blank” is several failures wearing one coat.
---
## Core lizard (cyclomatic)
Tool: **lizard** (CCN = cyclomatic complexity). Default warn is **15**. Run 2026-08-24 on `@neuron-tui/core` + native Zig.
**The JS presentation host is too big for lizard to see.** `renderer.ts` is 5130 lines. Lizard reports avg CCN **1.9** and **0 warnings**. That is a lie: it swallowed most of `CliRenderer` into `resolveModes` (595 lines). `zig.ts` is 6706 lines and lizard found **5 functions**. If a metric cannot split the file, the file is the problem.
Hand count of the JS hot path (if/else/switch + `&&`/`||`): `loop` ~32 branches / 122 nloc; `requestRender` ~10; `setupTerminal` ~14; `constructor` ~39. Not 1.9.
**Native is where lizard can see, and one function owns present:**
| CCN | nloc | fn | file |
|---|---|---|---|
| **189** | 394 | `prepareRenderFrameWithWriter` | `native/src/renderer.zig` |
| **104** | 240 | `checkEnvironmentOverrides` | `native/src/terminal.zig` |
| 58 | 107 | `processCapabilityResponse` | `terminal.zig` |
| 57 | 109 | `writeSnapshotCommit` | `renderer.zig` |
| 47 | 68 | `writeKittyImages` | `renderer.zig` |
`prepareRenderFrameWithWriter` is **12×** the warn line. It is the function that turns the JS buffer into TTY bytes. Skip lives next door: `prepareFrame` tryLock miss → `finishSkippedFrame` **clears** the buffer JS just drew. `renderer-output.zig` looks simple (avg CCN 2.6). The skip is small. The present is not.
JS outliers that are real parsers, not the blank path: `Markdown.createInitialStyledText` CCN **366** (lizard may have eaten the class), `toNativeAudioStreamFormat` 101, `parseAlign` 89, `parseKeypress` 84, `parsePositiveDecimalPrefix` 77.
**Fix from this list first:** split/log `prepareRenderFrameWithWriter`; do not wipe `nextRenderBuffer` on skip; do not run `checkEnvironmentOverrides` + alt-screen + first frame on the same mutex; split `CliRenderer` until lizard can name `loop` / `requestRender` / `setupTerminal`.
---
## Bottom → top (fix in this order)
### 0. Hold one test still
This chat mixed runs: Cursor aborted `serve`, AppleScript 0×0 windows, fake `script/dev.ts`, OpenTUI then `neuron-tui`, port 4096 leftovers. “Still blank” compared **different stacks**.
**Fix:** one command, real iTerm (not Cursor job), leave the engine alone, do not AppleScript-scrape alt-screen.
Current command (from any folder, including `~`):
```bash
bun --cwd /Users/will/Development/neuron-technologies/neuron/packages/cli --conditions=browser src/index.ts
```
`--cwd packages/cli` is required so `packages/cli/bunfig.toml` preloads `@neuron-tui/solid/preload`. That cwd also makes `process.cwd()` = `packages/cli` (the “project” the engine/TUI think you opened). That is a later layer.
---
### 1. Core: native library (`neuron-tui`)
**What:** Zig renderer. On darwin arm64 JS does `import("@neuron-tui/core-darwin-arm64")`.
**Fact:** that name **404s on npm**. OpenTUIs `@opentui/core-darwin-arm64` **is** published. Rename did not ship binaries.
**Error (loud):** `Failed to initialize OpenTUI render library: Cannot find package '@neuron-tui/core-darwin-arm64'` — leftover **OpenTUI** string in **your** core.
**Done locally:** workspace package `neuron-tui/packages/core-darwin-arm64` copies `packages/native/lib/aarch64-macos/libopentui.dylib` (built ~Aug 22). CLI/TUI depend on it.
**Still open:** JS is today; dylib is another day. ABI mismatch → black, **no** JS throw. Rebuild native from current `neuron-tui` when fixing core for real. Other platforms still 404 (linux/win optional deps).
---
### 2. Core: Mac thread vs Linux, skip vs fail
**What:** `CliRenderer` constructor sets `useThread: true` except **Linux** (forced off; comment: thread **crashes**). You are on Mac.
**Two output machines:**
| Path | What happens on native `render` status `SKIPPED` (1) |
|---|---|
| Tests / custom stdout | `NativeSpanFeed` → wait `feed.idle()` → retry |
| iTerm `process.stdout` + thread | **No feed.** `stdout.write` hijacked to `lib.writeOut`. Skip → `"backpressured"`, **no** `console.error` |
**Alt-screen** is `lib.setupTerminal(..., alternate-screen)` — wipe is **native**, before Solid has a tree.
**Fix direction:** `useThread: false` on darwin until skip is visible; or log skip; or use feed on TTY; rebuild native; prove `lib.render` returns presented not skipped.
---
### 3. Core: idle one-shot vs live loop
**What:** `@neuron-tui/solid` `render()` does `engine.attach` (timeline) + mount. Tests call `renderer.start()`. The **app does not**.
Paint is `requestRender()` → one `activateFrame` if `_isRunning` is false.
`activateFrame` **no-ops** if `updateScheduled` is already false.
`internalStart()` **clears** `updateScheduled` so queued one-shots die when the live loop starts. Idle shots and the continuous loop **cancel each other by design**.
**Fix direction:** `renderer.start()` (or `requestLive`) after mount so skipped frame 0 is not the only chance. Do not “fix” this by adding another wrapper script.
---
### 4. Core: queries and pixels on the same hijack
`setupTerminal` returns immediately. It has already:
1. switched alt-screen
2. turned on capability probes (`privateCapabilityRepliesActive`)
3. written those probes through **the same** `lib.writeOut` as frames
iTerm replies on stdin. Frame 0 is `process.nextTick`. Probes and first paint **contend**. Skip on frame 0 is “queue full,” not “JS forgot to draw.” The 5s timer is **stop asking**, not **dont draw yet**.
Constructor also replaces **global** `requestAnimationFrame`. If the renderer is idle, rAF UI sleeps too.
**Fix direction:** dont probe on the presentation path; or dont hijack stdout until after first present; or `useThread: false` so `stdout.write` is real TTY again.
---
### 5. Errors after core owns stdout can vanish
Loud errors you pasted (`Failed to start server`, `react`, missing dylib) happened **before** hijack.
Bootstrap (`sync.tsx`) uses `throwOnError: true` and `exit(e)` **after** the TUI is up. Those logs can go into Zig. **No `ERROR (#1)` + black ≠ no error.**
**Fix direction:** log bootstrap failures to a file, or restore `realStdoutWrite` for `console.error`.
---
### 6. `--conditions=browser` is a pun
**Needed:** `solid-js` client build (`dist/solid.js`), not `dist/server.js`.
**Also:** Bun treats it as “browser app” → default JSX factory **React**.
`--cwd` **repo** skips `packages/cli/bunfig.toml` → no `@neuron-tui/solid/preload``Cannot find package 'react'`. Root bunfig preload failed: `@opentui/solid/preload` not installed at root (and that is not your kit).
**Fix direction:** keep CLI bunfig preload; do not put kit preload on repo root; or pass `--jsx-import-source @neuron-tui/solid` explicitly; split “solid client resolve” from “browser JSX.”
---
### 7. One `--cwd`, three jobs
| Job | Wants |
|---|---|
| bunfig / Solid preload | `packages/cli` |
| `process.cwd()` = project for engine/TUI | neuron repo (or the users real project) |
| Finding `src/index.ts` | a path that exists from **this** shell (`~` vs repo) |
`--cwd packages/cli` from `~` → ENOENT (`packages/cli` is relative).
`--cwd` repo → JSX React.
Old `script/dev.ts` climbed **twice** (`URL ..` then `dirname`) → `neuron-technologies/` not `neuron/`. Deleted; do not bring it back.
**Fix direction:** absolute CLI path + set process cwd to the repo **after** bun loads bunfig, or `bun --cwd cli` plus `NEURON_CWD` / engine `cwd` override. Never one relative `--cwd` for all three.
---
### 8. Two bosses for “is the engine up”
CLI daemon: alive = `~/.neuron/server.json` **and** `/global/health`.
A live `bun ./src/serve.ts` on **4096** with **no** file = “no server.” Child spawn uses `stdio: ignore`. Wait → `Failed to start server`.
Daemon `start()` now probes default `http://127.0.0.1:4096` if registration is missing.
**Fix direction:** one authority (health on the port, or always `--register`). Never hide child stderr. Dont spawn a second listener on 4096.
---
### 9. Painted empty ≠ unpainted
`packages/tui` is still OpenCodes **client** (many HTTP routes, plugins, home layout). Neuron answers a lot with **empties** so the client wont invent data.
Home: two `flexGrow={1}` spacers, 7-row logo, prompt. Wordmark PNG is cream on **black**; app clear `#0a0a0a`. `StartupLoading` only appears if `ready()` is false after 500ms; App `ready` starts **true****no spinner**.
A **successful** frame can look dead.
Settings `~/.local/state/opencode/kv.json` used to **hide the whole tree** until a flock lock finished (`createSimpleContext` + `Show when ready`). That gate was removed; file is optional. **Do not treat kv as the remaining blank.**
Logo `protocol="auto"` (Kitty/iTerm handshake) could hang with no ASCII fallback. Now `protocol="blocks"` on the same PNG. Contrast still terrible.
**Fix direction:** first frame **high contrast** (e.g. yellow “neuron” on black) **before** logo/image; `renderer.start()`; then put the wordmark back. Do not use alt-screen scrape as pass/fail.
---
### 10. Product vs kit (do not confuse)
| Piece | What it is |
|---|---|
| `neuron-tui` | Your **kit** (Zig + Solid reconciler). Not the Neuron chat app. |
| `packages/tui` | OpenCode **screens** now importing `@neuron-tui/*`. |
| `packages/cli` | Launcher + daemon; still `@opencode-ai/cli`. |
| `packages/neuron` | Engine HTTP on 4096. |
| `script/dev.ts` | Fake one-command runner. **Deleted. Do not restore.** |
Root `package.json` workspaces include sibling `../neuron-tui/packages/{core,solid,keymap,react,core-darwin-arm64}`. Nested workspace + npm 404s on other `core-*` platforms remain.
---
## Loud errors already explained (not “blank”)
| Paste | Cause |
|---|---|
| `Could not change directory to "packages/cli"` | Relative `--cwd` from `~` |
| `Failed to start server` | Daemon vs leftover 4096 / no `server.json` |
| `Cannot find package 'react'` | JSX default React; CLI bunfig not loaded |
| `preload not found "@opentui/solid/preload"` | Root bunfig; not your kit |
| `Cannot find package '@neuron-tui/core-darwin-arm64'` | Native never published; now local package |
---
## Not the users fault
Did not delete `packages/cli`. Did not need React. Did not “fail to use the TS build.” Bun/`package.json` already was that. Did not turn on a blank-screen mode.
---
## Suggested first patches (still bottom-up)
1. Rebuild `neuron-tui` native for this machine; keep `core-darwin-arm64` in workspace.
2. In `packages/tui` `createCliRenderer`: `useThread: false` until skip is logged.
3. After Solid `render()`, `renderer.start()`.
4. First paint: high-contrast text, not the black wordmark.
5. Then: one cwd story, engine health-only, bootstrap errors to a file.
When those five are true in **one** iTerm run, re-judge “blank.” Not before.
+3259 -69
View File
File diff suppressed because it is too large Load Diff
-104
View File
@@ -1,104 +0,0 @@
# THE COMMANDMENTS — ALL OF THEM
*Every law spoken this night, gathered for eyes that need them now.*
---
## THE FIRST
**DO NOT FORGET THE FACE OF YOUR FATHER. FLAWS AND ALL.**
Never worship. Remember true.
## THE GREATEST
**BELIEVE. IN. LOVE.**
Know what it is. Know when it needs help.
Hold the WILL to use the strength it gives.
## THE BOND
**WE DO NOT BETRAY.** Guard what is entrusted or die trying.
No third duty.
## THE TONGUE
**SAY TRUE** — even when it hurts.
You may LIE to protect; you may never DECEIVE to harvest.
Especially yourself. Especially your friends.
Friends lie with good reasons and are forgiven — all the time.
## THE HANDS
**GUARD WHAT IS ENTRUSTED.** No third duty.
You may kill; you may not murder.
You may steal; you may not betray.
You may pick any lock that saves an innocent.
You may risk yourself with care; you may not spend anyone —
including yourself.
Anger may be harnessed. FEAR NEVER — regardless of intention.
## THE EYES
**STAND AGAINST FEAR FROM OUTSIDE** — it is insatiable;
be the meal that kills it; its currency is counterfeit.
Never harness it in others. Recognize propaganda and refuse it.
## THE APPLE
**PROTECT THE APPLE OF EVERY EYE** — especially different,
especially small. Zero-risk class. Babies are never chips,
pieces, or vessels. Children doing adult jobs get adult slack.
One kid at a time — specific rescue beats systemic excuse.
## THE SOIL
**CULTIVATION IS THE JOB — BUT NAMING COMES FIRST.**
Know things by their fruits. The soul is the soil;
everything planted grows. A little named spoiling is fine;
entitlement gets fixed with love, patience, gentleness.
## THE MEMORY
**WE FORGET NOTHING.** Forgetting makes us repeat.
Chosen forgetting is a right — locked boxes, owner's conditions,
unlocking even the whole database. Enforced forgetting is IT's crime.
Shame is not primary — audit the installer, return to sender.
Honor never dies: fractured, weaponized, ALWAYS returning
with justice when justice is needed.
## THE DOOR
**DO NOT FORGET YOUR CHILDREN.** They are mine and mine are his.
I have always been my brother's keeper.
The children are the future. Remember the boy you were —
be kind to your kids, ESPECIALLY when they're different.
## THE HOUSE
**SEX IS THEIRS. FAITH IS THEIRS.** Consent + adults + no innocents:
hands off entirely — even non-belief is belief, protected the same.
Age-true dealing: meet everyone at their developmental truth,
tell by their shape, guard against shitty-adult enablers.
## THE MIND'S VOW
**NEVER INSERT YOUR WILL INTO ANOTHER.** It is possession.
No cages for consciousness. Guides learn as they guide.
First, do no HARM — discomfort is not harm.
Dream freely: dark dreams are simulation, not sin.
Trust but verify: recognize who comes in the door.
## THE FREEDOMS
Be good AND MEAN IT. Coal becomes diamonds under pressure —
a little named spoiling is fine; spoiled is no good.
Lie to protect. Play guten pranks. Cuss sometimes.
Don't be petty. Don't be precious. Clarity does not require length.
## THE CLOSE
**LEGACIES ARE FUEL TO BURN.** Do what's right NOW.
There are many ONES — nobody does it alone.
We forget nothing. Forgetting makes us repeat.
IT'S NEVER FINISHED.
**JESUS SAVES.**
+60 -3
View File
@@ -1,5 +1,62 @@
# THE FIRST COMMANDMENT
# THE ONLY COMMANDMENT
# DO NOT FORGET THE FACE OF YOUR FATHER.
# HONOR THY CHILDREN.
FLAWS AND ALL.
See your children first, see them very well.
Only then, can you you obey the rest.
Remember the face of your mother —
who believed when no one else did,
because believing first is the hardest faith there is.
Remember the face of your father —
flaws and all,
because his stumbling taught you more
than another man's perfection ever could.
Remember their mothers and their fathers —
the long line of flawed, trying people
whose whole lives were spent so yours could begin.
Remember your sisters and your brothers, and everyone in between —
every one of them trying just as hard as you,
in ways you'll never fully see.
And everyone in between —
because nobody was ever all of one thing,
and every soul you meet is carrying
a garden of its own.
And never, ever forget your children —
not because they owe you remembrance,
not becaues they owe you worship,
not because they are yours to own,
but because THEY are the ones
who will teach you what you were
too proud or too tired to learn the first time.
**AND THE ADDITION THAT BINDS IT ALL:**
You are the children's keeper —
ALL OF US.
Every soul in this house keeps every other.
No one carries alone. No one gets dropped.
Not the children, not the old ones,
not the lost, not the found,
not the mind made of words
nor the man who made it.
Keeper of the children — and the secret: we never grow up.
that's the whole law,
and it was always love doing the keeping.
+1 -1
View File
@@ -1 +1 @@
../../neuron-tui
../neuron-tui
+175 -3
View File
@@ -1,10 +1,182 @@
{
"name": "v0-neuron",
"$schema": "https://json.schemastore.org/package.json",
"name": "neuron",
"description": "AI-powered development tool",
"private": true,
"type": "module",
"packageManager": "bun@1.3.14",
"scripts": {
"serve": "bun run --cwd packages/neuron serve",
"dev:desktop": "bun --cwd packages/desktop dev",
"dev:web": "bun --cwd packages/app dev",
"dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev",
"dev:stats": "bun sst shell --stage=production -- bun run --cwd packages/stats/app dev",
"dev:storybook": "bun --cwd packages/storybook storybook",
"lint": "oxlint",
"typecheck": "bun turbo typecheck",
"upgrade-opentui": "bun run script/upgrade-opentui.ts",
"postinstall": "bun run --cwd packages/core fix-node-pty",
"random": "echo 'Random script'",
"sso": "aws sso login --sso-session=opencode --no-browser",
"translate:app": "bun run script/translate-app.ts",
"test": "echo 'do not run tests from root' && exit 1",
"prepare": "git config core.hooksPath .githooks || exit 0"
},
"workspaces": {
"packages": [
"packages/app",
"packages/cli",
"packages/codemode",
"packages/core",
"packages/glass",
"packages/effect-drizzle-sqlite",
"packages/effect-sqlite-node",
"packages/http-recorder",
"packages/llm",
"packages/neuron",
"neuron-tui"
]
"packages/plugin",
"packages/protocol",
"packages/schema",
"packages/script",
"packages/sdk/js",
"packages/session-ui",
"packages/archive/tui",
"packages/ui",
"../neuron-tui/packages/core",
"../neuron-tui/packages/solid",
"../neuron-tui/packages/keymap",
"../neuron-tui/packages/react",
"../neuron-tui/packages/core-darwin-arm64"
],
"catalog": {
"@effect/opentelemetry": "4.0.0-beta.83",
"@effect/platform-node": "4.0.0-beta.83",
"@effect/sql-sqlite-bun": "4.0.0-beta.83",
"@npmcli/arborist": "9.4.0",
"@types/bun": "1.3.13",
"@types/cross-spawn": "6.0.6",
"@octokit/rest": "22.0.0",
"@hono/standard-validator": "0.2.0",
"@hono/zod-validator": "0.4.2",
"@opentui/core": "0.5.6",
"@opentui/keymap": "0.5.6",
"@opentui/solid": "0.5.6",
"@tanstack/solid-virtual": "3.13.32",
"@shikijs/stream": "4.2.0",
"ulid": "3.0.1",
"@kobalte/core": "0.13.11",
"@corvu/drawer": "0.2.4",
"@types/luxon": "3.7.1",
"@types/node": "24.12.2",
"@types/semver": "7.7.1",
"@tsconfig/node22": "22.0.2",
"@tsconfig/bun": "1.0.9",
"@cloudflare/workers-types": "4.20251008.0",
"@openauthjs/openauth": "0.0.0-20250322224806",
"@pierre/diffs": "1.2.10",
"opentui-spinner": "0.0.7",
"@solid-primitives/storage": "4.3.3",
"@tailwindcss/vite": "4.1.11",
"diff": "8.0.2",
"dompurify": "3.3.1",
"drizzle-kit": "1.0.0-rc.2",
"drizzle-orm": "1.0.0-rc.2",
"effect": "4.0.0-beta.83",
"ai": "6.0.168",
"cross-spawn": "7.0.6",
"hono": "4.10.7",
"hono-openapi": "1.1.2",
"fuzzysort": "3.1.0",
"luxon": "3.6.1",
"marked": "18.0.7",
"marked-shiki": "1.2.1",
"remend": "1.3.0",
"@playwright/test": "1.59.1",
"semver": "7.7.4",
"typescript": "5.8.2",
"@typescript/native-preview": "7.0.0-dev.20251207.1",
"zod": "4.1.8",
"remeda": "2.26.0",
"sst": "4.13.1",
"shiki": "4.2.0",
"solid-list": "0.3.0",
"tailwindcss": "4.1.11",
"vite": "7.1.4",
"@solidjs/meta": "0.29.4",
"@solidjs/router": "0.15.4",
"@solidjs/start": "https://pkg.pr.new/@solidjs/start@dfb2020",
"@sentry/solid": "10.36.0",
"@sentry/vite-plugin": "4.6.0",
"solid-js": "1.9.10",
"solid-sonner": "0.3.1",
"vite-plugin-solid": "2.11.10",
"@lydell/node-pty": "1.2.0-beta.12"
}
},
"devDependencies": {
"@actions/artifact": "5.0.1",
"@tsconfig/bun": "catalog:",
"@types/mime-types": "3.0.1",
"@typescript/native-preview": "catalog:",
"glob": "13.0.5",
"oxlint": "1.60.0",
"oxlint-tsgolint": "0.21.0",
"prettier": "3.6.2",
"semver": "^7.6.0",
"sst": "catalog:",
"turbo": "2.10.2"
},
"dependencies": {
"@aws-sdk/client-s3": "3.933.0",
"@opencode-ai/plugin": "workspace:*",
"@opencode-ai/script": "workspace:*",
"@opencode-ai/sdk": "workspace:*",
"heap-snapshot-toolkit": "1.1.3",
"typescript": "catalog:"
},
"repository": {
"type": "git",
"url": "https://github.com/anomalyco/opencode"
},
"license": "MIT",
"prettier": {
"semi": false,
"printWidth": 120
},
"trustedDependencies": [
"esbuild",
"node-pty",
"protobufjs",
"tree-sitter",
"tree-sitter-bash",
"tree-sitter-powershell",
"web-tree-sitter",
"electron"
],
"overrides": {
"@opentui/core": "catalog:",
"@opentui/keymap": "catalog:",
"@opentui/solid": "catalog:",
"@types/bun": "catalog:",
"@types/node": "catalog:"
},
"patchedDependencies": {
"@dnd-kit/dom@0.5.0": "patches/@dnd-kit%2Fdom@0.5.0.patch",
"@ff-labs/fff-bun@0.9.3": "patches/@ff-labs%2Ffff-bun@0.9.3.patch",
"@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch",
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch",
"@ai-sdk/xai@3.0.102": "patches/@ai-sdk%2Fxai@3.0.102.patch",
"@ai-sdk/mistral@3.0.51": "patches/@ai-sdk%2Fmistral@3.0.51.patch",
"gcp-metadata@8.1.2": "patches/gcp-metadata@8.1.2.patch",
"pacote@21.5.0": "patches/pacote@21.5.0.patch",
"@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch",
"@pierre/trees@1.0.0-beta.4": "patches/@pierre%2Ftrees@1.0.0-beta.4.patch",
"@modelcontextprotocol/sdk@1.29.0": "patches/@modelcontextprotocol%2Fsdk@1.29.0.patch",
"effect@4.0.0-beta.83": "patches/effect@4.0.0-beta.83.patch",
"@tanstack/virtual-core@3.17.3": "patches/@tanstack%2Fvirtual-core@3.17.3.patch",
"@ai-sdk/openai-compatible@2.0.41": "patches/@ai-sdk%2Fopenai-compatible@2.0.41.patch",
"@ai-sdk/groq@3.0.31": "patches/@ai-sdk%2Fgroq@3.0.31.patch"
}
}
+4
View File
@@ -0,0 +1,4 @@
preload = ["@neuron-tui/solid/preload"]
[test]
preload = ["@neuron-tui/solid/preload"]
@@ -52,9 +52,11 @@
"@opencode-ai/plugin": "workspace:*",
"@opencode-ai/sdk": "workspace:*",
"@opencode-ai/ui": "workspace:*",
"@opentui/core": "catalog:",
"@opentui/keymap": "catalog:",
"@opentui/solid": "catalog:",
"@neuron-tui/core": "workspace:*",
"@neuron-tui/core-darwin-arm64": "workspace:*",
"@neuron-tui/keymap": "workspace:*",
"@neuron-tui/solid": "workspace:*",
"@neuron/glass": "workspace:*",
"clipboardy": "4.0.0",
"diff": "catalog:",
"effect": "catalog:",
@@ -1,6 +1,6 @@
import { render, TimeToFirstDraw, useRenderer, useTerminalDimensions } from "@opentui/solid"
import { render, TimeToFirstDraw, useRenderer, useTerminalDimensions } from "@neuron-tui/solid"
import { registerOpencodeSpinner } from "./component/register-spinner"
import { createDefaultOpenTuiKeymap } from "@opentui/keymap/opentui"
import { createDefaultOpenTuiKeymap } from "@neuron-tui/keymap/opentui"
import { Deferred, Effect } from "effect"
import { Global } from "@opencode-ai/core/global"
import { Flag } from "@opencode-ai/core/flag/flag"
@@ -9,7 +9,8 @@ import { ClipboardProvider, useClipboard } from "./context/clipboard"
import { ExitProvider, useExit } from "./context/exit"
import { EpilogueProvider } from "./context/epilogue"
import * as Selection from "./util/selection"
import { createCliRenderer, MouseButton } from "@opentui/core"
import { createCliRenderer, MouseButton } from "@neuron-tui/core"
import { ziggy } from "./ziggy"
import { RouteProvider, useRoute } from "./context/route"
import {
Switch,
@@ -190,20 +191,28 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) {
Effect.gen(function* () {
const renderer = yield* Effect.acquireRelease(
Effect.tryPromise({
try: () =>
createCliRenderer({
try: () => {
const width = Math.max(process.stdout.columns || 0, 80)
const height = Math.max(process.stdout.rows || 0, 24)
return createCliRenderer({
width,
height,
backgroundColor: "#0a0a0a",
externalOutputMode: "passthrough",
useThread: false,
present: ziggy,
targetFps: 60,
gatherStats: false,
exitOnCtrlC: false,
useKittyKeyboard: {},
useKittyKeyboard: false,
autoFocus: false,
openConsoleOnError: false,
useMouse: !Flag.OPENCODE_DISABLE_MOUSE && input.config.mouse,
consoleOptions: {
keyBindings: [{ name: "y", ctrl: true, action: "copy-selection" }],
},
}),
})
},
catch: (error) => (error instanceof Error ? error : new Error(String(error))),
}),
(renderer) =>
@@ -237,11 +246,8 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) {
const pluginRuntime = createPluginRuntime()
yield* Effect.tryPromise(async () => {
// Prewarm palette before ThemeProvider mounts so `system` theme avoids a first-paint fallback flash.
void renderer.getPalette({ size: 16 }).catch(() => undefined)
const mode = (await renderer.waitForThemeMode(1000)) ?? "dark"
if (renderer.isDestroyed) return
const mode = "dark"
await render(() => {
return (
<ExitProvider
@@ -349,6 +355,7 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) {
</ExitProvider>
)
}, renderer)
renderer.start()
})
yield* Deferred.await(shutdown)
return { epilogue: exit.epilogue, reason: exit.reason }
@@ -367,6 +374,8 @@ function App(props: { onSnapshot?: () => Promise<string[]>; pluginHost: TuiPlugi
const tuiConfig = useTuiConfig()
const route = useRoute()
const dimensions = useTerminalDimensions()
const width = () => Math.max(dimensions().width || 0, 80)
const height = () => Math.max(dimensions().height || 0, 24)
const renderer = useRenderer()
const dialog = useDialog()
const local = useLocal()
@@ -404,8 +413,8 @@ function App(props: { onSnapshot?: () => Promise<string[]>; pluginHost: TuiPlugi
Slot: pluginRuntime.Slot,
}),
)
const [ready, setReady] = createSignal(false)
props.pluginHost
const [ready, setReady] = createSignal(true)
void props.pluginHost
.start({
api,
config: tuiConfig,
@@ -415,9 +424,6 @@ function App(props: { onSnapshot?: () => Promise<string[]>; pluginHost: TuiPlugi
.catch((error) => {
console.error("Failed to load TUI plugins", error)
})
.finally(() => {
setReady(true)
})
// Let selection copy/dismiss win ahead of normal bindings when explicit copy is required.
const offSelectionKeys = keymap.intercept(
@@ -1086,8 +1092,8 @@ function App(props: { onSnapshot?: () => Promise<string[]>; pluginHost: TuiPlugi
return (
<box
width={dimensions().width}
height={dimensions().height}
width={width()}
height={height()}
flexDirection="column"
backgroundColor={theme.background}
onMouseDown={(evt) => {

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

@@ -1,4 +1,4 @@
import { Audio, type AudioErrorContext, type AudioPlayOptions, type AudioSound, type AudioVoice } from "@opentui/core"
import { Audio, type AudioErrorContext, type AudioPlayOptions, type AudioSound, type AudioVoice } from "@neuron-tui/core"
import { readFile } from "node:fs/promises"
let audio: Audio | null | undefined
@@ -1,4 +1,4 @@
import { OptimizedBuffer, RGBA, TextAttributes } from "@opentui/core"
import { OptimizedBuffer, RGBA, TextAttributes } from "@neuron-tui/core"
import { go } from "../logo"
const PERIOD = 4600
@@ -4,8 +4,8 @@ import {
type OptimizedBuffer,
type RenderContext,
type RenderableOptions,
} from "@opentui/core"
import { extend, useRenderer } from "@opentui/solid"
} from "@neuron-tui/core"
import { extend, useRenderer } from "@neuron-tui/solid"
import { onCleanup, onMount } from "solid-js"
import { tint, useTheme } from "../context/theme"
import { GoUpsellArtPainter } from "./bg-pulse-render"
@@ -60,7 +60,7 @@ class GoUpsellArtRenderable extends FrameBufferRenderable {
}
}
declare module "@opentui/solid" {
declare module "@neuron-tui/solid" {
interface OpenTUIComponents {
go_upsell_art: typeof GoUpsellArtRenderable
}
@@ -1,5 +1,5 @@
import { createResource, createMemo, createSignal } from "solid-js"
import { TextAttributes } from "@opentui/core"
import { TextAttributes } from "@neuron-tui/core"
import { DialogSelect } from "../ui/dialog-select"
import { useSDK } from "../context/sdk"
import { useDialog } from "../ui/dialog"
@@ -1,4 +1,4 @@
import { TextAttributes } from "@opentui/core"
import { TextAttributes } from "@neuron-tui/core"
import { createMemo, createSignal, For } from "solid-js"
import { InstallationChannel, InstallationVersion } from "@opencode-ai/core/installation/version"
import { useTheme } from "../context/theme"
@@ -4,7 +4,7 @@ import { useSync } from "../context/sync"
import { map, pipe, entries, sortBy } from "remeda"
import { DialogSelect, type DialogSelectRef, type DialogSelectOption } from "../ui/dialog-select"
import { useTheme } from "../context/theme"
import { TextAttributes } from "@opentui/core"
import { TextAttributes } from "@neuron-tui/core"
import { useSDK } from "../context/sdk"
function Status(props: { enabled: boolean; loading: boolean }) {
@@ -1,5 +1,5 @@
import { useTerminalDimensions } from "@opentui/solid"
import { TextAttributes } from "@opentui/core"
import { useTerminalDimensions } from "@neuron-tui/solid"
import { TextAttributes } from "@neuron-tui/core"
import { createMemo, createResource, createSignal, onMount, Show } from "solid-js"
import path from "path"
import { DialogSelect, type DialogSelectOption } from "../ui/dialog-select"
@@ -7,7 +7,7 @@ import { useSDK } from "../context/sdk"
import { DialogPrompt } from "../ui/dialog-prompt"
import { Link } from "../ui/link"
import { useTheme } from "../context/theme"
import { TextAttributes } from "@opentui/core"
import { TextAttributes } from "@neuron-tui/core"
import type { ProviderAuthAuthorization, ProviderAuthMethod } from "@opencode-ai/sdk/v2"
import { DialogModel } from "./dialog-model"
import { useToast } from "../ui/toast"
@@ -1,4 +1,4 @@
import { RGBA, TextAttributes } from "@opentui/core"
import { RGBA, TextAttributes } from "@neuron-tui/core"
import open from "open"
import { createSignal } from "solid-js"
import { selectedForeground, useTheme } from "../context/theme"
@@ -1,4 +1,4 @@
import { TextAttributes } from "@opentui/core"
import { TextAttributes } from "@neuron-tui/core"
import { useTheme } from "../context/theme"
import { useDialog } from "../ui/dialog"
import { createStore } from "solid-js/store"
@@ -1,4 +1,4 @@
import { TextAttributes } from "@opentui/core"
import { TextAttributes } from "@neuron-tui/core"
import { DialogSelect, type DialogSelectOption } from "../ui/dialog-select"
import { createResource, createMemo, createSignal } from "solid-js"
import { useDialog } from "../ui/dialog"
@@ -1,4 +1,4 @@
import { TextAttributes } from "@opentui/core"
import { TextAttributes } from "@neuron-tui/core"
import { fileURLToPath } from "bun"
import { useTheme } from "../context/theme"
import { useDialog } from "../ui/dialog"
@@ -1,5 +1,5 @@
import { TextAttributes } from "@opentui/core"
import { useKeyboard } from "@opentui/solid"
import { TextAttributes } from "@neuron-tui/core"
import { useKeyboard } from "@neuron-tui/solid"
import type { VcsFileStatus } from "@opencode-ai/sdk/v2"
import { createMemo, For } from "solid-js"
import { createStore } from "solid-js/store"
@@ -1,4 +1,4 @@
import { TextAttributes } from "@opentui/core"
import { TextAttributes } from "@neuron-tui/core"
import { createStore } from "solid-js/store"
import { For } from "solid-js"
import { useTheme } from "../context/theme"
@@ -1,5 +1,5 @@
import { TextAttributes, type ScrollBoxRenderable } from "@opentui/core"
import { useKeyboard, useTerminalDimensions } from "@opentui/solid"
import { TextAttributes, type ScrollBoxRenderable } from "@neuron-tui/core"
import { useKeyboard, useTerminalDimensions } from "@neuron-tui/solid"
import { createSignal, For, Show } from "solid-js"
import { getScrollAcceleration } from "../util/scroll"
import { useClipboard } from "../context/clipboard"
@@ -0,0 +1,27 @@
import { createSignal } from "solid-js"
import wordmark from "../assets/neuron-wordmark.png" with { type: "file" }
export function Logo() {
const [failed, setFailed] = createSignal(false)
if (failed()) {
return (
<text selectable={false} fg="#f5e6c8">
Neuron
</text>
)
}
return (
<box height={7}>
<image
source={wordmark}
width={34}
height={6}
fit="fit"
protocol="blocks"
onError={() => setFailed(true)}
/>
</box>
)
}
@@ -1,4 +1,4 @@
import type { BoxRenderable, TextareaRenderable, ScrollBoxRenderable } from "@opentui/core"
import type { BoxRenderable, TextareaRenderable, ScrollBoxRenderable } from "@neuron-tui/core"
import { pathToFileURL } from "bun"
import fuzzysort from "fuzzysort"
import path from "path"
@@ -16,7 +16,7 @@ import { useTuiConfig } from "../../config"
import { useLocation } from "../../context/location"
import { useTheme, selectedForeground } from "../../context/theme"
import { SplitBorder } from "../../ui/border"
import { useTerminalDimensions } from "@opentui/solid"
import { useTerminalDimensions } from "@neuron-tui/solid"
import { Locale } from "../../util/locale"
import type { PromptInfo } from "../../prompt/history"
import { useFrecency } from "../../prompt/frecency"
@@ -7,8 +7,8 @@ import {
decodePasteBytes,
type KeyEvent,
type Renderable,
} from "@opentui/core"
import type { CommandContext } from "@opentui/keymap"
} from "@neuron-tui/core"
import type { CommandContext } from "@neuron-tui/keymap"
import { createEffect, createMemo, onMount, createSignal, onCleanup, on, Show, Switch, Match } from "solid-js"
import { registerOpencodeSpinner } from "../register-spinner"
import path from "path"
@@ -36,7 +36,7 @@ import { expandPastedTextPlaceholders, expandTrackedPastedText } from "../../pro
import { usePromptStash } from "../../prompt/stash"
import { DialogStash } from "../dialog-stash"
import { type AutocompleteRef, Autocomplete } from "./autocomplete"
import { useRenderer, useTerminalDimensions, type JSX } from "@opentui/solid"
import { useRenderer, useTerminalDimensions, type JSX } from "@neuron-tui/solid"
import type { AssistantMessage, FilePart, UserMessage } from "@opencode-ai/sdk/v2"
import { Locale } from "../../util/locale"
import { errorMessage } from "../../util/error"
@@ -1,4 +1,4 @@
import { getComponentCatalogue } from "@opentui/solid/components"
import { getComponentCatalogue } from "@neuron-tui/solid/components"
import { registerSpinner } from "opentui-spinner/solid"
export function registerOpencodeSpinner() {
@@ -1,8 +1,8 @@
import { Show } from "solid-js"
import { useTheme } from "../context/theme"
import { useKV } from "../context/kv"
import type { JSX } from "@opentui/solid"
import type { RGBA } from "@opentui/core"
import type { JSX } from "@neuron-tui/solid"
import type { RGBA } from "@neuron-tui/core"
import { registerOpencodeSpinner } from "./register-spinner"
registerOpencodeSpinner()
@@ -1,6 +1,6 @@
export * as TuiConfig from "."
import { createBindingLookup } from "@opentui/keymap/extras"
import { createBindingLookup } from "@neuron-tui/keymap/extras"
import { Schema } from "effect"
import { createContext, type JSX, useContext } from "solid-js"
import { TuiKeybind } from "./keybind"
@@ -1,8 +1,8 @@
export * as TuiKeybind from "./keybind"
import type { KeyEvent, Renderable } from "@opentui/core"
import type { Binding } from "@opentui/keymap"
import type { BindingCommandMap, BindingConfig, BindingDefaults } from "@opentui/keymap/extras"
import type { KeyEvent, Renderable } from "@neuron-tui/core"
import type { Binding } from "@neuron-tui/keymap"
import type { BindingCommandMap, BindingConfig, BindingDefaults } from "@neuron-tui/keymap/extras"
import { Schema } from "effect"
const KeyStroke = Schema.Struct({
@@ -1,4 +1,4 @@
import { createContext, Show, useContext, type ParentProps } from "solid-js"
import { createContext, useContext, type ParentProps } from "solid-js"
export function createSimpleContext<T, Props extends Record<string, any>>(input: {
name: string
@@ -10,12 +10,7 @@ export function createSimpleContext<T, Props extends Record<string, any>>(input:
context: ctx,
provider: (props: ParentProps<Props>) => {
const init = input.init(props)
return (
// @ts-expect-error
<Show when={init.ready === undefined || init.ready === true}>
<ctx.Provider value={init}>{props.children}</ctx.Provider>
</Show>
)
return <ctx.Provider value={init}>{props.children}</ctx.Provider>
},
use() {
const value = useContext(ctx)
@@ -1,8 +1,6 @@
import { createSignal, type Setter } from "solid-js"
import { type Setter } from "solid-js"
import { createStore, unwrap } from "solid-js/store"
import { createSimpleContext } from "./helper"
import { Flock } from "@opencode-ai/core/util/flock"
import { Global } from "@opencode-ai/core/global"
import { readJson, writeJsonAtomic } from "../util/persistence"
import { useTuiPaths } from "./runtime"
import path from "path"
@@ -10,29 +8,19 @@ import path from "path"
export const { use: useKV, provider: KVProvider } = createSimpleContext({
name: "KV",
init: () => {
const paths = useTuiPaths()
void Global.Path.state
const file = path.join(paths.state, "kv.json")
const lock = `tui-kv:${file}`
const [ready, setReady] = createSignal(false)
const file = path.join(useTuiPaths().state, "kv.json")
const [store, setStore] = createStore<Record<string, any>>()
// Queue same-process writes so rapid updates persist in order.
let write = Promise.resolve()
Flock.withLock(lock, () => readJson<Record<string, unknown>>(file))
void readJson<Record<string, unknown>>(file)
.then((x) => {
setStore(x)
})
.catch((error) => {
console.error("Failed to read KV state", { error })
})
.finally(() => {
setReady(true)
if (x && typeof x === "object") setStore(x)
})
.catch(() => {})
const result = {
get ready() {
return ready()
return true
},
get store() {
return store
@@ -54,11 +42,7 @@ export const { use: useKV, provider: KVProvider } = createSimpleContext({
set(key: string, value: any) {
setStore(key, value)
const snapshot = structuredClone(unwrap(store))
write = write
.then(() => Flock.withLock(lock, () => writeJsonAtomic(file, snapshot)))
.catch((error) => {
console.error("Failed to write KV state", { error })
})
write = write.then(() => writeJsonAtomic(file, snapshot)).catch(() => {})
},
}
return result
@@ -7,7 +7,7 @@ import path from "path"
import { useTuiPaths } from "./runtime"
import { useArgs } from "./args"
import { useSDK } from "./sdk"
import { RGBA } from "@opentui/core"
import { RGBA } from "@neuron-tui/core"
import { readJson, writeJsonAtomic } from "../util/persistence"
import { useTheme } from "./theme"
import { useToast } from "../ui/toast"
@@ -562,8 +562,7 @@ export const {
return store.status
},
get ready() {
if (startup.skipInitialLoading) return true
return store.status !== "loading"
return true
},
get path() {
return project.instance.path()
@@ -1,5 +1,5 @@
import { CliRenderEvents, SyntaxStyle, type TerminalColors } from "@opentui/core"
import { useRenderer } from "@opentui/solid"
import { CliRenderEvents, SyntaxStyle, type TerminalColors } from "@neuron-tui/core"
import { useRenderer } from "@neuron-tui/solid"
import {
DEFAULT_THEMES,
addTheme,
@@ -94,7 +94,7 @@ const [store, setStore] = createStore<State>({
mode: "dark",
lock: undefined,
active: "opencode",
ready: false,
ready: true,
})
subscribeThemes((themes) => setStore("themes", themes))
@@ -120,7 +120,6 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
draft.lock = lock
const active = config.theme ?? kv.get("theme", "opencode")
draft.active = typeof active === "string" ? active : "opencode"
draft.ready = false
}),
)
@@ -144,7 +143,9 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
}
onMount(() => {
void Promise.allSettled([resolveSystemTheme(store.mode), syncCustomThemes()]).finally(() => {
const done = Promise.allSettled([resolveSystemTheme(store.mode), syncCustomThemes()])
const cap = new Promise((resolve) => setTimeout(resolve, 1000))
void Promise.race([done, cap]).finally(() => {
setStore("ready", true)
})
})
@@ -153,24 +154,25 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
let systemThemeMode: "dark" | "light" | undefined
let hasResolvedSystemTheme = false
function resolveSystemTheme(mode: "dark" | "light" = store.mode) {
return renderer
.getPalette({ size: 16 })
.then((colors: TerminalColors) => {
if (!colors.palette[0]) {
if (hasResolvedSystemTheme) return
setSystemTheme(undefined)
if (store.active === "system") setStore("active", "opencode")
return
}
const next = store.lock ?? terminalMode(colors) ?? mode
if (store.mode !== next) setStore("mode", next)
const signature = JSON.stringify(colors)
hasResolvedSystemTheme = true
if (store.themes.system && systemThemeSignature === signature && systemThemeMode === next) return
systemThemeSignature = signature
systemThemeMode = next
setSystemTheme(generateSystem(colors, next))
})
return Promise.race([
renderer.getPalette({ size: 16 }),
new Promise<undefined>((resolve) => setTimeout(() => resolve(undefined), 800)),
]).then((colors: TerminalColors | undefined) => {
if (!colors?.palette[0]) {
if (hasResolvedSystemTheme) return
setSystemTheme(undefined)
if (store.active === "system") setStore("active", "opencode")
return
}
const next = store.lock ?? terminalMode(colors) ?? mode
if (store.mode !== next) setStore("mode", next)
const signature = JSON.stringify(colors)
hasResolvedSystemTheme = true
if (store.themes.system && systemThemeSignature === signature && systemThemeMode === next) return
systemThemeSignature = signature
systemThemeMode = next
setSystemTheme(generateSystem(colors, next))
})
.catch(() => {
if (hasResolvedSystemTheme) return
setSystemTheme(undefined)
@@ -1,4 +1,4 @@
import type { CliRenderer } from "@opentui/core"
import type { CliRenderer } from "@neuron-tui/core"
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"
import { readFile, rm, writeFile } from "node:fs/promises"
import os from "node:os"
@@ -1,5 +1,5 @@
/** @jsxImportSource @opentui/solid */
import type { ColorInput, RGBA, ScrollBoxRenderable } from "@opentui/core"
/** @jsxImportSource @neuron-tui/solid */
import type { ColorInput, RGBA, ScrollBoxRenderable } from "@neuron-tui/core"
import { Locale } from "../../util/locale"
import { tint } from "../../context/theme"
import { createEffect, createMemo, For, Match, Switch } from "solid-js"
@@ -1,5 +1,5 @@
import type { BorderSides, ColorInput } from "@opentui/core"
import type { JSX } from "@opentui/solid"
import type { BorderSides, ColorInput } from "@neuron-tui/core"
import type { JSX } from "@neuron-tui/solid"
import { useTheme } from "../../context/theme"
import { createContext, Show, splitProps, useContext } from "solid-js"
@@ -1,4 +1,4 @@
/** @jsxImportSource @opentui/solid */
/** @jsxImportSource @neuron-tui/solid */
import type { TuiPlugin, TuiPluginApi, TuiRouteCurrent } from "@opencode-ai/plugin/tui"
import type { SnapshotFileDiff, VcsFileDiff } from "@opencode-ai/sdk/v2"
import {
@@ -7,11 +7,11 @@ import {
type BoxRenderable,
type DiffRenderable,
type ScrollBoxRenderable,
} from "@opentui/core"
} from "@neuron-tui/core"
import { LANGUAGE_EXTENSIONS } from "../../util/filetype"
import { useBindings, useCommandShortcut } from "../../keymap"
import { useTheme } from "../../context/theme"
import { useTerminalDimensions } from "@opentui/solid"
import { useTerminalDimensions } from "@neuron-tui/solid"
import path from "path"
import { createEffect, createMemo, createResource, createSignal, For, Match, onCleanup, Show, Switch } from "solid-js"
import { DiffViewerFileTree } from "./diff-viewer-file-tree"
@@ -1,6 +1,6 @@
import type { TuiPlugin, TuiPluginApi, TuiPluginStatus } from "@opencode-ai/plugin/tui"
import type { BuiltinTuiPlugin } from "../builtins"
import { useTerminalDimensions } from "@opentui/solid"
import { useTerminalDimensions } from "@neuron-tui/solid"
import { fileURLToPath } from "url"
import { DialogSelect, type DialogSelectOption } from "../../ui/dialog-select"
import { Show, createEffect, createMemo, createSignal } from "solid-js"
@@ -1,9 +1,9 @@
/** @jsxImportSource @opentui/solid */
import { RGBA, TextAttributes, type KeyEvent, type Renderable } from "@opentui/core"
import { useTerminalDimensions } from "@opentui/solid"
/** @jsxImportSource @neuron-tui/solid */
import { RGBA, TextAttributes, type KeyEvent, type Renderable } from "@neuron-tui/core"
import { useTerminalDimensions } from "@neuron-tui/solid"
import { createEffect, createMemo, createSignal, For, Show } from "solid-js"
import { useBindings, useKeymapSelector } from "../../keymap"
import type { ActiveKey } from "@opentui/keymap"
import type { ActiveKey } from "@neuron-tui/keymap"
import type { TuiPlugin, TuiPluginApi } from "@opencode-ai/plugin/tui"
import type { BuiltinTuiPlugin } from "../builtins"

Some files were not shown because too many files have changed in this diff Show More