@carlesandres/house 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -2
- package/README.md +13 -5
- package/dist/index.js +488 -236
- package/package.json +11 -11
package/CHANGELOG.md
CHANGED
|
@@ -2,10 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to house land here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html) from v0.1.0 onward.
|
|
4
4
|
|
|
5
|
-
The publish workflow (`.github/workflows/publish.yml`) runs
|
|
5
|
+
The publish workflow (`.github/workflows/publish.yml`) runs for a published release or a release-tag dispatch, builds per-platform binaries, publishes platform packages then the main package via Trusted Publisher, and lets GitHub auto-generate release notes from commit subjects; this file is the curated, narrative version.
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.9.0] — 2026-09-12
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Opening a selected file now hides the Sidebar by default so the Reader gets the full pane; set
|
|
14
|
+
`autoHideSidebar = false` or use `--no-auto-hide-sidebar` to keep the two-pane view.
|
|
15
|
+
- Release preparation now runs from a dispatchable GitHub workflow, and merging its PR creates and verifies the release, so publishing no longer depends on a clean local checkout or a long-running terminal.
|
|
16
|
+
- The publish job no longer waits for a GitHub `npm` environment reviewer, so creating the GitHub Release (or a `main` dispatch) is enough to ship. The environment still allows only `v*` tags and `main`.
|
|
17
|
+
- Empty CLI `--extensions` / `--show` again override file and env lists after the Effect 4.0.0-beta.107 bump, so an explicit empty allow-list still wins.
|
|
18
|
+
- House now runs on OpenTUI 0.5.9, matching `@house/ui` and the current renderer/input stack.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Release preparation now creates GitHub-verified commits, so release pull requests satisfy the
|
|
23
|
+
repository's signed-commit rule and can be merged normally.
|
|
24
|
+
- Release preparation now rejects stale managed branches, requests maintainer review, and reports
|
|
25
|
+
dispatched CI on the pull request, so release approvals remain visible through GitHub and `gh`.
|
|
26
|
+
- Release preparation now passes GitHub authentication and step outputs through Turborepo, so
|
|
27
|
+
dispatches can create release pull requests and start their CI.
|
|
28
|
+
- Shift+A no longer drops a hidden-file selection when toggling hidden files back on, so the original file is reselected after the re-walk.
|
|
29
|
+
- Theme resolution now keeps 8-digit hex alpha from bundled palettes, so muted and border tokens in themes like Cursor stay distinct from opaque text.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## [0.8.0] — 2026-08-30
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- `@house/options` specs may declare `footer: { icon, activate? }` so House can derive clickable footer controls from the catalog; wrap's `W` indicator now comes from that opt-in instead of a hard-coded Browser entry.
|
|
37
|
+
- Footer `T` and `O` controls cycle theme and File Navigator browse order one step forward (with a short notice), so multi-value options are adjustable without leaving the keyboard-or-click chrome.
|
|
38
|
+
- Choice footer controls show a declared short abbreviation of the current value (for example `op` / `re`) instead of a fixed letter, so the chip itself reflects state between Activate clicks.
|
|
39
|
+
|
|
40
|
+
|
|
9
41
|
## [0.7.0] — 2026-08-30
|
|
10
42
|
|
|
11
43
|
### Added
|
|
@@ -433,7 +465,9 @@ The v1 MVP, published as `@carlesandres/openmdr` on npm.
|
|
|
433
465
|
|
|
434
466
|
Search, stdin, URL fetching, cross-file link following, `$EDITOR` hand-off, syntax highlighting, persistent config, OS-appearance auto-detect, single-binary distribution (issue [#2](https://github.com/carlesandres/openmdr/issues/2)), Homebrew tap. All tracked.
|
|
435
467
|
|
|
436
|
-
[Unreleased]: https://github.com/carlesandres/house/compare/v0.
|
|
468
|
+
[Unreleased]: https://github.com/carlesandres/house/compare/v0.9.0...HEAD
|
|
469
|
+
[0.9.0]: https://github.com/carlesandres/house/compare/v0.8.0...v0.9.0
|
|
470
|
+
[0.8.0]: https://github.com/carlesandres/house/compare/v0.7.0...v0.8.0
|
|
437
471
|
[0.7.0]: https://github.com/carlesandres/house/compare/v0.6.0...v0.7.0
|
|
438
472
|
[0.6.0]: https://github.com/carlesandres/house/compare/v0.5.2...v0.6.0
|
|
439
473
|
[0.5.2]: https://github.com/carlesandres/house/compare/v0.5.1...v0.5.2
|
package/README.md
CHANGED
|
@@ -68,6 +68,8 @@ house --serve README.md
|
|
|
68
68
|
| `--width <N>` | `80` | Reader wrap width used when wrapping is enabled |
|
|
69
69
|
| `--wrap` | off | Start with reader wrapping enabled |
|
|
70
70
|
| `--no-wrap` | off | Start with reader wrapping disabled |
|
|
71
|
+
| `--auto-hide-sidebar` | on | Hide the sidebar after explicitly opening a file |
|
|
72
|
+
| `--no-auto-hide-sidebar` | off | Keep the sidebar visible after explicitly opening a file |
|
|
71
73
|
| `--show <list>` | `""` | Reveal normally-skipped entries; comma-separated subset of `hidden`, `gitignored`. Use `--show ""` to clear. |
|
|
72
74
|
| `--root <dir>` | current directory | Discovery root to walk; overrides `defaultRoot` config/env |
|
|
73
75
|
| `--focus <mode>` | `sidebar` | Startup focus: `sidebar`, `reader`, or `filter`. `filter` opens the sidebar filter prompt immediately. |
|
|
@@ -97,27 +99,33 @@ tone = "dark"
|
|
|
97
99
|
extensions = []
|
|
98
100
|
width = 80
|
|
99
101
|
wrap = false
|
|
102
|
+
autoHideSidebar = true
|
|
100
103
|
show = ["hidden", "gitignored"]
|
|
101
104
|
focus = "sidebar"
|
|
102
105
|
defaultRoot = "cwd" # or "git"
|
|
103
106
|
order = "recently-modified" # or "tree"
|
|
104
107
|
```
|
|
105
108
|
|
|
106
|
-
Supported keys: `theme`, `tone`, `extensions`, `width`, `wrap`, `show`, `focus`, `defaultRoot`, `order`.
|
|
109
|
+
Supported keys: `theme`, `tone`, `extensions`, `width`, `wrap`, `autoHideSidebar`, `show`, `focus`, `defaultRoot`, `order`.
|
|
107
110
|
|
|
108
111
|
`show` is a list of normally-skipped categories to opt into. Known categories: `hidden` (dot-prefixed entries), `gitignored` (entries matched by a `.gitignore`). Default is the empty list. Hard skips (`node_modules`, `.git`, `.venv`) always apply.
|
|
109
112
|
|
|
110
113
|
Precedence, highest to lowest:
|
|
111
114
|
|
|
112
|
-
1. CLI flags (`--theme`, `--tone`, `--ext`, `--width`, `--wrap`, `--no-wrap`, `--show`, `--focus`, `--order`, `--root`)
|
|
113
|
-
2. Env vars (`HOUSE_THEME`, `HOUSE_TONE`, `HOUSE_EXTENSIONS`, `HOUSE_WIDTH`, `HOUSE_WRAP`, `HOUSE_SHOW`, `HOUSE_FOCUS`, `HOUSE_DEFAULT_ROOT`, `HOUSE_ORDER`)
|
|
115
|
+
1. CLI flags (`--theme`, `--tone`, `--ext`, `--width`, `--wrap`, `--no-wrap`, `--auto-hide-sidebar`, `--no-auto-hide-sidebar`, `--show`, `--focus`, `--order`, `--root`)
|
|
116
|
+
2. Env vars (`HOUSE_THEME`, `HOUSE_TONE`, `HOUSE_EXTENSIONS`, `HOUSE_WIDTH`, `HOUSE_WRAP`, `HOUSE_AUTO_HIDE_SIDEBAR`, `HOUSE_SHOW`, `HOUSE_FOCUS`, `HOUSE_DEFAULT_ROOT`, `HOUSE_ORDER`)
|
|
114
117
|
3. Config file
|
|
115
|
-
4. Built-in defaults (`opencode` / `dark` / `extensions = []` / `width = 80` / `wrap = false` / `show = []` / `focus = "sidebar"` / `defaultRoot = "cwd"` / `order = "recently-modified"`)
|
|
118
|
+
4. Built-in defaults (`opencode` / `dark` / `extensions = []` / `width = 80` / `wrap = false` / `autoHideSidebar = true` / `show = []` / `focus = "sidebar"` / `defaultRoot = "cwd"` / `order = "recently-modified"`)
|
|
116
119
|
|
|
117
120
|
`HOUSE_SHOW` takes a comma-separated list (`HOUSE_SHOW=hidden,gitignored`). For `show` specifically, each source completely replaces the next — categories don't merge across layers. Press `shift+a` in the TUI to round-trip between the configured set and the full vocabulary without editing config.
|
|
118
121
|
|
|
119
122
|
`width` is the fixed reader width used when wrapping is enabled. `wrap` controls startup mode; press `w` in the TUI to toggle reader wrapping for the current session without editing config.
|
|
120
123
|
|
|
124
|
+
`autoHideSidebar` hides the sidebar when you explicitly open a selected file with `↵`, `→`, `l`,
|
|
125
|
+
the command palette, or a matching filter Return. Selection movement still previews files without
|
|
126
|
+
hiding it. Press `s` or `/` to bring the sidebar back. Set the option to `false` to retain the
|
|
127
|
+
two-pane view after Open file.
|
|
128
|
+
|
|
121
129
|
`order` is the File Navigator browse order when no filter query is active. `recently-modified` (default) lists the most recently updated files first, using filesystem modification time, then tree order for ties. `tree` lists files before directories, alphabetically within each group, so current-directory files appear before nested subtrees. An active filter still ranks by relevance first.
|
|
122
130
|
|
|
123
131
|
The file is optional — a missing file is fine. Invalid keys, unknown themes, invalid `defaultRoot`, or malformed TOML fail loudly with a one-line error. Per-project config (`.house/config.toml`) and additional keys are deferred.
|
|
@@ -157,7 +165,7 @@ The footer only shows essential app controls (`W`, `q`, `tab`, `s`, `ctrl+p`) to
|
|
|
157
165
|
| `G` | Last file |
|
|
158
166
|
| `/` | Filter files (fuzzy match on path) |
|
|
159
167
|
| `A` | Toggle hidden + gitignored entries (session-only; round-trips with the configured `show`) |
|
|
160
|
-
| `↵` / `→` / `l` | Open file (focus reader)
|
|
168
|
+
| `↵` / `→` / `l` | Open file (focus reader and auto-hide the sidebar when enabled) |
|
|
161
169
|
|
|
162
170
|
### Reader
|
|
163
171
|
|
package/dist/index.js
CHANGED
|
@@ -3066,7 +3066,7 @@ import { useState as useState6 } from "react";
|
|
|
3066
3066
|
// package.json
|
|
3067
3067
|
var package_default = {
|
|
3068
3068
|
name: "@carlesandres/house",
|
|
3069
|
-
version: "0.
|
|
3069
|
+
version: "0.9.0",
|
|
3070
3070
|
description: "TUI-first markdown reader on opentui",
|
|
3071
3071
|
type: "module",
|
|
3072
3072
|
license: "MIT",
|
|
@@ -3134,15 +3134,15 @@ var package_default = {
|
|
|
3134
3134
|
"@carlesandres/house-linux-x64": "0.5.1"
|
|
3135
3135
|
},
|
|
3136
3136
|
dependencies: {
|
|
3137
|
-
"@effect/atom-react": "4.0.0-beta.
|
|
3138
|
-
"@opentui/core": "0.
|
|
3139
|
-
"@opentui/react": "0.
|
|
3137
|
+
"@effect/atom-react": "4.0.0-beta.107",
|
|
3138
|
+
"@opentui/core": "0.5.9",
|
|
3139
|
+
"@opentui/react": "0.5.9",
|
|
3140
3140
|
"@parcel/watcher": "2.6.0",
|
|
3141
3141
|
commander: "^14.0.3",
|
|
3142
|
-
effect: "4.0.0-beta.
|
|
3142
|
+
effect: "4.0.0-beta.107",
|
|
3143
3143
|
ignore: "^7.0.5",
|
|
3144
3144
|
marked: "^18.0.4",
|
|
3145
|
-
react: "19.2.
|
|
3145
|
+
react: "19.2.8",
|
|
3146
3146
|
scheduler: "0.27.0",
|
|
3147
3147
|
"string-width": "7.2.0",
|
|
3148
3148
|
"web-tree-sitter": "0.25.10"
|
|
@@ -3150,7 +3150,7 @@ var package_default = {
|
|
|
3150
3150
|
devDependencies: {
|
|
3151
3151
|
"@effect/language-service": "0.86.2",
|
|
3152
3152
|
"@types/bun": "1.3.14",
|
|
3153
|
-
"@types/react": "19.2.
|
|
3153
|
+
"@types/react": "19.2.18",
|
|
3154
3154
|
emulate: "0.9.0",
|
|
3155
3155
|
oxfmt: "0.51.0",
|
|
3156
3156
|
oxlint: "1.66.0",
|
|
@@ -3162,6 +3162,279 @@ var package_default = {
|
|
|
3162
3162
|
// src/Browser.tsx
|
|
3163
3163
|
import { join as join4 } from "path";
|
|
3164
3164
|
import { SyntaxStyle } from "@opentui/core";
|
|
3165
|
+
|
|
3166
|
+
// ../../packages/options/src/footer.ts
|
|
3167
|
+
var footerActivate = (spec) => {
|
|
3168
|
+
if (spec.footer?.activate !== undefined)
|
|
3169
|
+
return spec.footer.activate;
|
|
3170
|
+
if (spec.type === "boolean")
|
|
3171
|
+
return "toggle";
|
|
3172
|
+
if (spec.type === "string" && spec.choices !== undefined && spec.choices.length > 0) {
|
|
3173
|
+
return "cycle";
|
|
3174
|
+
}
|
|
3175
|
+
return;
|
|
3176
|
+
};
|
|
3177
|
+
var footerKeys = (specs) => {
|
|
3178
|
+
const keys = [];
|
|
3179
|
+
for (const key of Object.keys(specs)) {
|
|
3180
|
+
if (specs[key]?.footer !== undefined)
|
|
3181
|
+
keys.push(key);
|
|
3182
|
+
}
|
|
3183
|
+
return keys;
|
|
3184
|
+
};
|
|
3185
|
+
var nextFooterValue = (spec, current) => {
|
|
3186
|
+
const activate = footerActivate(spec);
|
|
3187
|
+
if (activate === "toggle") {
|
|
3188
|
+
if (spec.type !== "boolean") {
|
|
3189
|
+
throw new Error('footer activate "toggle" requires a boolean option');
|
|
3190
|
+
}
|
|
3191
|
+
return !current;
|
|
3192
|
+
}
|
|
3193
|
+
if (activate === "cycle") {
|
|
3194
|
+
if (spec.type !== "string" || spec.choices === undefined || spec.choices.length === 0) {
|
|
3195
|
+
throw new Error('footer activate "cycle" requires a string option with choices');
|
|
3196
|
+
}
|
|
3197
|
+
const choices = spec.choices;
|
|
3198
|
+
const currentIndex = choices.indexOf(current);
|
|
3199
|
+
const nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % choices.length;
|
|
3200
|
+
return choices[nextIndex];
|
|
3201
|
+
}
|
|
3202
|
+
throw new Error("option has no footer activate strategy");
|
|
3203
|
+
};
|
|
3204
|
+
var footerControlActive = (spec, value) => {
|
|
3205
|
+
if (spec.type === "boolean")
|
|
3206
|
+
return Boolean(value);
|
|
3207
|
+
return true;
|
|
3208
|
+
};
|
|
3209
|
+
var footerControlGlyph = (spec, value) => {
|
|
3210
|
+
const footer = spec.footer;
|
|
3211
|
+
if (footer === undefined) {
|
|
3212
|
+
throw new Error("footerControlGlyph requires a footer-opted spec");
|
|
3213
|
+
}
|
|
3214
|
+
if (typeof value === "string" && footer.labels !== undefined) {
|
|
3215
|
+
const label = footer.labels[value];
|
|
3216
|
+
if (label !== undefined && label.length > 0)
|
|
3217
|
+
return label;
|
|
3218
|
+
}
|
|
3219
|
+
return footer.icon;
|
|
3220
|
+
};
|
|
3221
|
+
var validateFooterSpecs = (specs) => {
|
|
3222
|
+
for (const key of Object.keys(specs)) {
|
|
3223
|
+
const spec = specs[key];
|
|
3224
|
+
const footer = spec.footer;
|
|
3225
|
+
if (footer === undefined)
|
|
3226
|
+
continue;
|
|
3227
|
+
if (footer.icon.length === 0) {
|
|
3228
|
+
throw new Error(`option ${JSON.stringify(key)}: footer.icon must be non-empty`);
|
|
3229
|
+
}
|
|
3230
|
+
const activate = footerActivate(spec);
|
|
3231
|
+
if (activate === undefined) {
|
|
3232
|
+
throw new Error(`option ${JSON.stringify(key)}: footer requires activate "toggle" or "cycle" ` + "(booleans toggle; strings with choices cycle; other types need an explicit strategy)");
|
|
3233
|
+
}
|
|
3234
|
+
if (activate === "toggle" && spec.type !== "boolean") {
|
|
3235
|
+
throw new Error(`option ${JSON.stringify(key)}: footer activate "toggle" requires type "boolean"`);
|
|
3236
|
+
}
|
|
3237
|
+
if (activate === "cycle") {
|
|
3238
|
+
if (spec.type !== "string" || spec.choices === undefined || spec.choices.length === 0) {
|
|
3239
|
+
throw new Error(`option ${JSON.stringify(key)}: footer activate "cycle" requires string choices`);
|
|
3240
|
+
}
|
|
3241
|
+
const labels = footer.labels;
|
|
3242
|
+
if (labels === undefined) {
|
|
3243
|
+
throw new Error(`option ${JSON.stringify(key)}: footer.labels is required for choice options`);
|
|
3244
|
+
}
|
|
3245
|
+
for (const choice of spec.choices) {
|
|
3246
|
+
const label = labels[choice];
|
|
3247
|
+
if (label === undefined || label.length === 0) {
|
|
3248
|
+
throw new Error(`option ${JSON.stringify(key)}: footer.labels missing entry for ${JSON.stringify(choice)}`);
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
};
|
|
3254
|
+
|
|
3255
|
+
// ../../packages/options/src/decode.ts
|
|
3256
|
+
var isPresent = (value) => value !== undefined && value !== null;
|
|
3257
|
+
var stringifyReceived = (value) => {
|
|
3258
|
+
if (typeof value === "string")
|
|
3259
|
+
return JSON.stringify(value);
|
|
3260
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
3261
|
+
return JSON.stringify(value);
|
|
3262
|
+
if (value === null)
|
|
3263
|
+
return "null";
|
|
3264
|
+
return typeof value;
|
|
3265
|
+
};
|
|
3266
|
+
var formatResolveError = (error, filePath) => {
|
|
3267
|
+
if (error.layer === "file") {
|
|
3268
|
+
const where = filePath === undefined ? "" : ` in ${filePath}`;
|
|
3269
|
+
return `invalid value for ${error.key}${where}: ${error.message}`;
|
|
3270
|
+
}
|
|
3271
|
+
return `${error.key}: ${error.message}, got ${stringifyReceived(error.received)}`;
|
|
3272
|
+
};
|
|
3273
|
+
var fail = (key, layer, message, received) => ({
|
|
3274
|
+
key,
|
|
3275
|
+
layer,
|
|
3276
|
+
message,
|
|
3277
|
+
received
|
|
3278
|
+
});
|
|
3279
|
+
var booleanMessage = "expected true or false";
|
|
3280
|
+
var numberMessage = (spec) => {
|
|
3281
|
+
if (spec.integer === true && spec.min === 1 && spec.max === undefined) {
|
|
3282
|
+
return "expected a positive integer";
|
|
3283
|
+
}
|
|
3284
|
+
if (spec.integer === true)
|
|
3285
|
+
return "expected an integer";
|
|
3286
|
+
return "expected a number";
|
|
3287
|
+
};
|
|
3288
|
+
var inRange = (value, spec) => {
|
|
3289
|
+
if (spec.min !== undefined && value < spec.min)
|
|
3290
|
+
return false;
|
|
3291
|
+
if (spec.max !== undefined && value > spec.max)
|
|
3292
|
+
return false;
|
|
3293
|
+
if (spec.integer === true && !Number.isSafeInteger(value))
|
|
3294
|
+
return false;
|
|
3295
|
+
if (!Number.isFinite(value))
|
|
3296
|
+
return false;
|
|
3297
|
+
return true;
|
|
3298
|
+
};
|
|
3299
|
+
var decodeBoolean = (key, layer, value) => {
|
|
3300
|
+
if (layer === "env") {
|
|
3301
|
+
if (value === "true")
|
|
3302
|
+
return { ok: true, value: true };
|
|
3303
|
+
if (value === "false")
|
|
3304
|
+
return { ok: true, value: false };
|
|
3305
|
+
return { ok: false, error: fail(key, layer, booleanMessage, value) };
|
|
3306
|
+
}
|
|
3307
|
+
if (typeof value === "boolean")
|
|
3308
|
+
return { ok: true, value };
|
|
3309
|
+
return { ok: false, error: fail(key, layer, booleanMessage, value) };
|
|
3310
|
+
};
|
|
3311
|
+
var decodeNumber = (key, layer, value, spec) => {
|
|
3312
|
+
const message = numberMessage(spec);
|
|
3313
|
+
if (layer === "env") {
|
|
3314
|
+
if (typeof value !== "string" || !/^-?\d+(\.\d+)?$/.test(value)) {
|
|
3315
|
+
return { ok: false, error: fail(key, layer, message, value) };
|
|
3316
|
+
}
|
|
3317
|
+
if (spec.integer === true && !/^-?\d+$/.test(value)) {
|
|
3318
|
+
return { ok: false, error: fail(key, layer, message, value) };
|
|
3319
|
+
}
|
|
3320
|
+
const parsed = spec.integer === true ? Number.parseInt(value, 10) : Number.parseFloat(value);
|
|
3321
|
+
if (!inRange(parsed, spec))
|
|
3322
|
+
return { ok: false, error: fail(key, layer, message, value) };
|
|
3323
|
+
return { ok: true, value: parsed };
|
|
3324
|
+
}
|
|
3325
|
+
if (typeof value !== "number" || !inRange(value, spec)) {
|
|
3326
|
+
return { ok: false, error: fail(key, layer, message, value) };
|
|
3327
|
+
}
|
|
3328
|
+
return { ok: true, value };
|
|
3329
|
+
};
|
|
3330
|
+
var decodeString = (key, layer, value, spec) => {
|
|
3331
|
+
if (typeof value !== "string") {
|
|
3332
|
+
const message = spec.choices === undefined ? "expected a string" : `expected one of ${spec.choices.join(", ")}`;
|
|
3333
|
+
return { ok: false, error: fail(key, layer, message, value) };
|
|
3334
|
+
}
|
|
3335
|
+
if (spec.choices !== undefined && !spec.choices.includes(value)) {
|
|
3336
|
+
return {
|
|
3337
|
+
ok: false,
|
|
3338
|
+
error: fail(key, layer, `expected one of ${spec.choices.join(", ")}`, value)
|
|
3339
|
+
};
|
|
3340
|
+
}
|
|
3341
|
+
return { ok: true, value };
|
|
3342
|
+
};
|
|
3343
|
+
var decodeValue = (key, spec, layer, value) => {
|
|
3344
|
+
if (spec.type === "boolean")
|
|
3345
|
+
return decodeBoolean(key, layer, value);
|
|
3346
|
+
if (spec.type === "number")
|
|
3347
|
+
return decodeNumber(key, layer, value, spec);
|
|
3348
|
+
return decodeString(key, layer, value, spec);
|
|
3349
|
+
};
|
|
3350
|
+
|
|
3351
|
+
// ../../packages/options/src/resolve.ts
|
|
3352
|
+
var LAYERS = ["cli", "env", "file"];
|
|
3353
|
+
var resolveOptions = (specs, layers = {}) => {
|
|
3354
|
+
const values = {};
|
|
3355
|
+
for (const key of Object.keys(specs)) {
|
|
3356
|
+
const spec = specs[key];
|
|
3357
|
+
let found = false;
|
|
3358
|
+
for (const layer of LAYERS) {
|
|
3359
|
+
const source = layers[layer];
|
|
3360
|
+
if (source === undefined)
|
|
3361
|
+
continue;
|
|
3362
|
+
const raw = source[key];
|
|
3363
|
+
if (!isPresent(raw))
|
|
3364
|
+
continue;
|
|
3365
|
+
const decoded = decodeValue(key, spec, layer, raw);
|
|
3366
|
+
if (!decoded.ok)
|
|
3367
|
+
return decoded;
|
|
3368
|
+
values[key] = decoded.value;
|
|
3369
|
+
found = true;
|
|
3370
|
+
break;
|
|
3371
|
+
}
|
|
3372
|
+
if (!found)
|
|
3373
|
+
values[key] = spec.default;
|
|
3374
|
+
}
|
|
3375
|
+
return { ok: true, value: values };
|
|
3376
|
+
};
|
|
3377
|
+
var catalogDefaults = (specs) => {
|
|
3378
|
+
const values = {};
|
|
3379
|
+
for (const key of Object.keys(specs))
|
|
3380
|
+
values[key] = specs[key].default;
|
|
3381
|
+
return values;
|
|
3382
|
+
};
|
|
3383
|
+
|
|
3384
|
+
// ../../packages/options/src/session.ts
|
|
3385
|
+
var persistPolicy = (persist) => persist ?? "session";
|
|
3386
|
+
var createSession = (specs, values, options = {}) => {
|
|
3387
|
+
let snapshot = values;
|
|
3388
|
+
const listeners = new Set;
|
|
3389
|
+
const get = (key) => snapshot[key];
|
|
3390
|
+
const set = async (key, value) => {
|
|
3391
|
+
const spec = specs[key];
|
|
3392
|
+
if (spec === undefined) {
|
|
3393
|
+
throw new Error(`unknown option ${JSON.stringify(key)}`);
|
|
3394
|
+
}
|
|
3395
|
+
const decoded = decodeValue(key, spec, "cli", value);
|
|
3396
|
+
if (!decoded.ok) {
|
|
3397
|
+
throw new Error(`${decoded.error.key}: ${decoded.error.message}`);
|
|
3398
|
+
}
|
|
3399
|
+
if (Object.is(snapshot[key], decoded.value))
|
|
3400
|
+
return;
|
|
3401
|
+
snapshot = { ...snapshot, [key]: decoded.value };
|
|
3402
|
+
for (const listener of listeners)
|
|
3403
|
+
listener(snapshot);
|
|
3404
|
+
if (persistPolicy(spec.persist) === "file" && options.persist !== undefined) {
|
|
3405
|
+
await options.persist({
|
|
3406
|
+
key,
|
|
3407
|
+
value: decoded.value,
|
|
3408
|
+
values: snapshot
|
|
3409
|
+
});
|
|
3410
|
+
}
|
|
3411
|
+
};
|
|
3412
|
+
return {
|
|
3413
|
+
get,
|
|
3414
|
+
set,
|
|
3415
|
+
subscribe: (listener) => {
|
|
3416
|
+
listeners.add(listener);
|
|
3417
|
+
return () => {
|
|
3418
|
+
listeners.delete(listener);
|
|
3419
|
+
};
|
|
3420
|
+
},
|
|
3421
|
+
get values() {
|
|
3422
|
+
return snapshot;
|
|
3423
|
+
}
|
|
3424
|
+
};
|
|
3425
|
+
};
|
|
3426
|
+
|
|
3427
|
+
// ../../packages/options/src/define.ts
|
|
3428
|
+
var defineOptions = (specs) => {
|
|
3429
|
+
validateFooterSpecs(specs);
|
|
3430
|
+
return {
|
|
3431
|
+
specs,
|
|
3432
|
+
defaults: catalogDefaults(specs),
|
|
3433
|
+
resolve: (layers) => resolveOptions(specs, layers),
|
|
3434
|
+
createSession: (values, options) => createSession(specs, values, options)
|
|
3435
|
+
};
|
|
3436
|
+
};
|
|
3437
|
+
// src/Browser.tsx
|
|
3165
3438
|
import { useKeyboard, useRenderer, useTerminalDimensions as useTerminalDimensions2 } from "@opentui/react";
|
|
3166
3439
|
import { useAtomValue, useAtomSet } from "@effect/atom-react";
|
|
3167
3440
|
import { Effect as Effect3 } from "effect";
|
|
@@ -3347,7 +3620,7 @@ var browserBindings = [
|
|
|
3347
3620
|
description: "Open file (focus reader)",
|
|
3348
3621
|
keys: ["return", "right", "l"],
|
|
3349
3622
|
when: inSidebar,
|
|
3350
|
-
run: (c) => c.
|
|
3623
|
+
run: (c) => c.openSelectedFile()
|
|
3351
3624
|
},
|
|
3352
3625
|
{
|
|
3353
3626
|
id: "serve.current",
|
|
@@ -3572,17 +3845,16 @@ var HARD_FALLBACK_RAW = {
|
|
|
3572
3845
|
syntaxPunctuation: "#D8DEE9"
|
|
3573
3846
|
};
|
|
3574
3847
|
var HEX_RE_INIT = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
|
|
3575
|
-
var
|
|
3576
|
-
const v =
|
|
3848
|
+
var normalizeHex = (value) => {
|
|
3849
|
+
const v = value.replace("#", "");
|
|
3577
3850
|
const expanded = v.length === 3 || v.length === 4 ? v.split("").map((c) => c + c).join("") : v;
|
|
3578
|
-
|
|
3579
|
-
return `#${rgb.toLowerCase()}`;
|
|
3851
|
+
return `#${expanded.toLowerCase()}`;
|
|
3580
3852
|
};
|
|
3581
3853
|
for (const v of Object.values(HARD_FALLBACK_RAW)) {
|
|
3582
3854
|
if (!HEX_RE_INIT.test(v))
|
|
3583
3855
|
throw new Error(`bad fallback hex: ${v}`);
|
|
3584
3856
|
}
|
|
3585
|
-
var HARD_FALLBACK = Object.fromEntries(Object.entries(HARD_FALLBACK_RAW).map(([k, v]) => [k,
|
|
3857
|
+
var HARD_FALLBACK = Object.fromEntries(Object.entries(HARD_FALLBACK_RAW).map(([k, v]) => [k, normalizeHex(v)]));
|
|
3586
3858
|
var TOKEN_FALLBACK = {
|
|
3587
3859
|
selectedListItemText: "text",
|
|
3588
3860
|
markdownText: "text",
|
|
@@ -3591,12 +3863,6 @@ var TOKEN_FALLBACK = {
|
|
|
3591
3863
|
};
|
|
3592
3864
|
var HEX_RE = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
|
|
3593
3865
|
var isHex = (value) => HEX_RE.test(value);
|
|
3594
|
-
var normalizeHex = (value) => {
|
|
3595
|
-
const v = value.replace("#", "");
|
|
3596
|
-
const expanded = v.length === 3 || v.length === 4 ? v.split("").map((c) => c + c).join("") : v;
|
|
3597
|
-
const rgb = expanded.length === 8 ? expanded.slice(0, 6) : expanded;
|
|
3598
|
-
return `#${rgb.toLowerCase()}`;
|
|
3599
|
-
};
|
|
3600
3866
|
var resolveSide = (value, defs) => {
|
|
3601
3867
|
if (isHex(value))
|
|
3602
3868
|
return normalizeHex(value);
|
|
@@ -4090,7 +4356,7 @@ var BRAND = "\u2302";
|
|
|
4090
4356
|
var BRAND_NAME = "house";
|
|
4091
4357
|
|
|
4092
4358
|
// src/Footer.tsx
|
|
4093
|
-
import
|
|
4359
|
+
import stringWidth2 from "string-width";
|
|
4094
4360
|
|
|
4095
4361
|
// src/keymap/displayKey.ts
|
|
4096
4362
|
var displayKey = (raw) => {
|
|
@@ -4145,7 +4411,9 @@ var Spinner = ({
|
|
|
4145
4411
|
};
|
|
4146
4412
|
|
|
4147
4413
|
// src/StatusIndicator.tsx
|
|
4414
|
+
import stringWidth from "string-width";
|
|
4148
4415
|
import { jsxDEV as jsxDEV5 } from "@opentui/react/jsx-dev-runtime";
|
|
4416
|
+
var statusIndicatorWidth = (icon) => 2 + Math.max(1, stringWidth(icon));
|
|
4149
4417
|
var statusIndicatorFg = (variant) => {
|
|
4150
4418
|
switch (variant) {
|
|
4151
4419
|
case "info":
|
|
@@ -4164,18 +4432,20 @@ var StatusIndicator = ({
|
|
|
4164
4432
|
active = true,
|
|
4165
4433
|
onMouseUp
|
|
4166
4434
|
}) => {
|
|
4435
|
+
const glyph = icon.toLocaleUpperCase();
|
|
4167
4436
|
const activeColor = statusIndicatorFg(variant);
|
|
4168
4437
|
const backgroundColor = active ? activeColor : colors.backgroundElement;
|
|
4438
|
+
const width = statusIndicatorWidth(glyph);
|
|
4169
4439
|
return /* @__PURE__ */ jsxDEV5("box", {
|
|
4170
4440
|
...onMouseUp === undefined ? {} : { onMouseUp },
|
|
4171
4441
|
style: {
|
|
4172
|
-
width
|
|
4442
|
+
width,
|
|
4173
4443
|
height: 1,
|
|
4174
4444
|
flexDirection: "row",
|
|
4175
4445
|
backgroundColor
|
|
4176
4446
|
},
|
|
4177
4447
|
children: /* @__PURE__ */ jsxDEV5("text", {
|
|
4178
|
-
content: ` ${
|
|
4448
|
+
content: ` ${glyph} `,
|
|
4179
4449
|
wrapMode: "none",
|
|
4180
4450
|
style: {
|
|
4181
4451
|
fg: active ? colors.backgroundPanel : colors.textMuted,
|
|
@@ -4191,7 +4461,7 @@ import { jsxDEV as jsxDEV6 } from "@opentui/react/jsx-dev-runtime";
|
|
|
4191
4461
|
var FOOTER_HEIGHT = 1;
|
|
4192
4462
|
var normalizeStatusLine = (status) => status.replace(/\s+/g, " ").trim();
|
|
4193
4463
|
var HINT_SEPARATOR = " ";
|
|
4194
|
-
var hintWidth = (h) =>
|
|
4464
|
+
var hintWidth = (h) => stringWidth2(h.key) + 1 + stringWidth2(h.label);
|
|
4195
4465
|
var formatHint = (b) => {
|
|
4196
4466
|
if (!b.hint)
|
|
4197
4467
|
return null;
|
|
@@ -4218,6 +4488,8 @@ var fitHints = (hints, width) => {
|
|
|
4218
4488
|
return [{ key: first.key.slice(0, width), label: "" }];
|
|
4219
4489
|
};
|
|
4220
4490
|
var STATUS_SEPARATOR = " \xB7 ";
|
|
4491
|
+
var INDICATOR_SEPARATOR = " ";
|
|
4492
|
+
var INDICATOR_SEPARATOR_WIDTH = 1;
|
|
4221
4493
|
var NON_WARNING_STATUS_PREFIX_WIDTH = 2;
|
|
4222
4494
|
var isPartialDiscoveryWarning = (status) => status?.startsWith("scan incomplete:") ?? false;
|
|
4223
4495
|
var Footer = ({
|
|
@@ -4265,7 +4537,9 @@ var Footer = ({
|
|
|
4265
4537
|
] : [],
|
|
4266
4538
|
...indicators
|
|
4267
4539
|
];
|
|
4268
|
-
const
|
|
4540
|
+
const indicatorWidths = renderedIndicators.map((indicator) => statusIndicatorWidth(indicator.icon));
|
|
4541
|
+
const indicatorSeparatorBudget = renderedIndicators.length > 1 ? (renderedIndicators.length - 1) * INDICATOR_SEPARATOR_WIDTH : 0;
|
|
4542
|
+
const indicatorBudget = Math.min(usableWidth, indicatorWidths.reduce((sum, width2) => sum + width2, 0) + indicatorSeparatorBudget);
|
|
4269
4543
|
const hasContentAfterIndicators = notice !== null || status !== null || hints.length > 0;
|
|
4270
4544
|
const indicatorContentGapBudget = renderedIndicators.length > 0 && hasContentAfterIndicators && indicatorBudget < usableWidth ? 1 : 0;
|
|
4271
4545
|
const contentBudget = Math.max(0, usableWidth - indicatorBudget - indicatorContentGapBudget);
|
|
@@ -4277,9 +4551,21 @@ var Footer = ({
|
|
|
4277
4551
|
const statusContent = status ? status.slice(0, Math.max(0, statusBudget - statusChromeWidth)) : "";
|
|
4278
4552
|
const statusSeparatorContent = status ? STATUS_SEPARATOR.slice(0, Math.max(0, statusBudget - nonWarningStatusPrefixBudget - statusContent.length)) : "";
|
|
4279
4553
|
const noticeContent = notice ? notice.length > contentBudget ? notice.slice(0, contentBudget) : notice : null;
|
|
4280
|
-
const renderIndicators = () => renderedIndicators.
|
|
4281
|
-
|
|
4282
|
-
|
|
4554
|
+
const renderIndicators = () => renderedIndicators.flatMap(({ id, ...props }, index) => {
|
|
4555
|
+
const chip = /* @__PURE__ */ jsxDEV6(StatusIndicator, {
|
|
4556
|
+
...props
|
|
4557
|
+
}, id, false, undefined, this);
|
|
4558
|
+
if (index === 0)
|
|
4559
|
+
return [chip];
|
|
4560
|
+
return [
|
|
4561
|
+
/* @__PURE__ */ jsxDEV6("text", {
|
|
4562
|
+
content: INDICATOR_SEPARATOR,
|
|
4563
|
+
wrapMode: "none",
|
|
4564
|
+
style: { fg: colors.textMuted }
|
|
4565
|
+
}, `indicator-sep-${id}`, false, undefined, this),
|
|
4566
|
+
chip
|
|
4567
|
+
];
|
|
4568
|
+
});
|
|
4283
4569
|
const renderIndicatorContentGap = () => indicatorContentGapBudget > 0 ? /* @__PURE__ */ jsxDEV6("text", {
|
|
4284
4570
|
content: " ",
|
|
4285
4571
|
wrapMode: "none",
|
|
@@ -12742,192 +13028,76 @@ var map = loadBundledThemes();
|
|
|
12742
13028
|
var themeDefinitions = [...map.values()];
|
|
12743
13029
|
var getThemeDefinition = (id) => map.get(id);
|
|
12744
13030
|
|
|
12745
|
-
//
|
|
12746
|
-
var
|
|
12747
|
-
|
|
12748
|
-
|
|
12749
|
-
|
|
12750
|
-
|
|
12751
|
-
|
|
12752
|
-
|
|
12753
|
-
|
|
12754
|
-
|
|
12755
|
-
|
|
12756
|
-
|
|
12757
|
-
|
|
12758
|
-
|
|
12759
|
-
|
|
12760
|
-
|
|
12761
|
-
|
|
12762
|
-
|
|
12763
|
-
|
|
12764
|
-
|
|
12765
|
-
|
|
12766
|
-
|
|
12767
|
-
|
|
12768
|
-
});
|
|
12769
|
-
var booleanMessage = "expected true or false";
|
|
12770
|
-
var numberMessage = (spec) => {
|
|
12771
|
-
if (spec.integer === true && spec.min === 1 && spec.max === undefined) {
|
|
12772
|
-
return "expected a positive integer";
|
|
12773
|
-
}
|
|
12774
|
-
if (spec.integer === true)
|
|
12775
|
-
return "expected an integer";
|
|
12776
|
-
return "expected a number";
|
|
12777
|
-
};
|
|
12778
|
-
var inRange = (value, spec) => {
|
|
12779
|
-
if (spec.min !== undefined && value < spec.min)
|
|
12780
|
-
return false;
|
|
12781
|
-
if (spec.max !== undefined && value > spec.max)
|
|
12782
|
-
return false;
|
|
12783
|
-
if (spec.integer === true && !Number.isSafeInteger(value))
|
|
12784
|
-
return false;
|
|
12785
|
-
if (!Number.isFinite(value))
|
|
12786
|
-
return false;
|
|
12787
|
-
return true;
|
|
12788
|
-
};
|
|
12789
|
-
var decodeBoolean = (key, layer, value) => {
|
|
12790
|
-
if (layer === "env") {
|
|
12791
|
-
if (value === "true")
|
|
12792
|
-
return { ok: true, value: true };
|
|
12793
|
-
if (value === "false")
|
|
12794
|
-
return { ok: true, value: false };
|
|
12795
|
-
return { ok: false, error: fail(key, layer, booleanMessage, value) };
|
|
12796
|
-
}
|
|
12797
|
-
if (typeof value === "boolean")
|
|
12798
|
-
return { ok: true, value };
|
|
12799
|
-
return { ok: false, error: fail(key, layer, booleanMessage, value) };
|
|
12800
|
-
};
|
|
12801
|
-
var decodeNumber = (key, layer, value, spec) => {
|
|
12802
|
-
const message = numberMessage(spec);
|
|
12803
|
-
if (layer === "env") {
|
|
12804
|
-
if (typeof value !== "string" || !/^-?\d+(\.\d+)?$/.test(value)) {
|
|
12805
|
-
return { ok: false, error: fail(key, layer, message, value) };
|
|
12806
|
-
}
|
|
12807
|
-
if (spec.integer === true && !/^-?\d+$/.test(value)) {
|
|
12808
|
-
return { ok: false, error: fail(key, layer, message, value) };
|
|
12809
|
-
}
|
|
12810
|
-
const parsed = spec.integer === true ? Number.parseInt(value, 10) : Number.parseFloat(value);
|
|
12811
|
-
if (!inRange(parsed, spec))
|
|
12812
|
-
return { ok: false, error: fail(key, layer, message, value) };
|
|
12813
|
-
return { ok: true, value: parsed };
|
|
12814
|
-
}
|
|
12815
|
-
if (typeof value !== "number" || !inRange(value, spec)) {
|
|
12816
|
-
return { ok: false, error: fail(key, layer, message, value) };
|
|
12817
|
-
}
|
|
12818
|
-
return { ok: true, value };
|
|
12819
|
-
};
|
|
12820
|
-
var decodeString = (key, layer, value, spec) => {
|
|
12821
|
-
if (typeof value !== "string") {
|
|
12822
|
-
const message = spec.choices === undefined ? "expected a string" : `expected one of ${spec.choices.join(", ")}`;
|
|
12823
|
-
return { ok: false, error: fail(key, layer, message, value) };
|
|
12824
|
-
}
|
|
12825
|
-
if (spec.choices !== undefined && !spec.choices.includes(value)) {
|
|
12826
|
-
return {
|
|
12827
|
-
ok: false,
|
|
12828
|
-
error: fail(key, layer, `expected one of ${spec.choices.join(", ")}`, value)
|
|
12829
|
-
};
|
|
13031
|
+
// src/config/footerControls.ts
|
|
13032
|
+
var footerControlsFromSession = (options, session, handlers = {}) => {
|
|
13033
|
+
const controls = [];
|
|
13034
|
+
for (const key of footerKeys(options.specs)) {
|
|
13035
|
+
const spec = options.specs[key];
|
|
13036
|
+
const footer = spec.footer;
|
|
13037
|
+
if (footer === undefined)
|
|
13038
|
+
continue;
|
|
13039
|
+
const value = session.get(key);
|
|
13040
|
+
const handler = handlers[key];
|
|
13041
|
+
controls.push({
|
|
13042
|
+
id: key,
|
|
13043
|
+
icon: footerControlGlyph(spec, value),
|
|
13044
|
+
variant: "info",
|
|
13045
|
+
active: handler?.active ?? footerControlActive(spec, value),
|
|
13046
|
+
onMouseUp: () => {
|
|
13047
|
+
if (handler?.onActivate !== undefined) {
|
|
13048
|
+
handler.onActivate();
|
|
13049
|
+
return;
|
|
13050
|
+
}
|
|
13051
|
+
const current = session.get(key);
|
|
13052
|
+
session.set(key, nextFooterValue(spec, current));
|
|
13053
|
+
}
|
|
13054
|
+
});
|
|
12830
13055
|
}
|
|
12831
|
-
return
|
|
12832
|
-
};
|
|
12833
|
-
var decodeValue = (key, spec, layer, value) => {
|
|
12834
|
-
if (spec.type === "boolean")
|
|
12835
|
-
return decodeBoolean(key, layer, value);
|
|
12836
|
-
if (spec.type === "number")
|
|
12837
|
-
return decodeNumber(key, layer, value, spec);
|
|
12838
|
-
return decodeString(key, layer, value, spec);
|
|
13056
|
+
return controls;
|
|
12839
13057
|
};
|
|
12840
13058
|
|
|
12841
|
-
//
|
|
12842
|
-
var
|
|
12843
|
-
|
|
12844
|
-
const
|
|
12845
|
-
for (const
|
|
12846
|
-
const
|
|
12847
|
-
|
|
12848
|
-
|
|
12849
|
-
|
|
12850
|
-
|
|
12851
|
-
|
|
12852
|
-
|
|
12853
|
-
|
|
12854
|
-
|
|
12855
|
-
|
|
12856
|
-
|
|
12857
|
-
|
|
12858
|
-
|
|
12859
|
-
|
|
12860
|
-
|
|
13059
|
+
// src/config/footerLabels.ts
|
|
13060
|
+
var uniqueFooterLabels = (ids) => {
|
|
13061
|
+
const used = new Set;
|
|
13062
|
+
const out = {};
|
|
13063
|
+
for (const id of ids) {
|
|
13064
|
+
const compact = id.replace(/[^a-zA-Z0-9]+/g, "").toLowerCase();
|
|
13065
|
+
const source = compact.length > 0 ? compact : "x";
|
|
13066
|
+
let label = source.slice(0, 2).padEnd(2, "x");
|
|
13067
|
+
let offset = 2;
|
|
13068
|
+
while (used.has(label)) {
|
|
13069
|
+
const next = source[offset];
|
|
13070
|
+
if (next !== undefined) {
|
|
13071
|
+
label = `${source[0]}${next}`;
|
|
13072
|
+
offset += 1;
|
|
13073
|
+
} else {
|
|
13074
|
+
label = `${source[0]}${used.size % 10}`;
|
|
13075
|
+
offset += 1;
|
|
13076
|
+
if (offset > source.length + 20) {
|
|
13077
|
+
throw new Error(`unable to allocate unique footer label for ${JSON.stringify(id)}`);
|
|
13078
|
+
}
|
|
13079
|
+
}
|
|
12861
13080
|
}
|
|
12862
|
-
|
|
12863
|
-
|
|
13081
|
+
used.add(label);
|
|
13082
|
+
out[id] = label;
|
|
12864
13083
|
}
|
|
12865
|
-
return
|
|
12866
|
-
};
|
|
12867
|
-
var catalogDefaults = (specs) => {
|
|
12868
|
-
const values = {};
|
|
12869
|
-
for (const key of Object.keys(specs))
|
|
12870
|
-
values[key] = specs[key].default;
|
|
12871
|
-
return values;
|
|
12872
|
-
};
|
|
12873
|
-
|
|
12874
|
-
// ../../packages/options/src/session.ts
|
|
12875
|
-
var persistPolicy = (persist) => persist ?? "session";
|
|
12876
|
-
var createSession = (specs, values, options = {}) => {
|
|
12877
|
-
let snapshot = values;
|
|
12878
|
-
const listeners = new Set;
|
|
12879
|
-
const get = (key) => snapshot[key];
|
|
12880
|
-
const set = async (key, value) => {
|
|
12881
|
-
const spec = specs[key];
|
|
12882
|
-
if (spec === undefined) {
|
|
12883
|
-
throw new Error(`unknown option ${JSON.stringify(key)}`);
|
|
12884
|
-
}
|
|
12885
|
-
const decoded = decodeValue(key, spec, "cli", value);
|
|
12886
|
-
if (!decoded.ok) {
|
|
12887
|
-
throw new Error(`${decoded.error.key}: ${decoded.error.message}`);
|
|
12888
|
-
}
|
|
12889
|
-
if (Object.is(snapshot[key], decoded.value))
|
|
12890
|
-
return;
|
|
12891
|
-
snapshot = { ...snapshot, [key]: decoded.value };
|
|
12892
|
-
for (const listener of listeners)
|
|
12893
|
-
listener(snapshot);
|
|
12894
|
-
if (persistPolicy(spec.persist) === "file" && options.persist !== undefined) {
|
|
12895
|
-
await options.persist({
|
|
12896
|
-
key,
|
|
12897
|
-
value: decoded.value,
|
|
12898
|
-
values: snapshot
|
|
12899
|
-
});
|
|
12900
|
-
}
|
|
12901
|
-
};
|
|
12902
|
-
return {
|
|
12903
|
-
get,
|
|
12904
|
-
set,
|
|
12905
|
-
subscribe: (listener) => {
|
|
12906
|
-
listeners.add(listener);
|
|
12907
|
-
return () => {
|
|
12908
|
-
listeners.delete(listener);
|
|
12909
|
-
};
|
|
12910
|
-
},
|
|
12911
|
-
get values() {
|
|
12912
|
-
return snapshot;
|
|
12913
|
-
}
|
|
12914
|
-
};
|
|
13084
|
+
return out;
|
|
12915
13085
|
};
|
|
12916
13086
|
|
|
12917
|
-
// ../../packages/options/src/define.ts
|
|
12918
|
-
var defineOptions = (specs) => ({
|
|
12919
|
-
specs,
|
|
12920
|
-
defaults: catalogDefaults(specs),
|
|
12921
|
-
resolve: (layers) => resolveOptions(specs, layers),
|
|
12922
|
-
createSession: (values, options) => createSession(specs, values, options)
|
|
12923
|
-
});
|
|
12924
13087
|
// src/config/options.ts
|
|
12925
13088
|
var FILE_NAVIGATOR_ORDERS = ["tree", "recently-modified"];
|
|
13089
|
+
var themeIds = themeDefinitions.map((theme) => theme.id);
|
|
13090
|
+
var themeFooterLabels = uniqueFooterLabels(themeIds);
|
|
12926
13091
|
var houseOptions = defineOptions({
|
|
13092
|
+
autoHideSidebar: {
|
|
13093
|
+
type: "boolean",
|
|
13094
|
+
default: true
|
|
13095
|
+
},
|
|
12927
13096
|
wrap: {
|
|
12928
13097
|
type: "boolean",
|
|
12929
13098
|
default: false,
|
|
12930
|
-
persist: "session"
|
|
13099
|
+
persist: "session",
|
|
13100
|
+
footer: { icon: "W" }
|
|
12931
13101
|
},
|
|
12932
13102
|
width: {
|
|
12933
13103
|
type: "number",
|
|
@@ -12939,7 +13109,8 @@ var houseOptions = defineOptions({
|
|
|
12939
13109
|
type: "string",
|
|
12940
13110
|
default: "opencode",
|
|
12941
13111
|
persist: "file",
|
|
12942
|
-
choices:
|
|
13112
|
+
choices: themeIds,
|
|
13113
|
+
footer: { icon: "T", labels: themeFooterLabels }
|
|
12943
13114
|
},
|
|
12944
13115
|
tone: {
|
|
12945
13116
|
type: "string",
|
|
@@ -12960,7 +13131,15 @@ var houseOptions = defineOptions({
|
|
|
12960
13131
|
order: {
|
|
12961
13132
|
type: "string",
|
|
12962
13133
|
default: "recently-modified",
|
|
12963
|
-
choices: FILE_NAVIGATOR_ORDERS
|
|
13134
|
+
choices: FILE_NAVIGATOR_ORDERS,
|
|
13135
|
+
persist: "session",
|
|
13136
|
+
footer: {
|
|
13137
|
+
icon: "O",
|
|
13138
|
+
labels: {
|
|
13139
|
+
tree: "tr",
|
|
13140
|
+
"recently-modified": "re"
|
|
13141
|
+
}
|
|
13142
|
+
}
|
|
12964
13143
|
}
|
|
12965
13144
|
});
|
|
12966
13145
|
|
|
@@ -12996,6 +13175,7 @@ var parseShowList = (raw) => {
|
|
|
12996
13175
|
var DEFAULT_EXTENSIONS = [];
|
|
12997
13176
|
var DEFAULT_SHOW = "";
|
|
12998
13177
|
var KNOWN_FILE_KEYS = new Set([
|
|
13178
|
+
"autoHideSidebar",
|
|
12999
13179
|
"theme",
|
|
13000
13180
|
"tone",
|
|
13001
13181
|
"extensions",
|
|
@@ -13010,7 +13190,8 @@ var schema = Config.all({
|
|
|
13010
13190
|
extensions: Config.schema(Schema.String, "extensions"),
|
|
13011
13191
|
show: Config.schema(Schema.String, "show")
|
|
13012
13192
|
});
|
|
13013
|
-
var
|
|
13193
|
+
var fromUnknown = (root) => ConfigProvider.fromUnknown(root, { preserveEmptyStrings: true });
|
|
13194
|
+
var defaultsProvider = () => fromUnknown({
|
|
13014
13195
|
extensions: DEFAULT_EXTENSIONS.join(","),
|
|
13015
13196
|
show: DEFAULT_SHOW
|
|
13016
13197
|
});
|
|
@@ -13097,7 +13278,7 @@ var envProvider = (env) => {
|
|
|
13097
13278
|
entries.push(["extensions", extensions]);
|
|
13098
13279
|
if (show !== undefined)
|
|
13099
13280
|
entries.push(["show", show]);
|
|
13100
|
-
return
|
|
13281
|
+
return fromUnknown(Object.fromEntries(entries));
|
|
13101
13282
|
};
|
|
13102
13283
|
var cliProvider = (overrides) => {
|
|
13103
13284
|
const entries = [];
|
|
@@ -13105,7 +13286,7 @@ var cliProvider = (overrides) => {
|
|
|
13105
13286
|
entries.push(["extensions", overrides.extensions.join(",")]);
|
|
13106
13287
|
if (overrides.show !== null)
|
|
13107
13288
|
entries.push(["show", overrides.show.join(",")]);
|
|
13108
|
-
return
|
|
13289
|
+
return fromUnknown(Object.fromEntries(entries));
|
|
13109
13290
|
};
|
|
13110
13291
|
var defaultConfigPath = () => join3(process.env["XDG_CONFIG_HOME"] ?? join3(homedir(), ".config"), "house", "config.toml");
|
|
13111
13292
|
var formatConfigError = (err) => {
|
|
@@ -13120,6 +13301,7 @@ var formatConfigError = (err) => {
|
|
|
13120
13301
|
};
|
|
13121
13302
|
var loadConfig = (options = {}) => Effect2.gen(function* () {
|
|
13122
13303
|
const cli = options.cli ?? {
|
|
13304
|
+
autoHideSidebar: null,
|
|
13123
13305
|
theme: null,
|
|
13124
13306
|
tone: null,
|
|
13125
13307
|
extensions: null,
|
|
@@ -13142,6 +13324,7 @@ var loadConfig = (options = {}) => Effect2.gen(function* () {
|
|
|
13142
13324
|
}
|
|
13143
13325
|
const resolved = houseOptions.resolve({
|
|
13144
13326
|
cli: {
|
|
13327
|
+
autoHideSidebar: cli.autoHideSidebar,
|
|
13145
13328
|
wrap: cli.wrap,
|
|
13146
13329
|
width: cli.width,
|
|
13147
13330
|
theme: cli.theme,
|
|
@@ -13150,6 +13333,7 @@ var loadConfig = (options = {}) => Effect2.gen(function* () {
|
|
|
13150
13333
|
order: cli.order
|
|
13151
13334
|
},
|
|
13152
13335
|
env: {
|
|
13336
|
+
autoHideSidebar: env["HOUSE_AUTO_HIDE_SIDEBAR"],
|
|
13153
13337
|
wrap: env["HOUSE_WRAP"],
|
|
13154
13338
|
width: env["HOUSE_WIDTH"],
|
|
13155
13339
|
theme: env["HOUSE_THEME"],
|
|
@@ -13159,6 +13343,7 @@ var loadConfig = (options = {}) => Effect2.gen(function* () {
|
|
|
13159
13343
|
order: env["HOUSE_ORDER"]
|
|
13160
13344
|
},
|
|
13161
13345
|
file: {
|
|
13346
|
+
autoHideSidebar: fileData?.["autoHideSidebar"],
|
|
13162
13347
|
wrap: fileData?.["wrap"],
|
|
13163
13348
|
width: fileData?.["width"],
|
|
13164
13349
|
theme: fileData?.["theme"],
|
|
@@ -13188,6 +13373,7 @@ var loadConfig = (options = {}) => Effect2.gen(function* () {
|
|
|
13188
13373
|
return yield* Effect2.fail(new Error(`order: expected one of ${FILE_NAVIGATOR_ORDERS.join(", ")}, got ${JSON.stringify(order)}`));
|
|
13189
13374
|
}
|
|
13190
13375
|
return {
|
|
13376
|
+
autoHideSidebar: resolved.value.autoHideSidebar,
|
|
13191
13377
|
theme: resolved.value.theme,
|
|
13192
13378
|
tone,
|
|
13193
13379
|
defaultRoot,
|
|
@@ -13297,6 +13483,7 @@ var persistHouseOption = async (event, path) => {
|
|
|
13297
13483
|
|
|
13298
13484
|
// src/Browser.tsx
|
|
13299
13485
|
import { jsxDEV as jsxDEV13 } from "@opentui/react/jsx-dev-runtime";
|
|
13486
|
+
var asFileNavigatorOrder = (value) => typeof value === "string" && FILE_NAVIGATOR_ORDERS.includes(value) ? value : "recently-modified";
|
|
13300
13487
|
var defaultReadFile = (path) => Effect3.runPromise(readFileText(path));
|
|
13301
13488
|
var defaultDiscoveryPolicy = {
|
|
13302
13489
|
revision: "house-markdown",
|
|
@@ -13343,6 +13530,7 @@ var Browser = ({
|
|
|
13343
13530
|
initialQuery = "",
|
|
13344
13531
|
wrapWidth = 80,
|
|
13345
13532
|
initialWrap = false,
|
|
13533
|
+
initialAutoHideSidebar = true,
|
|
13346
13534
|
rootLabel = "current root",
|
|
13347
13535
|
discoveryStatus = null,
|
|
13348
13536
|
discoverySpinnerIntervalMs,
|
|
@@ -13357,6 +13545,7 @@ var Browser = ({
|
|
|
13357
13545
|
updateNotice = null,
|
|
13358
13546
|
updateNoticeTtlMs = 1e4,
|
|
13359
13547
|
disableFooterNoticeAutoClear = false,
|
|
13548
|
+
onNavigatorIdle,
|
|
13360
13549
|
onToggleAll,
|
|
13361
13550
|
startupFocus = null,
|
|
13362
13551
|
order = "recently-modified",
|
|
@@ -13376,17 +13565,20 @@ var Browser = ({
|
|
|
13376
13565
|
optionsSession.current = houseOptions.createSession({
|
|
13377
13566
|
...houseOptions.defaults,
|
|
13378
13567
|
wrap: initialWrap,
|
|
13568
|
+
autoHideSidebar: initialAutoHideSidebar,
|
|
13379
13569
|
width: wrapWidth,
|
|
13380
13570
|
theme: theme.id,
|
|
13381
|
-
tone: theme.tone
|
|
13571
|
+
tone: theme.tone,
|
|
13572
|
+
order: asFileNavigatorOrder(order)
|
|
13382
13573
|
}, { persist: persistHouseOption });
|
|
13383
13574
|
}
|
|
13384
13575
|
const wrapEnabled = useSyncExternalStore(optionsSession.current.subscribe, () => optionsSession.current.get("wrap"));
|
|
13576
|
+
const browseOrder = useSyncExternalStore(optionsSession.current.subscribe, () => asFileNavigatorOrder(optionsSession.current.get("order")));
|
|
13385
13577
|
const toggleWrap = () => {
|
|
13386
13578
|
const session = optionsSession.current;
|
|
13387
13579
|
if (session === null)
|
|
13388
13580
|
return;
|
|
13389
|
-
session.set("wrap",
|
|
13581
|
+
session.set("wrap", nextFooterValue(houseOptions.specs.wrap, session.get("wrap")));
|
|
13390
13582
|
};
|
|
13391
13583
|
const [loaded, setLoaded] = useState4(null);
|
|
13392
13584
|
const [error, setError] = useState4(null);
|
|
@@ -13520,11 +13712,36 @@ var Browser = ({
|
|
|
13520
13712
|
const paletteInputReadyRef = useRef3(false);
|
|
13521
13713
|
const promptInputReadyRef = useRef3(false);
|
|
13522
13714
|
const focusRef = useRef3(focus);
|
|
13715
|
+
const autoHideSidebar = optionsSession.current.get("autoHideSidebar");
|
|
13716
|
+
const openSelectedFile = () => {
|
|
13717
|
+
if (autoHideSidebar && navigator2.getSnapshot().selectedFile !== null)
|
|
13718
|
+
setShown(false);
|
|
13719
|
+
focusRef.current = "reader";
|
|
13720
|
+
setFocus("reader");
|
|
13721
|
+
};
|
|
13523
13722
|
const restoreFilterOnSidebarFocusRef = useRef3(startInFilter);
|
|
13524
13723
|
const [footerNotice, setFooterNoticeState] = useState4(null);
|
|
13525
13724
|
const pushFooterNotice = (text, ttlMs = 2000) => setFooterNoticeState({ text, ttlMs });
|
|
13526
13725
|
const serverRef = useRef3(initialPreviewServer);
|
|
13527
13726
|
const pendingSelectionPathRef = useRef3(null);
|
|
13727
|
+
const pendingSelectionMissingRef = useRef3(false);
|
|
13728
|
+
const clearPendingSelection = () => {
|
|
13729
|
+
pendingSelectionPathRef.current = null;
|
|
13730
|
+
pendingSelectionMissingRef.current = false;
|
|
13731
|
+
};
|
|
13732
|
+
const takePendingRestoreTarget = (files) => {
|
|
13733
|
+
const target = pendingSelectionPathRef.current;
|
|
13734
|
+
if (target === null)
|
|
13735
|
+
return null;
|
|
13736
|
+
const inList = files.some((file) => file.absolutePath === target);
|
|
13737
|
+
if (!inList) {
|
|
13738
|
+
pendingSelectionMissingRef.current = true;
|
|
13739
|
+
return null;
|
|
13740
|
+
}
|
|
13741
|
+
if (!pendingSelectionMissingRef.current)
|
|
13742
|
+
return null;
|
|
13743
|
+
return target;
|
|
13744
|
+
};
|
|
13528
13745
|
useLayoutEffect3(() => {
|
|
13529
13746
|
if (readerRootRef.current === root)
|
|
13530
13747
|
return;
|
|
@@ -13574,6 +13791,15 @@ var Browser = ({
|
|
|
13574
13791
|
useEffect4(() => {
|
|
13575
13792
|
focusRef.current = focus;
|
|
13576
13793
|
}, [focus]);
|
|
13794
|
+
const onNavigatorIdleRef = useRef3(onNavigatorIdle);
|
|
13795
|
+
onNavigatorIdleRef.current = onNavigatorIdle;
|
|
13796
|
+
const navigatorIdleNotifiedRef = useRef3(false);
|
|
13797
|
+
useEffect4(() => {
|
|
13798
|
+
if (navigatorIdleNotifiedRef.current || liveSnapshot.scanning)
|
|
13799
|
+
return;
|
|
13800
|
+
navigatorIdleNotifiedRef.current = true;
|
|
13801
|
+
onNavigatorIdleRef.current?.();
|
|
13802
|
+
}, [liveSnapshot.scanning]);
|
|
13577
13803
|
useEffect4(() => {
|
|
13578
13804
|
if (!updateNotice)
|
|
13579
13805
|
return;
|
|
@@ -13606,6 +13832,14 @@ var Browser = ({
|
|
|
13606
13832
|
rememberAppearance(session.set("theme", next.id));
|
|
13607
13833
|
pushFooterNotice(`theme: ${next.name}`);
|
|
13608
13834
|
};
|
|
13835
|
+
const cycleOrder = () => {
|
|
13836
|
+
const session = optionsSession.current;
|
|
13837
|
+
if (session === null)
|
|
13838
|
+
return;
|
|
13839
|
+
const next = nextFooterValue(houseOptions.specs.order, session.get("order"));
|
|
13840
|
+
session.set("order", next);
|
|
13841
|
+
pushFooterNotice(`order: ${next}`);
|
|
13842
|
+
};
|
|
13609
13843
|
const toggleTone = () => {
|
|
13610
13844
|
const session = optionsSession.current;
|
|
13611
13845
|
if (session === null)
|
|
@@ -13620,15 +13854,13 @@ var Browser = ({
|
|
|
13620
13854
|
};
|
|
13621
13855
|
const filterHasNoMatches = filterInput.length > 0 && displayedFiles.length === 0;
|
|
13622
13856
|
useEffect4(() => {
|
|
13623
|
-
const target =
|
|
13857
|
+
const target = takePendingRestoreTarget(displayedFiles);
|
|
13624
13858
|
if (target === null)
|
|
13625
13859
|
return;
|
|
13626
|
-
|
|
13627
|
-
|
|
13628
|
-
|
|
13629
|
-
|
|
13630
|
-
pendingSelectionPathRef.current = null;
|
|
13631
|
-
}
|
|
13860
|
+
const snapshot = navigator2.selectPath(target);
|
|
13861
|
+
if (snapshot.selectedFile?.absolutePath !== target)
|
|
13862
|
+
return;
|
|
13863
|
+
clearPendingSelection();
|
|
13632
13864
|
}, [displayedFiles, liveSnapshot.files]);
|
|
13633
13865
|
useEffect4(() => {
|
|
13634
13866
|
if (liveSnapshot.scanning || initialIndexRef.current === 0)
|
|
@@ -13974,18 +14206,19 @@ var Browser = ({
|
|
|
13974
14206
|
paletteOpen,
|
|
13975
14207
|
wrapEnabled,
|
|
13976
14208
|
setFocus,
|
|
14209
|
+
openSelectedFile,
|
|
13977
14210
|
moveSelectionBy: (delta) => {
|
|
13978
|
-
|
|
14211
|
+
clearPendingSelection();
|
|
13979
14212
|
newFileEditPathRef.current = null;
|
|
13980
14213
|
navigator2.moveBy(delta);
|
|
13981
14214
|
},
|
|
13982
14215
|
selectFirst: () => {
|
|
13983
|
-
|
|
14216
|
+
clearPendingSelection();
|
|
13984
14217
|
newFileEditPathRef.current = null;
|
|
13985
14218
|
navigator2.selectFirst();
|
|
13986
14219
|
},
|
|
13987
14220
|
selectLast: () => {
|
|
13988
|
-
|
|
14221
|
+
clearPendingSelection();
|
|
13989
14222
|
newFileEditPathRef.current = null;
|
|
13990
14223
|
navigator2.selectLast();
|
|
13991
14224
|
},
|
|
@@ -14042,6 +14275,7 @@ var Browser = ({
|
|
|
14042
14275
|
const current = navigator2.getSnapshot().selectedFile;
|
|
14043
14276
|
if (pendingSelectionPathRef.current === null && current) {
|
|
14044
14277
|
pendingSelectionPathRef.current = current.absolutePath;
|
|
14278
|
+
pendingSelectionMissingRef.current = false;
|
|
14045
14279
|
}
|
|
14046
14280
|
onToggleAll?.();
|
|
14047
14281
|
},
|
|
@@ -14185,6 +14419,10 @@ var Browser = ({
|
|
|
14185
14419
|
key.preventDefault();
|
|
14186
14420
|
return;
|
|
14187
14421
|
}
|
|
14422
|
+
if (key.ctrl && !key.meta && key.name === "c") {
|
|
14423
|
+
key.preventDefault();
|
|
14424
|
+
return;
|
|
14425
|
+
}
|
|
14188
14426
|
if (!promptInputReadyRef.current) {
|
|
14189
14427
|
if (key.name === "backspace" || key.name === "delete") {
|
|
14190
14428
|
if (promptInputRef.current.length === 0)
|
|
@@ -14209,8 +14447,7 @@ var Browser = ({
|
|
|
14209
14447
|
filterOpenRef.current = false;
|
|
14210
14448
|
setFilterOpen(false);
|
|
14211
14449
|
if (effectiveCommit) {
|
|
14212
|
-
|
|
14213
|
-
setFocus("reader");
|
|
14450
|
+
openSelectedFile();
|
|
14214
14451
|
} else {
|
|
14215
14452
|
const nextFocus = shown ? "sidebar" : "reader";
|
|
14216
14453
|
focusRef.current = nextFocus;
|
|
@@ -14326,15 +14563,11 @@ var Browser = ({
|
|
|
14326
14563
|
width,
|
|
14327
14564
|
notice: footerNotice?.text ?? null,
|
|
14328
14565
|
discoveryStatus: effectiveDiscoveryStatus,
|
|
14329
|
-
indicators: [
|
|
14330
|
-
{
|
|
14331
|
-
|
|
14332
|
-
|
|
14333
|
-
|
|
14334
|
-
active: wrapEnabled,
|
|
14335
|
-
onMouseUp: toggleWrap
|
|
14336
|
-
}
|
|
14337
|
-
],
|
|
14566
|
+
indicators: optionsSession.current === null ? [] : footerControlsFromSession(houseOptions, optionsSession.current, {
|
|
14567
|
+
wrap: { onActivate: toggleWrap },
|
|
14568
|
+
theme: { onActivate: () => cycleTheme(1) },
|
|
14569
|
+
order: { onActivate: cycleOrder }
|
|
14570
|
+
}),
|
|
14338
14571
|
...discoverySpinnerIntervalMs === undefined ? {} : { discoverySpinnerIntervalMs },
|
|
14339
14572
|
...discoverySpinnerInitialFrameIndex === undefined ? {} : { discoverySpinnerInitialFrameIndex },
|
|
14340
14573
|
...discoverySpinnerRegisterTick === undefined ? {} : { discoverySpinnerRegisterTick },
|
|
@@ -14369,7 +14602,7 @@ var Browser = ({
|
|
|
14369
14602
|
root,
|
|
14370
14603
|
policy,
|
|
14371
14604
|
watch: watch2,
|
|
14372
|
-
order,
|
|
14605
|
+
order: browseOrder,
|
|
14373
14606
|
debounceMs: filterDebounceMs,
|
|
14374
14607
|
navigatorRef,
|
|
14375
14608
|
snapshot: liveSnapshot,
|
|
@@ -14398,9 +14631,9 @@ var Browser = ({
|
|
|
14398
14631
|
if (!handle)
|
|
14399
14632
|
return;
|
|
14400
14633
|
const next = handle.getSnapshot();
|
|
14401
|
-
const target =
|
|
14402
|
-
if (target
|
|
14403
|
-
|
|
14634
|
+
const target = takePendingRestoreTarget(next.filteredFiles);
|
|
14635
|
+
if (target !== null) {
|
|
14636
|
+
clearPendingSelection();
|
|
14404
14637
|
setNavigatorSnapshot(handle.selectPath(target));
|
|
14405
14638
|
return;
|
|
14406
14639
|
}
|
|
@@ -14554,7 +14787,7 @@ var Browser = ({
|
|
|
14554
14787
|
|
|
14555
14788
|
// src/cli/argv.ts
|
|
14556
14789
|
import { Command } from "commander";
|
|
14557
|
-
var createProgram = () => new Command().allowUnknownOption(true).allowExcessArguments(true).exitOverride().helpOption(false).option("--theme [id]").option("--tone [mode]").option("--width [N]").option("--wrap").option("--no-wrap").option("--serve").option("--port [N]").option("--config-path").option("--no-update-check").option("--ext [list]").option("--focus [mode]").option("--order [mode]").option("--show [list]").option("--root [dir]").option("-h, --help").option("-v, --version").argument("[path]");
|
|
14790
|
+
var createProgram = () => new Command().allowUnknownOption(true).allowExcessArguments(true).exitOverride().helpOption(false).option("--theme [id]").option("--tone [mode]").option("--width [N]").option("--wrap").option("--no-wrap").option("--auto-hide-sidebar").option("--no-auto-hide-sidebar").option("--serve").option("--port [N]").option("--config-path").option("--no-update-check").option("--ext [list]").option("--focus [mode]").option("--order [mode]").option("--show [list]").option("--root [dir]").option("-h, --help").option("-v, --version").argument("[path]");
|
|
14558
14791
|
var VALUE_FLAGS = new Set([
|
|
14559
14792
|
"--theme",
|
|
14560
14793
|
"--tone",
|
|
@@ -14573,6 +14806,8 @@ var BOOLEAN_FLAGS = new Set([
|
|
|
14573
14806
|
"--no-update-check",
|
|
14574
14807
|
"--wrap",
|
|
14575
14808
|
"--no-wrap",
|
|
14809
|
+
"--auto-hide-sidebar",
|
|
14810
|
+
"--no-auto-hide-sidebar",
|
|
14576
14811
|
"--help",
|
|
14577
14812
|
"-h",
|
|
14578
14813
|
"--version",
|
|
@@ -14603,6 +14838,8 @@ var parseArgv = (argv) => {
|
|
|
14603
14838
|
const stringOrNull = (value) => typeof value === "string" ? value : null;
|
|
14604
14839
|
const hasWrap = argv.includes("--wrap");
|
|
14605
14840
|
const hasNoWrap = argv.includes("--no-wrap");
|
|
14841
|
+
const hasAutoHideSidebar = argv.includes("--auto-hide-sidebar");
|
|
14842
|
+
const hasNoAutoHideSidebar = argv.includes("--no-auto-hide-sidebar");
|
|
14606
14843
|
return {
|
|
14607
14844
|
path: typeof pathArg === "string" ? pathArg : null,
|
|
14608
14845
|
root: stringOrNull(opts["root"]),
|
|
@@ -14611,6 +14848,8 @@ var parseArgv = (argv) => {
|
|
|
14611
14848
|
width: stringOrNull(opts["width"]),
|
|
14612
14849
|
wrap: hasWrap ? true : hasNoWrap ? false : null,
|
|
14613
14850
|
wrapConflict: hasWrap && hasNoWrap,
|
|
14851
|
+
autoHideSidebar: hasAutoHideSidebar ? true : hasNoAutoHideSidebar ? false : null,
|
|
14852
|
+
autoHideSidebarConflict: hasAutoHideSidebar && hasNoAutoHideSidebar,
|
|
14614
14853
|
serve: opts["serve"] === true,
|
|
14615
14854
|
port: stringOrNull(opts["port"]),
|
|
14616
14855
|
help: opts["help"] === true,
|
|
@@ -14636,6 +14875,8 @@ options:
|
|
|
14636
14875
|
--width <N> reader wrap width used when wrapping is enabled (default: 80)
|
|
14637
14876
|
--wrap start with reader wrapping enabled
|
|
14638
14877
|
--no-wrap start with reader wrapping disabled
|
|
14878
|
+
--auto-hide-sidebar hide the sidebar after opening a file (default)
|
|
14879
|
+
--no-auto-hide-sidebar keep the sidebar visible after opening a file
|
|
14639
14880
|
--show <list> reveal normally-skipped entries; comma-separated subset of:
|
|
14640
14881
|
hidden, gitignored. Use --show "" to clear.
|
|
14641
14882
|
--root <dir> discovery root to walk (overrides defaultRoot config/env)
|
|
@@ -14656,8 +14897,8 @@ examples:
|
|
|
14656
14897
|
|
|
14657
14898
|
configuration:
|
|
14658
14899
|
file: $XDG_CONFIG_HOME/house/config.toml (default ~/.config/house/config.toml)
|
|
14659
|
-
keys: theme, tone, width, wrap, extensions, show, focus, defaultRoot, order
|
|
14660
|
-
env: HOUSE_THEME, HOUSE_TONE, HOUSE_WIDTH, HOUSE_WRAP, HOUSE_EXTENSIONS, HOUSE_SHOW, HOUSE_FOCUS, HOUSE_DEFAULT_ROOT, HOUSE_ORDER
|
|
14900
|
+
keys: theme, tone, width, wrap, autoHideSidebar, extensions, show, focus, defaultRoot, order
|
|
14901
|
+
env: HOUSE_THEME, HOUSE_TONE, HOUSE_WIDTH, HOUSE_WRAP, HOUSE_AUTO_HIDE_SIDEBAR, HOUSE_EXTENSIONS, HOUSE_SHOW, HOUSE_FOCUS, HOUSE_DEFAULT_ROOT, HOUSE_ORDER
|
|
14661
14902
|
precedence (high \u2192 low): flags \u2192 env \u2192 file \u2192 defaults`;
|
|
14662
14903
|
|
|
14663
14904
|
// src/cli/fast-exit.ts
|
|
@@ -14671,6 +14912,10 @@ var parseAndHandleFastExit = (argv) => {
|
|
|
14671
14912
|
console.error("house: --wrap and --no-wrap cannot be used together");
|
|
14672
14913
|
process.exit(2);
|
|
14673
14914
|
}
|
|
14915
|
+
if (args.autoHideSidebarConflict) {
|
|
14916
|
+
console.error("house: --auto-hide-sidebar and --no-auto-hide-sidebar cannot be used together");
|
|
14917
|
+
process.exit(2);
|
|
14918
|
+
}
|
|
14674
14919
|
if (argv.includes("--width") && args.width === null) {
|
|
14675
14920
|
console.error("house: --width requires a positive integer");
|
|
14676
14921
|
process.exit(2);
|
|
@@ -14996,6 +15241,7 @@ var DiscoverShell = ({
|
|
|
14996
15241
|
extensions,
|
|
14997
15242
|
wrapWidth,
|
|
14998
15243
|
initialWrap,
|
|
15244
|
+
initialAutoHideSidebar,
|
|
14999
15245
|
startupFocus,
|
|
15000
15246
|
order = "recently-modified"
|
|
15001
15247
|
}) => {
|
|
@@ -15008,6 +15254,7 @@ var DiscoverShell = ({
|
|
|
15008
15254
|
initialQuery,
|
|
15009
15255
|
wrapWidth,
|
|
15010
15256
|
initialWrap,
|
|
15257
|
+
initialAutoHideSidebar,
|
|
15011
15258
|
rootLabel,
|
|
15012
15259
|
startupFocus,
|
|
15013
15260
|
order,
|
|
@@ -15063,7 +15310,8 @@ async function main(argv = Bun.argv.slice(2)) {
|
|
|
15063
15310
|
}
|
|
15064
15311
|
return n;
|
|
15065
15312
|
})(),
|
|
15066
|
-
wrap: args.wrap
|
|
15313
|
+
wrap: args.wrap,
|
|
15314
|
+
autoHideSidebar: args.autoHideSidebar
|
|
15067
15315
|
}
|
|
15068
15316
|
})).catch((err) => {
|
|
15069
15317
|
console.error(`house: ${formatConfigError(err)}`);
|
|
@@ -15078,6 +15326,7 @@ async function main(argv = Bun.argv.slice(2)) {
|
|
|
15078
15326
|
defaultRoot,
|
|
15079
15327
|
width: wrapWidth,
|
|
15080
15328
|
wrap: initialWrap,
|
|
15329
|
+
autoHideSidebar: initialAutoHideSidebar,
|
|
15081
15330
|
order
|
|
15082
15331
|
} = config;
|
|
15083
15332
|
const themeDef = getThemeDefinition(themeId);
|
|
@@ -15133,6 +15382,7 @@ async function main(argv = Bun.argv.slice(2)) {
|
|
|
15133
15382
|
tone,
|
|
15134
15383
|
wrapWidth,
|
|
15135
15384
|
initialWrap,
|
|
15385
|
+
initialAutoHideSidebar,
|
|
15136
15386
|
show,
|
|
15137
15387
|
extensions,
|
|
15138
15388
|
startupFocus,
|
|
@@ -15150,6 +15400,7 @@ async function runTui({
|
|
|
15150
15400
|
tone,
|
|
15151
15401
|
wrapWidth,
|
|
15152
15402
|
initialWrap,
|
|
15403
|
+
initialAutoHideSidebar,
|
|
15153
15404
|
show,
|
|
15154
15405
|
extensions,
|
|
15155
15406
|
startupFocus,
|
|
@@ -15191,6 +15442,7 @@ async function runTui({
|
|
|
15191
15442
|
extensions,
|
|
15192
15443
|
wrapWidth,
|
|
15193
15444
|
initialWrap,
|
|
15445
|
+
initialAutoHideSidebar,
|
|
15194
15446
|
startupFocus,
|
|
15195
15447
|
order
|
|
15196
15448
|
}, undefined, false, undefined, this)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carlesandres/house",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "TUI-first markdown reader on opentui",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -62,21 +62,21 @@
|
|
|
62
62
|
"version:set": "bun run dev/set-version.ts"
|
|
63
63
|
},
|
|
64
64
|
"optionalDependencies": {
|
|
65
|
-
"@carlesandres/house-darwin-arm64": "0.
|
|
66
|
-
"@carlesandres/house-darwin-x64": "0.
|
|
67
|
-
"@carlesandres/house-linux-arm64": "0.
|
|
68
|
-
"@carlesandres/house-linux-x64": "0.
|
|
65
|
+
"@carlesandres/house-darwin-arm64": "0.9.0",
|
|
66
|
+
"@carlesandres/house-darwin-x64": "0.9.0",
|
|
67
|
+
"@carlesandres/house-linux-arm64": "0.9.0",
|
|
68
|
+
"@carlesandres/house-linux-x64": "0.9.0"
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@effect/atom-react": "4.0.0-beta.
|
|
72
|
-
"@opentui/core": "0.
|
|
73
|
-
"@opentui/react": "0.
|
|
71
|
+
"@effect/atom-react": "4.0.0-beta.107",
|
|
72
|
+
"@opentui/core": "0.5.9",
|
|
73
|
+
"@opentui/react": "0.5.9",
|
|
74
74
|
"@parcel/watcher": "2.6.0",
|
|
75
75
|
"commander": "^14.0.3",
|
|
76
|
-
"effect": "4.0.0-beta.
|
|
76
|
+
"effect": "4.0.0-beta.107",
|
|
77
77
|
"ignore": "^7.0.5",
|
|
78
78
|
"marked": "^18.0.4",
|
|
79
|
-
"react": "19.2.
|
|
79
|
+
"react": "19.2.8",
|
|
80
80
|
"scheduler": "0.27.0",
|
|
81
81
|
"string-width": "7.2.0",
|
|
82
82
|
"web-tree-sitter": "0.25.10"
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"devDependencies": {
|
|
85
85
|
"@effect/language-service": "0.86.2",
|
|
86
86
|
"@types/bun": "1.3.14",
|
|
87
|
-
"@types/react": "19.2.
|
|
87
|
+
"@types/react": "19.2.18",
|
|
88
88
|
"emulate": "0.9.0",
|
|
89
89
|
"oxfmt": "0.51.0",
|
|
90
90
|
"oxlint": "1.66.0",
|