@carlesandres/house 0.6.0 → 0.8.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 +24 -1
- package/README.md +2 -1
- package/dist/index.js +998 -467
- 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.8.0",
|
|
3070
3070
|
description: "TUI-first markdown reader on opentui",
|
|
3071
3071
|
type: "module",
|
|
3072
3072
|
license: "MIT",
|
|
@@ -3160,15 +3160,287 @@ 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";
|
|
3165
|
+
|
|
3166
|
+
// ../../packages/options/src/footer.ts
|
|
3167
|
+
var footerActivate = (spec) => {
|
|
3168
|
+
if (spec.footer?.activate !== undefined)
|
|
3169
|
+
return spec.footer.activate;
|
|
3170
|
+
if (spec.type === "boolean")
|
|
3171
|
+
return "toggle";
|
|
3172
|
+
if (spec.type === "string" && spec.choices !== undefined && spec.choices.length > 0) {
|
|
3173
|
+
return "cycle";
|
|
3174
|
+
}
|
|
3175
|
+
return;
|
|
3176
|
+
};
|
|
3177
|
+
var footerKeys = (specs) => {
|
|
3178
|
+
const keys = [];
|
|
3179
|
+
for (const key of Object.keys(specs)) {
|
|
3180
|
+
if (specs[key]?.footer !== undefined)
|
|
3181
|
+
keys.push(key);
|
|
3182
|
+
}
|
|
3183
|
+
return keys;
|
|
3184
|
+
};
|
|
3185
|
+
var nextFooterValue = (spec, current) => {
|
|
3186
|
+
const activate = footerActivate(spec);
|
|
3187
|
+
if (activate === "toggle") {
|
|
3188
|
+
if (spec.type !== "boolean") {
|
|
3189
|
+
throw new Error('footer activate "toggle" requires a boolean option');
|
|
3190
|
+
}
|
|
3191
|
+
return !current;
|
|
3192
|
+
}
|
|
3193
|
+
if (activate === "cycle") {
|
|
3194
|
+
if (spec.type !== "string" || spec.choices === undefined || spec.choices.length === 0) {
|
|
3195
|
+
throw new Error('footer activate "cycle" requires a string option with choices');
|
|
3196
|
+
}
|
|
3197
|
+
const choices = spec.choices;
|
|
3198
|
+
const currentIndex = choices.indexOf(current);
|
|
3199
|
+
const nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % choices.length;
|
|
3200
|
+
return choices[nextIndex];
|
|
3201
|
+
}
|
|
3202
|
+
throw new Error("option has no footer activate strategy");
|
|
3203
|
+
};
|
|
3204
|
+
var footerControlActive = (spec, value) => {
|
|
3205
|
+
if (spec.type === "boolean")
|
|
3206
|
+
return Boolean(value);
|
|
3207
|
+
return true;
|
|
3208
|
+
};
|
|
3209
|
+
var footerControlGlyph = (spec, value) => {
|
|
3210
|
+
const footer = spec.footer;
|
|
3211
|
+
if (footer === undefined) {
|
|
3212
|
+
throw new Error("footerControlGlyph requires a footer-opted spec");
|
|
3213
|
+
}
|
|
3214
|
+
if (typeof value === "string" && footer.labels !== undefined) {
|
|
3215
|
+
const label = footer.labels[value];
|
|
3216
|
+
if (label !== undefined && label.length > 0)
|
|
3217
|
+
return label;
|
|
3218
|
+
}
|
|
3219
|
+
return footer.icon;
|
|
3220
|
+
};
|
|
3221
|
+
var validateFooterSpecs = (specs) => {
|
|
3222
|
+
for (const key of Object.keys(specs)) {
|
|
3223
|
+
const spec = specs[key];
|
|
3224
|
+
const footer = spec.footer;
|
|
3225
|
+
if (footer === undefined)
|
|
3226
|
+
continue;
|
|
3227
|
+
if (footer.icon.length === 0) {
|
|
3228
|
+
throw new Error(`option ${JSON.stringify(key)}: footer.icon must be non-empty`);
|
|
3229
|
+
}
|
|
3230
|
+
const activate = footerActivate(spec);
|
|
3231
|
+
if (activate === undefined) {
|
|
3232
|
+
throw new Error(`option ${JSON.stringify(key)}: footer requires activate "toggle" or "cycle" ` + "(booleans toggle; strings with choices cycle; other types need an explicit strategy)");
|
|
3233
|
+
}
|
|
3234
|
+
if (activate === "toggle" && spec.type !== "boolean") {
|
|
3235
|
+
throw new Error(`option ${JSON.stringify(key)}: footer activate "toggle" requires type "boolean"`);
|
|
3236
|
+
}
|
|
3237
|
+
if (activate === "cycle") {
|
|
3238
|
+
if (spec.type !== "string" || spec.choices === undefined || spec.choices.length === 0) {
|
|
3239
|
+
throw new Error(`option ${JSON.stringify(key)}: footer activate "cycle" requires string choices`);
|
|
3240
|
+
}
|
|
3241
|
+
const labels = footer.labels;
|
|
3242
|
+
if (labels === undefined) {
|
|
3243
|
+
throw new Error(`option ${JSON.stringify(key)}: footer.labels is required for choice options`);
|
|
3244
|
+
}
|
|
3245
|
+
for (const choice of spec.choices) {
|
|
3246
|
+
const label = labels[choice];
|
|
3247
|
+
if (label === undefined || label.length === 0) {
|
|
3248
|
+
throw new Error(`option ${JSON.stringify(key)}: footer.labels missing entry for ${JSON.stringify(choice)}`);
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
};
|
|
3254
|
+
|
|
3255
|
+
// ../../packages/options/src/decode.ts
|
|
3256
|
+
var isPresent = (value) => value !== undefined && value !== null;
|
|
3257
|
+
var stringifyReceived = (value) => {
|
|
3258
|
+
if (typeof value === "string")
|
|
3259
|
+
return JSON.stringify(value);
|
|
3260
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
3261
|
+
return JSON.stringify(value);
|
|
3262
|
+
if (value === null)
|
|
3263
|
+
return "null";
|
|
3264
|
+
return typeof value;
|
|
3265
|
+
};
|
|
3266
|
+
var formatResolveError = (error, filePath) => {
|
|
3267
|
+
if (error.layer === "file") {
|
|
3268
|
+
const where = filePath === undefined ? "" : ` in ${filePath}`;
|
|
3269
|
+
return `invalid value for ${error.key}${where}: ${error.message}`;
|
|
3270
|
+
}
|
|
3271
|
+
return `${error.key}: ${error.message}, got ${stringifyReceived(error.received)}`;
|
|
3272
|
+
};
|
|
3273
|
+
var fail = (key, layer, message, received) => ({
|
|
3274
|
+
key,
|
|
3275
|
+
layer,
|
|
3276
|
+
message,
|
|
3277
|
+
received
|
|
3278
|
+
});
|
|
3279
|
+
var booleanMessage = "expected true or false";
|
|
3280
|
+
var numberMessage = (spec) => {
|
|
3281
|
+
if (spec.integer === true && spec.min === 1 && spec.max === undefined) {
|
|
3282
|
+
return "expected a positive integer";
|
|
3283
|
+
}
|
|
3284
|
+
if (spec.integer === true)
|
|
3285
|
+
return "expected an integer";
|
|
3286
|
+
return "expected a number";
|
|
3287
|
+
};
|
|
3288
|
+
var inRange = (value, spec) => {
|
|
3289
|
+
if (spec.min !== undefined && value < spec.min)
|
|
3290
|
+
return false;
|
|
3291
|
+
if (spec.max !== undefined && value > spec.max)
|
|
3292
|
+
return false;
|
|
3293
|
+
if (spec.integer === true && !Number.isSafeInteger(value))
|
|
3294
|
+
return false;
|
|
3295
|
+
if (!Number.isFinite(value))
|
|
3296
|
+
return false;
|
|
3297
|
+
return true;
|
|
3298
|
+
};
|
|
3299
|
+
var decodeBoolean = (key, layer, value) => {
|
|
3300
|
+
if (layer === "env") {
|
|
3301
|
+
if (value === "true")
|
|
3302
|
+
return { ok: true, value: true };
|
|
3303
|
+
if (value === "false")
|
|
3304
|
+
return { ok: true, value: false };
|
|
3305
|
+
return { ok: false, error: fail(key, layer, booleanMessage, value) };
|
|
3306
|
+
}
|
|
3307
|
+
if (typeof value === "boolean")
|
|
3308
|
+
return { ok: true, value };
|
|
3309
|
+
return { ok: false, error: fail(key, layer, booleanMessage, value) };
|
|
3310
|
+
};
|
|
3311
|
+
var decodeNumber = (key, layer, value, spec) => {
|
|
3312
|
+
const message = numberMessage(spec);
|
|
3313
|
+
if (layer === "env") {
|
|
3314
|
+
if (typeof value !== "string" || !/^-?\d+(\.\d+)?$/.test(value)) {
|
|
3315
|
+
return { ok: false, error: fail(key, layer, message, value) };
|
|
3316
|
+
}
|
|
3317
|
+
if (spec.integer === true && !/^-?\d+$/.test(value)) {
|
|
3318
|
+
return { ok: false, error: fail(key, layer, message, value) };
|
|
3319
|
+
}
|
|
3320
|
+
const parsed = spec.integer === true ? Number.parseInt(value, 10) : Number.parseFloat(value);
|
|
3321
|
+
if (!inRange(parsed, spec))
|
|
3322
|
+
return { ok: false, error: fail(key, layer, message, value) };
|
|
3323
|
+
return { ok: true, value: parsed };
|
|
3324
|
+
}
|
|
3325
|
+
if (typeof value !== "number" || !inRange(value, spec)) {
|
|
3326
|
+
return { ok: false, error: fail(key, layer, message, value) };
|
|
3327
|
+
}
|
|
3328
|
+
return { ok: true, value };
|
|
3329
|
+
};
|
|
3330
|
+
var decodeString = (key, layer, value, spec) => {
|
|
3331
|
+
if (typeof value !== "string") {
|
|
3332
|
+
const message = spec.choices === undefined ? "expected a string" : `expected one of ${spec.choices.join(", ")}`;
|
|
3333
|
+
return { ok: false, error: fail(key, layer, message, value) };
|
|
3334
|
+
}
|
|
3335
|
+
if (spec.choices !== undefined && !spec.choices.includes(value)) {
|
|
3336
|
+
return {
|
|
3337
|
+
ok: false,
|
|
3338
|
+
error: fail(key, layer, `expected one of ${spec.choices.join(", ")}`, value)
|
|
3339
|
+
};
|
|
3340
|
+
}
|
|
3341
|
+
return { ok: true, value };
|
|
3342
|
+
};
|
|
3343
|
+
var decodeValue = (key, spec, layer, value) => {
|
|
3344
|
+
if (spec.type === "boolean")
|
|
3345
|
+
return decodeBoolean(key, layer, value);
|
|
3346
|
+
if (spec.type === "number")
|
|
3347
|
+
return decodeNumber(key, layer, value, spec);
|
|
3348
|
+
return decodeString(key, layer, value, spec);
|
|
3349
|
+
};
|
|
3350
|
+
|
|
3351
|
+
// ../../packages/options/src/resolve.ts
|
|
3352
|
+
var LAYERS = ["cli", "env", "file"];
|
|
3353
|
+
var resolveOptions = (specs, layers = {}) => {
|
|
3354
|
+
const values = {};
|
|
3355
|
+
for (const key of Object.keys(specs)) {
|
|
3356
|
+
const spec = specs[key];
|
|
3357
|
+
let found = false;
|
|
3358
|
+
for (const layer of LAYERS) {
|
|
3359
|
+
const source = layers[layer];
|
|
3360
|
+
if (source === undefined)
|
|
3361
|
+
continue;
|
|
3362
|
+
const raw = source[key];
|
|
3363
|
+
if (!isPresent(raw))
|
|
3364
|
+
continue;
|
|
3365
|
+
const decoded = decodeValue(key, spec, layer, raw);
|
|
3366
|
+
if (!decoded.ok)
|
|
3367
|
+
return decoded;
|
|
3368
|
+
values[key] = decoded.value;
|
|
3369
|
+
found = true;
|
|
3370
|
+
break;
|
|
3371
|
+
}
|
|
3372
|
+
if (!found)
|
|
3373
|
+
values[key] = spec.default;
|
|
3374
|
+
}
|
|
3375
|
+
return { ok: true, value: values };
|
|
3376
|
+
};
|
|
3377
|
+
var catalogDefaults = (specs) => {
|
|
3378
|
+
const values = {};
|
|
3379
|
+
for (const key of Object.keys(specs))
|
|
3380
|
+
values[key] = specs[key].default;
|
|
3381
|
+
return values;
|
|
3382
|
+
};
|
|
3383
|
+
|
|
3384
|
+
// ../../packages/options/src/session.ts
|
|
3385
|
+
var persistPolicy = (persist) => persist ?? "session";
|
|
3386
|
+
var createSession = (specs, values, options = {}) => {
|
|
3387
|
+
let snapshot = values;
|
|
3388
|
+
const listeners = new Set;
|
|
3389
|
+
const get = (key) => snapshot[key];
|
|
3390
|
+
const set = async (key, value) => {
|
|
3391
|
+
const spec = specs[key];
|
|
3392
|
+
if (spec === undefined) {
|
|
3393
|
+
throw new Error(`unknown option ${JSON.stringify(key)}`);
|
|
3394
|
+
}
|
|
3395
|
+
const decoded = decodeValue(key, spec, "cli", value);
|
|
3396
|
+
if (!decoded.ok) {
|
|
3397
|
+
throw new Error(`${decoded.error.key}: ${decoded.error.message}`);
|
|
3398
|
+
}
|
|
3399
|
+
if (Object.is(snapshot[key], decoded.value))
|
|
3400
|
+
return;
|
|
3401
|
+
snapshot = { ...snapshot, [key]: decoded.value };
|
|
3402
|
+
for (const listener of listeners)
|
|
3403
|
+
listener(snapshot);
|
|
3404
|
+
if (persistPolicy(spec.persist) === "file" && options.persist !== undefined) {
|
|
3405
|
+
await options.persist({
|
|
3406
|
+
key,
|
|
3407
|
+
value: decoded.value,
|
|
3408
|
+
values: snapshot
|
|
3409
|
+
});
|
|
3410
|
+
}
|
|
3411
|
+
};
|
|
3412
|
+
return {
|
|
3413
|
+
get,
|
|
3414
|
+
set,
|
|
3415
|
+
subscribe: (listener) => {
|
|
3416
|
+
listeners.add(listener);
|
|
3417
|
+
return () => {
|
|
3418
|
+
listeners.delete(listener);
|
|
3419
|
+
};
|
|
3420
|
+
},
|
|
3421
|
+
get values() {
|
|
3422
|
+
return snapshot;
|
|
3423
|
+
}
|
|
3424
|
+
};
|
|
3425
|
+
};
|
|
3426
|
+
|
|
3427
|
+
// ../../packages/options/src/define.ts
|
|
3428
|
+
var defineOptions = (specs) => {
|
|
3429
|
+
validateFooterSpecs(specs);
|
|
3430
|
+
return {
|
|
3431
|
+
specs,
|
|
3432
|
+
defaults: catalogDefaults(specs),
|
|
3433
|
+
resolve: (layers) => resolveOptions(specs, layers),
|
|
3434
|
+
createSession: (values, options) => createSession(specs, values, options)
|
|
3435
|
+
};
|
|
3436
|
+
};
|
|
3437
|
+
// src/Browser.tsx
|
|
3166
3438
|
import { useKeyboard, useRenderer, useTerminalDimensions as useTerminalDimensions2 } from "@opentui/react";
|
|
3167
3439
|
import { useAtomValue, useAtomSet } from "@effect/atom-react";
|
|
3168
3440
|
import { Effect as Effect3 } from "effect";
|
|
3169
3441
|
import {
|
|
3170
3442
|
useEffect as useEffect4,
|
|
3171
|
-
useLayoutEffect as
|
|
3443
|
+
useLayoutEffect as useLayoutEffect3,
|
|
3172
3444
|
useMemo as useMemo2,
|
|
3173
3445
|
useReducer as useReducer2,
|
|
3174
3446
|
useRef as useRef3,
|
|
@@ -3238,7 +3510,7 @@ var browserBindings = [
|
|
|
3238
3510
|
group: "Global",
|
|
3239
3511
|
description: "Command palette",
|
|
3240
3512
|
hint: "palette",
|
|
3241
|
-
keys: ["ctrl+p"],
|
|
3513
|
+
keys: ["ctrl+p", "?"],
|
|
3242
3514
|
hintWhen: () => true,
|
|
3243
3515
|
when: paletteClosed,
|
|
3244
3516
|
run: (c) => c.openPalette()
|
|
@@ -3373,6 +3645,14 @@ var browserBindings = [
|
|
|
3373
3645
|
keys: ["shift+n"],
|
|
3374
3646
|
run: (c) => c.openNewFilePrompt()
|
|
3375
3647
|
},
|
|
3648
|
+
{
|
|
3649
|
+
id: "file.rename",
|
|
3650
|
+
group: "File",
|
|
3651
|
+
description: "Rename selected file",
|
|
3652
|
+
keys: ["shift+r"],
|
|
3653
|
+
when: hasSelected,
|
|
3654
|
+
run: (c) => c.openRenamePrompt()
|
|
3655
|
+
},
|
|
3376
3656
|
{
|
|
3377
3657
|
id: "reader.prevFile",
|
|
3378
3658
|
group: "File",
|
|
@@ -3442,6 +3722,11 @@ var annotations = {
|
|
|
3442
3722
|
category: "File",
|
|
3443
3723
|
keywords: ["new", "create", "nvim", "vim", "editor"]
|
|
3444
3724
|
},
|
|
3725
|
+
"file.rename": {
|
|
3726
|
+
title: "Rename\u2026",
|
|
3727
|
+
category: "File",
|
|
3728
|
+
keywords: ["rename", "mv", "basename"]
|
|
3729
|
+
},
|
|
3445
3730
|
"theme.next": { category: "Appearance" },
|
|
3446
3731
|
"theme.prev": { category: "Appearance" },
|
|
3447
3732
|
"theme.toneToggle": { title: "Toggle dark/light tone", category: "Appearance" },
|
|
@@ -3695,43 +3980,54 @@ var setActiveTheme = (definition, tone) => {
|
|
|
3695
3980
|
};
|
|
3696
3981
|
|
|
3697
3982
|
// src/PromptRow.tsx
|
|
3983
|
+
import { useLayoutEffect } from "react";
|
|
3698
3984
|
import { jsxDEV } from "@opentui/react/jsx-dev-runtime";
|
|
3699
3985
|
var PREFIX = "> ";
|
|
3700
|
-
var CURSOR = "\u258F";
|
|
3701
3986
|
var PromptRow = ({
|
|
3702
3987
|
query,
|
|
3703
3988
|
editing,
|
|
3704
3989
|
placeholder = "",
|
|
3705
3990
|
showPlaceholderWhileEditing = false,
|
|
3706
|
-
width
|
|
3991
|
+
width,
|
|
3992
|
+
onInput,
|
|
3993
|
+
onEditingReady
|
|
3707
3994
|
}) => {
|
|
3708
3995
|
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
3996
|
const prefixFg = editing ? colors.secondary : colors.primary;
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3997
|
+
useLayoutEffect(() => {
|
|
3998
|
+
onEditingReady?.(editing);
|
|
3999
|
+
return () => onEditingReady?.(false);
|
|
4000
|
+
}, [editing, onEditingReady]);
|
|
4001
|
+
if (editing) {
|
|
4002
|
+
return /* @__PURE__ */ jsxDEV("box", {
|
|
4003
|
+
style: { flexDirection: "row", width, height: 1, flexShrink: 0 },
|
|
3719
4004
|
children: [
|
|
3720
|
-
/* @__PURE__ */ jsxDEV("
|
|
3721
|
-
|
|
3722
|
-
children:
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
children: placeholderBody
|
|
4005
|
+
/* @__PURE__ */ jsxDEV("text", {
|
|
4006
|
+
wrapMode: "none",
|
|
4007
|
+
children: /* @__PURE__ */ jsxDEV("span", {
|
|
4008
|
+
style: { fg: prefixFg },
|
|
4009
|
+
children: PREFIX
|
|
4010
|
+
}, undefined, false, undefined, this)
|
|
3727
4011
|
}, undefined, false, undefined, this),
|
|
3728
|
-
/* @__PURE__ */ jsxDEV("
|
|
3729
|
-
|
|
3730
|
-
|
|
4012
|
+
/* @__PURE__ */ jsxDEV("input", {
|
|
4013
|
+
focused: true,
|
|
4014
|
+
value: query,
|
|
4015
|
+
placeholder: showPlaceholderWhileEditing ? placeholder : "",
|
|
4016
|
+
...onInput === undefined ? {} : { onInput },
|
|
4017
|
+
backgroundColor: "transparent",
|
|
4018
|
+
focusedBackgroundColor: "transparent",
|
|
4019
|
+
textColor: colors.primary,
|
|
4020
|
+
focusedTextColor: colors.primary,
|
|
4021
|
+
cursorColor: colors.primary,
|
|
4022
|
+
placeholderColor: colors.textMuted,
|
|
4023
|
+
width: bodyBudget
|
|
3731
4024
|
}, undefined, false, undefined, this)
|
|
3732
4025
|
]
|
|
3733
4026
|
}, undefined, true, undefined, this);
|
|
3734
4027
|
}
|
|
4028
|
+
const rawBody = query.length > 0 ? query : placeholder;
|
|
4029
|
+
const bodyFg = query.length > 0 ? colors.text : colors.textMuted;
|
|
4030
|
+
const body = rawBody.length <= bodyBudget ? rawBody : rawBody.slice(0, bodyBudget - 1) + "\u2026";
|
|
3735
4031
|
return /* @__PURE__ */ jsxDEV("text", {
|
|
3736
4032
|
wrapMode: "none",
|
|
3737
4033
|
children: [
|
|
@@ -3791,7 +4087,9 @@ var CommandPalette = ({
|
|
|
3791
4087
|
query,
|
|
3792
4088
|
selectedIndex,
|
|
3793
4089
|
viewportWidth,
|
|
3794
|
-
viewportHeight
|
|
4090
|
+
viewportHeight,
|
|
4091
|
+
onQueryChange,
|
|
4092
|
+
onInputReady
|
|
3795
4093
|
}) => {
|
|
3796
4094
|
const overlayWidth = Math.min(viewportWidth - 4, 64);
|
|
3797
4095
|
const rows = buildRows(commands);
|
|
@@ -3846,7 +4144,9 @@ var CommandPalette = ({
|
|
|
3846
4144
|
/* @__PURE__ */ jsxDEV2(PromptRow, {
|
|
3847
4145
|
query,
|
|
3848
4146
|
editing: true,
|
|
3849
|
-
width: rowWidth
|
|
4147
|
+
width: rowWidth,
|
|
4148
|
+
onInput: onQueryChange,
|
|
4149
|
+
...onInputReady === undefined ? {} : { onEditingReady: onInputReady }
|
|
3850
4150
|
}, undefined, false, undefined, this),
|
|
3851
4151
|
/* @__PURE__ */ jsxDEV2("text", {
|
|
3852
4152
|
content: " "
|
|
@@ -3904,6 +4204,23 @@ var CommandPalette = ({
|
|
|
3904
4204
|
|
|
3905
4205
|
// src/PromptModal.tsx
|
|
3906
4206
|
import { RGBA as RGBA2 } from "@opentui/core";
|
|
4207
|
+
|
|
4208
|
+
// src/ui/middleTruncate.ts
|
|
4209
|
+
var middleTruncate = (value, width, options = {}) => {
|
|
4210
|
+
const ellipsis = options.ellipsis ?? "\u2026";
|
|
4211
|
+
if (width <= 0)
|
|
4212
|
+
return "";
|
|
4213
|
+
if (value.length <= width)
|
|
4214
|
+
return value;
|
|
4215
|
+
if (width <= ellipsis.length)
|
|
4216
|
+
return value.slice(0, width);
|
|
4217
|
+
const available = width - ellipsis.length;
|
|
4218
|
+
const left = Math.ceil(available / 2);
|
|
4219
|
+
const right = Math.floor(available / 2);
|
|
4220
|
+
return value.slice(0, left) + ellipsis + value.slice(value.length - right);
|
|
4221
|
+
};
|
|
4222
|
+
|
|
4223
|
+
// src/PromptModal.tsx
|
|
3907
4224
|
import { jsxDEV as jsxDEV3 } from "@opentui/react/jsx-dev-runtime";
|
|
3908
4225
|
var SCRIM2 = RGBA2.fromInts(0, 0, 0, 150);
|
|
3909
4226
|
var PromptStatusLine = ({
|
|
@@ -3924,13 +4241,18 @@ var PromptModal = ({
|
|
|
3924
4241
|
placeholder,
|
|
3925
4242
|
hints,
|
|
3926
4243
|
status,
|
|
4244
|
+
context,
|
|
3927
4245
|
viewportWidth,
|
|
3928
|
-
viewportHeight
|
|
4246
|
+
viewportHeight,
|
|
4247
|
+
onQueryChange,
|
|
4248
|
+
onInputReady,
|
|
4249
|
+
inputEnabled = true
|
|
3929
4250
|
}) => {
|
|
3930
4251
|
const overlayWidth = Math.min(viewportWidth - 4, 64);
|
|
3931
4252
|
const statusLines = status?.lines ?? [];
|
|
3932
4253
|
const statusHeight = Math.max(1, statusLines.length);
|
|
3933
|
-
const
|
|
4254
|
+
const contextHeight = context !== undefined && context.length > 0 ? 1 : 0;
|
|
4255
|
+
const overlayHeight = 2 + contextHeight + 1 + statusHeight + 1;
|
|
3934
4256
|
const left = Math.max(0, Math.floor((viewportWidth - overlayWidth) / 2));
|
|
3935
4257
|
const top = Math.max(0, Math.floor((viewportHeight - overlayHeight) / 2));
|
|
3936
4258
|
const rowWidth = Math.max(4, overlayWidth - 4);
|
|
@@ -3960,12 +4282,19 @@ var PromptModal = ({
|
|
|
3960
4282
|
backgroundColor: colors.backgroundPanel
|
|
3961
4283
|
},
|
|
3962
4284
|
children: [
|
|
4285
|
+
context !== undefined && context.length > 0 && /* @__PURE__ */ jsxDEV3("text", {
|
|
4286
|
+
wrapMode: "none",
|
|
4287
|
+
content: middleTruncate(context, rowWidth),
|
|
4288
|
+
style: { fg: colors.textMuted }
|
|
4289
|
+
}, undefined, false, undefined, this),
|
|
3963
4290
|
/* @__PURE__ */ jsxDEV3(PromptRow, {
|
|
3964
4291
|
query,
|
|
3965
|
-
editing:
|
|
4292
|
+
editing: inputEnabled,
|
|
3966
4293
|
placeholder,
|
|
3967
4294
|
showPlaceholderWhileEditing: true,
|
|
3968
|
-
width: rowWidth
|
|
4295
|
+
width: rowWidth,
|
|
4296
|
+
onInput: onQueryChange,
|
|
4297
|
+
...onInputReady === undefined ? {} : { onEditingReady: onInputReady }
|
|
3969
4298
|
}, undefined, false, undefined, this),
|
|
3970
4299
|
status === null || statusLines.length === 0 ? /* @__PURE__ */ jsxDEV3("text", {
|
|
3971
4300
|
content: " "
|
|
@@ -4034,7 +4363,7 @@ var BRAND = "\u2302";
|
|
|
4034
4363
|
var BRAND_NAME = "house";
|
|
4035
4364
|
|
|
4036
4365
|
// src/Footer.tsx
|
|
4037
|
-
import
|
|
4366
|
+
import stringWidth2 from "string-width";
|
|
4038
4367
|
|
|
4039
4368
|
// src/keymap/displayKey.ts
|
|
4040
4369
|
var displayKey = (raw) => {
|
|
@@ -4089,7 +4418,9 @@ var Spinner = ({
|
|
|
4089
4418
|
};
|
|
4090
4419
|
|
|
4091
4420
|
// src/StatusIndicator.tsx
|
|
4421
|
+
import stringWidth from "string-width";
|
|
4092
4422
|
import { jsxDEV as jsxDEV5 } from "@opentui/react/jsx-dev-runtime";
|
|
4423
|
+
var statusIndicatorWidth = (icon) => 2 + Math.max(1, stringWidth(icon));
|
|
4093
4424
|
var statusIndicatorFg = (variant) => {
|
|
4094
4425
|
switch (variant) {
|
|
4095
4426
|
case "info":
|
|
@@ -4108,18 +4439,20 @@ var StatusIndicator = ({
|
|
|
4108
4439
|
active = true,
|
|
4109
4440
|
onMouseUp
|
|
4110
4441
|
}) => {
|
|
4442
|
+
const glyph = icon.toLocaleUpperCase();
|
|
4111
4443
|
const activeColor = statusIndicatorFg(variant);
|
|
4112
4444
|
const backgroundColor = active ? activeColor : colors.backgroundElement;
|
|
4445
|
+
const width = statusIndicatorWidth(glyph);
|
|
4113
4446
|
return /* @__PURE__ */ jsxDEV5("box", {
|
|
4114
4447
|
...onMouseUp === undefined ? {} : { onMouseUp },
|
|
4115
4448
|
style: {
|
|
4116
|
-
width
|
|
4449
|
+
width,
|
|
4117
4450
|
height: 1,
|
|
4118
4451
|
flexDirection: "row",
|
|
4119
4452
|
backgroundColor
|
|
4120
4453
|
},
|
|
4121
4454
|
children: /* @__PURE__ */ jsxDEV5("text", {
|
|
4122
|
-
content: ` ${
|
|
4455
|
+
content: ` ${glyph} `,
|
|
4123
4456
|
wrapMode: "none",
|
|
4124
4457
|
style: {
|
|
4125
4458
|
fg: active ? colors.backgroundPanel : colors.textMuted,
|
|
@@ -4135,7 +4468,7 @@ import { jsxDEV as jsxDEV6 } from "@opentui/react/jsx-dev-runtime";
|
|
|
4135
4468
|
var FOOTER_HEIGHT = 1;
|
|
4136
4469
|
var normalizeStatusLine = (status) => status.replace(/\s+/g, " ").trim();
|
|
4137
4470
|
var HINT_SEPARATOR = " ";
|
|
4138
|
-
var hintWidth = (h) =>
|
|
4471
|
+
var hintWidth = (h) => stringWidth2(h.key) + 1 + stringWidth2(h.label);
|
|
4139
4472
|
var formatHint = (b) => {
|
|
4140
4473
|
if (!b.hint)
|
|
4141
4474
|
return null;
|
|
@@ -4162,6 +4495,8 @@ var fitHints = (hints, width) => {
|
|
|
4162
4495
|
return [{ key: first.key.slice(0, width), label: "" }];
|
|
4163
4496
|
};
|
|
4164
4497
|
var STATUS_SEPARATOR = " \xB7 ";
|
|
4498
|
+
var INDICATOR_SEPARATOR = " ";
|
|
4499
|
+
var INDICATOR_SEPARATOR_WIDTH = 1;
|
|
4165
4500
|
var NON_WARNING_STATUS_PREFIX_WIDTH = 2;
|
|
4166
4501
|
var isPartialDiscoveryWarning = (status) => status?.startsWith("scan incomplete:") ?? false;
|
|
4167
4502
|
var Footer = ({
|
|
@@ -4209,7 +4544,9 @@ var Footer = ({
|
|
|
4209
4544
|
] : [],
|
|
4210
4545
|
...indicators
|
|
4211
4546
|
];
|
|
4212
|
-
const
|
|
4547
|
+
const indicatorWidths = renderedIndicators.map((indicator) => statusIndicatorWidth(indicator.icon));
|
|
4548
|
+
const indicatorSeparatorBudget = renderedIndicators.length > 1 ? (renderedIndicators.length - 1) * INDICATOR_SEPARATOR_WIDTH : 0;
|
|
4549
|
+
const indicatorBudget = Math.min(usableWidth, indicatorWidths.reduce((sum, width2) => sum + width2, 0) + indicatorSeparatorBudget);
|
|
4213
4550
|
const hasContentAfterIndicators = notice !== null || status !== null || hints.length > 0;
|
|
4214
4551
|
const indicatorContentGapBudget = renderedIndicators.length > 0 && hasContentAfterIndicators && indicatorBudget < usableWidth ? 1 : 0;
|
|
4215
4552
|
const contentBudget = Math.max(0, usableWidth - indicatorBudget - indicatorContentGapBudget);
|
|
@@ -4221,9 +4558,21 @@ var Footer = ({
|
|
|
4221
4558
|
const statusContent = status ? status.slice(0, Math.max(0, statusBudget - statusChromeWidth)) : "";
|
|
4222
4559
|
const statusSeparatorContent = status ? STATUS_SEPARATOR.slice(0, Math.max(0, statusBudget - nonWarningStatusPrefixBudget - statusContent.length)) : "";
|
|
4223
4560
|
const noticeContent = notice ? notice.length > contentBudget ? notice.slice(0, contentBudget) : notice : null;
|
|
4224
|
-
const renderIndicators = () => renderedIndicators.
|
|
4225
|
-
|
|
4226
|
-
|
|
4561
|
+
const renderIndicators = () => renderedIndicators.flatMap(({ id, ...props }, index) => {
|
|
4562
|
+
const chip = /* @__PURE__ */ jsxDEV6(StatusIndicator, {
|
|
4563
|
+
...props
|
|
4564
|
+
}, id, false, undefined, this);
|
|
4565
|
+
if (index === 0)
|
|
4566
|
+
return [chip];
|
|
4567
|
+
return [
|
|
4568
|
+
/* @__PURE__ */ jsxDEV6("text", {
|
|
4569
|
+
content: INDICATOR_SEPARATOR,
|
|
4570
|
+
wrapMode: "none",
|
|
4571
|
+
style: { fg: colors.textMuted }
|
|
4572
|
+
}, `indicator-sep-${id}`, false, undefined, this),
|
|
4573
|
+
chip
|
|
4574
|
+
];
|
|
4575
|
+
});
|
|
4227
4576
|
const renderIndicatorContentGap = () => indicatorContentGapBudget > 0 ? /* @__PURE__ */ jsxDEV6("text", {
|
|
4228
4577
|
content: " ",
|
|
4229
4578
|
wrapMode: "none",
|
|
@@ -4309,21 +4658,6 @@ var Footer = ({
|
|
|
4309
4658
|
}, undefined, true, undefined, this);
|
|
4310
4659
|
};
|
|
4311
4660
|
|
|
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
4661
|
// src/Header.tsx
|
|
4328
4662
|
import { jsxDEV as jsxDEV7 } from "@opentui/react/jsx-dev-runtime";
|
|
4329
4663
|
var HEADER_HEIGHT = 1;
|
|
@@ -4578,12 +4912,95 @@ var readFileText = (path) => Effect.tryPromise({
|
|
|
4578
4912
|
catch: (cause) => new FileReadError({ path, cause })
|
|
4579
4913
|
});
|
|
4580
4914
|
|
|
4915
|
+
// src/io/renameFile.ts
|
|
4916
|
+
import { rename, readdir, stat } from "fs/promises";
|
|
4917
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "path";
|
|
4918
|
+
var isPathInsideRoot = (root, absolutePath) => {
|
|
4919
|
+
const resolvedRoot = resolve(root);
|
|
4920
|
+
const resolvedPath = resolve(absolutePath);
|
|
4921
|
+
const rel = relative(resolvedRoot, resolvedPath);
|
|
4922
|
+
return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
|
|
4923
|
+
};
|
|
4924
|
+
var uniqueTempBasename = async (entries, finalBasename) => {
|
|
4925
|
+
const stem = finalBasename.endsWith(".md") ? finalBasename.slice(0, -3) : finalBasename;
|
|
4926
|
+
const lower = new Set(entries.map((e) => e.toLowerCase()));
|
|
4927
|
+
for (let i = 0;i < 1000; i++) {
|
|
4928
|
+
const candidate = `${stem}.house-rename-${process.pid}-${Date.now()}-${i}.tmp`;
|
|
4929
|
+
if (!lower.has(candidate.toLowerCase()))
|
|
4930
|
+
return candidate;
|
|
4931
|
+
}
|
|
4932
|
+
throw new Error("could not allocate a temporary rename name");
|
|
4933
|
+
};
|
|
4934
|
+
var renameMarkdownFile = async (request) => {
|
|
4935
|
+
const discoveryRoot = resolve(request.discoveryRoot);
|
|
4936
|
+
const sourcePath = resolve(request.sourcePath);
|
|
4937
|
+
const parentDir = resolve(request.parentDir);
|
|
4938
|
+
const newBasename = request.newBasename;
|
|
4939
|
+
const destPath = join(parentDir, newBasename);
|
|
4940
|
+
const sourceBasename = basename(sourcePath);
|
|
4941
|
+
if (sourceBasename === newBasename && dirname(sourcePath) === parentDir) {
|
|
4942
|
+
return { ok: true, from: sourcePath, to: destPath, noop: true };
|
|
4943
|
+
}
|
|
4944
|
+
let sourceStat;
|
|
4945
|
+
try {
|
|
4946
|
+
sourceStat = await stat(sourcePath);
|
|
4947
|
+
} catch {
|
|
4948
|
+
return { ok: false, reason: "target-missing", message: "target missing" };
|
|
4949
|
+
}
|
|
4950
|
+
if (!sourceStat.isFile()) {
|
|
4951
|
+
return { ok: false, reason: "target-changed", message: "target changed" };
|
|
4952
|
+
}
|
|
4953
|
+
if (!isPathInsideRoot(discoveryRoot, sourcePath)) {
|
|
4954
|
+
return { ok: false, reason: "target-changed", message: "target changed" };
|
|
4955
|
+
}
|
|
4956
|
+
if (dirname(sourcePath) !== parentDir) {
|
|
4957
|
+
return { ok: false, reason: "target-changed", message: "target changed" };
|
|
4958
|
+
}
|
|
4959
|
+
let entries;
|
|
4960
|
+
try {
|
|
4961
|
+
entries = await readdir(parentDir);
|
|
4962
|
+
} catch (err) {
|
|
4963
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4964
|
+
return { ok: false, reason: "io", message: `rename failed: ${message}` };
|
|
4965
|
+
}
|
|
4966
|
+
if (!entries.includes(sourceBasename)) {
|
|
4967
|
+
return { ok: false, reason: "target-changed", message: "target changed" };
|
|
4968
|
+
}
|
|
4969
|
+
const exactDest = entries.includes(newBasename);
|
|
4970
|
+
const caseFoldDest = entries.find((e) => e.toLowerCase() === newBasename.toLowerCase());
|
|
4971
|
+
const caseOnly = sourceBasename.toLowerCase() === newBasename.toLowerCase() && sourceBasename !== newBasename;
|
|
4972
|
+
if (exactDest || caseFoldDest !== undefined && caseFoldDest !== sourceBasename) {
|
|
4973
|
+
return { ok: false, reason: "already-exists", message: "already exists" };
|
|
4974
|
+
}
|
|
4975
|
+
try {
|
|
4976
|
+
if (caseOnly) {
|
|
4977
|
+
const tempBasename = await uniqueTempBasename(entries, newBasename);
|
|
4978
|
+
const tempPath = join(parentDir, tempBasename);
|
|
4979
|
+
await rename(sourcePath, tempPath);
|
|
4980
|
+
try {
|
|
4981
|
+
await rename(tempPath, destPath);
|
|
4982
|
+
} catch (err) {
|
|
4983
|
+
try {
|
|
4984
|
+
await rename(tempPath, sourcePath);
|
|
4985
|
+
} catch {}
|
|
4986
|
+
throw err;
|
|
4987
|
+
}
|
|
4988
|
+
} else {
|
|
4989
|
+
await rename(sourcePath, destPath);
|
|
4990
|
+
}
|
|
4991
|
+
return { ok: true, from: sourcePath, to: destPath };
|
|
4992
|
+
} catch (err) {
|
|
4993
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4994
|
+
return { ok: false, reason: "io", message: `rename failed: ${message}` };
|
|
4995
|
+
}
|
|
4996
|
+
};
|
|
4997
|
+
|
|
4581
4998
|
// ../../packages/ui/src/file-navigator/FilesystemFileNavigator.tsx
|
|
4582
4999
|
import {
|
|
4583
5000
|
forwardRef,
|
|
4584
5001
|
useEffect as useEffect3,
|
|
4585
5002
|
useImperativeHandle,
|
|
4586
|
-
useLayoutEffect,
|
|
5003
|
+
useLayoutEffect as useLayoutEffect2,
|
|
4587
5004
|
useReducer,
|
|
4588
5005
|
useRef as useRef2
|
|
4589
5006
|
} from "react";
|
|
@@ -4688,23 +5105,23 @@ var Sidebar = ({
|
|
|
4688
5105
|
};
|
|
4689
5106
|
// ../../packages/ui/src/file-navigator/core/engine.ts
|
|
4690
5107
|
import { platform } from "os";
|
|
4691
|
-
import { relative as
|
|
5108
|
+
import { relative as relative3, resolve as resolve3 } from "path";
|
|
4692
5109
|
|
|
4693
5110
|
// ../../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";
|
|
5111
|
+
import { basename as basename2, extname, join as join2, relative as relative2, resolve as resolve2, sep as sep2 } from "path";
|
|
5112
|
+
import { readFile as readFile2, readdir as readdir2, realpath, stat as stat2 } from "fs/promises";
|
|
4696
5113
|
import ignore from "ignore";
|
|
4697
5114
|
var DEFAULT_BATCH_SIZE = 64;
|
|
4698
|
-
var slash = (value) => value.split(
|
|
5115
|
+
var slash = (value) => value.split(sep2).join("/");
|
|
4699
5116
|
var compare = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
4700
5117
|
var missing = (error) => typeof error === "object" && error !== null && ("code" in error) && error.code === "ENOENT";
|
|
4701
5118
|
var inside = (root, path) => {
|
|
4702
|
-
const candidate =
|
|
4703
|
-
return candidate === "" || !candidate.startsWith(`..${
|
|
5119
|
+
const candidate = relative2(root, path);
|
|
5120
|
+
return candidate === "" || !candidate.startsWith(`..${sep2}`) && candidate !== "..";
|
|
4704
5121
|
};
|
|
4705
5122
|
var thenable = (value) => typeof value === "object" && value !== null && typeof value.then === "function";
|
|
4706
5123
|
var readStat = async (path) => {
|
|
4707
|
-
const value = await
|
|
5124
|
+
const value = await stat2(path);
|
|
4708
5125
|
return { size: value.size, mtimeMs: value.mtimeMs };
|
|
4709
5126
|
};
|
|
4710
5127
|
var filterPresentIgnoreFiles = (ignoreFiles, present) => {
|
|
@@ -4713,8 +5130,8 @@ var filterPresentIgnoreFiles = (ignoreFiles, present) => {
|
|
|
4713
5130
|
};
|
|
4714
5131
|
var frozenRecord = (path, root, metadata) => Object.freeze({
|
|
4715
5132
|
absolutePath: path,
|
|
4716
|
-
relativePath: slash(
|
|
4717
|
-
basename:
|
|
5133
|
+
relativePath: slash(relative2(root, path)),
|
|
5134
|
+
basename: basename2(path),
|
|
4718
5135
|
extension: extname(path).toLowerCase(),
|
|
4719
5136
|
size: metadata.size,
|
|
4720
5137
|
mtimeMs: metadata.mtimeMs
|
|
@@ -4722,7 +5139,7 @@ var frozenRecord = (path, root, metadata) => Object.freeze({
|
|
|
4722
5139
|
var ignored = (path, directory, levels) => {
|
|
4723
5140
|
let result = false;
|
|
4724
5141
|
for (const level of levels) {
|
|
4725
|
-
const candidate = slash(
|
|
5142
|
+
const candidate = slash(relative2(level.directory, path));
|
|
4726
5143
|
if (!candidate || candidate === ".." || candidate.startsWith("../"))
|
|
4727
5144
|
continue;
|
|
4728
5145
|
const match = level.matcher.test(directory ? `${candidate}/` : candidate);
|
|
@@ -4741,7 +5158,7 @@ var loadLevels = async (physical, lexical, ignoreFiles, parent) => {
|
|
|
4741
5158
|
...levels,
|
|
4742
5159
|
{
|
|
4743
5160
|
directory: lexical,
|
|
4744
|
-
matcher: ignore().add(await readFile2(
|
|
5161
|
+
matcher: ignore().add(await readFile2(join2(physical, filename), "utf8"))
|
|
4745
5162
|
}
|
|
4746
5163
|
];
|
|
4747
5164
|
} catch (error) {
|
|
@@ -4755,10 +5172,10 @@ var abort = (signal) => {
|
|
|
4755
5172
|
if (signal?.aborted)
|
|
4756
5173
|
throw new DOMException("scan aborted", "AbortError");
|
|
4757
5174
|
};
|
|
4758
|
-
var normalizeRoot = (input) =>
|
|
5175
|
+
var normalizeRoot = (input) => resolve2(input);
|
|
4759
5176
|
var physicalWatchRoot = async (input) => {
|
|
4760
5177
|
const root = normalizeRoot(input);
|
|
4761
|
-
const rootStat = await
|
|
5178
|
+
const rootStat = await stat2(root);
|
|
4762
5179
|
if (!rootStat.isDirectory())
|
|
4763
5180
|
throw new Error(`root is not a directory: ${root}`);
|
|
4764
5181
|
return realpath(root);
|
|
@@ -4852,7 +5269,7 @@ var scanFiles = async (input, policyInput, options = {}) => {
|
|
|
4852
5269
|
const beforeRead = options.barrier?.("before-read", lexical);
|
|
4853
5270
|
if (thenable(beforeRead))
|
|
4854
5271
|
await beforeRead;
|
|
4855
|
-
const entries = (await
|
|
5272
|
+
const entries = (await readdir2(physical, { withFileTypes: true })).sort((a, b) => compare(a.name, b.name));
|
|
4856
5273
|
const afterRead = options.barrier?.("after-read", lexical);
|
|
4857
5274
|
if (thenable(afterRead))
|
|
4858
5275
|
await afterRead;
|
|
@@ -4867,8 +5284,8 @@ var scanFiles = async (input, policyInput, options = {}) => {
|
|
|
4867
5284
|
for (const entry of files) {
|
|
4868
5285
|
if (options.topologyOnly)
|
|
4869
5286
|
continue;
|
|
4870
|
-
const path =
|
|
4871
|
-
const lexicalPath =
|
|
5287
|
+
const path = join2(physical, entry.name);
|
|
5288
|
+
const lexicalPath = join2(lexical, entry.name);
|
|
4872
5289
|
try {
|
|
4873
5290
|
if (controlFiles.has(entry.name) || ignored(lexicalPath, false, levels))
|
|
4874
5291
|
continue;
|
|
@@ -4889,18 +5306,18 @@ var scanFiles = async (input, policyInput, options = {}) => {
|
|
|
4889
5306
|
abort(options.signal);
|
|
4890
5307
|
if (controlFiles.has(entry.name))
|
|
4891
5308
|
continue;
|
|
4892
|
-
const physicalPath =
|
|
4893
|
-
const lexicalPath =
|
|
5309
|
+
const physicalPath = join2(physical, entry.name);
|
|
5310
|
+
const lexicalPath = join2(lexical, entry.name);
|
|
4894
5311
|
try {
|
|
4895
5312
|
if (entry.isSymbolicLink() && !policy.followSymlinks)
|
|
4896
5313
|
continue;
|
|
4897
5314
|
const target = entry.isSymbolicLink() ? await realpath(physicalPath) : physicalPath;
|
|
4898
|
-
const targetStat = await
|
|
5315
|
+
const targetStat = await stat2(target);
|
|
4899
5316
|
if (targetStat.isDirectory()) {
|
|
4900
5317
|
if (ignored(lexicalPath, true, levels) || ancestry.has(target))
|
|
4901
5318
|
continue;
|
|
4902
5319
|
if ((targetStat.mode & 511) === 0) {
|
|
4903
|
-
options.onDiagnostic?.(new Error(`skipped directory: ${
|
|
5320
|
+
options.onDiagnostic?.(new Error(`skipped directory: ${basename2(lexicalPath)}`));
|
|
4904
5321
|
continue;
|
|
4905
5322
|
}
|
|
4906
5323
|
try {
|
|
@@ -4915,7 +5332,7 @@ var scanFiles = async (input, policyInput, options = {}) => {
|
|
|
4915
5332
|
} catch (error) {
|
|
4916
5333
|
if (missing(error))
|
|
4917
5334
|
continue;
|
|
4918
|
-
options.onDiagnostic?.(new Error(`skipped directory: ${
|
|
5335
|
+
options.onDiagnostic?.(new Error(`skipped directory: ${basename2(lexicalPath)}`));
|
|
4919
5336
|
}
|
|
4920
5337
|
} else if (targetStat.isFile() && !entry.isDirectory()) {
|
|
4921
5338
|
if (!options.topologyOnly) {
|
|
@@ -4927,7 +5344,7 @@ var scanFiles = async (input, policyInput, options = {}) => {
|
|
|
4927
5344
|
} catch (error) {
|
|
4928
5345
|
if (missing(error))
|
|
4929
5346
|
continue;
|
|
4930
|
-
options.onDiagnostic?.(new Error(`skipped directory: ${
|
|
5347
|
+
options.onDiagnostic?.(new Error(`skipped directory: ${basename2(lexicalPath)}`));
|
|
4931
5348
|
}
|
|
4932
5349
|
}
|
|
4933
5350
|
};
|
|
@@ -5022,7 +5439,7 @@ var projectFiles = (files, query, order = "tree", search = fuzzySearch) => {
|
|
|
5022
5439
|
// ../../packages/ui/src/file-navigator/core/engine.ts
|
|
5023
5440
|
var STREAM_SNAPSHOT = 256;
|
|
5024
5441
|
var errorValue = (value) => value instanceof Error ? value : new Error(String(value));
|
|
5025
|
-
var afterPaint = (value) => Promise.resolve(value).then(() => new Promise((
|
|
5442
|
+
var afterPaint = (value) => Promise.resolve(value).then(() => new Promise((resolve4) => setImmediate(resolve4)));
|
|
5026
5443
|
var byPath = (files) => new Map(files.map((file) => [file.relativePath, file]));
|
|
5027
5444
|
var publication = (previous, next) => {
|
|
5028
5445
|
const added = [], changed = [], removed = [];
|
|
@@ -5491,13 +5908,13 @@ class FileNavigatorCore {
|
|
|
5491
5908
|
if (path === this.#root)
|
|
5492
5909
|
return "";
|
|
5493
5910
|
if (path.startsWith(this.#root + "/"))
|
|
5494
|
-
return
|
|
5911
|
+
return relative3(this.#root, resolve3(path));
|
|
5495
5912
|
if (generation)
|
|
5496
5913
|
for (const directory of generation.watchDirectories)
|
|
5497
5914
|
for (const lexical of directory.lexicalPaths) {
|
|
5498
|
-
const suffix =
|
|
5915
|
+
const suffix = relative3(directory.physicalPath, path);
|
|
5499
5916
|
if (suffix === "" || !suffix.startsWith("..") && !suffix.startsWith("/"))
|
|
5500
|
-
return
|
|
5917
|
+
return relative3(this.#root, resolve3(lexical, suffix));
|
|
5501
5918
|
}
|
|
5502
5919
|
return path;
|
|
5503
5920
|
}
|
|
@@ -5592,17 +6009,17 @@ var formatFileRow = (path, width) => {
|
|
|
5592
6009
|
const slash2 = path.lastIndexOf("/");
|
|
5593
6010
|
if (slash2 < 0)
|
|
5594
6011
|
return { basename: fitTail(path, width), parent: "", separator: "" };
|
|
5595
|
-
const
|
|
6012
|
+
const basename3 = path.slice(slash2 + 1);
|
|
5596
6013
|
const parent = path.slice(0, slash2);
|
|
5597
|
-
if (
|
|
5598
|
-
return { basename: fitTail(
|
|
6014
|
+
if (basename3.length >= width) {
|
|
6015
|
+
return { basename: fitTail(basename3, width), parent: "", separator: "" };
|
|
5599
6016
|
}
|
|
5600
|
-
const remaining = width -
|
|
6017
|
+
const remaining = width - basename3.length - SEPARATOR.length;
|
|
5601
6018
|
if (remaining < MIN_PARENT_BUDGET || parent.length === 0) {
|
|
5602
|
-
return { basename:
|
|
6019
|
+
return { basename: basename3, parent: "", separator: "" };
|
|
5603
6020
|
}
|
|
5604
6021
|
return {
|
|
5605
|
-
basename:
|
|
6022
|
+
basename: basename3,
|
|
5606
6023
|
parent: middleTruncate2(parent, remaining),
|
|
5607
6024
|
separator: SEPARATOR
|
|
5608
6025
|
};
|
|
@@ -5613,13 +6030,13 @@ var DefaultFileRow = ({
|
|
|
5613
6030
|
width,
|
|
5614
6031
|
theme
|
|
5615
6032
|
}) => {
|
|
5616
|
-
const { basename:
|
|
6033
|
+
const { basename: basename3, parent, separator } = formatFileRow(path, width);
|
|
5617
6034
|
return /* @__PURE__ */ jsxDEV9("text", {
|
|
5618
6035
|
wrapMode: "none",
|
|
5619
6036
|
children: [
|
|
5620
6037
|
/* @__PURE__ */ jsxDEV9("span", {
|
|
5621
6038
|
style: { fg: selected ? theme.selectedListItemText : theme.text },
|
|
5622
|
-
children:
|
|
6039
|
+
children: basename3
|
|
5623
6040
|
}, undefined, false, undefined, this),
|
|
5624
6041
|
parent !== "" && /* @__PURE__ */ jsxDEV9("span", {
|
|
5625
6042
|
style: { fg: theme.textMuted },
|
|
@@ -5698,7 +6115,7 @@ var Component = (props, ref) => {
|
|
|
5698
6115
|
const timerRef = useRef2(null);
|
|
5699
6116
|
const redrawRef = useRef2(() => {});
|
|
5700
6117
|
const flushQueryRef = useRef2(() => snapshotOf(null, normalizedRoot, true, null));
|
|
5701
|
-
|
|
6118
|
+
useLayoutEffect2(() => {
|
|
5702
6119
|
activeRootRef.current = normalizedRoot;
|
|
5703
6120
|
callbacks.current = { onSelectionChange, onSelectedFileInvalidated, onDiagnostic, onSnapshot };
|
|
5704
6121
|
}, [normalizedRoot, onSelectionChange, onSelectedFileInvalidated, onDiagnostic, onSnapshot]);
|
|
@@ -5964,6 +6381,7 @@ var queryWouldShowRelativePath = (query, relativePath) => {
|
|
|
5964
6381
|
|
|
5965
6382
|
// src/new-file/resolveName.ts
|
|
5966
6383
|
var MD_SUFFIX = /\.md$/i;
|
|
6384
|
+
var pathShapedError = (mode) => mode === "rename" ? "name must be a single file name" : "name must be a single file in the discovery root";
|
|
5967
6385
|
var hasControlChar = (raw) => {
|
|
5968
6386
|
for (let i = 0;i < raw.length; i++) {
|
|
5969
6387
|
const code = raw.charCodeAt(i);
|
|
@@ -5972,14 +6390,14 @@ var hasControlChar = (raw) => {
|
|
|
5972
6390
|
}
|
|
5973
6391
|
return false;
|
|
5974
6392
|
};
|
|
5975
|
-
var
|
|
6393
|
+
var resolveMarkdownBasename = (raw, mode = "new-file") => {
|
|
5976
6394
|
if (hasControlChar(raw))
|
|
5977
6395
|
return { ok: false, error: "name contains invalid characters" };
|
|
5978
6396
|
const trimmed = raw.trim();
|
|
5979
6397
|
if (trimmed.length === 0)
|
|
5980
6398
|
return { ok: false, error: "name required" };
|
|
5981
6399
|
if (trimmed.includes("/") || trimmed.includes("\\") || trimmed === "." || trimmed === "..") {
|
|
5982
|
-
return { ok: false, error:
|
|
6400
|
+
return { ok: false, error: pathShapedError(mode) };
|
|
5983
6401
|
}
|
|
5984
6402
|
if (trimmed.startsWith("."))
|
|
5985
6403
|
return { ok: false, error: "hidden names aren't supported yet" };
|
|
@@ -5992,14 +6410,59 @@ var resolveNewFileName = (raw) => {
|
|
|
5992
6410
|
warnings: ["extension will be saved as .md"]
|
|
5993
6411
|
};
|
|
5994
6412
|
}
|
|
5995
|
-
const
|
|
6413
|
+
const basename3 = `${trimmed}.md`;
|
|
5996
6414
|
return {
|
|
5997
6415
|
ok: true,
|
|
5998
|
-
basename:
|
|
5999
|
-
warnings: trimmed.includes(".") ? [`will be created as ${
|
|
6416
|
+
basename: basename3,
|
|
6417
|
+
warnings: trimmed.includes(".") ? [`will be created as ${basename3}`] : []
|
|
6000
6418
|
};
|
|
6001
6419
|
};
|
|
6002
6420
|
|
|
6421
|
+
// src/prompts/helpers.ts
|
|
6422
|
+
import { readdirSync } from "fs";
|
|
6423
|
+
import { dirname as dirname2 } from "path";
|
|
6424
|
+
var parentRelativeOf = (relativePath) => {
|
|
6425
|
+
const idx = relativePath.lastIndexOf("/");
|
|
6426
|
+
return idx === -1 ? "" : relativePath.slice(0, idx);
|
|
6427
|
+
};
|
|
6428
|
+
var destinationRelativePath = (parentRelative, newBasename) => parentRelative.length === 0 ? newBasename : `${parentRelative}/${newBasename}`;
|
|
6429
|
+
var captureActionTarget = (file) => ({
|
|
6430
|
+
absolutePath: file.absolutePath,
|
|
6431
|
+
parentDir: dirname2(file.absolutePath),
|
|
6432
|
+
relativePath: file.relativePath,
|
|
6433
|
+
basename: file.basename,
|
|
6434
|
+
parentRelative: parentRelativeOf(file.relativePath)
|
|
6435
|
+
});
|
|
6436
|
+
var siblingExistsExact = (parentDir, name, exceptBasename) => {
|
|
6437
|
+
try {
|
|
6438
|
+
const entries = readdirSync(parentDir);
|
|
6439
|
+
return entries.includes(name) && name !== exceptBasename;
|
|
6440
|
+
} catch {
|
|
6441
|
+
return false;
|
|
6442
|
+
}
|
|
6443
|
+
};
|
|
6444
|
+
var promptLiveStatus = (raw, mode, query, matchRelativePath, destExists) => {
|
|
6445
|
+
const resolved = resolveMarkdownBasename(raw, mode);
|
|
6446
|
+
if (!resolved.ok) {
|
|
6447
|
+
if (resolved.error === "name required")
|
|
6448
|
+
return null;
|
|
6449
|
+
return { kind: "error", lines: [resolved.error] };
|
|
6450
|
+
}
|
|
6451
|
+
const lines = [...resolved.warnings];
|
|
6452
|
+
if (query.length > 0 && !queryWouldShowRelativePath(query, matchRelativePath)) {
|
|
6453
|
+
lines.push(`filter will change to ${resolved.basename}`);
|
|
6454
|
+
}
|
|
6455
|
+
if (destExists)
|
|
6456
|
+
lines.push("already exists");
|
|
6457
|
+
if (lines.length === 0)
|
|
6458
|
+
return null;
|
|
6459
|
+
return { kind: "warning", lines };
|
|
6460
|
+
};
|
|
6461
|
+
var retargetPreviewIfNeeded = (preview, sourcePath, destPath) => {
|
|
6462
|
+
if (preview?.currentTarget() === sourcePath)
|
|
6463
|
+
preview.setTarget(destPath);
|
|
6464
|
+
};
|
|
6465
|
+
|
|
6003
6466
|
// src/keymap/keymap.ts
|
|
6004
6467
|
var parseChord = (raw) => {
|
|
6005
6468
|
const parts = raw.toLowerCase().split("+");
|
|
@@ -6075,7 +6538,9 @@ var Sidebar2 = ({
|
|
|
6075
6538
|
onSnapshot,
|
|
6076
6539
|
onSelectedFileInvalidated,
|
|
6077
6540
|
onDiagnostic,
|
|
6078
|
-
order = "recently-modified"
|
|
6541
|
+
order = "recently-modified",
|
|
6542
|
+
onFilterInput,
|
|
6543
|
+
onFilterEditingReady
|
|
6079
6544
|
}) => {
|
|
6080
6545
|
const headerVisible = snapshot.files.length > 0 || discoveryActive;
|
|
6081
6546
|
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 +6566,9 @@ var Sidebar2 = ({
|
|
|
6101
6566
|
query: filterInput,
|
|
6102
6567
|
editing: filterOpen,
|
|
6103
6568
|
placeholder: "type / to filter",
|
|
6104
|
-
width: rowWidth
|
|
6569
|
+
width: rowWidth,
|
|
6570
|
+
...onFilterInput === undefined ? {} : { onInput: onFilterInput },
|
|
6571
|
+
...onFilterEditingReady === undefined ? {} : { onEditingReady: onFilterEditingReady }
|
|
6105
6572
|
}, undefined, false, undefined, this)
|
|
6106
6573
|
} : {},
|
|
6107
6574
|
...emptyState === undefined ? {} : {
|
|
@@ -6267,7 +6734,7 @@ var openInBrowser = (url) => {
|
|
|
6267
6734
|
};
|
|
6268
6735
|
|
|
6269
6736
|
// src/serve/server.ts
|
|
6270
|
-
import { basename as
|
|
6737
|
+
import { basename as basename3 } from "path";
|
|
6271
6738
|
import { watch } from "fs";
|
|
6272
6739
|
import { readFile as readFile3 } from "fs/promises";
|
|
6273
6740
|
|
|
@@ -6503,7 +6970,7 @@ var startServer = ({ path, port = 0 }) => {
|
|
|
6503
6970
|
}
|
|
6504
6971
|
try {
|
|
6505
6972
|
const md = await readFile3(target, "utf8");
|
|
6506
|
-
const html = renderHtml(md,
|
|
6973
|
+
const html = renderHtml(md, basename3(target));
|
|
6507
6974
|
return new Response(html, {
|
|
6508
6975
|
headers: {
|
|
6509
6976
|
"content-type": "text/html; charset=utf-8",
|
|
@@ -12542,218 +13009,98 @@ var bundled = [
|
|
|
12542
13009
|
{ id: "rosepine", json: rosepine_default },
|
|
12543
13010
|
{ id: "solarized", json: solarized_default },
|
|
12544
13011
|
{ id: "synthwave84", json: synthwave84_default },
|
|
12545
|
-
{ id: "tokyonight", json: tokyonight_default },
|
|
12546
|
-
{ id: "vercel", json: vercel_default },
|
|
12547
|
-
{ id: "vesper", json: vesper_default },
|
|
12548
|
-
{ id: "zenburn", json: zenburn_default }
|
|
12549
|
-
];
|
|
12550
|
-
var toDefinition = (id, raw) => {
|
|
12551
|
-
if (!isThemeJson(raw))
|
|
12552
|
-
return null;
|
|
12553
|
-
const json = raw;
|
|
12554
|
-
return { id, name: json.name ?? id, source: json };
|
|
12555
|
-
};
|
|
12556
|
-
var loadBundledThemes = () => {
|
|
12557
|
-
const map = new Map;
|
|
12558
|
-
for (const { id, json } of bundled) {
|
|
12559
|
-
const def = toDefinition(id, json);
|
|
12560
|
-
if (def)
|
|
12561
|
-
map.set(id, def);
|
|
12562
|
-
}
|
|
12563
|
-
return map;
|
|
12564
|
-
};
|
|
12565
|
-
|
|
12566
|
-
// src/theme/registry.ts
|
|
12567
|
-
var map = loadBundledThemes();
|
|
12568
|
-
var themeDefinitions = [...map.values()];
|
|
12569
|
-
var getThemeDefinition = (id) => map.get(id);
|
|
12570
|
-
|
|
12571
|
-
// ../../packages/options/src/decode.ts
|
|
12572
|
-
var isPresent = (value) => value !== undefined && value !== null;
|
|
12573
|
-
var stringifyReceived = (value) => {
|
|
12574
|
-
if (typeof value === "string")
|
|
12575
|
-
return JSON.stringify(value);
|
|
12576
|
-
if (typeof value === "number" || typeof value === "boolean")
|
|
12577
|
-
return JSON.stringify(value);
|
|
12578
|
-
if (value === null)
|
|
12579
|
-
return "null";
|
|
12580
|
-
return typeof value;
|
|
12581
|
-
};
|
|
12582
|
-
var formatResolveError = (error, filePath) => {
|
|
12583
|
-
if (error.layer === "file") {
|
|
12584
|
-
const where = filePath === undefined ? "" : ` in ${filePath}`;
|
|
12585
|
-
return `invalid value for ${error.key}${where}: ${error.message}`;
|
|
12586
|
-
}
|
|
12587
|
-
return `${error.key}: ${error.message}, got ${stringifyReceived(error.received)}`;
|
|
12588
|
-
};
|
|
12589
|
-
var fail = (key, layer, message, received) => ({
|
|
12590
|
-
key,
|
|
12591
|
-
layer,
|
|
12592
|
-
message,
|
|
12593
|
-
received
|
|
12594
|
-
});
|
|
12595
|
-
var booleanMessage = "expected true or false";
|
|
12596
|
-
var numberMessage = (spec) => {
|
|
12597
|
-
if (spec.integer === true && spec.min === 1 && spec.max === undefined) {
|
|
12598
|
-
return "expected a positive integer";
|
|
12599
|
-
}
|
|
12600
|
-
if (spec.integer === true)
|
|
12601
|
-
return "expected an integer";
|
|
12602
|
-
return "expected a number";
|
|
12603
|
-
};
|
|
12604
|
-
var inRange = (value, spec) => {
|
|
12605
|
-
if (spec.min !== undefined && value < spec.min)
|
|
12606
|
-
return false;
|
|
12607
|
-
if (spec.max !== undefined && value > spec.max)
|
|
12608
|
-
return false;
|
|
12609
|
-
if (spec.integer === true && !Number.isSafeInteger(value))
|
|
12610
|
-
return false;
|
|
12611
|
-
if (!Number.isFinite(value))
|
|
12612
|
-
return false;
|
|
12613
|
-
return true;
|
|
12614
|
-
};
|
|
12615
|
-
var decodeBoolean = (key, layer, value) => {
|
|
12616
|
-
if (layer === "env") {
|
|
12617
|
-
if (value === "true")
|
|
12618
|
-
return { ok: true, value: true };
|
|
12619
|
-
if (value === "false")
|
|
12620
|
-
return { ok: true, value: false };
|
|
12621
|
-
return { ok: false, error: fail(key, layer, booleanMessage, value) };
|
|
12622
|
-
}
|
|
12623
|
-
if (typeof value === "boolean")
|
|
12624
|
-
return { ok: true, value };
|
|
12625
|
-
return { ok: false, error: fail(key, layer, booleanMessage, value) };
|
|
12626
|
-
};
|
|
12627
|
-
var decodeNumber = (key, layer, value, spec) => {
|
|
12628
|
-
const message = numberMessage(spec);
|
|
12629
|
-
if (layer === "env") {
|
|
12630
|
-
if (typeof value !== "string" || !/^-?\d+(\.\d+)?$/.test(value)) {
|
|
12631
|
-
return { ok: false, error: fail(key, layer, message, value) };
|
|
12632
|
-
}
|
|
12633
|
-
if (spec.integer === true && !/^-?\d+$/.test(value)) {
|
|
12634
|
-
return { ok: false, error: fail(key, layer, message, value) };
|
|
12635
|
-
}
|
|
12636
|
-
const parsed = spec.integer === true ? Number.parseInt(value, 10) : Number.parseFloat(value);
|
|
12637
|
-
if (!inRange(parsed, spec))
|
|
12638
|
-
return { ok: false, error: fail(key, layer, message, value) };
|
|
12639
|
-
return { ok: true, value: parsed };
|
|
12640
|
-
}
|
|
12641
|
-
if (typeof value !== "number" || !inRange(value, spec)) {
|
|
12642
|
-
return { ok: false, error: fail(key, layer, message, value) };
|
|
12643
|
-
}
|
|
12644
|
-
return { ok: true, value };
|
|
13012
|
+
{ id: "tokyonight", json: tokyonight_default },
|
|
13013
|
+
{ id: "vercel", json: vercel_default },
|
|
13014
|
+
{ id: "vesper", json: vesper_default },
|
|
13015
|
+
{ id: "zenburn", json: zenburn_default }
|
|
13016
|
+
];
|
|
13017
|
+
var toDefinition = (id, raw) => {
|
|
13018
|
+
if (!isThemeJson(raw))
|
|
13019
|
+
return null;
|
|
13020
|
+
const json = raw;
|
|
13021
|
+
return { id, name: json.name ?? id, source: json };
|
|
12645
13022
|
};
|
|
12646
|
-
var
|
|
12647
|
-
|
|
12648
|
-
|
|
12649
|
-
|
|
12650
|
-
|
|
12651
|
-
|
|
12652
|
-
return {
|
|
12653
|
-
ok: false,
|
|
12654
|
-
error: fail(key, layer, `expected one of ${spec.choices.join(", ")}`, value)
|
|
12655
|
-
};
|
|
13023
|
+
var loadBundledThemes = () => {
|
|
13024
|
+
const map = new Map;
|
|
13025
|
+
for (const { id, json } of bundled) {
|
|
13026
|
+
const def = toDefinition(id, json);
|
|
13027
|
+
if (def)
|
|
13028
|
+
map.set(id, def);
|
|
12656
13029
|
}
|
|
12657
|
-
return
|
|
12658
|
-
};
|
|
12659
|
-
var decodeValue = (key, spec, layer, value) => {
|
|
12660
|
-
if (spec.type === "boolean")
|
|
12661
|
-
return decodeBoolean(key, layer, value);
|
|
12662
|
-
if (spec.type === "number")
|
|
12663
|
-
return decodeNumber(key, layer, value, spec);
|
|
12664
|
-
return decodeString(key, layer, value, spec);
|
|
13030
|
+
return map;
|
|
12665
13031
|
};
|
|
12666
13032
|
|
|
12667
|
-
//
|
|
12668
|
-
var
|
|
12669
|
-
var
|
|
12670
|
-
|
|
12671
|
-
|
|
12672
|
-
|
|
12673
|
-
|
|
12674
|
-
|
|
12675
|
-
|
|
12676
|
-
|
|
12677
|
-
|
|
12678
|
-
|
|
12679
|
-
|
|
12680
|
-
|
|
12681
|
-
|
|
12682
|
-
|
|
12683
|
-
|
|
12684
|
-
|
|
12685
|
-
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12689
|
-
|
|
13033
|
+
// src/theme/registry.ts
|
|
13034
|
+
var map = loadBundledThemes();
|
|
13035
|
+
var themeDefinitions = [...map.values()];
|
|
13036
|
+
var getThemeDefinition = (id) => map.get(id);
|
|
13037
|
+
|
|
13038
|
+
// src/config/footerControls.ts
|
|
13039
|
+
var footerControlsFromSession = (options, session, handlers = {}) => {
|
|
13040
|
+
const controls = [];
|
|
13041
|
+
for (const key of footerKeys(options.specs)) {
|
|
13042
|
+
const spec = options.specs[key];
|
|
13043
|
+
const footer = spec.footer;
|
|
13044
|
+
if (footer === undefined)
|
|
13045
|
+
continue;
|
|
13046
|
+
const value = session.get(key);
|
|
13047
|
+
const handler = handlers[key];
|
|
13048
|
+
controls.push({
|
|
13049
|
+
id: key,
|
|
13050
|
+
icon: footerControlGlyph(spec, value),
|
|
13051
|
+
variant: "info",
|
|
13052
|
+
active: handler?.active ?? footerControlActive(spec, value),
|
|
13053
|
+
onMouseUp: () => {
|
|
13054
|
+
if (handler?.onActivate !== undefined) {
|
|
13055
|
+
handler.onActivate();
|
|
13056
|
+
return;
|
|
13057
|
+
}
|
|
13058
|
+
const current = session.get(key);
|
|
13059
|
+
session.set(key, nextFooterValue(spec, current));
|
|
13060
|
+
}
|
|
13061
|
+
});
|
|
12690
13062
|
}
|
|
12691
|
-
return
|
|
12692
|
-
};
|
|
12693
|
-
var catalogDefaults = (specs) => {
|
|
12694
|
-
const values = {};
|
|
12695
|
-
for (const key of Object.keys(specs))
|
|
12696
|
-
values[key] = specs[key].default;
|
|
12697
|
-
return values;
|
|
13063
|
+
return controls;
|
|
12698
13064
|
};
|
|
12699
13065
|
|
|
12700
|
-
//
|
|
12701
|
-
var
|
|
12702
|
-
|
|
12703
|
-
|
|
12704
|
-
const
|
|
12705
|
-
|
|
12706
|
-
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12710
|
-
|
|
12711
|
-
|
|
12712
|
-
|
|
12713
|
-
|
|
12714
|
-
|
|
12715
|
-
|
|
12716
|
-
|
|
12717
|
-
|
|
12718
|
-
|
|
12719
|
-
|
|
12720
|
-
|
|
12721
|
-
await options.persist({
|
|
12722
|
-
key,
|
|
12723
|
-
value: decoded.value,
|
|
12724
|
-
values: snapshot
|
|
12725
|
-
});
|
|
12726
|
-
}
|
|
12727
|
-
};
|
|
12728
|
-
return {
|
|
12729
|
-
get,
|
|
12730
|
-
set,
|
|
12731
|
-
subscribe: (listener) => {
|
|
12732
|
-
listeners.add(listener);
|
|
12733
|
-
return () => {
|
|
12734
|
-
listeners.delete(listener);
|
|
12735
|
-
};
|
|
12736
|
-
},
|
|
12737
|
-
get values() {
|
|
12738
|
-
return snapshot;
|
|
13066
|
+
// src/config/footerLabels.ts
|
|
13067
|
+
var uniqueFooterLabels = (ids) => {
|
|
13068
|
+
const used = new Set;
|
|
13069
|
+
const out = {};
|
|
13070
|
+
for (const id of ids) {
|
|
13071
|
+
const compact = id.replace(/[^a-zA-Z0-9]+/g, "").toLowerCase();
|
|
13072
|
+
const source = compact.length > 0 ? compact : "x";
|
|
13073
|
+
let label = source.slice(0, 2).padEnd(2, "x");
|
|
13074
|
+
let offset = 2;
|
|
13075
|
+
while (used.has(label)) {
|
|
13076
|
+
const next = source[offset];
|
|
13077
|
+
if (next !== undefined) {
|
|
13078
|
+
label = `${source[0]}${next}`;
|
|
13079
|
+
offset += 1;
|
|
13080
|
+
} else {
|
|
13081
|
+
label = `${source[0]}${used.size % 10}`;
|
|
13082
|
+
offset += 1;
|
|
13083
|
+
if (offset > source.length + 20) {
|
|
13084
|
+
throw new Error(`unable to allocate unique footer label for ${JSON.stringify(id)}`);
|
|
13085
|
+
}
|
|
13086
|
+
}
|
|
12739
13087
|
}
|
|
12740
|
-
|
|
13088
|
+
used.add(label);
|
|
13089
|
+
out[id] = label;
|
|
13090
|
+
}
|
|
13091
|
+
return out;
|
|
12741
13092
|
};
|
|
12742
13093
|
|
|
12743
|
-
// ../../packages/options/src/define.ts
|
|
12744
|
-
var defineOptions = (specs) => ({
|
|
12745
|
-
specs,
|
|
12746
|
-
defaults: catalogDefaults(specs),
|
|
12747
|
-
resolve: (layers) => resolveOptions(specs, layers),
|
|
12748
|
-
createSession: (values, options) => createSession(specs, values, options)
|
|
12749
|
-
});
|
|
12750
13094
|
// src/config/options.ts
|
|
12751
13095
|
var FILE_NAVIGATOR_ORDERS = ["tree", "recently-modified"];
|
|
13096
|
+
var themeIds = themeDefinitions.map((theme) => theme.id);
|
|
13097
|
+
var themeFooterLabels = uniqueFooterLabels(themeIds);
|
|
12752
13098
|
var houseOptions = defineOptions({
|
|
12753
13099
|
wrap: {
|
|
12754
13100
|
type: "boolean",
|
|
12755
13101
|
default: false,
|
|
12756
|
-
persist: "session"
|
|
13102
|
+
persist: "session",
|
|
13103
|
+
footer: { icon: "W" }
|
|
12757
13104
|
},
|
|
12758
13105
|
width: {
|
|
12759
13106
|
type: "number",
|
|
@@ -12765,7 +13112,8 @@ var houseOptions = defineOptions({
|
|
|
12765
13112
|
type: "string",
|
|
12766
13113
|
default: "opencode",
|
|
12767
13114
|
persist: "file",
|
|
12768
|
-
choices:
|
|
13115
|
+
choices: themeIds,
|
|
13116
|
+
footer: { icon: "T", labels: themeFooterLabels }
|
|
12769
13117
|
},
|
|
12770
13118
|
tone: {
|
|
12771
13119
|
type: "string",
|
|
@@ -12786,17 +13134,25 @@ var houseOptions = defineOptions({
|
|
|
12786
13134
|
order: {
|
|
12787
13135
|
type: "string",
|
|
12788
13136
|
default: "recently-modified",
|
|
12789
|
-
choices: FILE_NAVIGATOR_ORDERS
|
|
13137
|
+
choices: FILE_NAVIGATOR_ORDERS,
|
|
13138
|
+
persist: "session",
|
|
13139
|
+
footer: {
|
|
13140
|
+
icon: "O",
|
|
13141
|
+
labels: {
|
|
13142
|
+
tree: "tr",
|
|
13143
|
+
"recently-modified": "re"
|
|
13144
|
+
}
|
|
13145
|
+
}
|
|
12790
13146
|
}
|
|
12791
13147
|
});
|
|
12792
13148
|
|
|
12793
13149
|
// 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";
|
|
13150
|
+
import { dirname as dirname3 } from "path";
|
|
13151
|
+
import { lstat, mkdir, readFile as readFile4, realpath as realpath2, rename as rename2, unlink, writeFile } from "fs/promises";
|
|
12796
13152
|
|
|
12797
13153
|
// src/config/load.ts
|
|
12798
13154
|
import { homedir } from "os";
|
|
12799
|
-
import { join as
|
|
13155
|
+
import { join as join3 } from "path";
|
|
12800
13156
|
import { Config, ConfigProvider, Effect as Effect2, Schema } from "effect";
|
|
12801
13157
|
|
|
12802
13158
|
// src/discovery/show.ts
|
|
@@ -12933,7 +13289,7 @@ var cliProvider = (overrides) => {
|
|
|
12933
13289
|
entries.push(["show", overrides.show.join(",")]);
|
|
12934
13290
|
return ConfigProvider.fromUnknown(Object.fromEntries(entries));
|
|
12935
13291
|
};
|
|
12936
|
-
var defaultConfigPath = () =>
|
|
13292
|
+
var defaultConfigPath = () => join3(process.env["XDG_CONFIG_HOME"] ?? join3(homedir(), ".config"), "house", "config.toml");
|
|
12937
13293
|
var formatConfigError = (err) => {
|
|
12938
13294
|
if (err instanceof Config.ConfigError) {
|
|
12939
13295
|
const cause = err.cause;
|
|
@@ -13071,8 +13427,8 @@ var updateThemePreferenceToml = (raw, record) => {
|
|
|
13071
13427
|
};
|
|
13072
13428
|
var resolveWritableConfigPath = async (path) => {
|
|
13073
13429
|
try {
|
|
13074
|
-
const
|
|
13075
|
-
if (
|
|
13430
|
+
const stat3 = await lstat(path);
|
|
13431
|
+
if (stat3.isSymbolicLink())
|
|
13076
13432
|
return await realpath2(path);
|
|
13077
13433
|
} catch (err) {
|
|
13078
13434
|
if (err.code !== "ENOENT")
|
|
@@ -13085,7 +13441,7 @@ var writeThemePreference = async (record, path) => {
|
|
|
13085
13441
|
path = targetPath;
|
|
13086
13442
|
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
13087
13443
|
try {
|
|
13088
|
-
await mkdir(
|
|
13444
|
+
await mkdir(dirname3(path), { recursive: true });
|
|
13089
13445
|
let next = `theme = ${encodeTomlString(record.theme)}
|
|
13090
13446
|
tone = ${encodeTomlString(record.tone)}
|
|
13091
13447
|
`;
|
|
@@ -13097,7 +13453,7 @@ tone = ${encodeTomlString(record.tone)}
|
|
|
13097
13453
|
throw err;
|
|
13098
13454
|
}
|
|
13099
13455
|
await writeFile(tmp, next, "utf8");
|
|
13100
|
-
await
|
|
13456
|
+
await rename2(tmp, path);
|
|
13101
13457
|
} catch (err) {
|
|
13102
13458
|
try {
|
|
13103
13459
|
await unlink(tmp);
|
|
@@ -13123,6 +13479,7 @@ var persistHouseOption = async (event, path) => {
|
|
|
13123
13479
|
|
|
13124
13480
|
// src/Browser.tsx
|
|
13125
13481
|
import { jsxDEV as jsxDEV13 } from "@opentui/react/jsx-dev-runtime";
|
|
13482
|
+
var asFileNavigatorOrder = (value) => typeof value === "string" && FILE_NAVIGATOR_ORDERS.includes(value) ? value : "recently-modified";
|
|
13126
13483
|
var defaultReadFile = (path) => Effect3.runPromise(readFileText(path));
|
|
13127
13484
|
var defaultDiscoveryPolicy = {
|
|
13128
13485
|
revision: "house-markdown",
|
|
@@ -13134,25 +13491,16 @@ var FILTER_DEBOUNCE_MS = 50;
|
|
|
13134
13491
|
var RENDERED_PATH_DEBOUNCE_MS = 80;
|
|
13135
13492
|
var NEW_FILE_MEMBERSHIP_TIMEOUT_MS = 2000;
|
|
13136
13493
|
var NEW_FILE_MEMBERSHIP_POLL_MS = 32;
|
|
13137
|
-
var sleep = (ms) => new Promise((
|
|
13494
|
+
var sleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
13138
13495
|
var isSingleCodePoint = (value) => Array.from(value).length === 1;
|
|
13139
13496
|
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}`);
|
|
13497
|
+
var printableFromKey = (key) => {
|
|
13498
|
+
if (key.name === "space")
|
|
13499
|
+
return " ";
|
|
13500
|
+
if (typeof key.name === "string" && isSingleCodePoint(key.name)) {
|
|
13501
|
+
return key.shift ? key.name.toUpperCase() : key.name;
|
|
13150
13502
|
}
|
|
13151
|
-
|
|
13152
|
-
lines.push("already exists");
|
|
13153
|
-
if (lines.length === 0)
|
|
13154
|
-
return null;
|
|
13155
|
-
return { kind: "warning", lines };
|
|
13503
|
+
return null;
|
|
13156
13504
|
};
|
|
13157
13505
|
var isPartialDiscoveryWarning2 = (status) => status?.trimStart().startsWith("scan incomplete:") ?? false;
|
|
13158
13506
|
var noFloatingOverlay = { kind: "none" };
|
|
@@ -13197,7 +13545,9 @@ var Browser = ({
|
|
|
13197
13545
|
order = "recently-modified",
|
|
13198
13546
|
launchEditor = openInEditor,
|
|
13199
13547
|
createEmptyFile = createEmptyFileExclusive,
|
|
13200
|
-
|
|
13548
|
+
renameFile = renameMarkdownFile,
|
|
13549
|
+
newFileMembershipTimeoutMs = NEW_FILE_MEMBERSHIP_TIMEOUT_MS,
|
|
13550
|
+
initialPreviewServer = null
|
|
13201
13551
|
}) => {
|
|
13202
13552
|
const renderer = useRenderer();
|
|
13203
13553
|
const { width, height } = useTerminalDimensions2();
|
|
@@ -13211,15 +13561,17 @@ var Browser = ({
|
|
|
13211
13561
|
wrap: initialWrap,
|
|
13212
13562
|
width: wrapWidth,
|
|
13213
13563
|
theme: theme.id,
|
|
13214
|
-
tone: theme.tone
|
|
13564
|
+
tone: theme.tone,
|
|
13565
|
+
order: asFileNavigatorOrder(order)
|
|
13215
13566
|
}, { persist: persistHouseOption });
|
|
13216
13567
|
}
|
|
13217
13568
|
const wrapEnabled = useSyncExternalStore(optionsSession.current.subscribe, () => optionsSession.current.get("wrap"));
|
|
13569
|
+
const browseOrder = useSyncExternalStore(optionsSession.current.subscribe, () => asFileNavigatorOrder(optionsSession.current.get("order")));
|
|
13218
13570
|
const toggleWrap = () => {
|
|
13219
13571
|
const session = optionsSession.current;
|
|
13220
13572
|
if (session === null)
|
|
13221
13573
|
return;
|
|
13222
|
-
session.set("wrap",
|
|
13574
|
+
session.set("wrap", nextFooterValue(houseOptions.specs.wrap, session.get("wrap")));
|
|
13223
13575
|
};
|
|
13224
13576
|
const [loaded, setLoaded] = useState4(null);
|
|
13225
13577
|
const [error, setError] = useState4(null);
|
|
@@ -13335,22 +13687,30 @@ var Browser = ({
|
|
|
13335
13687
|
const paletteIndexRef = useRef3(0);
|
|
13336
13688
|
const [promptInput, setPromptInput] = useState4("");
|
|
13337
13689
|
const [promptStatus, setPromptStatus] = useState4(null);
|
|
13690
|
+
const [promptPurpose, setPromptPurpose] = useState4("new-file");
|
|
13691
|
+
const [promptContext, setPromptContext] = useState4(undefined);
|
|
13338
13692
|
const promptInputRef = useRef3("");
|
|
13693
|
+
const promptPurposeRef = useRef3("new-file");
|
|
13339
13694
|
const promptFocusBeforeRef = useRef3("sidebar");
|
|
13340
13695
|
const promptSubmittingRef = useRef3(false);
|
|
13341
|
-
const
|
|
13696
|
+
const [promptSubmitting, setPromptSubmitting] = useState4(false);
|
|
13697
|
+
const actionTargetRef = useRef3(null);
|
|
13698
|
+
const promptTaskGenRef = useRef3(0);
|
|
13342
13699
|
const mountedRef = useRef3(true);
|
|
13343
13700
|
const [readerEmptyStateTipRotation, setReaderEmptyStateTipRotation] = useState4(() => nextReaderEmptyStateTipRotation);
|
|
13344
13701
|
const readerEmptyStateVisibleRef = useRef3(false);
|
|
13345
13702
|
const filterOpenRef = useRef3(startInFilter);
|
|
13346
13703
|
const filterInputRef = useRef3(initialQuery);
|
|
13704
|
+
const filterInputReadyRef = useRef3(false);
|
|
13705
|
+
const paletteInputReadyRef = useRef3(false);
|
|
13706
|
+
const promptInputReadyRef = useRef3(false);
|
|
13347
13707
|
const focusRef = useRef3(focus);
|
|
13348
13708
|
const restoreFilterOnSidebarFocusRef = useRef3(startInFilter);
|
|
13349
13709
|
const [footerNotice, setFooterNoticeState] = useState4(null);
|
|
13350
13710
|
const pushFooterNotice = (text, ttlMs = 2000) => setFooterNoticeState({ text, ttlMs });
|
|
13351
|
-
const serverRef = useRef3(
|
|
13711
|
+
const serverRef = useRef3(initialPreviewServer);
|
|
13352
13712
|
const pendingSelectionPathRef = useRef3(null);
|
|
13353
|
-
|
|
13713
|
+
useLayoutEffect3(() => {
|
|
13354
13714
|
if (readerRootRef.current === root)
|
|
13355
13715
|
return;
|
|
13356
13716
|
readerRootRef.current = root;
|
|
@@ -13363,14 +13723,14 @@ var Browser = ({
|
|
|
13363
13723
|
setLoaded(null);
|
|
13364
13724
|
setError(null);
|
|
13365
13725
|
}, [root]);
|
|
13366
|
-
|
|
13726
|
+
useLayoutEffect3(() => () => {
|
|
13367
13727
|
advanceReaderEpoch();
|
|
13368
13728
|
}, []);
|
|
13369
13729
|
useEffect4(() => {
|
|
13370
13730
|
mountedRef.current = true;
|
|
13371
13731
|
return () => {
|
|
13372
13732
|
mountedRef.current = false;
|
|
13373
|
-
|
|
13733
|
+
promptTaskGenRef.current += 1;
|
|
13374
13734
|
newFileEditPathRef.current = null;
|
|
13375
13735
|
serverRef.current?.stop();
|
|
13376
13736
|
serverRef.current = null;
|
|
@@ -13431,6 +13791,14 @@ var Browser = ({
|
|
|
13431
13791
|
rememberAppearance(session.set("theme", next.id));
|
|
13432
13792
|
pushFooterNotice(`theme: ${next.name}`);
|
|
13433
13793
|
};
|
|
13794
|
+
const cycleOrder = () => {
|
|
13795
|
+
const session = optionsSession.current;
|
|
13796
|
+
if (session === null)
|
|
13797
|
+
return;
|
|
13798
|
+
const next = nextFooterValue(houseOptions.specs.order, session.get("order"));
|
|
13799
|
+
session.set("order", next);
|
|
13800
|
+
pushFooterNotice(`order: ${next}`);
|
|
13801
|
+
};
|
|
13434
13802
|
const toggleTone = () => {
|
|
13435
13803
|
const session = optionsSession.current;
|
|
13436
13804
|
if (session === null)
|
|
@@ -13503,18 +13871,32 @@ var Browser = ({
|
|
|
13503
13871
|
const applyPromptInput = (next) => {
|
|
13504
13872
|
promptInputRef.current = next;
|
|
13505
13873
|
setPromptInput(next);
|
|
13506
|
-
const
|
|
13507
|
-
const
|
|
13508
|
-
|
|
13874
|
+
const mode = promptPurposeRef.current;
|
|
13875
|
+
const resolved = resolveMarkdownBasename(next, mode);
|
|
13876
|
+
const target = actionTargetRef.current;
|
|
13877
|
+
let destExists = false;
|
|
13878
|
+
let matchRelative = resolved.ok ? resolved.basename : "";
|
|
13879
|
+
if (resolved.ok && mode === "new-file") {
|
|
13880
|
+
destExists = siblingExistsExact(root, resolved.basename);
|
|
13881
|
+
} else if (resolved.ok && mode === "rename" && target !== null) {
|
|
13882
|
+
matchRelative = destinationRelativePath(target.parentRelative, resolved.basename);
|
|
13883
|
+
destExists = siblingExistsExact(target.parentDir, resolved.basename, target.basename);
|
|
13884
|
+
}
|
|
13885
|
+
setPromptStatus(promptLiveStatus(next, mode, filterInputRef.current, matchRelative, destExists));
|
|
13509
13886
|
};
|
|
13510
|
-
const
|
|
13887
|
+
const closePrompt = (restoreFocus) => {
|
|
13511
13888
|
promptSubmittingRef.current = false;
|
|
13889
|
+
setPromptSubmitting(false);
|
|
13512
13890
|
promptInputRef.current = "";
|
|
13513
13891
|
setPromptInput("");
|
|
13514
13892
|
setPromptStatus(null);
|
|
13893
|
+
setPromptContext(undefined);
|
|
13894
|
+
actionTargetRef.current = null;
|
|
13895
|
+
promptPurposeRef.current = "new-file";
|
|
13896
|
+
setPromptPurpose("new-file");
|
|
13515
13897
|
closeFloatingOverlay();
|
|
13516
13898
|
if (restoreFocus) {
|
|
13517
|
-
|
|
13899
|
+
promptTaskGenRef.current += 1;
|
|
13518
13900
|
const prev = promptFocusBeforeRef.current;
|
|
13519
13901
|
focusRef.current = prev;
|
|
13520
13902
|
setFocus(prev);
|
|
@@ -13581,6 +13963,23 @@ var Browser = ({
|
|
|
13581
13963
|
}
|
|
13582
13964
|
})();
|
|
13583
13965
|
};
|
|
13966
|
+
const beginPrompt = (purpose, initialValue, context) => {
|
|
13967
|
+
promptTaskGenRef.current += 1;
|
|
13968
|
+
promptSubmittingRef.current = false;
|
|
13969
|
+
setPromptSubmitting(false);
|
|
13970
|
+
promptPurposeRef.current = purpose;
|
|
13971
|
+
setPromptPurpose(purpose);
|
|
13972
|
+
setPromptContext(context);
|
|
13973
|
+
promptInputRef.current = initialValue;
|
|
13974
|
+
setPromptInput(initialValue);
|
|
13975
|
+
promptFocusBeforeRef.current = focusRef.current;
|
|
13976
|
+
if (filterOpenRef.current) {
|
|
13977
|
+
filterOpenRef.current = false;
|
|
13978
|
+
setFilterOpen(false);
|
|
13979
|
+
}
|
|
13980
|
+
dispatchFloatingOverlay({ type: "open-prompt" });
|
|
13981
|
+
applyPromptInput(initialValue);
|
|
13982
|
+
};
|
|
13584
13983
|
const openNewFilePrompt = () => {
|
|
13585
13984
|
const editor = resolveEditor(process.env);
|
|
13586
13985
|
if (!editor) {
|
|
@@ -13591,41 +13990,68 @@ var Browser = ({
|
|
|
13591
13990
|
pushFooterNotice("editor unavailable in this environment");
|
|
13592
13991
|
return;
|
|
13593
13992
|
}
|
|
13594
|
-
|
|
13595
|
-
|
|
13596
|
-
|
|
13597
|
-
|
|
13598
|
-
|
|
13599
|
-
|
|
13600
|
-
|
|
13601
|
-
|
|
13602
|
-
|
|
13993
|
+
actionTargetRef.current = null;
|
|
13994
|
+
beginPrompt("new-file", "", undefined);
|
|
13995
|
+
};
|
|
13996
|
+
const openRenamePrompt = () => {
|
|
13997
|
+
const file = navigator2.getSnapshot().selectedFile;
|
|
13998
|
+
if (!file)
|
|
13999
|
+
return;
|
|
14000
|
+
actionTargetRef.current = captureActionTarget(file);
|
|
14001
|
+
beginPrompt("rename", file.basename, file.relativePath);
|
|
14002
|
+
};
|
|
14003
|
+
const waitForMembership = async (opts) => {
|
|
14004
|
+
const stillWaiting = () => opts.task === promptTaskGenRef.current && mountedRef.current && opts.isArmed();
|
|
14005
|
+
await navigator2.refresh();
|
|
14006
|
+
const deadline = Date.now() + newFileMembershipTimeoutMs;
|
|
14007
|
+
while (Date.now() < deadline) {
|
|
14008
|
+
if (!stillWaiting())
|
|
14009
|
+
return;
|
|
14010
|
+
const snap = navigator2.getSnapshot();
|
|
14011
|
+
if (snap.files.some((file) => file.absolutePath === opts.dest)) {
|
|
14012
|
+
const selectedSnapshot = navigator2.selectPath(opts.dest);
|
|
14013
|
+
if (!stillWaiting())
|
|
14014
|
+
return;
|
|
14015
|
+
if (selectedSnapshot.selectedFile?.absolutePath === opts.dest) {
|
|
14016
|
+
pendingSelectionPathRef.current = null;
|
|
14017
|
+
opts.disarm();
|
|
14018
|
+
opts.onReady?.();
|
|
14019
|
+
return;
|
|
14020
|
+
}
|
|
14021
|
+
}
|
|
14022
|
+
await sleep(NEW_FILE_MEMBERSHIP_POLL_MS);
|
|
13603
14023
|
}
|
|
13604
|
-
|
|
14024
|
+
if (!stillWaiting())
|
|
14025
|
+
return;
|
|
14026
|
+
pendingSelectionPathRef.current = null;
|
|
14027
|
+
opts.disarm();
|
|
14028
|
+
pushFooterNotice(opts.timeoutNotice);
|
|
13605
14029
|
};
|
|
13606
14030
|
const submitNewFile = () => {
|
|
13607
14031
|
if (promptSubmittingRef.current)
|
|
13608
14032
|
return;
|
|
13609
14033
|
const raw = promptInputRef.current;
|
|
13610
|
-
const resolved =
|
|
14034
|
+
const resolved = resolveMarkdownBasename(raw, "new-file");
|
|
13611
14035
|
if (!resolved.ok) {
|
|
13612
14036
|
setPromptStatus({ kind: "error", lines: [resolved.error] });
|
|
13613
14037
|
return;
|
|
13614
14038
|
}
|
|
13615
14039
|
promptSubmittingRef.current = true;
|
|
13616
|
-
|
|
13617
|
-
const
|
|
14040
|
+
setPromptSubmitting(true);
|
|
14041
|
+
const dest = join4(root, resolved.basename);
|
|
14042
|
+
const task = promptTaskGenRef.current;
|
|
13618
14043
|
(async () => {
|
|
13619
14044
|
const created = await createEmptyFile(dest);
|
|
13620
14045
|
if (!mountedRef.current)
|
|
13621
14046
|
return;
|
|
13622
|
-
if (task !==
|
|
14047
|
+
if (task !== promptTaskGenRef.current) {
|
|
13623
14048
|
if (created.ok)
|
|
13624
14049
|
pushFooterNotice(`created ${resolved.basename}`);
|
|
13625
14050
|
return;
|
|
13626
14051
|
}
|
|
13627
14052
|
if (!created.ok) {
|
|
13628
14053
|
promptSubmittingRef.current = false;
|
|
14054
|
+
setPromptSubmitting(false);
|
|
13629
14055
|
setPromptStatus({
|
|
13630
14056
|
kind: "error",
|
|
13631
14057
|
lines: [
|
|
@@ -13634,7 +14060,7 @@ var Browser = ({
|
|
|
13634
14060
|
});
|
|
13635
14061
|
return;
|
|
13636
14062
|
}
|
|
13637
|
-
|
|
14063
|
+
closePrompt(false);
|
|
13638
14064
|
const query = filterInputRef.current;
|
|
13639
14065
|
if (query.length > 0 && !queryWouldShowRelativePath(query, resolved.basename)) {
|
|
13640
14066
|
filterInputRef.current = resolved.basename;
|
|
@@ -13643,33 +14069,93 @@ var Browser = ({
|
|
|
13643
14069
|
}
|
|
13644
14070
|
pendingSelectionPathRef.current = dest;
|
|
13645
14071
|
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;
|
|
14072
|
+
await waitForMembership({
|
|
14073
|
+
dest,
|
|
14074
|
+
task,
|
|
14075
|
+
isArmed: () => newFileEditPathRef.current === dest,
|
|
14076
|
+
disarm: () => {
|
|
14077
|
+
if (newFileEditPathRef.current === dest)
|
|
13659
14078
|
newFileEditPathRef.current = null;
|
|
13660
|
-
|
|
13661
|
-
|
|
13662
|
-
|
|
14079
|
+
},
|
|
14080
|
+
onReady: () => editCurrent(),
|
|
14081
|
+
timeoutNotice: `created ${resolved.basename}, but it isn't in the file list`
|
|
14082
|
+
});
|
|
14083
|
+
})();
|
|
14084
|
+
};
|
|
14085
|
+
const submitRename = () => {
|
|
14086
|
+
if (promptSubmittingRef.current)
|
|
14087
|
+
return;
|
|
14088
|
+
const target = actionTargetRef.current;
|
|
14089
|
+
if (!target)
|
|
14090
|
+
return;
|
|
14091
|
+
const raw = promptInputRef.current;
|
|
14092
|
+
const resolved = resolveMarkdownBasename(raw, "rename");
|
|
14093
|
+
if (!resolved.ok) {
|
|
14094
|
+
setPromptStatus({ kind: "error", lines: [resolved.error] });
|
|
14095
|
+
return;
|
|
14096
|
+
}
|
|
14097
|
+
if (resolved.basename === target.basename) {
|
|
14098
|
+
closePrompt(true);
|
|
14099
|
+
return;
|
|
14100
|
+
}
|
|
14101
|
+
promptSubmittingRef.current = true;
|
|
14102
|
+
setPromptSubmitting(true);
|
|
14103
|
+
const dest = join4(target.parentDir, resolved.basename);
|
|
14104
|
+
const destRelative = destinationRelativePath(target.parentRelative, resolved.basename);
|
|
14105
|
+
const task = promptTaskGenRef.current;
|
|
14106
|
+
const sourcePath = target.absolutePath;
|
|
14107
|
+
(async () => {
|
|
14108
|
+
const renamed = await renameFile({
|
|
14109
|
+
discoveryRoot: root,
|
|
14110
|
+
sourcePath,
|
|
14111
|
+
parentDir: target.parentDir,
|
|
14112
|
+
newBasename: resolved.basename
|
|
14113
|
+
});
|
|
14114
|
+
if (!mountedRef.current)
|
|
14115
|
+
return;
|
|
14116
|
+
if (task !== promptTaskGenRef.current) {
|
|
14117
|
+
if (renamed.ok && !renamed.noop) {
|
|
14118
|
+
pushFooterNotice(`renamed to ${resolved.basename}`);
|
|
13663
14119
|
}
|
|
13664
|
-
|
|
14120
|
+
return;
|
|
13665
14121
|
}
|
|
13666
|
-
if (!
|
|
14122
|
+
if (!renamed.ok) {
|
|
14123
|
+
promptSubmittingRef.current = false;
|
|
14124
|
+
setPromptSubmitting(false);
|
|
14125
|
+
setPromptStatus({
|
|
14126
|
+
kind: "error",
|
|
14127
|
+
lines: [renamed.reason === "already-exists" ? "already exists" : renamed.message]
|
|
14128
|
+
});
|
|
13667
14129
|
return;
|
|
13668
|
-
|
|
13669
|
-
|
|
13670
|
-
|
|
14130
|
+
}
|
|
14131
|
+
closePrompt(false);
|
|
14132
|
+
const query = filterInputRef.current;
|
|
14133
|
+
if (query.length > 0 && !queryWouldShowRelativePath(query, destRelative)) {
|
|
14134
|
+
filterInputRef.current = resolved.basename;
|
|
14135
|
+
setFilterInput(resolved.basename);
|
|
14136
|
+
navigator2.flushSearch(resolved.basename);
|
|
14137
|
+
}
|
|
14138
|
+
retargetPreviewIfNeeded(serverRef.current, sourcePath, dest);
|
|
14139
|
+
pendingSelectionPathRef.current = dest;
|
|
14140
|
+
await waitForMembership({
|
|
14141
|
+
dest,
|
|
14142
|
+
task,
|
|
14143
|
+
isArmed: () => pendingSelectionPathRef.current === dest,
|
|
14144
|
+
disarm: () => {
|
|
14145
|
+
if (pendingSelectionPathRef.current === dest)
|
|
14146
|
+
pendingSelectionPathRef.current = null;
|
|
14147
|
+
},
|
|
14148
|
+
onReady: null,
|
|
14149
|
+
timeoutNotice: `renamed to ${resolved.basename}, but it isn't in the file list`
|
|
14150
|
+
});
|
|
13671
14151
|
})();
|
|
13672
14152
|
};
|
|
14153
|
+
const submitPrompt = () => {
|
|
14154
|
+
if (promptPurposeRef.current === "rename")
|
|
14155
|
+
submitRename();
|
|
14156
|
+
else
|
|
14157
|
+
submitNewFile();
|
|
14158
|
+
};
|
|
13673
14159
|
const ctx = {
|
|
13674
14160
|
files: displayedFiles,
|
|
13675
14161
|
hasSelected: selected != null,
|
|
@@ -13784,6 +14270,7 @@ var Browser = ({
|
|
|
13784
14270
|
},
|
|
13785
14271
|
editCurrent,
|
|
13786
14272
|
openNewFilePrompt,
|
|
14273
|
+
openRenamePrompt,
|
|
13787
14274
|
copyCurrentContents: () => {
|
|
13788
14275
|
const file = navigator2.getSnapshot().selectedFile;
|
|
13789
14276
|
if (!file)
|
|
@@ -13822,77 +14309,88 @@ var Browser = ({
|
|
|
13822
14309
|
const allCommands = buildCommands(ctx);
|
|
13823
14310
|
const filtered = orderCommandsForPalette(filterCommands(allCommands, paletteQueryRef.current));
|
|
13824
14311
|
if (key.name === "escape") {
|
|
14312
|
+
key.preventDefault();
|
|
13825
14313
|
closePalette();
|
|
13826
14314
|
return;
|
|
13827
14315
|
}
|
|
13828
14316
|
if (key.name === "return") {
|
|
14317
|
+
key.preventDefault();
|
|
13829
14318
|
const picked = filtered[clampSelectedIndex(paletteIndexRef.current, filtered)];
|
|
13830
14319
|
closePalette();
|
|
13831
14320
|
picked?.run();
|
|
13832
14321
|
return;
|
|
13833
14322
|
}
|
|
13834
14323
|
if (key.name === "up") {
|
|
14324
|
+
key.preventDefault();
|
|
13835
14325
|
setPaletteIndexSync(Math.max(0, paletteIndexRef.current - 1));
|
|
13836
14326
|
return;
|
|
13837
14327
|
}
|
|
13838
14328
|
if (key.name === "down") {
|
|
14329
|
+
key.preventDefault();
|
|
13839
14330
|
setPaletteIndexSync(Math.min(Math.max(0, filtered.length - 1), paletteIndexRef.current + 1));
|
|
13840
14331
|
return;
|
|
13841
14332
|
}
|
|
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
14333
|
if (key.ctrl && !key.meta && key.name === "p") {
|
|
14334
|
+
key.preventDefault();
|
|
13851
14335
|
closePalette();
|
|
13852
14336
|
return;
|
|
13853
14337
|
}
|
|
13854
|
-
if (
|
|
13855
|
-
|
|
13856
|
-
|
|
13857
|
-
|
|
13858
|
-
|
|
13859
|
-
|
|
13860
|
-
|
|
13861
|
-
|
|
13862
|
-
|
|
13863
|
-
|
|
13864
|
-
|
|
13865
|
-
|
|
14338
|
+
if (!paletteInputReadyRef.current) {
|
|
14339
|
+
if (key.name === "backspace" || key.name === "delete") {
|
|
14340
|
+
if (paletteQueryRef.current.length === 0)
|
|
14341
|
+
return;
|
|
14342
|
+
paletteQueryRef.current = paletteQueryRef.current.slice(0, -1);
|
|
14343
|
+
setPaletteQuery(paletteQueryRef.current);
|
|
14344
|
+
setPaletteIndexSync(0);
|
|
14345
|
+
return;
|
|
14346
|
+
}
|
|
14347
|
+
if (key.ctrl || key.meta)
|
|
14348
|
+
return;
|
|
14349
|
+
const char = printableFromKey(key);
|
|
14350
|
+
if (char !== null) {
|
|
14351
|
+
paletteQueryRef.current = paletteQueryRef.current + char;
|
|
14352
|
+
setPaletteQuery(paletteQueryRef.current);
|
|
14353
|
+
setPaletteIndexSync(0);
|
|
14354
|
+
}
|
|
13866
14355
|
}
|
|
13867
14356
|
return;
|
|
13868
14357
|
}
|
|
13869
14358
|
if (floatingOverlayRef.current.kind === "prompt") {
|
|
13870
14359
|
if (key.name === "escape") {
|
|
13871
|
-
|
|
14360
|
+
key.preventDefault();
|
|
14361
|
+
closePrompt(true);
|
|
13872
14362
|
return;
|
|
13873
14363
|
}
|
|
13874
|
-
if (promptSubmittingRef.current)
|
|
14364
|
+
if (promptSubmittingRef.current) {
|
|
14365
|
+
key.preventDefault();
|
|
13875
14366
|
return;
|
|
14367
|
+
}
|
|
13876
14368
|
if (key.name === "return") {
|
|
13877
|
-
|
|
14369
|
+
key.preventDefault();
|
|
14370
|
+
submitPrompt();
|
|
13878
14371
|
return;
|
|
13879
14372
|
}
|
|
13880
|
-
if (key.name === "
|
|
13881
|
-
|
|
13882
|
-
return;
|
|
13883
|
-
applyPromptInput(dropLastCodePoint(promptInputRef.current));
|
|
14373
|
+
if (key.name === "up" || key.name === "down") {
|
|
14374
|
+
key.preventDefault();
|
|
13884
14375
|
return;
|
|
13885
14376
|
}
|
|
13886
|
-
if (key.ctrl
|
|
14377
|
+
if (key.ctrl && !key.meta && key.name === "p") {
|
|
14378
|
+
key.preventDefault();
|
|
13887
14379
|
return;
|
|
13888
|
-
|
|
13889
|
-
if (
|
|
13890
|
-
|
|
13891
|
-
|
|
13892
|
-
|
|
13893
|
-
|
|
13894
|
-
|
|
13895
|
-
|
|
14380
|
+
}
|
|
14381
|
+
if (!promptInputReadyRef.current) {
|
|
14382
|
+
if (key.name === "backspace" || key.name === "delete") {
|
|
14383
|
+
if (promptInputRef.current.length === 0)
|
|
14384
|
+
return;
|
|
14385
|
+
applyPromptInput(dropLastCodePoint(promptInputRef.current));
|
|
14386
|
+
return;
|
|
14387
|
+
}
|
|
14388
|
+
if (key.ctrl || key.meta)
|
|
14389
|
+
return;
|
|
14390
|
+
const char = printableFromKey(key);
|
|
14391
|
+
if (char !== null)
|
|
14392
|
+
applyPromptInput(promptInputRef.current + char);
|
|
14393
|
+
}
|
|
13896
14394
|
return;
|
|
13897
14395
|
}
|
|
13898
14396
|
if (filterOpenRef.current && focusRef.current === "sidebar") {
|
|
@@ -13913,14 +14411,17 @@ var Browser = ({
|
|
|
13913
14411
|
}
|
|
13914
14412
|
};
|
|
13915
14413
|
if (key.name === "escape") {
|
|
14414
|
+
key.preventDefault();
|
|
13916
14415
|
closeFilter(false);
|
|
13917
14416
|
return;
|
|
13918
14417
|
}
|
|
13919
14418
|
if (key.name === "return") {
|
|
14419
|
+
key.preventDefault();
|
|
13920
14420
|
closeFilter(true);
|
|
13921
14421
|
return;
|
|
13922
14422
|
}
|
|
13923
14423
|
if (key.name === "tab" || key.ctrl && key.name === "i" && !key.shift && !key.meta) {
|
|
14424
|
+
key.preventDefault();
|
|
13924
14425
|
focusRef.current = "reader";
|
|
13925
14426
|
restoreFilterOnSidebarFocusRef.current = true;
|
|
13926
14427
|
filterOpenRef.current = false;
|
|
@@ -13929,46 +14430,57 @@ var Browser = ({
|
|
|
13929
14430
|
return;
|
|
13930
14431
|
}
|
|
13931
14432
|
if (key.ctrl && !key.meta && key.name === "p") {
|
|
14433
|
+
key.preventDefault();
|
|
13932
14434
|
ctx.openPalette();
|
|
13933
14435
|
return;
|
|
13934
14436
|
}
|
|
13935
14437
|
if (key.ctrl && key.name === "\\") {
|
|
14438
|
+
key.preventDefault();
|
|
13936
14439
|
filterInputRef.current = "";
|
|
13937
14440
|
setFilterInput("");
|
|
13938
14441
|
navigator2.flushSearch("");
|
|
13939
14442
|
navigator2.selectFirst();
|
|
13940
14443
|
return;
|
|
13941
14444
|
}
|
|
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);
|
|
14445
|
+
if ((key.name === "backspace" || key.name === "delete") && filterInputRef.current.length === 0) {
|
|
14446
|
+
key.preventDefault();
|
|
14447
|
+
closeFilter(false);
|
|
13950
14448
|
return;
|
|
13951
14449
|
}
|
|
13952
14450
|
if (key.name === "up") {
|
|
14451
|
+
key.preventDefault();
|
|
13953
14452
|
navigator2.moveBy(-1);
|
|
13954
14453
|
return;
|
|
13955
14454
|
}
|
|
13956
14455
|
if (key.name === "down") {
|
|
14456
|
+
key.preventDefault();
|
|
13957
14457
|
navigator2.moveBy(1);
|
|
13958
14458
|
return;
|
|
13959
14459
|
}
|
|
13960
|
-
if (key.ctrl || key.
|
|
14460
|
+
if (key.ctrl && (key.name === "u" || key.name === "d")) {
|
|
14461
|
+
key.preventDefault();
|
|
13961
14462
|
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
14463
|
}
|
|
13968
|
-
if (
|
|
13969
|
-
|
|
13970
|
-
|
|
13971
|
-
|
|
14464
|
+
if (!filterInputReadyRef.current) {
|
|
14465
|
+
if (key.name === "backspace" || key.name === "delete") {
|
|
14466
|
+
if (filterInputRef.current.length === 0) {
|
|
14467
|
+
key.preventDefault();
|
|
14468
|
+
closeFilter(false);
|
|
14469
|
+
return;
|
|
14470
|
+
}
|
|
14471
|
+
filterInputRef.current = filterInputRef.current.slice(0, -1);
|
|
14472
|
+
setFilterInput(filterInputRef.current);
|
|
14473
|
+
navigator2.selectIndex(0);
|
|
14474
|
+
return;
|
|
14475
|
+
}
|
|
14476
|
+
if (key.ctrl || key.meta)
|
|
14477
|
+
return;
|
|
14478
|
+
const char = printableFromKey(key);
|
|
14479
|
+
if (char !== null) {
|
|
14480
|
+
filterInputRef.current = filterInputRef.current + char;
|
|
14481
|
+
setFilterInput(filterInputRef.current);
|
|
14482
|
+
navigator2.selectIndex(0);
|
|
14483
|
+
}
|
|
13972
14484
|
}
|
|
13973
14485
|
return;
|
|
13974
14486
|
}
|
|
@@ -14007,15 +14519,11 @@ var Browser = ({
|
|
|
14007
14519
|
width,
|
|
14008
14520
|
notice: footerNotice?.text ?? null,
|
|
14009
14521
|
discoveryStatus: effectiveDiscoveryStatus,
|
|
14010
|
-
indicators: [
|
|
14011
|
-
{
|
|
14012
|
-
|
|
14013
|
-
|
|
14014
|
-
|
|
14015
|
-
active: wrapEnabled,
|
|
14016
|
-
onMouseUp: toggleWrap
|
|
14017
|
-
}
|
|
14018
|
-
],
|
|
14522
|
+
indicators: optionsSession.current === null ? [] : footerControlsFromSession(houseOptions, optionsSession.current, {
|
|
14523
|
+
wrap: { onActivate: toggleWrap },
|
|
14524
|
+
theme: { onActivate: () => cycleTheme(1) },
|
|
14525
|
+
order: { onActivate: cycleOrder }
|
|
14526
|
+
}),
|
|
14019
14527
|
...discoverySpinnerIntervalMs === undefined ? {} : { discoverySpinnerIntervalMs },
|
|
14020
14528
|
...discoverySpinnerInitialFrameIndex === undefined ? {} : { discoverySpinnerInitialFrameIndex },
|
|
14021
14529
|
...discoverySpinnerRegisterTick === undefined ? {} : { discoverySpinnerRegisterTick },
|
|
@@ -14050,12 +14558,20 @@ var Browser = ({
|
|
|
14050
14558
|
root,
|
|
14051
14559
|
policy,
|
|
14052
14560
|
watch: watch2,
|
|
14053
|
-
order,
|
|
14561
|
+
order: browseOrder,
|
|
14054
14562
|
debounceMs: filterDebounceMs,
|
|
14055
14563
|
navigatorRef,
|
|
14056
14564
|
snapshot: liveSnapshot,
|
|
14057
14565
|
filterInput,
|
|
14058
14566
|
filterOpen,
|
|
14567
|
+
onFilterInput: (next) => {
|
|
14568
|
+
filterInputRef.current = next;
|
|
14569
|
+
setFilterInput(next);
|
|
14570
|
+
navigator2.selectIndex(0);
|
|
14571
|
+
},
|
|
14572
|
+
onFilterEditingReady: (ready) => {
|
|
14573
|
+
filterInputReadyRef.current = ready;
|
|
14574
|
+
},
|
|
14059
14575
|
discoveryActive,
|
|
14060
14576
|
rootLabel,
|
|
14061
14577
|
viewportHeight: height,
|
|
@@ -14191,16 +14707,31 @@ var Browser = ({
|
|
|
14191
14707
|
query: paletteQuery,
|
|
14192
14708
|
selectedIndex: paletteIndex,
|
|
14193
14709
|
viewportWidth: width,
|
|
14194
|
-
viewportHeight: height
|
|
14710
|
+
viewportHeight: height,
|
|
14711
|
+
onQueryChange: (next) => {
|
|
14712
|
+
paletteQueryRef.current = next;
|
|
14713
|
+
setPaletteQuery(next);
|
|
14714
|
+
paletteIndexRef.current = 0;
|
|
14715
|
+
setPaletteIndex(0);
|
|
14716
|
+
},
|
|
14717
|
+
onInputReady: (ready) => {
|
|
14718
|
+
paletteInputReadyRef.current = ready;
|
|
14719
|
+
}
|
|
14195
14720
|
}, undefined, false, undefined, this),
|
|
14196
14721
|
promptOpen && /* @__PURE__ */ jsxDEV13(PromptModal, {
|
|
14197
|
-
title: "New file",
|
|
14722
|
+
title: promptPurpose === "rename" ? "Rename" : "New file",
|
|
14198
14723
|
query: promptInput,
|
|
14199
14724
|
placeholder: "File name",
|
|
14200
|
-
hints: "enter create esc cancel",
|
|
14725
|
+
hints: promptPurpose === "rename" ? "enter rename esc cancel" : "enter create esc cancel",
|
|
14201
14726
|
status: promptStatus,
|
|
14727
|
+
...promptContext !== undefined ? { context: promptContext } : {},
|
|
14202
14728
|
viewportWidth: width,
|
|
14203
|
-
viewportHeight: height
|
|
14729
|
+
viewportHeight: height,
|
|
14730
|
+
onQueryChange: applyPromptInput,
|
|
14731
|
+
onInputReady: (ready) => {
|
|
14732
|
+
promptInputReadyRef.current = ready;
|
|
14733
|
+
},
|
|
14734
|
+
inputEnabled: !promptSubmitting
|
|
14204
14735
|
}, undefined, false, undefined, this),
|
|
14205
14736
|
activeStatusPopover && /* @__PURE__ */ jsxDEV13(StatusPopoverPanel, {
|
|
14206
14737
|
content: activeStatusPopover.content,
|
|
@@ -14345,15 +14876,15 @@ var parseAndHandleFastExit = (argv) => {
|
|
|
14345
14876
|
};
|
|
14346
14877
|
|
|
14347
14878
|
// src/discovery/rootLabel.ts
|
|
14348
|
-
import { isAbsolute, relative as
|
|
14879
|
+
import { isAbsolute as isAbsolute2, relative as relative4 } from "path";
|
|
14349
14880
|
var formatDiscoveryRootLabel = ({
|
|
14350
14881
|
discoveryRoot,
|
|
14351
14882
|
home
|
|
14352
14883
|
}) => {
|
|
14353
14884
|
if (discoveryRoot === home)
|
|
14354
14885
|
return "~";
|
|
14355
|
-
const homeRelative =
|
|
14356
|
-
if (homeRelative.length > 0 && !homeRelative.startsWith("..") && !
|
|
14886
|
+
const homeRelative = relative4(home, discoveryRoot);
|
|
14887
|
+
if (homeRelative.length > 0 && !homeRelative.startsWith("..") && !isAbsolute2(homeRelative)) {
|
|
14357
14888
|
return `~/${homeRelative}`;
|
|
14358
14889
|
}
|
|
14359
14890
|
return discoveryRoot;
|
|
@@ -14401,16 +14932,16 @@ var isNewer = (candidate, current) => {
|
|
|
14401
14932
|
};
|
|
14402
14933
|
|
|
14403
14934
|
// src/update/cache.ts
|
|
14404
|
-
import { dirname as
|
|
14935
|
+
import { dirname as dirname4, join as join5 } from "path";
|
|
14405
14936
|
import { homedir as homedir2 } from "os";
|
|
14406
|
-
import { mkdir as mkdir2, readFile as readFile5, rename as
|
|
14937
|
+
import { mkdir as mkdir2, readFile as readFile5, rename as rename3, unlink as unlink2, writeFile as writeFile2 } from "fs/promises";
|
|
14407
14938
|
var cacheDir = () => {
|
|
14408
14939
|
const xdg = process.env.XDG_CACHE_HOME;
|
|
14409
14940
|
if (xdg && xdg.length > 0)
|
|
14410
|
-
return
|
|
14411
|
-
return
|
|
14941
|
+
return join5(xdg, "house");
|
|
14942
|
+
return join5(homedir2(), ".cache", "house");
|
|
14412
14943
|
};
|
|
14413
|
-
var cachePath = () =>
|
|
14944
|
+
var cachePath = () => join5(cacheDir(), "update-check.json");
|
|
14414
14945
|
var readCache = async (path = cachePath()) => {
|
|
14415
14946
|
try {
|
|
14416
14947
|
const raw = await readFile5(path, "utf8");
|
|
@@ -14433,9 +14964,9 @@ var readCache = async (path = cachePath()) => {
|
|
|
14433
14964
|
var writeCache = async (record, path = cachePath()) => {
|
|
14434
14965
|
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
14435
14966
|
try {
|
|
14436
|
-
await mkdir2(
|
|
14967
|
+
await mkdir2(dirname4(path), { recursive: true });
|
|
14437
14968
|
await writeFile2(tmp, JSON.stringify(record), "utf8");
|
|
14438
|
-
await
|
|
14969
|
+
await rename3(tmp, path);
|
|
14439
14970
|
} catch {
|
|
14440
14971
|
try {
|
|
14441
14972
|
await unlink2(tmp);
|
|
@@ -14575,18 +15106,18 @@ var useUpdateNotice = () => {
|
|
|
14575
15106
|
import { jsxDEV as jsxDEV14 } from "@opentui/react/jsx-dev-runtime";
|
|
14576
15107
|
var pathIsDirectory = async (path) => {
|
|
14577
15108
|
try {
|
|
14578
|
-
return (await
|
|
15109
|
+
return (await stat3(path)).isDirectory();
|
|
14579
15110
|
} catch {
|
|
14580
15111
|
return false;
|
|
14581
15112
|
}
|
|
14582
15113
|
};
|
|
14583
15114
|
var findGitRoot = async (cwd) => {
|
|
14584
|
-
const start =
|
|
15115
|
+
const start = resolve4(cwd);
|
|
14585
15116
|
let current2 = start;
|
|
14586
15117
|
for (;; ) {
|
|
14587
|
-
if (await pathIsDirectory(
|
|
15118
|
+
if (await pathIsDirectory(resolve4(current2, ".git")))
|
|
14588
15119
|
return current2;
|
|
14589
|
-
const parent =
|
|
15120
|
+
const parent = dirname5(current2);
|
|
14590
15121
|
if (parent === current2)
|
|
14591
15122
|
return start;
|
|
14592
15123
|
current2 = parent;
|
|
@@ -14598,7 +15129,7 @@ var resolveDiscoveryRoot = async ({
|
|
|
14598
15129
|
cwd
|
|
14599
15130
|
}) => {
|
|
14600
15131
|
if (cliRoot !== null)
|
|
14601
|
-
return
|
|
15132
|
+
return resolve4(cwd, cliRoot);
|
|
14602
15133
|
if (defaultRoot === "git")
|
|
14603
15134
|
return findGitRoot(cwd);
|
|
14604
15135
|
return cwd;
|
|
@@ -14606,7 +15137,7 @@ var resolveDiscoveryRoot = async ({
|
|
|
14606
15137
|
var validateDiscoveryRoot = async (root) => {
|
|
14607
15138
|
let stats;
|
|
14608
15139
|
try {
|
|
14609
|
-
stats = await
|
|
15140
|
+
stats = await stat3(root);
|
|
14610
15141
|
} catch (err) {
|
|
14611
15142
|
throw new Error(`cannot access discovery root ${root}: ${String(err)}`);
|
|
14612
15143
|
}
|
|
@@ -14640,10 +15171,10 @@ var buildDiscoveryPolicy = (show, extensions) => {
|
|
|
14640
15171
|
followSymlinks: false,
|
|
14641
15172
|
ignoreFiles: show.includes("gitignored") ? [] : [".gitignore"],
|
|
14642
15173
|
includeFile: (path) => {
|
|
14643
|
-
const name =
|
|
15174
|
+
const name = basename4(path);
|
|
14644
15175
|
return allowed.has(name.slice(name.lastIndexOf(".")).toLowerCase()) && visiblePath(path) && !path.split(/[\\/]/).some((part) => hardSkips.has(part));
|
|
14645
15176
|
},
|
|
14646
|
-
includeDirectory: (path) => !hardSkips.has(
|
|
15177
|
+
includeDirectory: (path) => !hardSkips.has(basename4(path)) && visiblePath(path)
|
|
14647
15178
|
};
|
|
14648
15179
|
};
|
|
14649
15180
|
var DiscoverShell = ({
|
|
@@ -14683,11 +15214,11 @@ var resolveInitialQuery = ({
|
|
|
14683
15214
|
}) => {
|
|
14684
15215
|
if (pathArg === null)
|
|
14685
15216
|
return "";
|
|
14686
|
-
const resolvedPath =
|
|
14687
|
-
const rel =
|
|
15217
|
+
const resolvedPath = resolve4(cwd, pathArg);
|
|
15218
|
+
const rel = relative5(discoveryRoot, resolvedPath);
|
|
14688
15219
|
if (rel.length === 0)
|
|
14689
15220
|
return "";
|
|
14690
|
-
if (!rel.startsWith("..") && !
|
|
15221
|
+
if (!rel.startsWith("..") && !isAbsolute3(rel))
|
|
14691
15222
|
return rel;
|
|
14692
15223
|
if (pathArg.startsWith("./"))
|
|
14693
15224
|
return pathArg.slice(2);
|
|
@@ -14755,7 +15286,7 @@ async function main(argv = Bun.argv.slice(2)) {
|
|
|
14755
15286
|
}
|
|
14756
15287
|
let stats;
|
|
14757
15288
|
try {
|
|
14758
|
-
stats = await
|
|
15289
|
+
stats = await stat3(target);
|
|
14759
15290
|
} catch (err) {
|
|
14760
15291
|
console.error(`house: cannot access ${target}: ${String(err)}`);
|
|
14761
15292
|
process.exit(1);
|