@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/CHANGELOG.md +14 -1
- package/README.md +2 -1
- package/dist/index.js +591 -249
- package/package.json +5 -5
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((
|
|
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
|
-
|
|
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((
|
|
2782
|
+
return new Promise((resolve4) => {
|
|
2783
2783
|
childProcess.exec(command, (err, out) => {
|
|
2784
2784
|
commandOut = err ? " " : out;
|
|
2785
|
-
|
|
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
|
|
3058
|
+
import { stat as stat3 } from "fs/promises";
|
|
3059
3059
|
import { homedir as homedir3 } from "os";
|
|
3060
|
-
import { basename as
|
|
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.
|
|
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 {
|
|
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
|
|
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
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
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("
|
|
3721
|
-
|
|
3722
|
-
children:
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
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("
|
|
3729
|
-
|
|
3730
|
-
|
|
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
|
|
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:
|
|
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
|
|
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(
|
|
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 =
|
|
4703
|
-
return candidate === "" || !candidate.startsWith(`..${
|
|
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
|
|
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(
|
|
4717
|
-
basename:
|
|
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(
|
|
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(
|
|
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) =>
|
|
4882
|
+
var normalizeRoot = (input) => resolve2(input);
|
|
4759
4883
|
var physicalWatchRoot = async (input) => {
|
|
4760
4884
|
const root = normalizeRoot(input);
|
|
4761
|
-
const rootStat = await
|
|
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
|
|
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 =
|
|
4871
|
-
const lexicalPath =
|
|
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 =
|
|
4893
|
-
const lexicalPath =
|
|
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
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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((
|
|
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
|
|
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 =
|
|
5622
|
+
const suffix = relative3(directory.physicalPath, path);
|
|
5499
5623
|
if (suffix === "" || !suffix.startsWith("..") && !suffix.startsWith("/"))
|
|
5500
|
-
return
|
|
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
|
|
5719
|
+
const basename3 = path.slice(slash2 + 1);
|
|
5596
5720
|
const parent = path.slice(0, slash2);
|
|
5597
|
-
if (
|
|
5598
|
-
return { basename: fitTail(
|
|
5721
|
+
if (basename3.length >= width) {
|
|
5722
|
+
return { basename: fitTail(basename3, width), parent: "", separator: "" };
|
|
5599
5723
|
}
|
|
5600
|
-
const remaining = width -
|
|
5724
|
+
const remaining = width - basename3.length - SEPARATOR.length;
|
|
5601
5725
|
if (remaining < MIN_PARENT_BUDGET || parent.length === 0) {
|
|
5602
|
-
return { basename:
|
|
5726
|
+
return { basename: basename3, parent: "", separator: "" };
|
|
5603
5727
|
}
|
|
5604
5728
|
return {
|
|
5605
|
-
basename:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
|
6120
|
+
const basename3 = `${trimmed}.md`;
|
|
5996
6121
|
return {
|
|
5997
6122
|
ok: true,
|
|
5998
|
-
basename:
|
|
5999
|
-
warnings: trimmed.includes(".") ? [`will be created as ${
|
|
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
|
|
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,
|
|
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
|
|
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 = () =>
|
|
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
|
|
13075
|
-
if (
|
|
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(
|
|
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
|
|
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((
|
|
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
|
|
13141
|
-
|
|
13142
|
-
|
|
13143
|
-
|
|
13144
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
13526
|
+
const serverRef = useRef3(initialPreviewServer);
|
|
13352
13527
|
const pendingSelectionPathRef = useRef3(null);
|
|
13353
|
-
|
|
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
|
-
|
|
13541
|
+
useLayoutEffect3(() => () => {
|
|
13367
13542
|
advanceReaderEpoch();
|
|
13368
13543
|
}, []);
|
|
13369
13544
|
useEffect4(() => {
|
|
13370
13545
|
mountedRef.current = true;
|
|
13371
13546
|
return () => {
|
|
13372
13547
|
mountedRef.current = false;
|
|
13373
|
-
|
|
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
|
|
13507
|
-
const
|
|
13508
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
13595
|
-
|
|
13596
|
-
|
|
13597
|
-
|
|
13598
|
-
|
|
13599
|
-
|
|
13600
|
-
|
|
13601
|
-
|
|
13602
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
13617
|
-
const
|
|
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 !==
|
|
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
|
-
|
|
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
|
-
|
|
13647
|
-
|
|
13648
|
-
|
|
13649
|
-
|
|
13650
|
-
|
|
13651
|
-
|
|
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
|
-
|
|
13661
|
-
|
|
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
|
-
|
|
13927
|
+
return;
|
|
13665
13928
|
}
|
|
13666
|
-
if (!
|
|
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
|
-
|
|
13669
|
-
|
|
13670
|
-
|
|
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 (
|
|
13855
|
-
|
|
13856
|
-
|
|
13857
|
-
|
|
13858
|
-
|
|
13859
|
-
|
|
13860
|
-
|
|
13861
|
-
|
|
13862
|
-
|
|
13863
|
-
|
|
13864
|
-
|
|
13865
|
-
|
|
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
|
-
|
|
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
|
-
|
|
14176
|
+
key.preventDefault();
|
|
14177
|
+
submitPrompt();
|
|
13878
14178
|
return;
|
|
13879
14179
|
}
|
|
13880
|
-
if (key.name === "
|
|
13881
|
-
|
|
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
|
|
14184
|
+
if (key.ctrl && !key.meta && key.name === "p") {
|
|
14185
|
+
key.preventDefault();
|
|
13887
14186
|
return;
|
|
13888
|
-
|
|
13889
|
-
if (
|
|
13890
|
-
|
|
13891
|
-
|
|
13892
|
-
|
|
13893
|
-
|
|
13894
|
-
|
|
13895
|
-
|
|
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
|
-
|
|
13944
|
-
|
|
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.
|
|
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 (
|
|
13969
|
-
|
|
13970
|
-
|
|
13971
|
-
|
|
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
|
|
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 =
|
|
14356
|
-
if (homeRelative.length > 0 && !homeRelative.startsWith("..") && !
|
|
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
|
|
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
|
|
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
|
|
14411
|
-
return
|
|
14752
|
+
return join5(xdg, "house");
|
|
14753
|
+
return join5(homedir2(), ".cache", "house");
|
|
14412
14754
|
};
|
|
14413
|
-
var cachePath = () =>
|
|
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(
|
|
14778
|
+
await mkdir2(dirname4(path), { recursive: true });
|
|
14437
14779
|
await writeFile2(tmp, JSON.stringify(record), "utf8");
|
|
14438
|
-
await
|
|
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
|
|
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 =
|
|
14926
|
+
const start = resolve4(cwd);
|
|
14585
14927
|
let current2 = start;
|
|
14586
14928
|
for (;; ) {
|
|
14587
|
-
if (await pathIsDirectory(
|
|
14929
|
+
if (await pathIsDirectory(resolve4(current2, ".git")))
|
|
14588
14930
|
return current2;
|
|
14589
|
-
const parent =
|
|
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
|
|
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
|
|
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 =
|
|
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(
|
|
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 =
|
|
14687
|
-
const rel =
|
|
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("..") && !
|
|
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
|
|
15100
|
+
stats = await stat3(target);
|
|
14759
15101
|
} catch (err) {
|
|
14760
15102
|
console.error(`house: cannot access ${target}: ${String(err)}`);
|
|
14761
15103
|
process.exit(1);
|