diff --git a/.changeset/five-aliens-thank.md b/.changeset/five-aliens-thank.md new file mode 100644 index 0000000..e9c47bc --- /dev/null +++ b/.changeset/five-aliens-thank.md @@ -0,0 +1,5 @@ +--- +"wtc": minor +--- + +refactor: solidJS bindings diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index eb72184..bed13e2 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,6 +1,6 @@ ## Summary -- +Description ## Type of Change @@ -12,11 +12,11 @@ ## Local Verification +- [ ] `bun run fmt` - [ ] `bun run lint` -- [ ] `bun run fmt:check` - [ ] `bun run check` - [ ] `bun test` -- [ ] `bun run build` if this affects CLI, install, release, update, or native TUI packaging behavior +- [ ] `bun run build` ## Release Impact @@ -27,8 +27,8 @@ - [ ] Updates build/release packaging - [ ] Breaking change -## Notes - + diff --git a/.zed/settings.json b/.zed/settings.json new file mode 100644 index 0000000..4ba8ef4 --- /dev/null +++ b/.zed/settings.json @@ -0,0 +1,236 @@ +{ + "lsp": { + "oxlint": { + "initialization_options": { + "settings": { + "configPath": null, + "run": "onType", + "disableNestedConfig": false, + "fixKind": "safe_fix", + "unusedDisableDirectives": "deny" + } + } + }, + "oxfmt": { + "initialization_options": { + "settings": { + "fmt.configPath": null, + "run": "onSave" + } + } + } + }, + "languages": { + "CSS": { + "format_on_save": "on", + "prettier": { + "allowed": false + }, + "formatter": [ + { + "language_server": { + "name": "oxfmt" + } + } + ] + }, + "GraphQL": { + "format_on_save": "on", + "prettier": { + "allowed": false + }, + "formatter": [ + { + "language_server": { + "name": "oxfmt" + } + } + ] + }, + "Handlebars": { + "format_on_save": "on", + "prettier": { + "allowed": false + }, + "formatter": [ + { + "language_server": { + "name": "oxfmt" + } + } + ] + }, + "HTML": { + "format_on_save": "on", + "prettier": { + "allowed": false + }, + "formatter": [ + { + "language_server": { + "name": "oxfmt" + } + } + ] + }, + "JavaScript": { + "format_on_save": "on", + "prettier": { + "allowed": false + }, + "formatter": [ + { + "language_server": { + "name": "oxfmt" + } + }, + { + "code_action": "source.fixAll.oxc" + } + ] + }, + "JSON": { + "format_on_save": "on", + "prettier": { + "allowed": false + }, + "formatter": [ + { + "language_server": { + "name": "oxfmt" + } + } + ] + }, + "JSON5": { + "format_on_save": "on", + "prettier": { + "allowed": false + }, + "formatter": [ + { + "language_server": { + "name": "oxfmt" + } + } + ] + }, + "JSONC": { + "format_on_save": "on", + "prettier": { + "allowed": false + }, + "formatter": [ + { + "language_server": { + "name": "oxfmt" + } + } + ] + }, + "Less": { + "format_on_save": "on", + "prettier": { + "allowed": false + }, + "formatter": [ + { + "language_server": { + "name": "oxfmt" + } + } + ] + }, + "Markdown": { + "format_on_save": "on", + "prettier": { + "allowed": false + }, + "formatter": [ + { + "language_server": { + "name": "oxfmt" + } + } + ] + }, + "MDX": { + "format_on_save": "on", + "prettier": { + "allowed": false + }, + "formatter": [ + { + "language_server": { + "name": "oxfmt" + } + } + ] + }, + "SCSS": { + "format_on_save": "on", + "prettier": { + "allowed": false + }, + "formatter": [ + { + "language_server": { + "name": "oxfmt" + } + } + ] + }, + "TypeScript": { + "format_on_save": "on", + "prettier": { + "allowed": false + }, + "formatter": [ + { + "language_server": { + "name": "oxfmt" + } + } + ] + }, + "TSX": { + "format_on_save": "on", + "prettier": { + "allowed": false + }, + "formatter": [ + { + "language_server": { + "name": "oxfmt" + } + } + ] + }, + "Vue.js": { + "format_on_save": "on", + "prettier": { + "allowed": false + }, + "formatter": [ + { + "language_server": { + "name": "oxfmt" + } + } + ] + }, + "YAML": { + "format_on_save": "on", + "prettier": { + "allowed": false + }, + "formatter": [ + { + "language_server": { + "name": "oxfmt" + } + } + ] + } + } +} diff --git a/AGENTS.md b/AGENTS.md index 2ecdba3..e2982bd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,18 +15,18 @@ Before making changes, read these files for context: ## Tech Stack -| Concern | Choice | -| ----------------- | ------------------------------------------------ | -| Runtime | Bun | -| TUI | `@opentui/core` (functional API, no React/Solid) | -| CLI parser | yargs | -| Linter | oxlint | -| Formatter | oxfmt | -| Test runner | bun test | -| Pre-commit | husky + lint-staged | -| Release versions | Changesets | -| Encryption | Web Crypto (AES-256-GCM + PBKDF2) | -| Config validation | zod | +| Concern | Choice | +| ----------------- | --------------------------------- | +| Runtime | Bun | +| TUI | `@opentui/solid` + `solid-js` | +| CLI parser | yargs | +| Linter | oxlint | +| Formatter | oxfmt | +| Test runner | bun test | +| Pre-commit | husky + lint-staged | +| Release versions | Changesets | +| Encryption | Web Crypto (AES-256-GCM + PBKDF2) | +| Config validation | zod | ## Conventions diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 48ecf6b..bbb0c5e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,6 +4,10 @@ - [Bun](https://bun.com/) +Useful resources: + +- [OpenTUI Docs](https://opentui.com/) + ## Development Setup ```bash @@ -72,15 +76,14 @@ bun install --os="*" --cpu="*" @opentui/core Run the generated binary directly from the repository root: ```bash -./dist/wtc-linux-x64 --version -./dist/wtc-linux-x64 --help -./dist/wtc-linux-x64 +cd ./dist +wtc-linux-x64 --version +wtc-linux-x64 --help +wtc-linux-x64 ``` Use the filename that matches your platform. The dashboard starts with no arguments. Press `Ctrl+C` to exit. -Generated binaries live in `dist/`, which is ignored by git. - ## CI/CD and Releases CI runs automatically on pull requests targeting `main`. diff --git a/bun.lock b/bun.lock index a49e1a0..6a233f4 100644 --- a/bun.lock +++ b/bun.lock @@ -5,7 +5,10 @@ "": { "name": "my-opentui-project", "dependencies": { - "@opentui/core": "^0.4.0", + "@opentui/core": "^0.4.1", + "@opentui/keymap": "^0.4.1", + "@opentui/solid": "^0.4.1", + "solid-js": "^1.9.13", "yargs": "^18.0.0", "zod": "^4.4.3", }, @@ -25,8 +28,66 @@ }, }, "packages": { + "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], + + "@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], + + "@babel/compat-data": ["@babel/compat-data@7.29.7", "", {}, "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg=="], + + "@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="], + + "@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], + + "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" } }, "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.29.7", "", { "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g=="], + + "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.29.7", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-member-expression-to-functions": "^7.29.7", "@babel/helper-optimise-call-expression": "^7.29.7", "@babel/helper-replace-supers": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", "@babel/traverse": "^7.29.7", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], + + "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.29.7", "", { "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg=="], + + "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" } }, "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong=="], + + "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.29.7", "", {}, "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw=="], + + "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.29.7", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.29.7", "@babel/helper-optimise-call-expression": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ=="], + + "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.29.7", "", {}, "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw=="], + + "@babel/helpers": ["@babel/helpers@7.29.7", "", { "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg=="], + + "@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], + + "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A=="], + + "@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA=="], + + "@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.29.7", "", { "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ=="], + + "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.29.7", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", "@babel/plugin-syntax-typescript": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw=="], + + "@babel/preset-typescript": ["@babel/preset-typescript@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ=="], + "@babel/runtime": ["@babel/runtime@7.29.7", "", {}, "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw=="], + "@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], + + "@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], + + "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + "@changesets/apply-release-plan": ["@changesets/apply-release-plan@7.1.1", "", { "dependencies": { "@changesets/config": "^3.1.4", "@changesets/get-version-range-type": "^0.4.0", "@changesets/git": "^3.0.4", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "detect-indent": "^6.0.0", "fs-extra": "^7.0.1", "lodash.startcase": "^4.4.0", "outdent": "^0.5.0", "prettier": "^2.7.1", "resolve-from": "^5.0.0", "semver": "^7.5.3" } }, "sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA=="], "@changesets/assemble-release-plan": ["@changesets/assemble-release-plan@6.0.10", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.4", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "semver": "^7.5.3" } }, "sha512-rSDcqdJ9KbVyjpBIuCidhvZNIiVt1XaIYp73ycVQRIA5n/j6wQaEk0ChRLMUQ1vkxZe51PTQ9OIhbg6HQMW45A=="], @@ -67,6 +128,14 @@ "@inquirer/external-editor": ["@inquirer/external-editor@1.0.3", "", { "dependencies": { "chardet": "^2.1.1", "iconv-lite": "^0.7.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA=="], + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + "@manypkg/find-root": ["@manypkg/find-root@1.1.0", "", { "dependencies": { "@babel/runtime": "^7.5.5", "@types/node": "^12.7.1", "find-up": "^4.1.0", "fs-extra": "^8.1.0" } }, "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA=="], "@manypkg/get-packages": ["@manypkg/get-packages@1.1.3", "", { "dependencies": { "@babel/runtime": "^7.5.5", "@changesets/types": "^4.0.1", "@manypkg/find-root": "^1.1.0", "fs-extra": "^8.1.0", "globby": "^11.0.0", "read-yaml-file": "^1.1.0" } }, "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A=="], @@ -77,23 +146,27 @@ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - "@opentui/core": ["@opentui/core@0.4.0", "", { "dependencies": { "bun-ffi-structs": "0.2.3", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.4.0", "@opentui/core-darwin-x64": "0.4.0", "@opentui/core-linux-arm64": "0.4.0", "@opentui/core-linux-arm64-musl": "0.4.0", "@opentui/core-linux-x64": "0.4.0", "@opentui/core-linux-x64-musl": "0.4.0", "@opentui/core-win32-arm64": "0.4.0", "@opentui/core-win32-x64": "0.4.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-G3TmJmaPoxD6SadwevZNE30H/pMZsr/qneVaKc7bmFBxA+uHgxFSAsMoFYaEqcFJM1dGt22kMJb+YY2ZahzqVw=="], + "@opentui/core": ["@opentui/core@0.4.1", "", { "dependencies": { "bun-ffi-structs": "0.2.3", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.4.1", "@opentui/core-darwin-x64": "0.4.1", "@opentui/core-linux-arm64": "0.4.1", "@opentui/core-linux-arm64-musl": "0.4.1", "@opentui/core-linux-x64": "0.4.1", "@opentui/core-linux-x64-musl": "0.4.1", "@opentui/core-win32-arm64": "0.4.1", "@opentui/core-win32-x64": "0.4.1" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-ejlunoFCGLcghYGdfamI/DlWHsgCTLbuoL2JeOmFuLsN+DM5phje3CQbGR4tpl24cadCgHJQFomjoQ9Htvin+Q=="], + + "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.4.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ocs73hj9n0zLArOTpUWIXCWU6ERThG+3wQzO78EvfaR4hb5FRrDHGKWTzXpr6ukSKsUtKdztK5XYTPsJ5e3vww=="], - "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.4.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-t7VGLn2LyCGWSrhYdQ2xnq0L0sjg6VOdM6OyFxrZC3HU69m+PXqIhWMpGw/m8V6W7//uM1RX2GQrB/mgJ1Ce0Q=="], + "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.4.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-YogRtDBfxGeOkcSHDMsGkKFBIt3cWPMPGNu2AmEN6a5KKjDYwAZCudwbDJaUbZDCJjfAUHz9iXjhJVXJBXs9vQ=="], - "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.4.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-/3goEuLFxn9df4fsLw9MSzbp8T3Rcs0NltCS3k73LcCb27pBmHH2FFNDKIj96m5ktK2JIWglEXcX85/i1qy4ug=="], + "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.4.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-sBZTS1eEGeVSQ8fAmDALKQcT7FckrhK64oHfEO7W0lJ+lXapfJuOKtTM33na54V56GAM9guk4RD4cbPeTXEh4g=="], - "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.4.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-KBv05c0e+FRUpPKRqrE5bDmmIhiJsho43EDXJuYOUYJAuGsTii8J8ws0Q4GYBIxZa/atG1Wv3rEKR7+RSd29lA=="], + "@opentui/core-linux-arm64-musl": ["@opentui/core-linux-arm64-musl@0.4.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Eps9qB+vQ/Lel4ZYqMH87Um9oiU17Vu4oWzvRi40Yf+69vA1a3R4D7KUCeY3OxKWnRnwAHkMU9TxNnjKngPH/w=="], - "@opentui/core-linux-arm64-musl": ["@opentui/core-linux-arm64-musl@0.4.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-8SL+HCpXc1Ott4MYfPzBdNM6XUhknBlqAKOmdDnTxkFSnnadJxhBJo+JYX1jxQP/lkd3jKUpEKDc51k9wud4tg=="], + "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.4.1", "", { "os": "linux", "cpu": "x64" }, "sha512-9/xjYGzX5RdUl0qmGQY0OCayjJ4VffDhsBmApQdseUkMT6LGL3RumI4zPK3Y9vo1fuy6ffLnriLFOktOgutXDg=="], - "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.4.0", "", { "os": "linux", "cpu": "x64" }, "sha512-n2aZdF2vkzXyV0C0JL9Ok3EDImtWqewZVcFutMkCRK+EQk3a4oa+bl2Y07XpxGN8FZyJxS+R/HWSoDbKBNKDAg=="], + "@opentui/core-linux-x64-musl": ["@opentui/core-linux-x64-musl@0.4.1", "", { "os": "linux", "cpu": "x64" }, "sha512-UYcp8XGX4DZXN+VYUVuCrJkbFMJ0L+VUVu0t5KqqaeJ74fI4NZ+DmwNqPPg1+C+EIzoW4QChlEUdhlZRdiEQiA=="], - "@opentui/core-linux-x64-musl": ["@opentui/core-linux-x64-musl@0.4.0", "", { "os": "linux", "cpu": "x64" }, "sha512-EpBHP5S8O83VgI4YIM/CPZTUVIGJjYyk+dcYJl76phtRYF4CwZTvomQqyHb0lzXGZ7LwSDoQxsWtLs/hEOVyPA=="], + "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.4.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-hkIbpUJcKd4iLetTygPlFS45teOBTto49aXuxNeafYQUNd3ehiSwJENNBlAGULLfq+KP3dMJoWUiOcbuPVOQRA=="], - "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.4.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-nUoKrHTHDMyqDGSqN7XfKvjaevgS460Pd99tzLCY4rTZcyDTdEUDfnLgEQrkURRCfUsBbwB1lNnq1aP4u+GeZQ=="], + "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.4.1", "", { "os": "win32", "cpu": "x64" }, "sha512-s1kGBcloy4ksl3wFCMqqOUFtXWlTTpzxe6pkLFhFhzgqKsMXHE9pwebiS3pzJkkFUxah5MYG+kbcn2Dw2Gdncg=="], - "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.4.0", "", { "os": "win32", "cpu": "x64" }, "sha512-3uHjGfgjhw+tyxDoF6bs11Gbrt+8VP7k6vAMm9Qyvd8cExDFaNcR1E5Tf/6yR7oxGfh9E951ALue8M7h6l/lLQ=="], + "@opentui/keymap": ["@opentui/keymap@0.4.1", "", { "dependencies": { "@opentui/core": "0.4.1" }, "peerDependencies": { "@opentui/react": "0.4.1", "@opentui/solid": "0.4.1", "react": ">=19.2.0", "solid-js": "1.9.12" }, "optionalPeers": ["@opentui/react", "@opentui/solid", "react", "solid-js"] }, "sha512-4Wds3HynfTPURawkdlO8CjrzfSvxSF5RO2jlpv6etT+JTQjKSKdVrBcza6N77qWqKy0Qi0D/u2yuN6aTUJ0Z7Q=="], + + "@opentui/solid": ["@opentui/solid@0.4.1", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.4.1", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-CD3M3mlUFwy2r8fd43MFY8kR7bFO+CfL1LHjV6+adp7T4jpadb9zuhfKKJpMzSdgAqQof+F5ifGMx6RljiCmwQ=="], "@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.54.0", "", { "os": "android", "cpu": "arm" }, "sha512-NAtpl/SiaeU103e7/OmZw0MvUnsUUopW7hEm/ecegJg7YM0skQaA0IXEZoyTV6NUdiNPupdIUreRqUZTShbn/g=="], @@ -191,14 +264,30 @@ "array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="], + "babel-plugin-jsx-dom-expressions": ["babel-plugin-jsx-dom-expressions@0.40.7", "", { "dependencies": { "@babel/helper-module-imports": "7.18.6", "@babel/plugin-syntax-jsx": "^7.18.6", "@babel/types": "^7.20.7", "html-entities": "2.3.3", "parse5": "^7.1.2" }, "peerDependencies": { "@babel/core": "^7.20.12" } }, "sha512-/O6JWUmjv03OI9lL2ry9bUjpD5S3PclM55RRJEyCdcFZ5W2SEA/59d+l2hNsk3gI6kiWRdRPdOtqZmsQzFN1pQ=="], + + "babel-plugin-module-resolver": ["babel-plugin-module-resolver@5.0.2", "", { "dependencies": { "find-babel-config": "^2.1.1", "glob": "^9.3.3", "pkg-up": "^3.1.0", "reselect": "^4.1.7", "resolve": "^1.22.8" } }, "sha512-9KtaCazHee2xc0ibfqsDeamwDps6FZNo5S0Q81dUqEuFzVwPhcT4J5jOqIVvgCA3Q/wO9hKYxN/Ds3tIsp5ygg=="], + + "babel-preset-solid": ["babel-preset-solid@1.9.12", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.6" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.12" }, "optionalPeers": ["solid-js"] }, "sha512-LLqnuKVDlKpyBlMPcH6qEvs/wmS9a+NczppxJ3ryS/c0O5IiSFOIBQi9GzyiGDSbcJpx4Gr87jyFTos1MyEuWg=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.10.36", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-lVq/Df7LXlO79MVaaUHztSwWiG9oXoWHlgvNS51v8Dpd4+G4/VIy6qYePTw31nAVls33nUtnfezYeLkYAak9dg=="], + "better-path-resolve": ["better-path-resolve@1.0.0", "", { "dependencies": { "is-windows": "^1.0.0" } }, "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g=="], + "brace-expansion": ["brace-expansion@2.1.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA=="], + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], + "bun-ffi-structs": ["bun-ffi-structs@0.2.3", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-pgJiXP+hEgFo9qG51J6ItfY4ocs3vniwNzJ9WhoakB3QB2GdzQxX2EXssentPYlB2hOfJrTjO6iIQkWYzUodpg=="], "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], + "caniuse-lite": ["caniuse-lite@1.0.30001799", "", {}, "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw=="], + "chardet": ["chardet@2.1.1", "", {}, "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ=="], "cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], @@ -207,10 +296,16 @@ "cliui": ["cliui@9.0.1", "", { "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w=="], + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + "dataloader": ["dataloader@1.4.0", "", {}, "sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "detect-indent": ["detect-indent@6.1.0", "", {}, "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA=="], "diff": ["diff@9.0.0", "", {}, "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw=="], @@ -219,12 +314,18 @@ "dotenv": ["dotenv@8.6.0", "", {}, "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g=="], + "electron-to-chromium": ["electron-to-chromium@1.5.371", "", {}, "sha512-e9htk9mAYL6AzmkEhSvVVw7IWGSBJ/Bqdn2eRyRLrj1g6sncN4WbFt5qnILYoCktktr45pyjIrOiRvBThQ808w=="], + "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="], + "entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], + "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], @@ -239,20 +340,34 @@ "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + "find-babel-config": ["find-babel-config@2.1.2", "", { "dependencies": { "json5": "^2.2.3" } }, "sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg=="], + "find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], "fs-extra": ["fs-extra@7.0.1", "", { "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw=="], + "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], "get-east-asian-width": ["get-east-asian-width@1.6.0", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="], + "glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="], + "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], + + "html-entities": ["html-entities@2.3.3", "", {}, "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="], + "human-id": ["human-id@4.2.0", "", { "bin": { "human-id": "dist/cli.js" } }, "sha512-K3GbkIWqyvvlpfhBPlbEvD97TtqBpAYA4kt+cn2lD2x2HuohzZCibcA2nOlnJT6exqvJLggoB5nv2dNf192nEA=="], "husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="], @@ -261,6 +376,8 @@ "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + "is-core-module": ["is-core-module@2.16.2", "", { "dependencies": { "hasown": "^2.0.3" } }, "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA=="], + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], "is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], @@ -275,8 +392,14 @@ "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "js-yaml": ["js-yaml@4.2.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw=="], + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + "jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], "lint-staged": ["lint-staged@17.0.7", "", { "dependencies": { "listr2": "^10.2.1", "picomatch": "^4.0.4", "string-argv": "^0.3.2", "tinyexec": "^1.2.4" }, "optionalDependencies": { "yaml": "^2.9.0" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-JrSobt+tW3rH8IOMi8tDZd3foorM5yPEkLD/V2NxobgHrFfHWGee4MOLVuZeScgxftEwbHrPHIFA/ZL+nUJeuA=="], @@ -289,6 +412,8 @@ "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + "marked": ["marked@17.0.1", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg=="], "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], @@ -297,10 +422,18 @@ "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], + "minimatch": ["minimatch@8.0.7", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg=="], + + "minipass": ["minipass@4.2.8", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="], + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], + "node-releases": ["node-releases@2.0.47", "", {}, "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og=="], + "onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], "outdent": ["outdent@0.5.0", "", {}, "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q=="], @@ -321,10 +454,16 @@ "package-manager-detector": ["package-manager-detector@0.2.11", "", { "dependencies": { "quansync": "^0.2.7" } }, "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ=="], + "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], @@ -333,6 +472,8 @@ "pify": ["pify@4.0.1", "", {}, "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="], + "pkg-up": ["pkg-up@3.1.0", "", { "dependencies": { "find-up": "^3.0.0" } }, "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA=="], + "prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], "quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="], @@ -341,6 +482,10 @@ "read-yaml-file": ["read-yaml-file@1.1.0", "", { "dependencies": { "graceful-fs": "^4.1.5", "js-yaml": "^3.6.1", "pify": "^4.0.1", "strip-bom": "^3.0.0" } }, "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA=="], + "reselect": ["reselect@4.1.8", "", {}, "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="], + + "resolve": ["resolve@1.22.12", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="], + "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], @@ -351,10 +496,16 @@ "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + "s-js": ["s-js@0.4.9", "", {}, "sha512-RtpOm+cM6O0sHg6IA70wH+UC3FZcND+rccBZpBAHzlUgNO2Bm5BN+FnM8+OBxzXdwpKWFwX11JGF0MFRkhSoIQ=="], + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], "semver": ["semver@7.8.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-wnilbGyMxzbY7dNOl7jpKbLSjcfeweJWU5j4+u5qW+6/wuGD9KzIGOyZnQVSBM9E7DtWaaH3CyHkppYrKYoxwg=="], + "seroval": ["seroval@1.5.4", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="], + + "seroval-plugins": ["seroval-plugins@1.5.4", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="], + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], @@ -365,6 +516,8 @@ "slice-ansi": ["slice-ansi@8.0.0", "", { "dependencies": { "ansi-styles": "^6.2.3", "is-fullwidth-code-point": "^5.1.0" } }, "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg=="], + "solid-js": ["solid-js@1.9.13", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.5.0", "seroval-plugins": "~1.5.0" } }, "sha512-6hJeJMOcEX8ktqjpDoJZEmld3ijvcvWBDtiXBm7f4332SiFN66QeAQI1REQshvyUoISsSeJ4PHDauKYbwao9JQ=="], + "spawndamnit": ["spawndamnit@3.0.1", "", { "dependencies": { "cross-spawn": "^7.0.5", "signal-exit": "^4.0.1" } }, "sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg=="], "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], @@ -377,6 +530,8 @@ "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + "term-size": ["term-size@2.2.1", "", {}, "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg=="], "tinyexec": ["tinyexec@1.2.4", "", {}, "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg=="], @@ -393,6 +548,8 @@ "universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], + "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + "web-tree-sitter": ["web-tree-sitter@0.25.10", "", { "peerDependencies": { "@types/emscripten": "^1.40.0" }, "optionalPeers": ["@types/emscripten"] }, "sha512-Y09sF44/13XvgVKgO2cNDw5rGk6s26MgoZPXLESvMXeefBf7i6/73eFurre0IsTW6E14Y0ArIzhUMmjoc7xyzA=="], "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], @@ -405,16 +562,22 @@ "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + "yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], "yargs": ["yargs@18.0.0", "", { "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "string-width": "^7.2.0", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" } }, "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg=="], "yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="], - "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], - "zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@babel/helper-create-class-features-plugin/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "@manypkg/find-root/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], "@manypkg/find-root/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], @@ -423,6 +586,8 @@ "@manypkg/get-packages/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], + "babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="], + "cli-truncate/string-width": ["string-width@8.2.1", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA=="], "cliui/wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], @@ -435,12 +600,26 @@ "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + + "path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "path-scurry/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], + + "pkg-up/find-up": ["find-up@3.0.0", "", { "dependencies": { "locate-path": "^3.0.0" } }, "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg=="], + "read-yaml-file/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], "wrap-ansi/string-width": ["string-width@8.2.1", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA=="], "enquirer/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "pkg-up/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="], + "read-yaml-file/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + + "pkg-up/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="], + + "pkg-up/find-up/locate-path/path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="], } } diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..1af0ede --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,6 @@ +preload = ["@opentui/solid/preload"] + +[install] +exact = true +# Only install newly resolved package versions published at least 3 days ago. +minimumReleaseAge = 259200 diff --git a/package.json b/package.json index 6fd5221..58b13d6 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,10 @@ "changeset:version": "changeset version" }, "dependencies": { - "@opentui/core": "^0.4.0", + "@opentui/core": "^0.4.1", + "@opentui/keymap": "^0.4.1", + "@opentui/solid": "^0.4.1", + "solid-js": "^1.9.13", "yargs": "^18.0.0", "zod": "^4.4.3" }, diff --git a/plans/PLAN.md b/plans/PLAN.md index 7064e21..982b806 100644 --- a/plans/PLAN.md +++ b/plans/PLAN.md @@ -37,7 +37,7 @@ A terminal UI tool for developers to manage GitHub repos, AWS Amplify projects, ``` wtc/ ├── src/ -│ ├── index.ts # Entry point — CLI parser or TUI +│ ├── index.ts # Entry point — opens TUI with no args, CLI parser with args │ ├── cli/ │ │ ├── parser.ts # yargs command definitions │ │ └── commands/ # Subcommand handlers @@ -47,7 +47,8 @@ wtc/ │ │ ├── config.ts │ │ └── upgrade.ts # `wtc upgrade --check` — version check │ ├── tui/ -│ │ ├── app.ts # Main TUI app shell +│ │ ├── app.ts # TUI launcher compatibility wrapper +│ │ ├── app.tsx # Main Solid TUI app shell │ │ ├── components/ # Reusable TUI components │ │ │ ├── status-bar.ts │ │ │ ├── sidebar.ts @@ -175,14 +176,22 @@ See `MVP.md` for detailed deliverables. ### Phase 2 — TUI Refactor to Solid.js -- Add `@opentui/solid` + `solid-js` as dependencies -- Rewrite `src/tui/app.ts` → Solid root component with `` -- Rewrite `src/tui/components/modal.ts` → `Dialog` component + `useDialog` context (matching OpenCode's dialog pattern) -- Rewrite `src/tui/pages/dashboard.ts` → Solid JSX with reactive state -- Create keymap module (`useBindings`-style) for keyboard handling -- Create theme context (`useTheme`) consuming `tokens.ts` +See `SOLID_TUI_REFACTOR.md` for the detailed implementation plan, UX direction, design tokens, dialog/status bar/command palette architecture, testing boundaries, and migration sequence. + +- Add `@opentui/solid` + `solid-js` + `@opentui/keymap` as dependencies +- Configure TSX (`jsxImportSource`) and Solid build plugin; avoid top-level Bun preload so compiled binaries do not load dev-only modules at runtime +- Expand `tokens.ts` into full palette + semantic tokens +- Rewrite `src/tui/app.ts` → Solid root component with `` + `` +- Keep `src/index.ts` as the OpenCode-style entrypoint that decides whether to launch the TUI or parse CLI commands +- Rewrite `src/tui/components/modal.ts` → `DialogProvider` + `UpdateDialog` (OpenCode-inspired dialog pattern) +- Rewrite `src/tui/pages/dashboard.ts` → Solid JSX intro screen without dashboard navigation select +- Use `@opentui/keymap` directly for `KeymapProvider`, `useBindings`, and `useKeymapSelector` +- Import `tokens.ts` directly from components; do not add a theme provider unless runtime theming becomes necessary +- Add bottom status bar (mandatory — shows active hotkeys per context) +- Add command palette (mandatory — `ctrl/cmd+p` overlay for quick navigation) +- Add initial routes for GitHub and Settings, navigable through the command palette - Remove all `findDescendantById` patterns -- Update test setup for Solid-based TUI +- Update test setup to cover logic only, not TUI rendering ### Phase 3 — GitHub Repo Creation diff --git a/plans/SOLID_TUI_REFACTOR.md b/plans/SOLID_TUI_REFACTOR.md new file mode 100644 index 0000000..0224880 --- /dev/null +++ b/plans/SOLID_TUI_REFACTOR.md @@ -0,0 +1,318 @@ +# Phase 2 — Solid.js TUI Refactor + +## Status + +- **Started**: — +- **Branch**: `feature/solid-js-integration` (suggested) +- **TUI Stack**: `@opentui/solid` + `solid-js` + `@opentui/keymap` + +--- + +## Rationale + +The MVP TUI was built with the functional `@opentui/core` API (`Box`, `Text`, `Select`, imperative callbacks). +This works but does not scale to multi-page navigation, reusable dialogs, keyboard-driven UX, or reactive state. +Moving to Solid.js brings: + +- **Declarative JSX components** — no more manual renderable assembly +- **Fine-grained reactivity** — signals/memos instead of manual `content =` assignments +- **OpenCode-inspired patterns** — `DialogProvider`, `KeymapProvider`, contextual `useBindings`, status bar, command palette +- **Correct keyboard handling** — layered keymaps via `@opentui/keymap`, no ad-hoc `renderer.keyInput.on()` + +--- + +## UX Priorities (In Order) + +1. **Bottom status bar** (mandatory — OpenCode-inspired) + - Always visible at terminal bottom + - Shows available hotkeys for the current context (initially `ctrl/cmd+p commands · q quit`) + - Updates reactively as focus or key layers change +2. **Command palette** (mandatory — VSCode/Slack/OpenCode-style) + - Triggered by a key chord (e.g., `ctrl+p` or `cmd+p`) + - Overlay list of navigable commands + - Cross-cuts all pages and actions +3. **Dialog provider** — stack-based overlay dialogs for alerts, prompts, confirmations, the update notification +4. **Update dialog** — converts current imperative modal into a Solid component within the dialog provider + +--- + +## Token System + +### Palette + +Expand `src/tui/tokens.ts` to include all commented-out website colors as real palette entries. + +```ts +export const palette = { + // Brand core + black: "#101820", + pink50: "#fc6f83", + yellow: "#f8ea36", + teal50: "#9ad9e9", + teal75: "#2daccc", + green: "#8dc975", + white: "#ffffff", + + // Extended + black10: "#e4e4e5", + black25: "#c9c9cb", + black50: "#939497", + black75: "#5e5f61", + blue: "#8599f8", + blue10: "#eef0fe", + blue100: "#081c81", + brown: "#734400", + green10: "#eff7eb", + green100: "#2e5120", + greyBlue: "#334251", + maroon: "#7f0315", + orange: "#f7a836", + purple: "#c98bdb", + red: "#e40526", + skyblue: "#9ad9e9", + teal10: "#e9f7fa", + teal25: "#cdecf4", + teal75: "#2daccc", + yellow10: "#fefce2", + yellow75: "#f7a836", + pink10: "#ffeaed", + pink25: "#fecbd3", +} as const; +``` + +### Semantic Tokens + +Add a `tokens` object that references the palette. This keeps palette as the raw source and tokens as the theme contract so the rest of the codebase never reaches into palette directly. + +```ts +export const tokens = { + // Surfaces + bg: palette.black, + surface: palette.black75, + surfaceRaised: palette.greyBlue, + surfaceOverlay: palette.black, + + // Text + text: palette.white, + textDim: palette.black50, + textMuted: palette.black50, + textAccent: palette.teal75, + textInverse: palette.black, + + // Brand accent + accent: palette.teal75, + accentSoft: palette.teal50, + + // Semantic + success: palette.green, + warning: palette.yellow, + danger: palette.pink50, + info: palette.teal50, + + // Interactive + selectionBg: palette.teal50, + selectionText: palette.black, + + // Borders + border: palette.black75, + borderFocus: palette.teal75, +} as const; +``` + +### Design Direction + +No ASCII art logos unless they are genuinely brand-relevant. The WTC tiny-font logo from the MVP can stay as a lightweight branding element on the dashboard. + +--- + +## Migration Steps + +### Step 1 — Add Dependencies and Config + +Package manager additions in `package.json`: + +- `solid-js` (dependency) +- `@opentui/solid` (dependency) +- `@opentui/keymap` (dependency) + +`tsconfig.json` additions: + +```json +"jsx": "preserve", +"jsxImportSource": "@opentui/solid" +``` + +Avoid top-level `bunfig.toml` preload entries for Solid. Standalone binaries read project config at runtime, and a top-level preload can make the compiled executable try to resolve `@opentui/solid/preload` from disk. + +Keep any required preload scoped to tests or source-only workflows, not production binary startup. + +`scripts/build.ts` — add the Solid Bun plugin: + +```ts +import solidPlugin from "@opentui/solid/bun-plugin" +// … in Bun.build config: +plugins: [solidPlugin], +``` + +### Step 2 — Expand Tokens + +Edit `src/tui/tokens.ts` to include the full `palette` and the updated `tokens` object described above. + +### Step 3 — Use Tokens Directly + +Components should import `tokens` from `src/tui/tokens.ts` directly. + +```ts +import { tokens } from "../tokens.ts"; +``` + +Do not add a `ThemeProvider` or `useTheme` hook while the app has one fixed brand theme. Add a provider only if runtime theming becomes necessary. + +### Step 4 — Use OpenTUI Keymap Directly + +Use `@opentui/keymap` imports directly until the app needs custom keymap behavior. + +```ts +import { createDefaultOpenTuiKeymap } from "@opentui/keymap/opentui"; +import { KeymapProvider, useBindings, useKeymapSelector } from "@opentui/keymap/solid"; +``` + +Do not add a local `keymap.tsx` wrapper while it only re-exports library functions. Add one later if we introduce app-specific behavior such as user-configurable keybinds, command formatting, mode stacks, leader keys, or shared command registry helpers. + +Global bindings (quit, status bar update, command palette) are registered inside Solid components via `useBindings`. + +### Step 5 — Create Dialog Provider + +File: `src/tui/components/dialog.tsx` + +Simplified from OpenCode's pattern: + +- `DialogProvider` wraps children + an inline absolute overlay +- `useDialog()` returns `{ show, replace, clear }` +- `show(element, onClose?)` pushes onto a stack +- `replace(element, onClose?)` replaces the stack (for single-dialog mode) +- `clear()` closes all +- Escape key dispatches `clear` via `useBindings` +- Base `` component renders a full-screen semi-transparent overlay with a centered raised panel + +The implementation: + +- Uses `createStore` with a `stack` array +- Render the overlay inline after children, matching OpenCode's pattern. Do not use `Portal` here unless there is a concrete layering issue. +- The overlay box has `position: "absolute"`, `zIndex: 3000`, `backgroundColor: RGBA.fromInts(...)` for the dimming effect +- Dialog panel width is capped at 60 columns by default (or configurable) + +### Step 6 — Create Update Dialog + +File: `src/tui/components/update-dialog.tsx` + +Replaces the imperative `createModal` from MVP. + +Props: + +```ts +interface UpdateDialogProps { + currentVersion: string; + latestVersion: string; + repo: string; +} +``` + +Layout: + +``` +┌─────────────────────────────────────┐ +│ Update Available │ +│ │ +│ v0.1.9 → v0.2.0 │ +│ │ +│ curl -fsSL https://...install.sh │ +│ | bash │ +│ │ +│ [enter] close │ +└─────────────────────────────────────┘ +``` + +Behavior: + +- `enter` or `escape` closes via `dialog.clear()` +- Uses `useBindings` for keyboard handling, not `renderer.keyInput.on()` +- Text colors from tokens: title in `pink50`, version in `teal75`, command on `surfaceRaised` background + +### Step 7 — Convert Dashboard to Solid JSX + +File: `src/tui/pages/dashboard.tsx` + +Replace `createDashboard()` with an intro-only screen. Do not include a navigation ` { + setQuery(value); + setSelectedIndex(0); + }} + placeholder="Search" + ref={(renderable) => { + input = renderable; + // OpenTUI creates the underlying input renderable during reconciliation. + // Delay focus until the renderable exists and has not been destroyed by + // a fast close/reopen cycle. + setTimeout(() => { + if (!input || input.isDestroyed) return; + input.focus(); + }, 1); + }} + /> + + {filtered().map((option, index) => ( + option.onSelect?.()} + > + + {option.title} + + {option.description && — {option.description}} + + ))} + {filtered().length === 0 && No matching commands} + + ↑↓ navigate · enter select · esc close + + ); +} diff --git a/src/tui/components/dialog.tsx b/src/tui/components/dialog.tsx new file mode 100644 index 0000000..7cfc27f --- /dev/null +++ b/src/tui/components/dialog.tsx @@ -0,0 +1,152 @@ +import { createContext, useContext, type ParentProps, type JSX, Show } from "solid-js"; +import { createStore } from "solid-js/store"; +import { useTerminalDimensions } from "@opentui/solid"; +import { useBindings } from "@opentui/keymap/solid"; + +import { tokens } from "../tokens.ts"; + +/** + * Renderable dialog content. + * + * Prefer passing a factory, for example `dialog.replace(() => )`, + * when the dialog component uses Solid hooks or context. The provider renders the + * factory inside the active dialog owner so hooks like `useDialog()` and + * `useBindings()` resolve correctly. + */ +type DialogElement = JSX.Element | (() => JSX.Element); + +interface DialogItem { + element: DialogElement; + onClose?: () => void; +} + +/** + * Dialog stack controller exposed to components below `DialogProvider`. + * + * The stack lets temporary UI layers compose without each caller needing to know + * who opened the previous layer. `show()` pushes a new layer, `replace()` closes + * all current layers and opens one layer, and `clear()` closes everything. + */ +export interface DialogContextValue { + /** Pushes a dialog on top of the current stack. */ + show(element: DialogElement, onClose?: () => void): void; + /** Closes existing dialogs and opens a single replacement dialog. */ + replace(element: DialogElement, onClose?: () => void): void; + /** Closes all dialogs and runs their close callbacks. */ + clear(): void; +} + +const DialogContext = createContext(); + +function renderDialogElement(element: DialogElement | undefined) { + if (typeof element === "function") return element(); + return element; +} + +/** Full-screen overlay that centers the active dialog content. */ +function DialogOverlay(props: ParentProps<{ onClose: () => void }>) { + const dimensions = useTerminalDimensions(); + + return ( + + + {props.children} + + + ); +} + +/** + * Provides dialog stack state and keyboard behavior for modal overlays. + * + * `DialogProvider` must be rendered below `KeymapProvider` because it registers + * an Escape binding with `useBindings()`. That binding is enabled only while the + * stack has entries, so normal app-level Escape handling can remain inactive + * until a dialog is actually open. + */ +export function DialogProvider(props: ParentProps) { + const [store, setStore] = createStore({ + stack: [] as DialogItem[], + }); + + // Dialogs are stack based so future flows can open a confirmation dialog from + // another dialog without losing the original layer underneath it. + useBindings(() => ({ + enabled: store.stack.length > 0, + bindings: [ + { + key: "escape", + desc: "Close dialog", + group: "Dialog", + cmd: () => { + const current = store.stack.at(-1); + current?.onClose?.(); + // Escape only pops the active layer. Call `clear()` from dialog actions + // when the whole modal workflow should be dismissed. + setStore("stack", store.stack.slice(0, -1)); + }, + }, + ], + })); + + const value: DialogContextValue = { + show(element: DialogElement, onClose?: () => void) { + setStore("stack", [...store.stack, { element, onClose }]); + }, + replace(element: DialogElement, onClose?: () => void) { + for (const item of store.stack) { + item.onClose?.(); + } + setStore("stack", [{ element, onClose }]); + }, + clear() { + for (const item of store.stack) { + item.onClose?.(); + } + setStore("stack", []); + }, + }; + + return ( + + {props.children} + 0}> + value.clear()}> + {renderDialogElement(store.stack.at(-1)?.element)} + + + + ); +} + +/** + * Returns the active dialog controller. + * + * Use this inside components rendered below `DialogProvider` to open or close + * modal UI. Calling it outside the provider is a programmer error and throws. + */ +export function useDialog(): DialogContextValue { + const value = useContext(DialogContext); + if (!value) { + throw new Error("useDialog must be used within a DialogProvider"); + } + return value; +} diff --git a/src/tui/components/modal.ts b/src/tui/components/modal.ts deleted file mode 100644 index eb58368..0000000 --- a/src/tui/components/modal.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { - BoxRenderable, - TextRenderable, - Box, - Text, - TextAttributes, - type CliRenderer, - type KeyEvent, - type StyledText, -} from "@opentui/core"; -import { tokens } from "../tokens.ts"; - -export interface ModalController { - show(): void; - hide(): void; - setBody(content: string | StyledText): void; - destroy(): void; -} - -export function createModal( - renderer: CliRenderer, - opts: { id: string; title: string }, -): ModalController { - const bodyText = new TextRenderable(renderer, { - id: `${opts.id}-body`, - content: "", - }); - - const content = Box( - { - id: `${opts.id}-content`, - padding: 2, - flexDirection: "column", - gap: 1, - backgroundColor: tokens.bgRaised, - }, - Text({ content: opts.title, attributes: TextAttributes.BOLD }), - bodyText, - Text({ content: "Press ESC to close", attributes: TextAttributes.DIM }), - ); - - const root = new BoxRenderable(renderer, { - id: opts.id, - position: "absolute", - top: 0, - left: 0, - width: "100%", - height: "100%", - flexDirection: "column", - justifyContent: "center", - alignItems: "center", - zIndex: 1000, - visible: false, - }); - - root.add(content); - renderer.root.add(root); - - renderer.keyInput.on("keypress", (key: KeyEvent) => { - if (root.visible && key.name === "escape") { - root.visible = false; - } - }); - - function show() { - root.visible = true; - } - - function hide() { - root.visible = false; - } - - function setBody(content: string | StyledText) { - bodyText.content = content; - } - - function destroy() { - root.destroyRecursively(); - } - - return { show, hide, setBody, destroy }; -} diff --git a/src/tui/components/status-bar.tsx b/src/tui/components/status-bar.tsx new file mode 100644 index 0000000..10964ba --- /dev/null +++ b/src/tui/components/status-bar.tsx @@ -0,0 +1,25 @@ +import { tokens } from "../tokens.ts"; + +/** + * Bottom status strip for global hints and contextual state. + * + * Today this is intentionally static because the MVP has only global actions. + * Future feature screens should make this component data-driven, for example by + * passing status segments and active key hints from the current route. Keep this + * component as the single place that owns status bar styling so pages only + * describe what should be shown. + */ +export function StatusBar() { + return ( + + ctrl/cmd+p commands · q quit + + ); +} diff --git a/src/tui/components/update-dialog.tsx b/src/tui/components/update-dialog.tsx new file mode 100644 index 0000000..a8e1f0d --- /dev/null +++ b/src/tui/components/update-dialog.tsx @@ -0,0 +1,64 @@ +import { TextAttributes } from "@opentui/core"; +import { useBindings } from "@opentui/keymap/solid"; + +import { APP_VERSION, REPO } from "../../config/consts.ts"; + +import { tokens } from "../tokens.ts"; + +import { useDialog } from "./dialog.tsx"; + +/** Props for the update notification dialog. */ +export interface UpdateDialogProps { + /** Latest release tag returned by the update checker. */ + latestVersion: string; +} + +/** + * Modal shown when a newer WTC release is available. + * + * The dialog is opened by the app shell after `checkForUpdate()` resolves. It is + * intentionally passive: users copy or run the install command themselves, and + * Return/Escape only close the notice. + */ +export function UpdateDialog(props: UpdateDialogProps) { + const dialog = useDialog(); + + // Register the Return shortcut with the dialog itself so the binding exists + // only while the update notice is visible. + useBindings(() => ({ + bindings: [ + { + key: "return", + desc: "Close", + group: "Dialog", + cmd: () => dialog.clear(), + }, + ], + })); + + const installCmd = `curl -fsSL https://raw.githubusercontent.com/${REPO}/main/install.sh | bash`; + + return ( + + + Update Available + dialog.clear()}> + esc + + + + v{APP_VERSION} + + {props.latestVersion} + + + {installCmd} + + + dialog.clear()}> + ok + + + + ); +} diff --git a/src/tui/pages/dashboard.ts b/src/tui/pages/dashboard.ts deleted file mode 100644 index 23eb3ba..0000000 --- a/src/tui/pages/dashboard.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { ASCIIFont, Box, Select, Text, TextAttributes } from "@opentui/core"; -import { tokens } from "../tokens.ts"; - -const navItems = [ - { name: "GitHub (coming soon)", description: "Repository workflows" }, - { name: "Amplify (coming soon)", description: "Hosting setup" }, - { name: "Teamwork (coming soon)", description: "Tasks and timers" }, - { name: "Settings (coming soon)", description: "Configuration" }, -]; - -export function createDashboard(version = "0.1.0") { - return Box( - { - flexDirection: "column", - alignItems: "center", - justifyContent: "center", - flexGrow: 1, - gap: 1, - }, - Box( - { flexDirection: "column", justifyContent: "center", alignItems: "center", gap: 1 }, - ASCIIFont({ font: "tiny", text: "WTC" }), - Text({ content: "What will you build?", attributes: TextAttributes.DIM }), - ), - Select({ - id: "dashboard-nav", - width: 34, - height: 4, - options: navItems, - selectedIndex: 0, - showDescription: false, - wrapSelection: true, - selectedTextColor: tokens.selectionText, - selectedBackgroundColor: tokens.selectionBg, - }), - Text({ - content: `v${version} | Press Ctrl+C to exit`, - attributes: TextAttributes.DIM, - }), - ); -} diff --git a/src/tui/pages/dashboard.tsx b/src/tui/pages/dashboard.tsx new file mode 100644 index 0000000..a66881c --- /dev/null +++ b/src/tui/pages/dashboard.tsx @@ -0,0 +1,25 @@ +import { TextAttributes } from "@opentui/core"; + +import { APP_VERSION } from "../../config/consts.ts"; + +import { tokens } from "../tokens.ts"; + +/** + * Intro screen shown when the TUI starts. + * + * The dashboard intentionally stays navigation-light: global movement happens + * through the command palette, while this page establishes brand, version, and + * the primary keyboard affordance. + */ +export function Dashboard() { + return ( + + + + What will you build? + + Press ctrl/cmd+p to open the command palette. + v{APP_VERSION} · Press Ctrl+C to exit + + ); +} diff --git a/src/tui/pages/github.tsx b/src/tui/pages/github.tsx new file mode 100644 index 0000000..7541970 --- /dev/null +++ b/src/tui/pages/github.tsx @@ -0,0 +1,15 @@ +import { TextAttributes } from "@opentui/core"; +import { tokens } from "../tokens.ts"; + +/** Placeholder route for upcoming GitHub repository workflows. */ +export function GitHubPage() { + return ( + + + GitHub + + Repository workflows will live here. + Use ctrl/cmd+p to jump somewhere else. + + ); +} diff --git a/src/tui/pages/settings.tsx b/src/tui/pages/settings.tsx new file mode 100644 index 0000000..326153a --- /dev/null +++ b/src/tui/pages/settings.tsx @@ -0,0 +1,15 @@ +import { TextAttributes } from "@opentui/core"; +import { tokens } from "../tokens.ts"; + +/** Placeholder route for configuration and setup workflows. */ +export function SettingsPage() { + return ( + + + Settings + + Configuration and setup shortcuts will live here. + Use ctrl/cmd+p to jump somewhere else. + + ); +} diff --git a/src/tui/tokens.ts b/src/tui/tokens.ts index 7d60b64..f7c0bbb 100644 --- a/src/tui/tokens.ts +++ b/src/tui/tokens.ts @@ -1,32 +1,104 @@ +import { RGBA } from "@opentui/core"; + +/** + * Raw WTC brand color palette copied from the website design system. + * + * Prefer using `tokens` for component styling so UI code depends on semantic + * roles instead of raw brand names. Reach for `palette` only when defining new + * semantic tokens. + */ export const palette = { + // Brand core black: "#101820", - teal50: "#9ad9e9", - teal75: "#2daccc", pink50: "#fc6f83", yellow: "#f8ea36", + teal50: "#9ad9e9", + teal75: "#2daccc", green: "#8dc975", - white: "#ffffff", + + // Extended + black10: "#e4e4e5", + black25: "#c9c9cb", black50: "#939497", black75: "#5e5f61", + blue: "#8599f8", + blue10: "#eef0fe", + blue100: "#081c81", + brown: "#734400", + green10: "#eff7eb", + green100: "#2e5120", + green25: "#d6eccd", + green75: "#498233", greyBlue: "#334251", + greyBlue10: "#e9eef4", + greyBlue25: "#becede", + greyBlue50: "#658bb1", + greyBlue75: "#526b83", + maroon: "#7f0315", + orange: "#f7a836", + pink10: "#ffeaed", + pink25: "#fecbd3", + purple: "#c98bdb", + purple10: "#f0def5", + purple100: "#742a89", + purple25: "#e4c4ed", + purple75: "#a63cc3", + purple50: "#c98bdb", + red: "#e40526", + skyblue: "#9ad9e9", + teal: "#144d5b", + teal10: "#e9f7fa", + teal25: "#cdecf4", + yellow10: "#fefce2", + yellow25: "#fcf7af", + yellow75: "#f7a836", } as const; +/** + * Semantic TUI design tokens expressed as OpenTUI `RGBA` values. + * + * These tokens are the stable styling contract for components. Keeping them in + * one file makes it easier to evolve contrast, overlays, and focus states + * without touching every screen. + */ export const tokens = { - bg: palette.black, - bgRaised: palette.black75, - - text: palette.white, - textDim: palette.black50, - textAccent: palette.teal75, - - border: palette.black75, - borderFocus: palette.teal75, - selectionBg: palette.teal50, - selectionText: palette.teal75, - - primary: palette.teal75, - danger: palette.pink50, - success: palette.green, - warning: palette.yellow, + // Surfaces + bg: RGBA.fromHex(palette.black), + surface: RGBA.fromHex(palette.black75), + surfaceRaised: RGBA.fromHex(palette.black50), + surfaceOverlay: RGBA.fromValues( + RGBA.fromHex(palette.black75).r, + RGBA.fromHex(palette.black75).g, + RGBA.fromHex(palette.black75).b, + 0.2, + ), + + // Text + text: RGBA.fromHex(palette.white), + textDim: RGBA.fromHex(palette.black50), + textMuted: RGBA.fromHex(palette.black50), + textAccent: RGBA.fromHex(palette.teal75), + textInverse: RGBA.fromHex(palette.black), + + // Brand accent + accent: RGBA.fromHex(palette.teal75), + accentSoft: RGBA.fromHex(palette.teal50), + + // Semantic + success: RGBA.fromHex(palette.green), + warning: RGBA.fromHex(palette.yellow), + danger: RGBA.fromHex(palette.pink50), + info: RGBA.fromHex(palette.teal50), + + // Interactive + selectionBg: RGBA.fromHex(palette.teal50), + selectionText: RGBA.fromHex(palette.black), + + // Borders + border: RGBA.fromHex(palette.black75), + borderFocus: RGBA.fromHex(palette.teal75), } as const; + +/** Type helper for consumers that need the full token object shape. */ +export type Tokens = typeof tokens; diff --git a/src/utils/update-check.ts b/src/utils/update-check.ts index be1b7be..4c903a7 100644 --- a/src/utils/update-check.ts +++ b/src/utils/update-check.ts @@ -1,16 +1,27 @@ -const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours +import { APP_VERSION } from "../config/consts"; + +/** How long a successful GitHub release lookup remains fresh. */ +const CACHE_TTL_MS = 24 * 60 * 60 * 1000; const REPO = "wethegit/wtc"; +/** Returns the update cache location, allowing tests to override the directory. */ function getCachePaths(): { cacheDir: string; cachePath: string } { const homeDir = Bun.env.HOME ?? process.env.HOME ?? "."; const cacheDir = process.env.WTC_CACHE_DIR ?? `${homeDir}/.cache/wtc`; return { cacheDir, cachePath: `${cacheDir}/update-check.json` }; } +/** Normalizes release tags so `v1.2.3` and `1.2.3` compare equally. */ function normalizeVersion(version: string): string { return version.trim().replace(/^v/, ""); } +/** + * Compares semantic-looking versions from GitHub release tags. + * + * Falls back to string comparison when either side contains non-numeric parts so + * pre-releases or unexpected tags remain deterministic instead of throwing. + */ function compareVersions(left: string, right: string): number { const leftParts = normalizeVersion(left).split(".").map(Number); const rightParts = normalizeVersion(right).split(".").map(Number); @@ -30,17 +41,25 @@ function compareVersions(left: string, right: string): number { return 0; } +/** Cached latest release lookup. */ interface UpdateCache { + /** Latest version tag returned by GitHub. */ latestVersion: string; + /** Unix timestamp in milliseconds for when the lookup was cached. */ checkedAt: number; } +/** Result returned by the update checker. */ export interface UpdateInfo { + /** Version currently running. */ currentVersion: string; + /** Latest known release version. */ latestVersion: string; + /** Whether `latestVersion` is newer than `currentVersion`. */ updateAvailable: boolean; } +/** Reads the cached release lookup, returning null when missing or invalid. */ async function readCache(): Promise { try { const { cachePath } = getCachePaths(); @@ -51,12 +70,14 @@ async function readCache(): Promise { } } +/** Writes the latest successful release lookup to the local cache. */ async function writeCache(cache: UpdateCache): Promise { const { cachePath } = getCachePaths(); await Bun.write(cachePath, JSON.stringify(cache)); } +/** Fetches the latest GitHub release tag for the WTC repository. */ async function fetchLatestVersion(): Promise { const response = await fetch(`https://api.github.com/repos/${REPO}/releases/latest`, { headers: { "User-Agent": "wtc" }, @@ -71,7 +92,15 @@ async function fetchLatestVersion(): Promise { return data.tag_name; } -export async function checkForUpdate(currentVersion: string): Promise { +/** + * Checks whether a newer WTC release exists. + * + * The checker prefers cached data for fast startup, refreshes from GitHub when + * the cache is stale, and falls back to stale cache data if the network request + * fails. If no cache exists and GitHub is unavailable, it reports no update so + * startup remains quiet and non-blocking. + */ +export async function checkForUpdate(currentVersion = APP_VERSION): Promise { const cached = await readCache(); const now = Date.now(); diff --git a/src/version.ts b/src/version.ts deleted file mode 100644 index 670751d..0000000 --- a/src/version.ts +++ /dev/null @@ -1 +0,0 @@ -export const APP_VERSION = process.env.APP_VERSION ?? "0.1.0"; diff --git a/tests/tui/dashboard.test.ts b/tests/tui/dashboard.test.ts deleted file mode 100644 index ddaac93..0000000 --- a/tests/tui/dashboard.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { describe, expect, test } from "bun:test"; -import { createTestRenderer } from "@opentui/core/testing"; -import { createDashboard } from "../../src/tui/pages/dashboard.ts"; - -describe("dashboard", () => { - test("renders WTC ascii art", async () => { - const { renderer, renderOnce, captureCharFrame } = await createTestRenderer({ - width: 80, - height: 24, - }); - - renderer.root.add(createDashboard("0.1.0")); - await renderOnce(); - - const frame = captureCharFrame(); - - expect(frame).toContain("█"); - expect(frame).toContain("▀█▀"); - - renderer.destroy(); - }); - - test("renders subtitle text", async () => { - const { renderer, renderOnce, captureCharFrame } = await createTestRenderer({ - width: 80, - height: 24, - }); - - renderer.root.add(createDashboard("0.1.0")); - await renderOnce(); - - const frame = captureCharFrame(); - - expect(frame).toContain("What will you build?"); - - renderer.destroy(); - }); - - test("renders MVP navigation and footer", async () => { - const { renderer, renderOnce, captureCharFrame } = await createTestRenderer({ - width: 80, - height: 24, - }); - - renderer.root.add(createDashboard("0.1.0")); - await renderOnce(); - - const frame = captureCharFrame(); - - expect(frame).toContain("GitHub (coming soon)"); - expect(frame).toContain("Amplify (coming soon)"); - expect(frame).toContain("Teamwork (coming soon)"); - expect(frame).toContain("Settings (coming soon)"); - expect(frame).toContain("v0.1.0 | Press Ctrl+C to exit"); - - renderer.destroy(); - }); - - test("dashboard navigation responds to arrow keys", async () => { - const { renderer, mockInput, renderOnce, captureCharFrame } = await createTestRenderer({ - width: 80, - height: 24, - }); - - renderer.root.add(createDashboard("0.1.0")); - const nav = renderer.root.findDescendantById("dashboard-nav"); - if (nav && "focus" in nav && typeof nav.focus === "function") { - nav.focus(); - } - - mockInput.pressArrow("down"); - await renderOnce(); - - const frame = captureCharFrame(); - expect(frame).toContain("Amplify (coming soon)"); - - renderer.destroy(); - }); -}); diff --git a/tests/tui/dialog-select.test.ts b/tests/tui/dialog-select.test.ts new file mode 100644 index 0000000..a53c263 --- /dev/null +++ b/tests/tui/dialog-select.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, test } from "bun:test"; +import { + filterDialogSelectOptions, + type DialogSelectOption, +} from "../../src/tui/components/dialog-select.tsx"; + +function option(value: string, title: string, description?: string): DialogSelectOption { + return { value, title, description }; +} + +describe("filterDialogSelectOptions", () => { + const options = [ + option("github.open", "Open GitHub", "Repository workflows"), + option("settings.open", "Open Settings", "Configuration and setup"), + ]; + + test("returns all options for empty query", () => { + expect(filterDialogSelectOptions(options, "").map((entry) => entry.value)).toEqual([ + "github.open", + "settings.open", + ]); + }); + + test("filters by title", () => { + expect(filterDialogSelectOptions(options, "git").map((entry) => entry.value)).toEqual([ + "github.open", + ]); + }); + + test("filters by description", () => { + expect(filterDialogSelectOptions(options, "config").map((entry) => entry.value)).toEqual([ + "settings.open", + ]); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index d4467c8..d0a0c3c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,8 @@ "module": "Preserve", "moduleDetection": "force", "allowJs": true, + "jsx": "preserve", + "jsxImportSource": "@opentui/solid", // Bundler mode "moduleResolution": "bundler",