@carlesandres/house 0.6.0 → 0.7.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/dist/index.js CHANGED
@@ -2711,14 +2711,14 @@ var require_filesystem = __commonJS((exports, module) => {
2711
2711
  fs.close(fd, () => {});
2712
2712
  return buffer.subarray(0, bytesRead);
2713
2713
  };
2714
- var readFile3 = (path) => new Promise((resolve3, reject) => {
2714
+ var readFile3 = (path) => new Promise((resolve4, reject) => {
2715
2715
  fs.open(path, "r", (err, fd) => {
2716
2716
  if (err) {
2717
2717
  reject(err);
2718
2718
  } else {
2719
2719
  const buffer = Buffer.alloc(MAX_LENGTH);
2720
2720
  fs.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
2721
- resolve3(buffer.subarray(0, bytesRead));
2721
+ resolve4(buffer.subarray(0, bytesRead));
2722
2722
  fs.close(fd, () => {});
2723
2723
  });
2724
2724
  }
@@ -2779,10 +2779,10 @@ var require_detect_libc = __commonJS((exports, module) => {
2779
2779
  var commandOut = "";
2780
2780
  var safeCommand = () => {
2781
2781
  if (!commandOut) {
2782
- return new Promise((resolve3) => {
2782
+ return new Promise((resolve4) => {
2783
2783
  childProcess.exec(command, (err, out) => {
2784
2784
  commandOut = err ? " " : out;
2785
- resolve3(commandOut);
2785
+ resolve4(commandOut);
2786
2786
  });
2787
2787
  });
2788
2788
  }
@@ -3055,9 +3055,9 @@ var require_watcher = __commonJS((exports) => {
3055
3055
  });
3056
3056
 
3057
3057
  // src/index.tsx
3058
- import { stat as stat2 } from "fs/promises";
3058
+ import { stat as stat3 } from "fs/promises";
3059
3059
  import { homedir as homedir3 } from "os";
3060
- import { basename as basename3, dirname as dirname3, isAbsolute as isAbsolute2, relative as relative4, resolve as resolve3 } from "path";
3060
+ import { basename as basename4, dirname as dirname5, isAbsolute as isAbsolute3, relative as relative5, resolve as resolve4 } from "path";
3061
3061
  import { createCliRenderer } from "@opentui/core";
3062
3062
  import { createRoot } from "@opentui/react";
3063
3063
  import { RegistryProvider } from "@effect/atom-react";
@@ -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.6.0",
3069
+ version: "0.7.0",
3070
3070
  description: "TUI-first markdown reader on opentui",
3071
3071
  type: "module",
3072
3072
  license: "MIT",
@@ -3160,15 +3160,14 @@ var package_default = {
3160
3160
  };
3161
3161
 
3162
3162
  // src/Browser.tsx
3163
- import { existsSync } from "fs";
3164
- import { join as join3 } from "path";
3163
+ import { join as join4 } from "path";
3165
3164
  import { SyntaxStyle } from "@opentui/core";
3166
3165
  import { useKeyboard, useRenderer, useTerminalDimensions as useTerminalDimensions2 } from "@opentui/react";
3167
3166
  import { useAtomValue, useAtomSet } from "@effect/atom-react";
3168
3167
  import { Effect as Effect3 } from "effect";
3169
3168
  import {
3170
3169
  useEffect as useEffect4,
3171
- useLayoutEffect as useLayoutEffect2,
3170
+ useLayoutEffect as useLayoutEffect3,
3172
3171
  useMemo as useMemo2,
3173
3172
  useReducer as useReducer2,
3174
3173
  useRef as useRef3,
@@ -3238,7 +3237,7 @@ var browserBindings = [
3238
3237
  group: "Global",
3239
3238
  description: "Command palette",
3240
3239
  hint: "palette",
3241
- keys: ["ctrl+p"],
3240
+ keys: ["ctrl+p", "?"],
3242
3241
  hintWhen: () => true,
3243
3242
  when: paletteClosed,
3244
3243
  run: (c) => c.openPalette()
@@ -3373,6 +3372,14 @@ var browserBindings = [
3373
3372
  keys: ["shift+n"],
3374
3373
  run: (c) => c.openNewFilePrompt()
3375
3374
  },
3375
+ {
3376
+ id: "file.rename",
3377
+ group: "File",
3378
+ description: "Rename selected file",
3379
+ keys: ["shift+r"],
3380
+ when: hasSelected,
3381
+ run: (c) => c.openRenamePrompt()
3382
+ },
3376
3383
  {
3377
3384
  id: "reader.prevFile",
3378
3385
  group: "File",
@@ -3442,6 +3449,11 @@ var annotations = {
3442
3449
  category: "File",
3443
3450
  keywords: ["new", "create", "nvim", "vim", "editor"]
3444
3451
  },
3452
+ "file.rename": {
3453
+ title: "Rename\u2026",
3454
+ category: "File",
3455
+ keywords: ["rename", "mv", "basename"]
3456
+ },
3445
3457
  "theme.next": { category: "Appearance" },
3446
3458
  "theme.prev": { category: "Appearance" },
3447
3459
  "theme.toneToggle": { title: "Toggle dark/light tone", category: "Appearance" },
@@ -3695,43 +3707,54 @@ var setActiveTheme = (definition, tone) => {
3695
3707
  };
3696
3708
 
3697
3709
  // src/PromptRow.tsx
3710
+ import { useLayoutEffect } from "react";
3698
3711
  import { jsxDEV } from "@opentui/react/jsx-dev-runtime";
3699
3712
  var PREFIX = "> ";
3700
- var CURSOR = "\u258F";
3701
3713
  var PromptRow = ({
3702
3714
  query,
3703
3715
  editing,
3704
3716
  placeholder = "",
3705
3717
  showPlaceholderWhileEditing = false,
3706
- width
3718
+ width,
3719
+ onInput,
3720
+ onEditingReady
3707
3721
  }) => {
3708
3722
  const bodyBudget = Math.max(1, width - PREFIX.length);
3709
- const placeholderWhileEditing = editing && query.length === 0 && showPlaceholderWhileEditing && placeholder.length > 0;
3710
- const rawBody = editing ? `${query}${CURSOR}` : query.length > 0 ? query : placeholder;
3711
- const bodyFg = editing ? colors.primary : query.length > 0 ? colors.text : colors.textMuted;
3712
- const body = rawBody.length <= bodyBudget ? rawBody : editing ? "\u2026" + rawBody.slice(rawBody.length - bodyBudget + 1) : rawBody.slice(0, bodyBudget - 1) + "\u2026";
3713
3723
  const prefixFg = editing ? colors.secondary : colors.primary;
3714
- if (placeholderWhileEditing) {
3715
- const placeholderBudget = Math.max(1, bodyBudget - CURSOR.length);
3716
- const placeholderBody = placeholder.length <= placeholderBudget ? placeholder : "\u2026" + placeholder.slice(placeholder.length - placeholderBudget + 1);
3717
- return /* @__PURE__ */ jsxDEV("text", {
3718
- wrapMode: "none",
3724
+ useLayoutEffect(() => {
3725
+ onEditingReady?.(editing);
3726
+ return () => onEditingReady?.(false);
3727
+ }, [editing, onEditingReady]);
3728
+ if (editing) {
3729
+ return /* @__PURE__ */ jsxDEV("box", {
3730
+ style: { flexDirection: "row", width, height: 1, flexShrink: 0 },
3719
3731
  children: [
3720
- /* @__PURE__ */ jsxDEV("span", {
3721
- style: { fg: prefixFg },
3722
- children: PREFIX
3723
- }, undefined, false, undefined, this),
3724
- /* @__PURE__ */ jsxDEV("span", {
3725
- style: { fg: colors.textMuted },
3726
- children: placeholderBody
3732
+ /* @__PURE__ */ jsxDEV("text", {
3733
+ wrapMode: "none",
3734
+ children: /* @__PURE__ */ jsxDEV("span", {
3735
+ style: { fg: prefixFg },
3736
+ children: PREFIX
3737
+ }, undefined, false, undefined, this)
3727
3738
  }, undefined, false, undefined, this),
3728
- /* @__PURE__ */ jsxDEV("span", {
3729
- style: { fg: colors.primary },
3730
- children: CURSOR
3739
+ /* @__PURE__ */ jsxDEV("input", {
3740
+ focused: true,
3741
+ value: query,
3742
+ placeholder: showPlaceholderWhileEditing ? placeholder : "",
3743
+ ...onInput === undefined ? {} : { onInput },
3744
+ backgroundColor: "transparent",
3745
+ focusedBackgroundColor: "transparent",
3746
+ textColor: colors.primary,
3747
+ focusedTextColor: colors.primary,
3748
+ cursorColor: colors.primary,
3749
+ placeholderColor: colors.textMuted,
3750
+ width: bodyBudget
3731
3751
  }, undefined, false, undefined, this)
3732
3752
  ]
3733
3753
  }, undefined, true, undefined, this);
3734
3754
  }
3755
+ const rawBody = query.length > 0 ? query : placeholder;
3756
+ const bodyFg = query.length > 0 ? colors.text : colors.textMuted;
3757
+ const body = rawBody.length <= bodyBudget ? rawBody : rawBody.slice(0, bodyBudget - 1) + "\u2026";
3735
3758
  return /* @__PURE__ */ jsxDEV("text", {
3736
3759
  wrapMode: "none",
3737
3760
  children: [
@@ -3791,7 +3814,9 @@ var CommandPalette = ({
3791
3814
  query,
3792
3815
  selectedIndex,
3793
3816
  viewportWidth,
3794
- viewportHeight
3817
+ viewportHeight,
3818
+ onQueryChange,
3819
+ onInputReady
3795
3820
  }) => {
3796
3821
  const overlayWidth = Math.min(viewportWidth - 4, 64);
3797
3822
  const rows = buildRows(commands);
@@ -3846,7 +3871,9 @@ var CommandPalette = ({
3846
3871
  /* @__PURE__ */ jsxDEV2(PromptRow, {
3847
3872
  query,
3848
3873
  editing: true,
3849
- width: rowWidth
3874
+ width: rowWidth,
3875
+ onInput: onQueryChange,
3876
+ ...onInputReady === undefined ? {} : { onEditingReady: onInputReady }
3850
3877
  }, undefined, false, undefined, this),
3851
3878
  /* @__PURE__ */ jsxDEV2("text", {
3852
3879
  content: " "
@@ -3904,6 +3931,23 @@ var CommandPalette = ({
3904
3931
 
3905
3932
  // src/PromptModal.tsx
3906
3933
  import { RGBA as RGBA2 } from "@opentui/core";
3934
+
3935
+ // src/ui/middleTruncate.ts
3936
+ var middleTruncate = (value, width, options = {}) => {
3937
+ const ellipsis = options.ellipsis ?? "\u2026";
3938
+ if (width <= 0)
3939
+ return "";
3940
+ if (value.length <= width)
3941
+ return value;
3942
+ if (width <= ellipsis.length)
3943
+ return value.slice(0, width);
3944
+ const available = width - ellipsis.length;
3945
+ const left = Math.ceil(available / 2);
3946
+ const right = Math.floor(available / 2);
3947
+ return value.slice(0, left) + ellipsis + value.slice(value.length - right);
3948
+ };
3949
+
3950
+ // src/PromptModal.tsx
3907
3951
  import { jsxDEV as jsxDEV3 } from "@opentui/react/jsx-dev-runtime";
3908
3952
  var SCRIM2 = RGBA2.fromInts(0, 0, 0, 150);
3909
3953
  var PromptStatusLine = ({
@@ -3924,13 +3968,18 @@ var PromptModal = ({
3924
3968
  placeholder,
3925
3969
  hints,
3926
3970
  status,
3971
+ context,
3927
3972
  viewportWidth,
3928
- viewportHeight
3973
+ viewportHeight,
3974
+ onQueryChange,
3975
+ onInputReady,
3976
+ inputEnabled = true
3929
3977
  }) => {
3930
3978
  const overlayWidth = Math.min(viewportWidth - 4, 64);
3931
3979
  const statusLines = status?.lines ?? [];
3932
3980
  const statusHeight = Math.max(1, statusLines.length);
3933
- const overlayHeight = 2 + 1 + statusHeight + 1;
3981
+ const contextHeight = context !== undefined && context.length > 0 ? 1 : 0;
3982
+ const overlayHeight = 2 + contextHeight + 1 + statusHeight + 1;
3934
3983
  const left = Math.max(0, Math.floor((viewportWidth - overlayWidth) / 2));
3935
3984
  const top = Math.max(0, Math.floor((viewportHeight - overlayHeight) / 2));
3936
3985
  const rowWidth = Math.max(4, overlayWidth - 4);
@@ -3960,12 +4009,19 @@ var PromptModal = ({
3960
4009
  backgroundColor: colors.backgroundPanel
3961
4010
  },
3962
4011
  children: [
4012
+ context !== undefined && context.length > 0 && /* @__PURE__ */ jsxDEV3("text", {
4013
+ wrapMode: "none",
4014
+ content: middleTruncate(context, rowWidth),
4015
+ style: { fg: colors.textMuted }
4016
+ }, undefined, false, undefined, this),
3963
4017
  /* @__PURE__ */ jsxDEV3(PromptRow, {
3964
4018
  query,
3965
- editing: true,
4019
+ editing: inputEnabled,
3966
4020
  placeholder,
3967
4021
  showPlaceholderWhileEditing: true,
3968
- width: rowWidth
4022
+ width: rowWidth,
4023
+ onInput: onQueryChange,
4024
+ ...onInputReady === undefined ? {} : { onEditingReady: onInputReady }
3969
4025
  }, undefined, false, undefined, this),
3970
4026
  status === null || statusLines.length === 0 ? /* @__PURE__ */ jsxDEV3("text", {
3971
4027
  content: " "
@@ -4309,21 +4365,6 @@ var Footer = ({
4309
4365
  }, undefined, true, undefined, this);
4310
4366
  };
4311
4367
 
4312
- // src/ui/middleTruncate.ts
4313
- var middleTruncate = (value, width, options = {}) => {
4314
- const ellipsis = options.ellipsis ?? "\u2026";
4315
- if (width <= 0)
4316
- return "";
4317
- if (value.length <= width)
4318
- return value;
4319
- if (width <= ellipsis.length)
4320
- return value.slice(0, width);
4321
- const available = width - ellipsis.length;
4322
- const left = Math.ceil(available / 2);
4323
- const right = Math.floor(available / 2);
4324
- return value.slice(0, left) + ellipsis + value.slice(value.length - right);
4325
- };
4326
-
4327
4368
  // src/Header.tsx
4328
4369
  import { jsxDEV as jsxDEV7 } from "@opentui/react/jsx-dev-runtime";
4329
4370
  var HEADER_HEIGHT = 1;
@@ -4578,12 +4619,95 @@ var readFileText = (path) => Effect.tryPromise({
4578
4619
  catch: (cause) => new FileReadError({ path, cause })
4579
4620
  });
4580
4621
 
4622
+ // src/io/renameFile.ts
4623
+ import { rename, readdir, stat } from "fs/promises";
4624
+ import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "path";
4625
+ var isPathInsideRoot = (root, absolutePath) => {
4626
+ const resolvedRoot = resolve(root);
4627
+ const resolvedPath = resolve(absolutePath);
4628
+ const rel = relative(resolvedRoot, resolvedPath);
4629
+ return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
4630
+ };
4631
+ var uniqueTempBasename = async (entries, finalBasename) => {
4632
+ const stem = finalBasename.endsWith(".md") ? finalBasename.slice(0, -3) : finalBasename;
4633
+ const lower = new Set(entries.map((e) => e.toLowerCase()));
4634
+ for (let i = 0;i < 1000; i++) {
4635
+ const candidate = `${stem}.house-rename-${process.pid}-${Date.now()}-${i}.tmp`;
4636
+ if (!lower.has(candidate.toLowerCase()))
4637
+ return candidate;
4638
+ }
4639
+ throw new Error("could not allocate a temporary rename name");
4640
+ };
4641
+ var renameMarkdownFile = async (request) => {
4642
+ const discoveryRoot = resolve(request.discoveryRoot);
4643
+ const sourcePath = resolve(request.sourcePath);
4644
+ const parentDir = resolve(request.parentDir);
4645
+ const newBasename = request.newBasename;
4646
+ const destPath = join(parentDir, newBasename);
4647
+ const sourceBasename = basename(sourcePath);
4648
+ if (sourceBasename === newBasename && dirname(sourcePath) === parentDir) {
4649
+ return { ok: true, from: sourcePath, to: destPath, noop: true };
4650
+ }
4651
+ let sourceStat;
4652
+ try {
4653
+ sourceStat = await stat(sourcePath);
4654
+ } catch {
4655
+ return { ok: false, reason: "target-missing", message: "target missing" };
4656
+ }
4657
+ if (!sourceStat.isFile()) {
4658
+ return { ok: false, reason: "target-changed", message: "target changed" };
4659
+ }
4660
+ if (!isPathInsideRoot(discoveryRoot, sourcePath)) {
4661
+ return { ok: false, reason: "target-changed", message: "target changed" };
4662
+ }
4663
+ if (dirname(sourcePath) !== parentDir) {
4664
+ return { ok: false, reason: "target-changed", message: "target changed" };
4665
+ }
4666
+ let entries;
4667
+ try {
4668
+ entries = await readdir(parentDir);
4669
+ } catch (err) {
4670
+ const message = err instanceof Error ? err.message : String(err);
4671
+ return { ok: false, reason: "io", message: `rename failed: ${message}` };
4672
+ }
4673
+ if (!entries.includes(sourceBasename)) {
4674
+ return { ok: false, reason: "target-changed", message: "target changed" };
4675
+ }
4676
+ const exactDest = entries.includes(newBasename);
4677
+ const caseFoldDest = entries.find((e) => e.toLowerCase() === newBasename.toLowerCase());
4678
+ const caseOnly = sourceBasename.toLowerCase() === newBasename.toLowerCase() && sourceBasename !== newBasename;
4679
+ if (exactDest || caseFoldDest !== undefined && caseFoldDest !== sourceBasename) {
4680
+ return { ok: false, reason: "already-exists", message: "already exists" };
4681
+ }
4682
+ try {
4683
+ if (caseOnly) {
4684
+ const tempBasename = await uniqueTempBasename(entries, newBasename);
4685
+ const tempPath = join(parentDir, tempBasename);
4686
+ await rename(sourcePath, tempPath);
4687
+ try {
4688
+ await rename(tempPath, destPath);
4689
+ } catch (err) {
4690
+ try {
4691
+ await rename(tempPath, sourcePath);
4692
+ } catch {}
4693
+ throw err;
4694
+ }
4695
+ } else {
4696
+ await rename(sourcePath, destPath);
4697
+ }
4698
+ return { ok: true, from: sourcePath, to: destPath };
4699
+ } catch (err) {
4700
+ const message = err instanceof Error ? err.message : String(err);
4701
+ return { ok: false, reason: "io", message: `rename failed: ${message}` };
4702
+ }
4703
+ };
4704
+
4581
4705
  // ../../packages/ui/src/file-navigator/FilesystemFileNavigator.tsx
4582
4706
  import {
4583
4707
  forwardRef,
4584
4708
  useEffect as useEffect3,
4585
4709
  useImperativeHandle,
4586
- useLayoutEffect,
4710
+ useLayoutEffect as useLayoutEffect2,
4587
4711
  useReducer,
4588
4712
  useRef as useRef2
4589
4713
  } from "react";
@@ -4688,23 +4812,23 @@ var Sidebar = ({
4688
4812
  };
4689
4813
  // ../../packages/ui/src/file-navigator/core/engine.ts
4690
4814
  import { platform } from "os";
4691
- import { relative as relative2, resolve as resolve2 } from "path";
4815
+ import { relative as relative3, resolve as resolve3 } from "path";
4692
4816
 
4693
4817
  // ../../packages/ui/src/file-navigator/core/scanner.ts
4694
- import { basename, extname, join, relative, resolve, sep } from "path";
4695
- import { readFile as readFile2, readdir, realpath, stat } from "fs/promises";
4818
+ import { basename as basename2, extname, join as join2, relative as relative2, resolve as resolve2, sep as sep2 } from "path";
4819
+ import { readFile as readFile2, readdir as readdir2, realpath, stat as stat2 } from "fs/promises";
4696
4820
  import ignore from "ignore";
4697
4821
  var DEFAULT_BATCH_SIZE = 64;
4698
- var slash = (value) => value.split(sep).join("/");
4822
+ var slash = (value) => value.split(sep2).join("/");
4699
4823
  var compare = (left, right) => left < right ? -1 : left > right ? 1 : 0;
4700
4824
  var missing = (error) => typeof error === "object" && error !== null && ("code" in error) && error.code === "ENOENT";
4701
4825
  var inside = (root, path) => {
4702
- const candidate = relative(root, path);
4703
- return candidate === "" || !candidate.startsWith(`..${sep}`) && candidate !== "..";
4826
+ const candidate = relative2(root, path);
4827
+ return candidate === "" || !candidate.startsWith(`..${sep2}`) && candidate !== "..";
4704
4828
  };
4705
4829
  var thenable = (value) => typeof value === "object" && value !== null && typeof value.then === "function";
4706
4830
  var readStat = async (path) => {
4707
- const value = await stat(path);
4831
+ const value = await stat2(path);
4708
4832
  return { size: value.size, mtimeMs: value.mtimeMs };
4709
4833
  };
4710
4834
  var filterPresentIgnoreFiles = (ignoreFiles, present) => {
@@ -4713,8 +4837,8 @@ var filterPresentIgnoreFiles = (ignoreFiles, present) => {
4713
4837
  };
4714
4838
  var frozenRecord = (path, root, metadata) => Object.freeze({
4715
4839
  absolutePath: path,
4716
- relativePath: slash(relative(root, path)),
4717
- basename: basename(path),
4840
+ relativePath: slash(relative2(root, path)),
4841
+ basename: basename2(path),
4718
4842
  extension: extname(path).toLowerCase(),
4719
4843
  size: metadata.size,
4720
4844
  mtimeMs: metadata.mtimeMs
@@ -4722,7 +4846,7 @@ var frozenRecord = (path, root, metadata) => Object.freeze({
4722
4846
  var ignored = (path, directory, levels) => {
4723
4847
  let result = false;
4724
4848
  for (const level of levels) {
4725
- const candidate = slash(relative(level.directory, path));
4849
+ const candidate = slash(relative2(level.directory, path));
4726
4850
  if (!candidate || candidate === ".." || candidate.startsWith("../"))
4727
4851
  continue;
4728
4852
  const match = level.matcher.test(directory ? `${candidate}/` : candidate);
@@ -4741,7 +4865,7 @@ var loadLevels = async (physical, lexical, ignoreFiles, parent) => {
4741
4865
  ...levels,
4742
4866
  {
4743
4867
  directory: lexical,
4744
- matcher: ignore().add(await readFile2(join(physical, filename), "utf8"))
4868
+ matcher: ignore().add(await readFile2(join2(physical, filename), "utf8"))
4745
4869
  }
4746
4870
  ];
4747
4871
  } catch (error) {
@@ -4755,10 +4879,10 @@ var abort = (signal) => {
4755
4879
  if (signal?.aborted)
4756
4880
  throw new DOMException("scan aborted", "AbortError");
4757
4881
  };
4758
- var normalizeRoot = (input) => resolve(input);
4882
+ var normalizeRoot = (input) => resolve2(input);
4759
4883
  var physicalWatchRoot = async (input) => {
4760
4884
  const root = normalizeRoot(input);
4761
- const rootStat = await stat(root);
4885
+ const rootStat = await stat2(root);
4762
4886
  if (!rootStat.isDirectory())
4763
4887
  throw new Error(`root is not a directory: ${root}`);
4764
4888
  return realpath(root);
@@ -4852,7 +4976,7 @@ var scanFiles = async (input, policyInput, options = {}) => {
4852
4976
  const beforeRead = options.barrier?.("before-read", lexical);
4853
4977
  if (thenable(beforeRead))
4854
4978
  await beforeRead;
4855
- const entries = (await readdir(physical, { withFileTypes: true })).sort((a, b) => compare(a.name, b.name));
4979
+ const entries = (await readdir2(physical, { withFileTypes: true })).sort((a, b) => compare(a.name, b.name));
4856
4980
  const afterRead = options.barrier?.("after-read", lexical);
4857
4981
  if (thenable(afterRead))
4858
4982
  await afterRead;
@@ -4867,8 +4991,8 @@ var scanFiles = async (input, policyInput, options = {}) => {
4867
4991
  for (const entry of files) {
4868
4992
  if (options.topologyOnly)
4869
4993
  continue;
4870
- const path = join(physical, entry.name);
4871
- const lexicalPath = join(lexical, entry.name);
4994
+ const path = join2(physical, entry.name);
4995
+ const lexicalPath = join2(lexical, entry.name);
4872
4996
  try {
4873
4997
  if (controlFiles.has(entry.name) || ignored(lexicalPath, false, levels))
4874
4998
  continue;
@@ -4889,18 +5013,18 @@ var scanFiles = async (input, policyInput, options = {}) => {
4889
5013
  abort(options.signal);
4890
5014
  if (controlFiles.has(entry.name))
4891
5015
  continue;
4892
- const physicalPath = join(physical, entry.name);
4893
- const lexicalPath = join(lexical, entry.name);
5016
+ const physicalPath = join2(physical, entry.name);
5017
+ const lexicalPath = join2(lexical, entry.name);
4894
5018
  try {
4895
5019
  if (entry.isSymbolicLink() && !policy.followSymlinks)
4896
5020
  continue;
4897
5021
  const target = entry.isSymbolicLink() ? await realpath(physicalPath) : physicalPath;
4898
- const targetStat = await stat(target);
5022
+ const targetStat = await stat2(target);
4899
5023
  if (targetStat.isDirectory()) {
4900
5024
  if (ignored(lexicalPath, true, levels) || ancestry.has(target))
4901
5025
  continue;
4902
5026
  if ((targetStat.mode & 511) === 0) {
4903
- options.onDiagnostic?.(new Error(`skipped directory: ${basename(lexicalPath)}`));
5027
+ options.onDiagnostic?.(new Error(`skipped directory: ${basename2(lexicalPath)}`));
4904
5028
  continue;
4905
5029
  }
4906
5030
  try {
@@ -4915,7 +5039,7 @@ var scanFiles = async (input, policyInput, options = {}) => {
4915
5039
  } catch (error) {
4916
5040
  if (missing(error))
4917
5041
  continue;
4918
- options.onDiagnostic?.(new Error(`skipped directory: ${basename(lexicalPath)}`));
5042
+ options.onDiagnostic?.(new Error(`skipped directory: ${basename2(lexicalPath)}`));
4919
5043
  }
4920
5044
  } else if (targetStat.isFile() && !entry.isDirectory()) {
4921
5045
  if (!options.topologyOnly) {
@@ -4927,7 +5051,7 @@ var scanFiles = async (input, policyInput, options = {}) => {
4927
5051
  } catch (error) {
4928
5052
  if (missing(error))
4929
5053
  continue;
4930
- options.onDiagnostic?.(new Error(`skipped directory: ${basename(lexicalPath)}`));
5054
+ options.onDiagnostic?.(new Error(`skipped directory: ${basename2(lexicalPath)}`));
4931
5055
  }
4932
5056
  }
4933
5057
  };
@@ -5022,7 +5146,7 @@ var projectFiles = (files, query, order = "tree", search = fuzzySearch) => {
5022
5146
  // ../../packages/ui/src/file-navigator/core/engine.ts
5023
5147
  var STREAM_SNAPSHOT = 256;
5024
5148
  var errorValue = (value) => value instanceof Error ? value : new Error(String(value));
5025
- var afterPaint = (value) => Promise.resolve(value).then(() => new Promise((resolve3) => setImmediate(resolve3)));
5149
+ var afterPaint = (value) => Promise.resolve(value).then(() => new Promise((resolve4) => setImmediate(resolve4)));
5026
5150
  var byPath = (files) => new Map(files.map((file) => [file.relativePath, file]));
5027
5151
  var publication = (previous, next) => {
5028
5152
  const added = [], changed = [], removed = [];
@@ -5491,13 +5615,13 @@ class FileNavigatorCore {
5491
5615
  if (path === this.#root)
5492
5616
  return "";
5493
5617
  if (path.startsWith(this.#root + "/"))
5494
- return relative2(this.#root, resolve2(path));
5618
+ return relative3(this.#root, resolve3(path));
5495
5619
  if (generation)
5496
5620
  for (const directory of generation.watchDirectories)
5497
5621
  for (const lexical of directory.lexicalPaths) {
5498
- const suffix = relative2(directory.physicalPath, path);
5622
+ const suffix = relative3(directory.physicalPath, path);
5499
5623
  if (suffix === "" || !suffix.startsWith("..") && !suffix.startsWith("/"))
5500
- return relative2(this.#root, resolve2(lexical, suffix));
5624
+ return relative3(this.#root, resolve3(lexical, suffix));
5501
5625
  }
5502
5626
  return path;
5503
5627
  }
@@ -5592,17 +5716,17 @@ var formatFileRow = (path, width) => {
5592
5716
  const slash2 = path.lastIndexOf("/");
5593
5717
  if (slash2 < 0)
5594
5718
  return { basename: fitTail(path, width), parent: "", separator: "" };
5595
- const basename2 = path.slice(slash2 + 1);
5719
+ const basename3 = path.slice(slash2 + 1);
5596
5720
  const parent = path.slice(0, slash2);
5597
- if (basename2.length >= width) {
5598
- return { basename: fitTail(basename2, width), parent: "", separator: "" };
5721
+ if (basename3.length >= width) {
5722
+ return { basename: fitTail(basename3, width), parent: "", separator: "" };
5599
5723
  }
5600
- const remaining = width - basename2.length - SEPARATOR.length;
5724
+ const remaining = width - basename3.length - SEPARATOR.length;
5601
5725
  if (remaining < MIN_PARENT_BUDGET || parent.length === 0) {
5602
- return { basename: basename2, parent: "", separator: "" };
5726
+ return { basename: basename3, parent: "", separator: "" };
5603
5727
  }
5604
5728
  return {
5605
- basename: basename2,
5729
+ basename: basename3,
5606
5730
  parent: middleTruncate2(parent, remaining),
5607
5731
  separator: SEPARATOR
5608
5732
  };
@@ -5613,13 +5737,13 @@ var DefaultFileRow = ({
5613
5737
  width,
5614
5738
  theme
5615
5739
  }) => {
5616
- const { basename: basename2, parent, separator } = formatFileRow(path, width);
5740
+ const { basename: basename3, parent, separator } = formatFileRow(path, width);
5617
5741
  return /* @__PURE__ */ jsxDEV9("text", {
5618
5742
  wrapMode: "none",
5619
5743
  children: [
5620
5744
  /* @__PURE__ */ jsxDEV9("span", {
5621
5745
  style: { fg: selected ? theme.selectedListItemText : theme.text },
5622
- children: basename2
5746
+ children: basename3
5623
5747
  }, undefined, false, undefined, this),
5624
5748
  parent !== "" && /* @__PURE__ */ jsxDEV9("span", {
5625
5749
  style: { fg: theme.textMuted },
@@ -5698,7 +5822,7 @@ var Component = (props, ref) => {
5698
5822
  const timerRef = useRef2(null);
5699
5823
  const redrawRef = useRef2(() => {});
5700
5824
  const flushQueryRef = useRef2(() => snapshotOf(null, normalizedRoot, true, null));
5701
- useLayoutEffect(() => {
5825
+ useLayoutEffect2(() => {
5702
5826
  activeRootRef.current = normalizedRoot;
5703
5827
  callbacks.current = { onSelectionChange, onSelectedFileInvalidated, onDiagnostic, onSnapshot };
5704
5828
  }, [normalizedRoot, onSelectionChange, onSelectedFileInvalidated, onDiagnostic, onSnapshot]);
@@ -5964,6 +6088,7 @@ var queryWouldShowRelativePath = (query, relativePath) => {
5964
6088
 
5965
6089
  // src/new-file/resolveName.ts
5966
6090
  var MD_SUFFIX = /\.md$/i;
6091
+ var pathShapedError = (mode) => mode === "rename" ? "name must be a single file name" : "name must be a single file in the discovery root";
5967
6092
  var hasControlChar = (raw) => {
5968
6093
  for (let i = 0;i < raw.length; i++) {
5969
6094
  const code = raw.charCodeAt(i);
@@ -5972,14 +6097,14 @@ var hasControlChar = (raw) => {
5972
6097
  }
5973
6098
  return false;
5974
6099
  };
5975
- var resolveNewFileName = (raw) => {
6100
+ var resolveMarkdownBasename = (raw, mode = "new-file") => {
5976
6101
  if (hasControlChar(raw))
5977
6102
  return { ok: false, error: "name contains invalid characters" };
5978
6103
  const trimmed = raw.trim();
5979
6104
  if (trimmed.length === 0)
5980
6105
  return { ok: false, error: "name required" };
5981
6106
  if (trimmed.includes("/") || trimmed.includes("\\") || trimmed === "." || trimmed === "..") {
5982
- return { ok: false, error: "name must be a single file in the discovery root" };
6107
+ return { ok: false, error: pathShapedError(mode) };
5983
6108
  }
5984
6109
  if (trimmed.startsWith("."))
5985
6110
  return { ok: false, error: "hidden names aren't supported yet" };
@@ -5992,14 +6117,59 @@ var resolveNewFileName = (raw) => {
5992
6117
  warnings: ["extension will be saved as .md"]
5993
6118
  };
5994
6119
  }
5995
- const basename2 = `${trimmed}.md`;
6120
+ const basename3 = `${trimmed}.md`;
5996
6121
  return {
5997
6122
  ok: true,
5998
- basename: basename2,
5999
- warnings: trimmed.includes(".") ? [`will be created as ${basename2}`] : []
6123
+ basename: basename3,
6124
+ warnings: trimmed.includes(".") ? [`will be created as ${basename3}`] : []
6000
6125
  };
6001
6126
  };
6002
6127
 
6128
+ // src/prompts/helpers.ts
6129
+ import { readdirSync } from "fs";
6130
+ import { dirname as dirname2 } from "path";
6131
+ var parentRelativeOf = (relativePath) => {
6132
+ const idx = relativePath.lastIndexOf("/");
6133
+ return idx === -1 ? "" : relativePath.slice(0, idx);
6134
+ };
6135
+ var destinationRelativePath = (parentRelative, newBasename) => parentRelative.length === 0 ? newBasename : `${parentRelative}/${newBasename}`;
6136
+ var captureActionTarget = (file) => ({
6137
+ absolutePath: file.absolutePath,
6138
+ parentDir: dirname2(file.absolutePath),
6139
+ relativePath: file.relativePath,
6140
+ basename: file.basename,
6141
+ parentRelative: parentRelativeOf(file.relativePath)
6142
+ });
6143
+ var siblingExistsExact = (parentDir, name, exceptBasename) => {
6144
+ try {
6145
+ const entries = readdirSync(parentDir);
6146
+ return entries.includes(name) && name !== exceptBasename;
6147
+ } catch {
6148
+ return false;
6149
+ }
6150
+ };
6151
+ var promptLiveStatus = (raw, mode, query, matchRelativePath, destExists) => {
6152
+ const resolved = resolveMarkdownBasename(raw, mode);
6153
+ if (!resolved.ok) {
6154
+ if (resolved.error === "name required")
6155
+ return null;
6156
+ return { kind: "error", lines: [resolved.error] };
6157
+ }
6158
+ const lines = [...resolved.warnings];
6159
+ if (query.length > 0 && !queryWouldShowRelativePath(query, matchRelativePath)) {
6160
+ lines.push(`filter will change to ${resolved.basename}`);
6161
+ }
6162
+ if (destExists)
6163
+ lines.push("already exists");
6164
+ if (lines.length === 0)
6165
+ return null;
6166
+ return { kind: "warning", lines };
6167
+ };
6168
+ var retargetPreviewIfNeeded = (preview, sourcePath, destPath) => {
6169
+ if (preview?.currentTarget() === sourcePath)
6170
+ preview.setTarget(destPath);
6171
+ };
6172
+
6003
6173
  // src/keymap/keymap.ts
6004
6174
  var parseChord = (raw) => {
6005
6175
  const parts = raw.toLowerCase().split("+");
@@ -6075,7 +6245,9 @@ var Sidebar2 = ({
6075
6245
  onSnapshot,
6076
6246
  onSelectedFileInvalidated,
6077
6247
  onDiagnostic,
6078
- order = "recently-modified"
6248
+ order = "recently-modified",
6249
+ onFilterInput,
6250
+ onFilterEditingReady
6079
6251
  }) => {
6080
6252
  const headerVisible = snapshot.files.length > 0 || discoveryActive;
6081
6253
  const emptyState = snapshot.filteredFiles.length > 0 ? undefined : snapshot.files.length === 0 ? discoveryActive ? { label: "Scanning", value: "\u2026" } : { label: "No markdown files in", value: rootLabel } : { label: "No files match", value: snapshot.appliedQuery };
@@ -6101,7 +6273,9 @@ var Sidebar2 = ({
6101
6273
  query: filterInput,
6102
6274
  editing: filterOpen,
6103
6275
  placeholder: "type / to filter",
6104
- width: rowWidth
6276
+ width: rowWidth,
6277
+ ...onFilterInput === undefined ? {} : { onInput: onFilterInput },
6278
+ ...onFilterEditingReady === undefined ? {} : { onEditingReady: onFilterEditingReady }
6105
6279
  }, undefined, false, undefined, this)
6106
6280
  } : {},
6107
6281
  ...emptyState === undefined ? {} : {
@@ -6267,7 +6441,7 @@ var openInBrowser = (url) => {
6267
6441
  };
6268
6442
 
6269
6443
  // src/serve/server.ts
6270
- import { basename as basename2 } from "path";
6444
+ import { basename as basename3 } from "path";
6271
6445
  import { watch } from "fs";
6272
6446
  import { readFile as readFile3 } from "fs/promises";
6273
6447
 
@@ -6503,7 +6677,7 @@ var startServer = ({ path, port = 0 }) => {
6503
6677
  }
6504
6678
  try {
6505
6679
  const md = await readFile3(target, "utf8");
6506
- const html = renderHtml(md, basename2(target));
6680
+ const html = renderHtml(md, basename3(target));
6507
6681
  return new Response(html, {
6508
6682
  headers: {
6509
6683
  "content-type": "text/html; charset=utf-8",
@@ -12791,12 +12965,12 @@ var houseOptions = defineOptions({
12791
12965
  });
12792
12966
 
12793
12967
  // src/config/save.ts
12794
- import { dirname } from "path";
12795
- import { lstat, mkdir, readFile as readFile4, realpath as realpath2, rename, unlink, writeFile } from "fs/promises";
12968
+ import { dirname as dirname3 } from "path";
12969
+ import { lstat, mkdir, readFile as readFile4, realpath as realpath2, rename as rename2, unlink, writeFile } from "fs/promises";
12796
12970
 
12797
12971
  // src/config/load.ts
12798
12972
  import { homedir } from "os";
12799
- import { join as join2 } from "path";
12973
+ import { join as join3 } from "path";
12800
12974
  import { Config, ConfigProvider, Effect as Effect2, Schema } from "effect";
12801
12975
 
12802
12976
  // src/discovery/show.ts
@@ -12933,7 +13107,7 @@ var cliProvider = (overrides) => {
12933
13107
  entries.push(["show", overrides.show.join(",")]);
12934
13108
  return ConfigProvider.fromUnknown(Object.fromEntries(entries));
12935
13109
  };
12936
- var defaultConfigPath = () => join2(process.env["XDG_CONFIG_HOME"] ?? join2(homedir(), ".config"), "house", "config.toml");
13110
+ var defaultConfigPath = () => join3(process.env["XDG_CONFIG_HOME"] ?? join3(homedir(), ".config"), "house", "config.toml");
12937
13111
  var formatConfigError = (err) => {
12938
13112
  if (err instanceof Config.ConfigError) {
12939
13113
  const cause = err.cause;
@@ -13071,8 +13245,8 @@ var updateThemePreferenceToml = (raw, record) => {
13071
13245
  };
13072
13246
  var resolveWritableConfigPath = async (path) => {
13073
13247
  try {
13074
- const stat2 = await lstat(path);
13075
- if (stat2.isSymbolicLink())
13248
+ const stat3 = await lstat(path);
13249
+ if (stat3.isSymbolicLink())
13076
13250
  return await realpath2(path);
13077
13251
  } catch (err) {
13078
13252
  if (err.code !== "ENOENT")
@@ -13085,7 +13259,7 @@ var writeThemePreference = async (record, path) => {
13085
13259
  path = targetPath;
13086
13260
  const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
13087
13261
  try {
13088
- await mkdir(dirname(path), { recursive: true });
13262
+ await mkdir(dirname3(path), { recursive: true });
13089
13263
  let next = `theme = ${encodeTomlString(record.theme)}
13090
13264
  tone = ${encodeTomlString(record.tone)}
13091
13265
  `;
@@ -13097,7 +13271,7 @@ tone = ${encodeTomlString(record.tone)}
13097
13271
  throw err;
13098
13272
  }
13099
13273
  await writeFile(tmp, next, "utf8");
13100
- await rename(tmp, path);
13274
+ await rename2(tmp, path);
13101
13275
  } catch (err) {
13102
13276
  try {
13103
13277
  await unlink(tmp);
@@ -13134,25 +13308,16 @@ var FILTER_DEBOUNCE_MS = 50;
13134
13308
  var RENDERED_PATH_DEBOUNCE_MS = 80;
13135
13309
  var NEW_FILE_MEMBERSHIP_TIMEOUT_MS = 2000;
13136
13310
  var NEW_FILE_MEMBERSHIP_POLL_MS = 32;
13137
- var sleep = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
13311
+ var sleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
13138
13312
  var isSingleCodePoint = (value) => Array.from(value).length === 1;
13139
13313
  var dropLastCodePoint = (value) => Array.from(value).slice(0, -1).join("");
13140
- var promptLiveStatus = (raw, query, destExists) => {
13141
- const resolved = resolveNewFileName(raw);
13142
- if (!resolved.ok) {
13143
- if (resolved.error === "name required")
13144
- return null;
13145
- return { kind: "error", lines: [resolved.error] };
13146
- }
13147
- const lines = [...resolved.warnings];
13148
- if (query.length > 0 && !queryWouldShowRelativePath(query, resolved.basename)) {
13149
- lines.push(`filter will change to ${resolved.basename}`);
13314
+ var printableFromKey = (key) => {
13315
+ if (key.name === "space")
13316
+ return " ";
13317
+ if (typeof key.name === "string" && isSingleCodePoint(key.name)) {
13318
+ return key.shift ? key.name.toUpperCase() : key.name;
13150
13319
  }
13151
- if (destExists)
13152
- lines.push("already exists");
13153
- if (lines.length === 0)
13154
- return null;
13155
- return { kind: "warning", lines };
13320
+ return null;
13156
13321
  };
13157
13322
  var isPartialDiscoveryWarning2 = (status) => status?.trimStart().startsWith("scan incomplete:") ?? false;
13158
13323
  var noFloatingOverlay = { kind: "none" };
@@ -13197,7 +13362,9 @@ var Browser = ({
13197
13362
  order = "recently-modified",
13198
13363
  launchEditor = openInEditor,
13199
13364
  createEmptyFile = createEmptyFileExclusive,
13200
- newFileMembershipTimeoutMs = NEW_FILE_MEMBERSHIP_TIMEOUT_MS
13365
+ renameFile = renameMarkdownFile,
13366
+ newFileMembershipTimeoutMs = NEW_FILE_MEMBERSHIP_TIMEOUT_MS,
13367
+ initialPreviewServer = null
13201
13368
  }) => {
13202
13369
  const renderer = useRenderer();
13203
13370
  const { width, height } = useTerminalDimensions2();
@@ -13335,22 +13502,30 @@ var Browser = ({
13335
13502
  const paletteIndexRef = useRef3(0);
13336
13503
  const [promptInput, setPromptInput] = useState4("");
13337
13504
  const [promptStatus, setPromptStatus] = useState4(null);
13505
+ const [promptPurpose, setPromptPurpose] = useState4("new-file");
13506
+ const [promptContext, setPromptContext] = useState4(undefined);
13338
13507
  const promptInputRef = useRef3("");
13508
+ const promptPurposeRef = useRef3("new-file");
13339
13509
  const promptFocusBeforeRef = useRef3("sidebar");
13340
13510
  const promptSubmittingRef = useRef3(false);
13341
- const newFileTaskGenRef = useRef3(0);
13511
+ const [promptSubmitting, setPromptSubmitting] = useState4(false);
13512
+ const actionTargetRef = useRef3(null);
13513
+ const promptTaskGenRef = useRef3(0);
13342
13514
  const mountedRef = useRef3(true);
13343
13515
  const [readerEmptyStateTipRotation, setReaderEmptyStateTipRotation] = useState4(() => nextReaderEmptyStateTipRotation);
13344
13516
  const readerEmptyStateVisibleRef = useRef3(false);
13345
13517
  const filterOpenRef = useRef3(startInFilter);
13346
13518
  const filterInputRef = useRef3(initialQuery);
13519
+ const filterInputReadyRef = useRef3(false);
13520
+ const paletteInputReadyRef = useRef3(false);
13521
+ const promptInputReadyRef = useRef3(false);
13347
13522
  const focusRef = useRef3(focus);
13348
13523
  const restoreFilterOnSidebarFocusRef = useRef3(startInFilter);
13349
13524
  const [footerNotice, setFooterNoticeState] = useState4(null);
13350
13525
  const pushFooterNotice = (text, ttlMs = 2000) => setFooterNoticeState({ text, ttlMs });
13351
- const serverRef = useRef3(null);
13526
+ const serverRef = useRef3(initialPreviewServer);
13352
13527
  const pendingSelectionPathRef = useRef3(null);
13353
- useLayoutEffect2(() => {
13528
+ useLayoutEffect3(() => {
13354
13529
  if (readerRootRef.current === root)
13355
13530
  return;
13356
13531
  readerRootRef.current = root;
@@ -13363,14 +13538,14 @@ var Browser = ({
13363
13538
  setLoaded(null);
13364
13539
  setError(null);
13365
13540
  }, [root]);
13366
- useLayoutEffect2(() => () => {
13541
+ useLayoutEffect3(() => () => {
13367
13542
  advanceReaderEpoch();
13368
13543
  }, []);
13369
13544
  useEffect4(() => {
13370
13545
  mountedRef.current = true;
13371
13546
  return () => {
13372
13547
  mountedRef.current = false;
13373
- newFileTaskGenRef.current += 1;
13548
+ promptTaskGenRef.current += 1;
13374
13549
  newFileEditPathRef.current = null;
13375
13550
  serverRef.current?.stop();
13376
13551
  serverRef.current = null;
@@ -13503,18 +13678,32 @@ var Browser = ({
13503
13678
  const applyPromptInput = (next) => {
13504
13679
  promptInputRef.current = next;
13505
13680
  setPromptInput(next);
13506
- const resolved = resolveNewFileName(next);
13507
- const destExists = resolved.ok && existsSync(join3(root, resolved.basename));
13508
- setPromptStatus(promptLiveStatus(next, filterInputRef.current, destExists));
13681
+ const mode = promptPurposeRef.current;
13682
+ const resolved = resolveMarkdownBasename(next, mode);
13683
+ const target = actionTargetRef.current;
13684
+ let destExists = false;
13685
+ let matchRelative = resolved.ok ? resolved.basename : "";
13686
+ if (resolved.ok && mode === "new-file") {
13687
+ destExists = siblingExistsExact(root, resolved.basename);
13688
+ } else if (resolved.ok && mode === "rename" && target !== null) {
13689
+ matchRelative = destinationRelativePath(target.parentRelative, resolved.basename);
13690
+ destExists = siblingExistsExact(target.parentDir, resolved.basename, target.basename);
13691
+ }
13692
+ setPromptStatus(promptLiveStatus(next, mode, filterInputRef.current, matchRelative, destExists));
13509
13693
  };
13510
- const closeNewFilePrompt = (restoreFocus) => {
13694
+ const closePrompt = (restoreFocus) => {
13511
13695
  promptSubmittingRef.current = false;
13696
+ setPromptSubmitting(false);
13512
13697
  promptInputRef.current = "";
13513
13698
  setPromptInput("");
13514
13699
  setPromptStatus(null);
13700
+ setPromptContext(undefined);
13701
+ actionTargetRef.current = null;
13702
+ promptPurposeRef.current = "new-file";
13703
+ setPromptPurpose("new-file");
13515
13704
  closeFloatingOverlay();
13516
13705
  if (restoreFocus) {
13517
- newFileTaskGenRef.current += 1;
13706
+ promptTaskGenRef.current += 1;
13518
13707
  const prev = promptFocusBeforeRef.current;
13519
13708
  focusRef.current = prev;
13520
13709
  setFocus(prev);
@@ -13581,6 +13770,23 @@ var Browser = ({
13581
13770
  }
13582
13771
  })();
13583
13772
  };
13773
+ const beginPrompt = (purpose, initialValue, context) => {
13774
+ promptTaskGenRef.current += 1;
13775
+ promptSubmittingRef.current = false;
13776
+ setPromptSubmitting(false);
13777
+ promptPurposeRef.current = purpose;
13778
+ setPromptPurpose(purpose);
13779
+ setPromptContext(context);
13780
+ promptInputRef.current = initialValue;
13781
+ setPromptInput(initialValue);
13782
+ promptFocusBeforeRef.current = focusRef.current;
13783
+ if (filterOpenRef.current) {
13784
+ filterOpenRef.current = false;
13785
+ setFilterOpen(false);
13786
+ }
13787
+ dispatchFloatingOverlay({ type: "open-prompt" });
13788
+ applyPromptInput(initialValue);
13789
+ };
13584
13790
  const openNewFilePrompt = () => {
13585
13791
  const editor = resolveEditor(process.env);
13586
13792
  if (!editor) {
@@ -13591,41 +13797,68 @@ var Browser = ({
13591
13797
  pushFooterNotice("editor unavailable in this environment");
13592
13798
  return;
13593
13799
  }
13594
- newFileTaskGenRef.current += 1;
13595
- promptSubmittingRef.current = false;
13596
- promptInputRef.current = "";
13597
- setPromptInput("");
13598
- setPromptStatus(null);
13599
- promptFocusBeforeRef.current = focusRef.current;
13600
- if (filterOpenRef.current) {
13601
- filterOpenRef.current = false;
13602
- setFilterOpen(false);
13800
+ actionTargetRef.current = null;
13801
+ beginPrompt("new-file", "", undefined);
13802
+ };
13803
+ const openRenamePrompt = () => {
13804
+ const file = navigator2.getSnapshot().selectedFile;
13805
+ if (!file)
13806
+ return;
13807
+ actionTargetRef.current = captureActionTarget(file);
13808
+ beginPrompt("rename", file.basename, file.relativePath);
13809
+ };
13810
+ const waitForMembership = async (opts) => {
13811
+ const stillWaiting = () => opts.task === promptTaskGenRef.current && mountedRef.current && opts.isArmed();
13812
+ await navigator2.refresh();
13813
+ const deadline = Date.now() + newFileMembershipTimeoutMs;
13814
+ while (Date.now() < deadline) {
13815
+ if (!stillWaiting())
13816
+ return;
13817
+ const snap = navigator2.getSnapshot();
13818
+ if (snap.files.some((file) => file.absolutePath === opts.dest)) {
13819
+ const selectedSnapshot = navigator2.selectPath(opts.dest);
13820
+ if (!stillWaiting())
13821
+ return;
13822
+ if (selectedSnapshot.selectedFile?.absolutePath === opts.dest) {
13823
+ pendingSelectionPathRef.current = null;
13824
+ opts.disarm();
13825
+ opts.onReady?.();
13826
+ return;
13827
+ }
13828
+ }
13829
+ await sleep(NEW_FILE_MEMBERSHIP_POLL_MS);
13603
13830
  }
13604
- dispatchFloatingOverlay({ type: "open-prompt" });
13831
+ if (!stillWaiting())
13832
+ return;
13833
+ pendingSelectionPathRef.current = null;
13834
+ opts.disarm();
13835
+ pushFooterNotice(opts.timeoutNotice);
13605
13836
  };
13606
13837
  const submitNewFile = () => {
13607
13838
  if (promptSubmittingRef.current)
13608
13839
  return;
13609
13840
  const raw = promptInputRef.current;
13610
- const resolved = resolveNewFileName(raw);
13841
+ const resolved = resolveMarkdownBasename(raw, "new-file");
13611
13842
  if (!resolved.ok) {
13612
13843
  setPromptStatus({ kind: "error", lines: [resolved.error] });
13613
13844
  return;
13614
13845
  }
13615
13846
  promptSubmittingRef.current = true;
13616
- const dest = join3(root, resolved.basename);
13617
- const task = newFileTaskGenRef.current;
13847
+ setPromptSubmitting(true);
13848
+ const dest = join4(root, resolved.basename);
13849
+ const task = promptTaskGenRef.current;
13618
13850
  (async () => {
13619
13851
  const created = await createEmptyFile(dest);
13620
13852
  if (!mountedRef.current)
13621
13853
  return;
13622
- if (task !== newFileTaskGenRef.current) {
13854
+ if (task !== promptTaskGenRef.current) {
13623
13855
  if (created.ok)
13624
13856
  pushFooterNotice(`created ${resolved.basename}`);
13625
13857
  return;
13626
13858
  }
13627
13859
  if (!created.ok) {
13628
13860
  promptSubmittingRef.current = false;
13861
+ setPromptSubmitting(false);
13629
13862
  setPromptStatus({
13630
13863
  kind: "error",
13631
13864
  lines: [
@@ -13634,7 +13867,7 @@ var Browser = ({
13634
13867
  });
13635
13868
  return;
13636
13869
  }
13637
- closeNewFilePrompt(false);
13870
+ closePrompt(false);
13638
13871
  const query = filterInputRef.current;
13639
13872
  if (query.length > 0 && !queryWouldShowRelativePath(query, resolved.basename)) {
13640
13873
  filterInputRef.current = resolved.basename;
@@ -13643,33 +13876,93 @@ var Browser = ({
13643
13876
  }
13644
13877
  pendingSelectionPathRef.current = dest;
13645
13878
  newFileEditPathRef.current = dest;
13646
- const stillWaiting = () => task === newFileTaskGenRef.current && mountedRef.current && newFileEditPathRef.current === dest;
13647
- await navigator2.refresh();
13648
- const deadline = Date.now() + newFileMembershipTimeoutMs;
13649
- while (Date.now() < deadline) {
13650
- if (!stillWaiting())
13651
- return;
13652
- const snap = navigator2.getSnapshot();
13653
- if (snap.files.some((file) => file.absolutePath === dest)) {
13654
- const selectedSnapshot = navigator2.selectPath(dest);
13655
- if (!stillWaiting())
13656
- return;
13657
- if (selectedSnapshot.selectedFile?.absolutePath === dest) {
13658
- pendingSelectionPathRef.current = null;
13879
+ await waitForMembership({
13880
+ dest,
13881
+ task,
13882
+ isArmed: () => newFileEditPathRef.current === dest,
13883
+ disarm: () => {
13884
+ if (newFileEditPathRef.current === dest)
13659
13885
  newFileEditPathRef.current = null;
13660
- editCurrent();
13661
- return;
13662
- }
13886
+ },
13887
+ onReady: () => editCurrent(),
13888
+ timeoutNotice: `created ${resolved.basename}, but it isn't in the file list`
13889
+ });
13890
+ })();
13891
+ };
13892
+ const submitRename = () => {
13893
+ if (promptSubmittingRef.current)
13894
+ return;
13895
+ const target = actionTargetRef.current;
13896
+ if (!target)
13897
+ return;
13898
+ const raw = promptInputRef.current;
13899
+ const resolved = resolveMarkdownBasename(raw, "rename");
13900
+ if (!resolved.ok) {
13901
+ setPromptStatus({ kind: "error", lines: [resolved.error] });
13902
+ return;
13903
+ }
13904
+ if (resolved.basename === target.basename) {
13905
+ closePrompt(true);
13906
+ return;
13907
+ }
13908
+ promptSubmittingRef.current = true;
13909
+ setPromptSubmitting(true);
13910
+ const dest = join4(target.parentDir, resolved.basename);
13911
+ const destRelative = destinationRelativePath(target.parentRelative, resolved.basename);
13912
+ const task = promptTaskGenRef.current;
13913
+ const sourcePath = target.absolutePath;
13914
+ (async () => {
13915
+ const renamed = await renameFile({
13916
+ discoveryRoot: root,
13917
+ sourcePath,
13918
+ parentDir: target.parentDir,
13919
+ newBasename: resolved.basename
13920
+ });
13921
+ if (!mountedRef.current)
13922
+ return;
13923
+ if (task !== promptTaskGenRef.current) {
13924
+ if (renamed.ok && !renamed.noop) {
13925
+ pushFooterNotice(`renamed to ${resolved.basename}`);
13663
13926
  }
13664
- await sleep(NEW_FILE_MEMBERSHIP_POLL_MS);
13927
+ return;
13665
13928
  }
13666
- if (!stillWaiting())
13929
+ if (!renamed.ok) {
13930
+ promptSubmittingRef.current = false;
13931
+ setPromptSubmitting(false);
13932
+ setPromptStatus({
13933
+ kind: "error",
13934
+ lines: [renamed.reason === "already-exists" ? "already exists" : renamed.message]
13935
+ });
13667
13936
  return;
13668
- pendingSelectionPathRef.current = null;
13669
- newFileEditPathRef.current = null;
13670
- pushFooterNotice(`created ${resolved.basename}, but it isn't in the file list`);
13937
+ }
13938
+ closePrompt(false);
13939
+ const query = filterInputRef.current;
13940
+ if (query.length > 0 && !queryWouldShowRelativePath(query, destRelative)) {
13941
+ filterInputRef.current = resolved.basename;
13942
+ setFilterInput(resolved.basename);
13943
+ navigator2.flushSearch(resolved.basename);
13944
+ }
13945
+ retargetPreviewIfNeeded(serverRef.current, sourcePath, dest);
13946
+ pendingSelectionPathRef.current = dest;
13947
+ await waitForMembership({
13948
+ dest,
13949
+ task,
13950
+ isArmed: () => pendingSelectionPathRef.current === dest,
13951
+ disarm: () => {
13952
+ if (pendingSelectionPathRef.current === dest)
13953
+ pendingSelectionPathRef.current = null;
13954
+ },
13955
+ onReady: null,
13956
+ timeoutNotice: `renamed to ${resolved.basename}, but it isn't in the file list`
13957
+ });
13671
13958
  })();
13672
13959
  };
13960
+ const submitPrompt = () => {
13961
+ if (promptPurposeRef.current === "rename")
13962
+ submitRename();
13963
+ else
13964
+ submitNewFile();
13965
+ };
13673
13966
  const ctx = {
13674
13967
  files: displayedFiles,
13675
13968
  hasSelected: selected != null,
@@ -13784,6 +14077,7 @@ var Browser = ({
13784
14077
  },
13785
14078
  editCurrent,
13786
14079
  openNewFilePrompt,
14080
+ openRenamePrompt,
13787
14081
  copyCurrentContents: () => {
13788
14082
  const file = navigator2.getSnapshot().selectedFile;
13789
14083
  if (!file)
@@ -13822,77 +14116,88 @@ var Browser = ({
13822
14116
  const allCommands = buildCommands(ctx);
13823
14117
  const filtered = orderCommandsForPalette(filterCommands(allCommands, paletteQueryRef.current));
13824
14118
  if (key.name === "escape") {
14119
+ key.preventDefault();
13825
14120
  closePalette();
13826
14121
  return;
13827
14122
  }
13828
14123
  if (key.name === "return") {
14124
+ key.preventDefault();
13829
14125
  const picked = filtered[clampSelectedIndex(paletteIndexRef.current, filtered)];
13830
14126
  closePalette();
13831
14127
  picked?.run();
13832
14128
  return;
13833
14129
  }
13834
14130
  if (key.name === "up") {
14131
+ key.preventDefault();
13835
14132
  setPaletteIndexSync(Math.max(0, paletteIndexRef.current - 1));
13836
14133
  return;
13837
14134
  }
13838
14135
  if (key.name === "down") {
14136
+ key.preventDefault();
13839
14137
  setPaletteIndexSync(Math.min(Math.max(0, filtered.length - 1), paletteIndexRef.current + 1));
13840
14138
  return;
13841
14139
  }
13842
- if (key.name === "backspace" || key.name === "delete") {
13843
- if (paletteQueryRef.current.length === 0)
13844
- return;
13845
- paletteQueryRef.current = paletteQueryRef.current.slice(0, -1);
13846
- setPaletteQuery(paletteQueryRef.current);
13847
- setPaletteIndexSync(0);
13848
- return;
13849
- }
13850
14140
  if (key.ctrl && !key.meta && key.name === "p") {
14141
+ key.preventDefault();
13851
14142
  closePalette();
13852
14143
  return;
13853
14144
  }
13854
- if (key.ctrl || key.meta)
13855
- return;
13856
- let char = null;
13857
- if (key.name === "space")
13858
- char = " ";
13859
- else if (typeof key.name === "string" && key.name.length === 1) {
13860
- char = key.shift ? key.name.toUpperCase() : key.name;
13861
- }
13862
- if (char !== null) {
13863
- paletteQueryRef.current = paletteQueryRef.current + char;
13864
- setPaletteQuery(paletteQueryRef.current);
13865
- setPaletteIndexSync(0);
14145
+ if (!paletteInputReadyRef.current) {
14146
+ if (key.name === "backspace" || key.name === "delete") {
14147
+ if (paletteQueryRef.current.length === 0)
14148
+ return;
14149
+ paletteQueryRef.current = paletteQueryRef.current.slice(0, -1);
14150
+ setPaletteQuery(paletteQueryRef.current);
14151
+ setPaletteIndexSync(0);
14152
+ return;
14153
+ }
14154
+ if (key.ctrl || key.meta)
14155
+ return;
14156
+ const char = printableFromKey(key);
14157
+ if (char !== null) {
14158
+ paletteQueryRef.current = paletteQueryRef.current + char;
14159
+ setPaletteQuery(paletteQueryRef.current);
14160
+ setPaletteIndexSync(0);
14161
+ }
13866
14162
  }
13867
14163
  return;
13868
14164
  }
13869
14165
  if (floatingOverlayRef.current.kind === "prompt") {
13870
14166
  if (key.name === "escape") {
13871
- closeNewFilePrompt(true);
14167
+ key.preventDefault();
14168
+ closePrompt(true);
13872
14169
  return;
13873
14170
  }
13874
- if (promptSubmittingRef.current)
14171
+ if (promptSubmittingRef.current) {
14172
+ key.preventDefault();
13875
14173
  return;
14174
+ }
13876
14175
  if (key.name === "return") {
13877
- submitNewFile();
14176
+ key.preventDefault();
14177
+ submitPrompt();
13878
14178
  return;
13879
14179
  }
13880
- if (key.name === "backspace" || key.name === "delete") {
13881
- if (promptInputRef.current.length === 0)
13882
- return;
13883
- applyPromptInput(dropLastCodePoint(promptInputRef.current));
14180
+ if (key.name === "up" || key.name === "down") {
14181
+ key.preventDefault();
13884
14182
  return;
13885
14183
  }
13886
- if (key.ctrl || key.meta)
14184
+ if (key.ctrl && !key.meta && key.name === "p") {
14185
+ key.preventDefault();
13887
14186
  return;
13888
- let char = null;
13889
- if (key.name === "space")
13890
- char = " ";
13891
- else if (typeof key.name === "string" && isSingleCodePoint(key.name)) {
13892
- char = key.shift ? key.name.toUpperCase() : key.name;
13893
- }
13894
- if (char !== null)
13895
- applyPromptInput(promptInputRef.current + char);
14187
+ }
14188
+ if (!promptInputReadyRef.current) {
14189
+ if (key.name === "backspace" || key.name === "delete") {
14190
+ if (promptInputRef.current.length === 0)
14191
+ return;
14192
+ applyPromptInput(dropLastCodePoint(promptInputRef.current));
14193
+ return;
14194
+ }
14195
+ if (key.ctrl || key.meta)
14196
+ return;
14197
+ const char = printableFromKey(key);
14198
+ if (char !== null)
14199
+ applyPromptInput(promptInputRef.current + char);
14200
+ }
13896
14201
  return;
13897
14202
  }
13898
14203
  if (filterOpenRef.current && focusRef.current === "sidebar") {
@@ -13913,14 +14218,17 @@ var Browser = ({
13913
14218
  }
13914
14219
  };
13915
14220
  if (key.name === "escape") {
14221
+ key.preventDefault();
13916
14222
  closeFilter(false);
13917
14223
  return;
13918
14224
  }
13919
14225
  if (key.name === "return") {
14226
+ key.preventDefault();
13920
14227
  closeFilter(true);
13921
14228
  return;
13922
14229
  }
13923
14230
  if (key.name === "tab" || key.ctrl && key.name === "i" && !key.shift && !key.meta) {
14231
+ key.preventDefault();
13924
14232
  focusRef.current = "reader";
13925
14233
  restoreFilterOnSidebarFocusRef.current = true;
13926
14234
  filterOpenRef.current = false;
@@ -13929,46 +14237,57 @@ var Browser = ({
13929
14237
  return;
13930
14238
  }
13931
14239
  if (key.ctrl && !key.meta && key.name === "p") {
14240
+ key.preventDefault();
13932
14241
  ctx.openPalette();
13933
14242
  return;
13934
14243
  }
13935
14244
  if (key.ctrl && key.name === "\\") {
14245
+ key.preventDefault();
13936
14246
  filterInputRef.current = "";
13937
14247
  setFilterInput("");
13938
14248
  navigator2.flushSearch("");
13939
14249
  navigator2.selectFirst();
13940
14250
  return;
13941
14251
  }
13942
- if (key.name === "backspace" || key.name === "delete") {
13943
- if (filterInputRef.current.length === 0) {
13944
- closeFilter(false);
13945
- return;
13946
- }
13947
- filterInputRef.current = filterInputRef.current.slice(0, -1);
13948
- setFilterInput(filterInputRef.current);
13949
- navigator2.selectIndex(0);
14252
+ if ((key.name === "backspace" || key.name === "delete") && filterInputRef.current.length === 0) {
14253
+ key.preventDefault();
14254
+ closeFilter(false);
13950
14255
  return;
13951
14256
  }
13952
14257
  if (key.name === "up") {
14258
+ key.preventDefault();
13953
14259
  navigator2.moveBy(-1);
13954
14260
  return;
13955
14261
  }
13956
14262
  if (key.name === "down") {
14263
+ key.preventDefault();
13957
14264
  navigator2.moveBy(1);
13958
14265
  return;
13959
14266
  }
13960
- if (key.ctrl || key.meta)
14267
+ if (key.ctrl && (key.name === "u" || key.name === "d")) {
14268
+ key.preventDefault();
13961
14269
  return;
13962
- let char = null;
13963
- if (key.name === "space")
13964
- char = " ";
13965
- else if (typeof key.name === "string" && key.name.length === 1) {
13966
- char = key.shift ? key.name.toUpperCase() : key.name;
13967
14270
  }
13968
- if (char !== null) {
13969
- filterInputRef.current = filterInputRef.current + char;
13970
- setFilterInput(filterInputRef.current);
13971
- navigator2.selectIndex(0);
14271
+ if (!filterInputReadyRef.current) {
14272
+ if (key.name === "backspace" || key.name === "delete") {
14273
+ if (filterInputRef.current.length === 0) {
14274
+ key.preventDefault();
14275
+ closeFilter(false);
14276
+ return;
14277
+ }
14278
+ filterInputRef.current = filterInputRef.current.slice(0, -1);
14279
+ setFilterInput(filterInputRef.current);
14280
+ navigator2.selectIndex(0);
14281
+ return;
14282
+ }
14283
+ if (key.ctrl || key.meta)
14284
+ return;
14285
+ const char = printableFromKey(key);
14286
+ if (char !== null) {
14287
+ filterInputRef.current = filterInputRef.current + char;
14288
+ setFilterInput(filterInputRef.current);
14289
+ navigator2.selectIndex(0);
14290
+ }
13972
14291
  }
13973
14292
  return;
13974
14293
  }
@@ -14056,6 +14375,14 @@ var Browser = ({
14056
14375
  snapshot: liveSnapshot,
14057
14376
  filterInput,
14058
14377
  filterOpen,
14378
+ onFilterInput: (next) => {
14379
+ filterInputRef.current = next;
14380
+ setFilterInput(next);
14381
+ navigator2.selectIndex(0);
14382
+ },
14383
+ onFilterEditingReady: (ready) => {
14384
+ filterInputReadyRef.current = ready;
14385
+ },
14059
14386
  discoveryActive,
14060
14387
  rootLabel,
14061
14388
  viewportHeight: height,
@@ -14191,16 +14518,31 @@ var Browser = ({
14191
14518
  query: paletteQuery,
14192
14519
  selectedIndex: paletteIndex,
14193
14520
  viewportWidth: width,
14194
- viewportHeight: height
14521
+ viewportHeight: height,
14522
+ onQueryChange: (next) => {
14523
+ paletteQueryRef.current = next;
14524
+ setPaletteQuery(next);
14525
+ paletteIndexRef.current = 0;
14526
+ setPaletteIndex(0);
14527
+ },
14528
+ onInputReady: (ready) => {
14529
+ paletteInputReadyRef.current = ready;
14530
+ }
14195
14531
  }, undefined, false, undefined, this),
14196
14532
  promptOpen && /* @__PURE__ */ jsxDEV13(PromptModal, {
14197
- title: "New file",
14533
+ title: promptPurpose === "rename" ? "Rename" : "New file",
14198
14534
  query: promptInput,
14199
14535
  placeholder: "File name",
14200
- hints: "enter create esc cancel",
14536
+ hints: promptPurpose === "rename" ? "enter rename esc cancel" : "enter create esc cancel",
14201
14537
  status: promptStatus,
14538
+ ...promptContext !== undefined ? { context: promptContext } : {},
14202
14539
  viewportWidth: width,
14203
- viewportHeight: height
14540
+ viewportHeight: height,
14541
+ onQueryChange: applyPromptInput,
14542
+ onInputReady: (ready) => {
14543
+ promptInputReadyRef.current = ready;
14544
+ },
14545
+ inputEnabled: !promptSubmitting
14204
14546
  }, undefined, false, undefined, this),
14205
14547
  activeStatusPopover && /* @__PURE__ */ jsxDEV13(StatusPopoverPanel, {
14206
14548
  content: activeStatusPopover.content,
@@ -14345,15 +14687,15 @@ var parseAndHandleFastExit = (argv) => {
14345
14687
  };
14346
14688
 
14347
14689
  // src/discovery/rootLabel.ts
14348
- import { isAbsolute, relative as relative3 } from "path";
14690
+ import { isAbsolute as isAbsolute2, relative as relative4 } from "path";
14349
14691
  var formatDiscoveryRootLabel = ({
14350
14692
  discoveryRoot,
14351
14693
  home
14352
14694
  }) => {
14353
14695
  if (discoveryRoot === home)
14354
14696
  return "~";
14355
- const homeRelative = relative3(home, discoveryRoot);
14356
- if (homeRelative.length > 0 && !homeRelative.startsWith("..") && !isAbsolute(homeRelative)) {
14697
+ const homeRelative = relative4(home, discoveryRoot);
14698
+ if (homeRelative.length > 0 && !homeRelative.startsWith("..") && !isAbsolute2(homeRelative)) {
14357
14699
  return `~/${homeRelative}`;
14358
14700
  }
14359
14701
  return discoveryRoot;
@@ -14401,16 +14743,16 @@ var isNewer = (candidate, current) => {
14401
14743
  };
14402
14744
 
14403
14745
  // src/update/cache.ts
14404
- import { dirname as dirname2, join as join4 } from "path";
14746
+ import { dirname as dirname4, join as join5 } from "path";
14405
14747
  import { homedir as homedir2 } from "os";
14406
- import { mkdir as mkdir2, readFile as readFile5, rename as rename2, unlink as unlink2, writeFile as writeFile2 } from "fs/promises";
14748
+ import { mkdir as mkdir2, readFile as readFile5, rename as rename3, unlink as unlink2, writeFile as writeFile2 } from "fs/promises";
14407
14749
  var cacheDir = () => {
14408
14750
  const xdg = process.env.XDG_CACHE_HOME;
14409
14751
  if (xdg && xdg.length > 0)
14410
- return join4(xdg, "house");
14411
- return join4(homedir2(), ".cache", "house");
14752
+ return join5(xdg, "house");
14753
+ return join5(homedir2(), ".cache", "house");
14412
14754
  };
14413
- var cachePath = () => join4(cacheDir(), "update-check.json");
14755
+ var cachePath = () => join5(cacheDir(), "update-check.json");
14414
14756
  var readCache = async (path = cachePath()) => {
14415
14757
  try {
14416
14758
  const raw = await readFile5(path, "utf8");
@@ -14433,9 +14775,9 @@ var readCache = async (path = cachePath()) => {
14433
14775
  var writeCache = async (record, path = cachePath()) => {
14434
14776
  const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
14435
14777
  try {
14436
- await mkdir2(dirname2(path), { recursive: true });
14778
+ await mkdir2(dirname4(path), { recursive: true });
14437
14779
  await writeFile2(tmp, JSON.stringify(record), "utf8");
14438
- await rename2(tmp, path);
14780
+ await rename3(tmp, path);
14439
14781
  } catch {
14440
14782
  try {
14441
14783
  await unlink2(tmp);
@@ -14575,18 +14917,18 @@ var useUpdateNotice = () => {
14575
14917
  import { jsxDEV as jsxDEV14 } from "@opentui/react/jsx-dev-runtime";
14576
14918
  var pathIsDirectory = async (path) => {
14577
14919
  try {
14578
- return (await stat2(path)).isDirectory();
14920
+ return (await stat3(path)).isDirectory();
14579
14921
  } catch {
14580
14922
  return false;
14581
14923
  }
14582
14924
  };
14583
14925
  var findGitRoot = async (cwd) => {
14584
- const start = resolve3(cwd);
14926
+ const start = resolve4(cwd);
14585
14927
  let current2 = start;
14586
14928
  for (;; ) {
14587
- if (await pathIsDirectory(resolve3(current2, ".git")))
14929
+ if (await pathIsDirectory(resolve4(current2, ".git")))
14588
14930
  return current2;
14589
- const parent = dirname3(current2);
14931
+ const parent = dirname5(current2);
14590
14932
  if (parent === current2)
14591
14933
  return start;
14592
14934
  current2 = parent;
@@ -14598,7 +14940,7 @@ var resolveDiscoveryRoot = async ({
14598
14940
  cwd
14599
14941
  }) => {
14600
14942
  if (cliRoot !== null)
14601
- return resolve3(cwd, cliRoot);
14943
+ return resolve4(cwd, cliRoot);
14602
14944
  if (defaultRoot === "git")
14603
14945
  return findGitRoot(cwd);
14604
14946
  return cwd;
@@ -14606,7 +14948,7 @@ var resolveDiscoveryRoot = async ({
14606
14948
  var validateDiscoveryRoot = async (root) => {
14607
14949
  let stats;
14608
14950
  try {
14609
- stats = await stat2(root);
14951
+ stats = await stat3(root);
14610
14952
  } catch (err) {
14611
14953
  throw new Error(`cannot access discovery root ${root}: ${String(err)}`);
14612
14954
  }
@@ -14640,10 +14982,10 @@ var buildDiscoveryPolicy = (show, extensions) => {
14640
14982
  followSymlinks: false,
14641
14983
  ignoreFiles: show.includes("gitignored") ? [] : [".gitignore"],
14642
14984
  includeFile: (path) => {
14643
- const name = basename3(path);
14985
+ const name = basename4(path);
14644
14986
  return allowed.has(name.slice(name.lastIndexOf(".")).toLowerCase()) && visiblePath(path) && !path.split(/[\\/]/).some((part) => hardSkips.has(part));
14645
14987
  },
14646
- includeDirectory: (path) => !hardSkips.has(basename3(path)) && visiblePath(path)
14988
+ includeDirectory: (path) => !hardSkips.has(basename4(path)) && visiblePath(path)
14647
14989
  };
14648
14990
  };
14649
14991
  var DiscoverShell = ({
@@ -14683,11 +15025,11 @@ var resolveInitialQuery = ({
14683
15025
  }) => {
14684
15026
  if (pathArg === null)
14685
15027
  return "";
14686
- const resolvedPath = resolve3(cwd, pathArg);
14687
- const rel = relative4(discoveryRoot, resolvedPath);
15028
+ const resolvedPath = resolve4(cwd, pathArg);
15029
+ const rel = relative5(discoveryRoot, resolvedPath);
14688
15030
  if (rel.length === 0)
14689
15031
  return "";
14690
- if (!rel.startsWith("..") && !isAbsolute2(rel))
15032
+ if (!rel.startsWith("..") && !isAbsolute3(rel))
14691
15033
  return rel;
14692
15034
  if (pathArg.startsWith("./"))
14693
15035
  return pathArg.slice(2);
@@ -14755,7 +15097,7 @@ async function main(argv = Bun.argv.slice(2)) {
14755
15097
  }
14756
15098
  let stats;
14757
15099
  try {
14758
- stats = await stat2(target);
15100
+ stats = await stat3(target);
14759
15101
  } catch (err) {
14760
15102
  console.error(`house: cannot access ${target}: ${String(err)}`);
14761
15103
  process.exit(1);