@carlesandres/house 0.8.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 CHANGED
@@ -2,10 +2,33 @@
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 on the `release: published` event, 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.
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
+
9
32
  ## [0.8.0] — 2026-08-30
10
33
 
11
34
  ### Added
@@ -442,7 +465,8 @@ The v1 MVP, published as `@carlesandres/openmdr` on npm.
442
465
 
443
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.
444
467
 
445
- [Unreleased]: https://github.com/carlesandres/house/compare/v0.8.0...HEAD
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
446
470
  [0.8.0]: https://github.com/carlesandres/house/compare/v0.7.0...v0.8.0
447
471
  [0.7.0]: https://github.com/carlesandres/house/compare/v0.6.0...v0.7.0
448
472
  [0.6.0]: https://github.com/carlesandres/house/compare/v0.5.2...v0.6.0
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.8.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.60",
3138
- "@opentui/core": "0.2.15",
3139
- "@opentui/react": "0.2.15",
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.60",
3142
+ effect: "4.0.0-beta.107",
3143
3143
  ignore: "^7.0.5",
3144
3144
  marked: "^18.0.4",
3145
- react: "19.2.6",
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.15",
3153
+ "@types/react": "19.2.18",
3154
3154
  emulate: "0.9.0",
3155
3155
  oxfmt: "0.51.0",
3156
3156
  oxlint: "1.66.0",
@@ -3620,7 +3620,7 @@ var browserBindings = [
3620
3620
  description: "Open file (focus reader)",
3621
3621
  keys: ["return", "right", "l"],
3622
3622
  when: inSidebar,
3623
- run: (c) => c.setFocus("reader")
3623
+ run: (c) => c.openSelectedFile()
3624
3624
  },
3625
3625
  {
3626
3626
  id: "serve.current",
@@ -3845,17 +3845,16 @@ var HARD_FALLBACK_RAW = {
3845
3845
  syntaxPunctuation: "#D8DEE9"
3846
3846
  };
3847
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})$/;
3848
- var lowerHex = (hex) => {
3849
- const v = hex.replace("#", "");
3848
+ var normalizeHex = (value) => {
3849
+ const v = value.replace("#", "");
3850
3850
  const expanded = v.length === 3 || v.length === 4 ? v.split("").map((c) => c + c).join("") : v;
3851
- const rgb = expanded.length === 8 ? expanded.slice(0, 6) : expanded;
3852
- return `#${rgb.toLowerCase()}`;
3851
+ return `#${expanded.toLowerCase()}`;
3853
3852
  };
3854
3853
  for (const v of Object.values(HARD_FALLBACK_RAW)) {
3855
3854
  if (!HEX_RE_INIT.test(v))
3856
3855
  throw new Error(`bad fallback hex: ${v}`);
3857
3856
  }
3858
- var HARD_FALLBACK = Object.fromEntries(Object.entries(HARD_FALLBACK_RAW).map(([k, v]) => [k, lowerHex(v)]));
3857
+ var HARD_FALLBACK = Object.fromEntries(Object.entries(HARD_FALLBACK_RAW).map(([k, v]) => [k, normalizeHex(v)]));
3859
3858
  var TOKEN_FALLBACK = {
3860
3859
  selectedListItemText: "text",
3861
3860
  markdownText: "text",
@@ -3864,12 +3863,6 @@ var TOKEN_FALLBACK = {
3864
3863
  };
3865
3864
  var HEX_RE = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
3866
3865
  var isHex = (value) => HEX_RE.test(value);
3867
- var normalizeHex = (value) => {
3868
- const v = value.replace("#", "");
3869
- const expanded = v.length === 3 || v.length === 4 ? v.split("").map((c) => c + c).join("") : v;
3870
- const rgb = expanded.length === 8 ? expanded.slice(0, 6) : expanded;
3871
- return `#${rgb.toLowerCase()}`;
3872
- };
3873
3866
  var resolveSide = (value, defs) => {
3874
3867
  if (isHex(value))
3875
3868
  return normalizeHex(value);
@@ -13096,6 +13089,10 @@ var FILE_NAVIGATOR_ORDERS = ["tree", "recently-modified"];
13096
13089
  var themeIds = themeDefinitions.map((theme) => theme.id);
13097
13090
  var themeFooterLabels = uniqueFooterLabels(themeIds);
13098
13091
  var houseOptions = defineOptions({
13092
+ autoHideSidebar: {
13093
+ type: "boolean",
13094
+ default: true
13095
+ },
13099
13096
  wrap: {
13100
13097
  type: "boolean",
13101
13098
  default: false,
@@ -13178,6 +13175,7 @@ var parseShowList = (raw) => {
13178
13175
  var DEFAULT_EXTENSIONS = [];
13179
13176
  var DEFAULT_SHOW = "";
13180
13177
  var KNOWN_FILE_KEYS = new Set([
13178
+ "autoHideSidebar",
13181
13179
  "theme",
13182
13180
  "tone",
13183
13181
  "extensions",
@@ -13192,7 +13190,8 @@ var schema = Config.all({
13192
13190
  extensions: Config.schema(Schema.String, "extensions"),
13193
13191
  show: Config.schema(Schema.String, "show")
13194
13192
  });
13195
- var defaultsProvider = () => ConfigProvider.fromUnknown({
13193
+ var fromUnknown = (root) => ConfigProvider.fromUnknown(root, { preserveEmptyStrings: true });
13194
+ var defaultsProvider = () => fromUnknown({
13196
13195
  extensions: DEFAULT_EXTENSIONS.join(","),
13197
13196
  show: DEFAULT_SHOW
13198
13197
  });
@@ -13279,7 +13278,7 @@ var envProvider = (env) => {
13279
13278
  entries.push(["extensions", extensions]);
13280
13279
  if (show !== undefined)
13281
13280
  entries.push(["show", show]);
13282
- return ConfigProvider.fromUnknown(Object.fromEntries(entries));
13281
+ return fromUnknown(Object.fromEntries(entries));
13283
13282
  };
13284
13283
  var cliProvider = (overrides) => {
13285
13284
  const entries = [];
@@ -13287,7 +13286,7 @@ var cliProvider = (overrides) => {
13287
13286
  entries.push(["extensions", overrides.extensions.join(",")]);
13288
13287
  if (overrides.show !== null)
13289
13288
  entries.push(["show", overrides.show.join(",")]);
13290
- return ConfigProvider.fromUnknown(Object.fromEntries(entries));
13289
+ return fromUnknown(Object.fromEntries(entries));
13291
13290
  };
13292
13291
  var defaultConfigPath = () => join3(process.env["XDG_CONFIG_HOME"] ?? join3(homedir(), ".config"), "house", "config.toml");
13293
13292
  var formatConfigError = (err) => {
@@ -13302,6 +13301,7 @@ var formatConfigError = (err) => {
13302
13301
  };
13303
13302
  var loadConfig = (options = {}) => Effect2.gen(function* () {
13304
13303
  const cli = options.cli ?? {
13304
+ autoHideSidebar: null,
13305
13305
  theme: null,
13306
13306
  tone: null,
13307
13307
  extensions: null,
@@ -13324,6 +13324,7 @@ var loadConfig = (options = {}) => Effect2.gen(function* () {
13324
13324
  }
13325
13325
  const resolved = houseOptions.resolve({
13326
13326
  cli: {
13327
+ autoHideSidebar: cli.autoHideSidebar,
13327
13328
  wrap: cli.wrap,
13328
13329
  width: cli.width,
13329
13330
  theme: cli.theme,
@@ -13332,6 +13333,7 @@ var loadConfig = (options = {}) => Effect2.gen(function* () {
13332
13333
  order: cli.order
13333
13334
  },
13334
13335
  env: {
13336
+ autoHideSidebar: env["HOUSE_AUTO_HIDE_SIDEBAR"],
13335
13337
  wrap: env["HOUSE_WRAP"],
13336
13338
  width: env["HOUSE_WIDTH"],
13337
13339
  theme: env["HOUSE_THEME"],
@@ -13341,6 +13343,7 @@ var loadConfig = (options = {}) => Effect2.gen(function* () {
13341
13343
  order: env["HOUSE_ORDER"]
13342
13344
  },
13343
13345
  file: {
13346
+ autoHideSidebar: fileData?.["autoHideSidebar"],
13344
13347
  wrap: fileData?.["wrap"],
13345
13348
  width: fileData?.["width"],
13346
13349
  theme: fileData?.["theme"],
@@ -13370,6 +13373,7 @@ var loadConfig = (options = {}) => Effect2.gen(function* () {
13370
13373
  return yield* Effect2.fail(new Error(`order: expected one of ${FILE_NAVIGATOR_ORDERS.join(", ")}, got ${JSON.stringify(order)}`));
13371
13374
  }
13372
13375
  return {
13376
+ autoHideSidebar: resolved.value.autoHideSidebar,
13373
13377
  theme: resolved.value.theme,
13374
13378
  tone,
13375
13379
  defaultRoot,
@@ -13526,6 +13530,7 @@ var Browser = ({
13526
13530
  initialQuery = "",
13527
13531
  wrapWidth = 80,
13528
13532
  initialWrap = false,
13533
+ initialAutoHideSidebar = true,
13529
13534
  rootLabel = "current root",
13530
13535
  discoveryStatus = null,
13531
13536
  discoverySpinnerIntervalMs,
@@ -13540,6 +13545,7 @@ var Browser = ({
13540
13545
  updateNotice = null,
13541
13546
  updateNoticeTtlMs = 1e4,
13542
13547
  disableFooterNoticeAutoClear = false,
13548
+ onNavigatorIdle,
13543
13549
  onToggleAll,
13544
13550
  startupFocus = null,
13545
13551
  order = "recently-modified",
@@ -13559,6 +13565,7 @@ var Browser = ({
13559
13565
  optionsSession.current = houseOptions.createSession({
13560
13566
  ...houseOptions.defaults,
13561
13567
  wrap: initialWrap,
13568
+ autoHideSidebar: initialAutoHideSidebar,
13562
13569
  width: wrapWidth,
13563
13570
  theme: theme.id,
13564
13571
  tone: theme.tone,
@@ -13705,11 +13712,36 @@ var Browser = ({
13705
13712
  const paletteInputReadyRef = useRef3(false);
13706
13713
  const promptInputReadyRef = useRef3(false);
13707
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
+ };
13708
13722
  const restoreFilterOnSidebarFocusRef = useRef3(startInFilter);
13709
13723
  const [footerNotice, setFooterNoticeState] = useState4(null);
13710
13724
  const pushFooterNotice = (text, ttlMs = 2000) => setFooterNoticeState({ text, ttlMs });
13711
13725
  const serverRef = useRef3(initialPreviewServer);
13712
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
+ };
13713
13745
  useLayoutEffect3(() => {
13714
13746
  if (readerRootRef.current === root)
13715
13747
  return;
@@ -13759,6 +13791,15 @@ var Browser = ({
13759
13791
  useEffect4(() => {
13760
13792
  focusRef.current = focus;
13761
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]);
13762
13803
  useEffect4(() => {
13763
13804
  if (!updateNotice)
13764
13805
  return;
@@ -13813,15 +13854,13 @@ var Browser = ({
13813
13854
  };
13814
13855
  const filterHasNoMatches = filterInput.length > 0 && displayedFiles.length === 0;
13815
13856
  useEffect4(() => {
13816
- const target = pendingSelectionPathRef.current;
13857
+ const target = takePendingRestoreTarget(displayedFiles);
13817
13858
  if (target === null)
13818
13859
  return;
13819
- if (displayedFiles.some((file) => file.absolutePath === target)) {
13820
- const snapshot = navigator2.selectPath(target);
13821
- if (snapshot.selectedFile?.absolutePath !== target)
13822
- return;
13823
- pendingSelectionPathRef.current = null;
13824
- }
13860
+ const snapshot = navigator2.selectPath(target);
13861
+ if (snapshot.selectedFile?.absolutePath !== target)
13862
+ return;
13863
+ clearPendingSelection();
13825
13864
  }, [displayedFiles, liveSnapshot.files]);
13826
13865
  useEffect4(() => {
13827
13866
  if (liveSnapshot.scanning || initialIndexRef.current === 0)
@@ -14167,18 +14206,19 @@ var Browser = ({
14167
14206
  paletteOpen,
14168
14207
  wrapEnabled,
14169
14208
  setFocus,
14209
+ openSelectedFile,
14170
14210
  moveSelectionBy: (delta) => {
14171
- pendingSelectionPathRef.current = null;
14211
+ clearPendingSelection();
14172
14212
  newFileEditPathRef.current = null;
14173
14213
  navigator2.moveBy(delta);
14174
14214
  },
14175
14215
  selectFirst: () => {
14176
- pendingSelectionPathRef.current = null;
14216
+ clearPendingSelection();
14177
14217
  newFileEditPathRef.current = null;
14178
14218
  navigator2.selectFirst();
14179
14219
  },
14180
14220
  selectLast: () => {
14181
- pendingSelectionPathRef.current = null;
14221
+ clearPendingSelection();
14182
14222
  newFileEditPathRef.current = null;
14183
14223
  navigator2.selectLast();
14184
14224
  },
@@ -14235,6 +14275,7 @@ var Browser = ({
14235
14275
  const current = navigator2.getSnapshot().selectedFile;
14236
14276
  if (pendingSelectionPathRef.current === null && current) {
14237
14277
  pendingSelectionPathRef.current = current.absolutePath;
14278
+ pendingSelectionMissingRef.current = false;
14238
14279
  }
14239
14280
  onToggleAll?.();
14240
14281
  },
@@ -14378,6 +14419,10 @@ var Browser = ({
14378
14419
  key.preventDefault();
14379
14420
  return;
14380
14421
  }
14422
+ if (key.ctrl && !key.meta && key.name === "c") {
14423
+ key.preventDefault();
14424
+ return;
14425
+ }
14381
14426
  if (!promptInputReadyRef.current) {
14382
14427
  if (key.name === "backspace" || key.name === "delete") {
14383
14428
  if (promptInputRef.current.length === 0)
@@ -14402,8 +14447,7 @@ var Browser = ({
14402
14447
  filterOpenRef.current = false;
14403
14448
  setFilterOpen(false);
14404
14449
  if (effectiveCommit) {
14405
- focusRef.current = "reader";
14406
- setFocus("reader");
14450
+ openSelectedFile();
14407
14451
  } else {
14408
14452
  const nextFocus = shown ? "sidebar" : "reader";
14409
14453
  focusRef.current = nextFocus;
@@ -14587,9 +14631,9 @@ var Browser = ({
14587
14631
  if (!handle)
14588
14632
  return;
14589
14633
  const next = handle.getSnapshot();
14590
- const target = pendingSelectionPathRef.current;
14591
- if (target && next.filteredFiles.some((file) => file.absolutePath === target)) {
14592
- pendingSelectionPathRef.current = null;
14634
+ const target = takePendingRestoreTarget(next.filteredFiles);
14635
+ if (target !== null) {
14636
+ clearPendingSelection();
14593
14637
  setNavigatorSnapshot(handle.selectPath(target));
14594
14638
  return;
14595
14639
  }
@@ -14743,7 +14787,7 @@ var Browser = ({
14743
14787
 
14744
14788
  // src/cli/argv.ts
14745
14789
  import { Command } from "commander";
14746
- 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]");
14747
14791
  var VALUE_FLAGS = new Set([
14748
14792
  "--theme",
14749
14793
  "--tone",
@@ -14762,6 +14806,8 @@ var BOOLEAN_FLAGS = new Set([
14762
14806
  "--no-update-check",
14763
14807
  "--wrap",
14764
14808
  "--no-wrap",
14809
+ "--auto-hide-sidebar",
14810
+ "--no-auto-hide-sidebar",
14765
14811
  "--help",
14766
14812
  "-h",
14767
14813
  "--version",
@@ -14792,6 +14838,8 @@ var parseArgv = (argv) => {
14792
14838
  const stringOrNull = (value) => typeof value === "string" ? value : null;
14793
14839
  const hasWrap = argv.includes("--wrap");
14794
14840
  const hasNoWrap = argv.includes("--no-wrap");
14841
+ const hasAutoHideSidebar = argv.includes("--auto-hide-sidebar");
14842
+ const hasNoAutoHideSidebar = argv.includes("--no-auto-hide-sidebar");
14795
14843
  return {
14796
14844
  path: typeof pathArg === "string" ? pathArg : null,
14797
14845
  root: stringOrNull(opts["root"]),
@@ -14800,6 +14848,8 @@ var parseArgv = (argv) => {
14800
14848
  width: stringOrNull(opts["width"]),
14801
14849
  wrap: hasWrap ? true : hasNoWrap ? false : null,
14802
14850
  wrapConflict: hasWrap && hasNoWrap,
14851
+ autoHideSidebar: hasAutoHideSidebar ? true : hasNoAutoHideSidebar ? false : null,
14852
+ autoHideSidebarConflict: hasAutoHideSidebar && hasNoAutoHideSidebar,
14803
14853
  serve: opts["serve"] === true,
14804
14854
  port: stringOrNull(opts["port"]),
14805
14855
  help: opts["help"] === true,
@@ -14825,6 +14875,8 @@ options:
14825
14875
  --width <N> reader wrap width used when wrapping is enabled (default: 80)
14826
14876
  --wrap start with reader wrapping enabled
14827
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
14828
14880
  --show <list> reveal normally-skipped entries; comma-separated subset of:
14829
14881
  hidden, gitignored. Use --show "" to clear.
14830
14882
  --root <dir> discovery root to walk (overrides defaultRoot config/env)
@@ -14845,8 +14897,8 @@ examples:
14845
14897
 
14846
14898
  configuration:
14847
14899
  file: $XDG_CONFIG_HOME/house/config.toml (default ~/.config/house/config.toml)
14848
- keys: theme, tone, width, wrap, extensions, show, focus, defaultRoot, order
14849
- 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
14850
14902
  precedence (high \u2192 low): flags \u2192 env \u2192 file \u2192 defaults`;
14851
14903
 
14852
14904
  // src/cli/fast-exit.ts
@@ -14860,6 +14912,10 @@ var parseAndHandleFastExit = (argv) => {
14860
14912
  console.error("house: --wrap and --no-wrap cannot be used together");
14861
14913
  process.exit(2);
14862
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
+ }
14863
14919
  if (argv.includes("--width") && args.width === null) {
14864
14920
  console.error("house: --width requires a positive integer");
14865
14921
  process.exit(2);
@@ -15185,6 +15241,7 @@ var DiscoverShell = ({
15185
15241
  extensions,
15186
15242
  wrapWidth,
15187
15243
  initialWrap,
15244
+ initialAutoHideSidebar,
15188
15245
  startupFocus,
15189
15246
  order = "recently-modified"
15190
15247
  }) => {
@@ -15197,6 +15254,7 @@ var DiscoverShell = ({
15197
15254
  initialQuery,
15198
15255
  wrapWidth,
15199
15256
  initialWrap,
15257
+ initialAutoHideSidebar,
15200
15258
  rootLabel,
15201
15259
  startupFocus,
15202
15260
  order,
@@ -15252,7 +15310,8 @@ async function main(argv = Bun.argv.slice(2)) {
15252
15310
  }
15253
15311
  return n;
15254
15312
  })(),
15255
- wrap: args.wrap
15313
+ wrap: args.wrap,
15314
+ autoHideSidebar: args.autoHideSidebar
15256
15315
  }
15257
15316
  })).catch((err) => {
15258
15317
  console.error(`house: ${formatConfigError(err)}`);
@@ -15267,6 +15326,7 @@ async function main(argv = Bun.argv.slice(2)) {
15267
15326
  defaultRoot,
15268
15327
  width: wrapWidth,
15269
15328
  wrap: initialWrap,
15329
+ autoHideSidebar: initialAutoHideSidebar,
15270
15330
  order
15271
15331
  } = config;
15272
15332
  const themeDef = getThemeDefinition(themeId);
@@ -15322,6 +15382,7 @@ async function main(argv = Bun.argv.slice(2)) {
15322
15382
  tone,
15323
15383
  wrapWidth,
15324
15384
  initialWrap,
15385
+ initialAutoHideSidebar,
15325
15386
  show,
15326
15387
  extensions,
15327
15388
  startupFocus,
@@ -15339,6 +15400,7 @@ async function runTui({
15339
15400
  tone,
15340
15401
  wrapWidth,
15341
15402
  initialWrap,
15403
+ initialAutoHideSidebar,
15342
15404
  show,
15343
15405
  extensions,
15344
15406
  startupFocus,
@@ -15380,6 +15442,7 @@ async function runTui({
15380
15442
  extensions,
15381
15443
  wrapWidth,
15382
15444
  initialWrap,
15445
+ initialAutoHideSidebar,
15383
15446
  startupFocus,
15384
15447
  order
15385
15448
  }, undefined, false, undefined, this)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carlesandres/house",
3
- "version": "0.8.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.8.0",
66
- "@carlesandres/house-darwin-x64": "0.8.0",
67
- "@carlesandres/house-linux-arm64": "0.8.0",
68
- "@carlesandres/house-linux-x64": "0.8.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.60",
72
- "@opentui/core": "0.2.15",
73
- "@opentui/react": "0.2.15",
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.60",
76
+ "effect": "4.0.0-beta.107",
77
77
  "ignore": "^7.0.5",
78
78
  "marked": "^18.0.4",
79
- "react": "19.2.6",
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.15",
87
+ "@types/react": "19.2.18",
88
88
  "emulate": "0.9.0",
89
89
  "oxfmt": "0.51.0",
90
90
  "oxlint": "1.66.0",