@narumitw/pi-files 0.0.0-stage → 0.1.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 +9 -0
- package/LICENSE +21 -0
- package/README.md +102 -2
- package/dist/chunks/chunk-3GQ7VS22.ts +273 -0
- package/dist/chunks/chunk-3GQ7VS22.ts.map +7 -0
- package/dist/chunks/chunk-AXW73HDJ.ts +31 -0
- package/dist/chunks/chunk-AXW73HDJ.ts.map +7 -0
- package/dist/chunks/chunk-F5QFC255.ts +101 -0
- package/dist/chunks/chunk-F5QFC255.ts.map +7 -0
- package/dist/chunks/chunk-NFNGJETD.ts +77 -0
- package/dist/chunks/chunk-NFNGJETD.ts.map +7 -0
- package/dist/chunks/chunk-OPGTWNJI.ts +258 -0
- package/dist/chunks/chunk-OPGTWNJI.ts.map +7 -0
- package/dist/chunks/chunk-WOTDNRTG.ts +242 -0
- package/dist/chunks/chunk-WOTDNRTG.ts.map +7 -0
- package/dist/chunks/file-context-explorer-WR6GQZHF.ts +1793 -0
- package/dist/chunks/file-context-explorer-WR6GQZHF.ts.map +7 -0
- package/dist/chunks/file-context-menu-VPLHUUQ6.ts +302 -0
- package/dist/chunks/file-context-menu-VPLHUUQ6.ts.map +7 -0
- package/dist/chunks/files-menu-MES4MQ4F.ts +119 -0
- package/dist/chunks/files-menu-MES4MQ4F.ts.map +7 -0
- package/dist/chunks/files-settings-NYXQU2HV.ts +295 -0
- package/dist/chunks/files-settings-NYXQU2HV.ts.map +7 -0
- package/dist/chunks/menu-P6IBMROH.ts +876 -0
- package/dist/chunks/menu-P6IBMROH.ts.map +7 -0
- package/dist/index.ts +1789 -0
- package/dist/index.ts.map +7 -0
- package/docs/edit-scope-reference.md +142 -0
- package/docs/file-context-reference.md +91 -0
- package/docs/migration.md +105 -0
- package/docs/verification.md +99 -0
- package/package.json +58 -4
- package/src/content-search-session.ts +204 -0
- package/src/content-search-ui.ts +159 -0
- package/src/content-search.ts +170 -0
- package/src/context-service.ts +515 -0
- package/src/external-editor.ts +143 -0
- package/src/file-browser-ui.ts +128 -0
- package/src/file-browser.ts +76 -0
- package/src/file-context-explorer.ts +992 -0
- package/src/file-context-menu.ts +360 -0
- package/src/file-context-preview-ui.ts +354 -0
- package/src/file-context-settings.ts +301 -0
- package/src/file-context.ts +321 -0
- package/src/file-search.ts +246 -0
- package/src/files-menu.ts +125 -0
- package/src/files-settings.ts +314 -0
- package/src/files-widget.ts +65 -0
- package/src/files.ts +148 -0
- package/src/git-context.ts +485 -0
- package/src/index.ts +1 -0
- package/src/scope/contracts.ts +243 -0
- package/src/scope/display.ts +27 -0
- package/src/scope/edit-scope.ts +435 -0
- package/src/scope/files.ts +118 -0
- package/src/scope/input.ts +207 -0
- package/src/scope/menu.ts +277 -0
- package/src/scope/paths.ts +142 -0
- package/src/scope/picker-keys.ts +154 -0
- package/src/scope/picker.ts +484 -0
- package/src/scope/policy.ts +55 -0
- package/src/scope/settings.ts +138 -0
- package/src/scope/state.ts +91 -0
- package/src/scope/ui.ts +106 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/file-context-menu.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ExtensionCommandContext } from \"@earendil-works/pi-coding-agent\";\nimport type { KeyId } from \"@earendil-works/pi-tui\";\nimport type { RunMenuResult } from \"@narumitw/pi-tui-kit\";\nimport { normalizeKeyId } from \"./file-context-settings.js\";\n\nexport interface FileContextMenuQuote {\n id: string;\n path: string;\n startLine: number;\n endLine: number;\n text: string;\n}\n\nexport interface FileContextMenuState {\n quotes: readonly FileContextMenuQuote[];\n shortcut: string | null;\n maximumQuotes: number;\n maximumBytes: number;\n totalBytes: number;\n settingsPath: string;\n settingsInvalidReason?: string;\n}\n\nexport type FileContextMenuAddResult = \"stay\" | \"close\";\n\nexport type FileContextMenuRemovalResult =\n | { kind: \"removed\"; quote: FileContextMenuQuote; remaining: number }\n | { kind: \"missing\" };\n\nexport interface FileContextMenuOptions {\n start?: \"main\" | \"remove\";\n signal: AbortSignal;\n isCurrent(): boolean;\n getState(): FileContextMenuState | Promise<FileContextMenuState>;\n addQuote(signal: AbortSignal): FileContextMenuAddResult | Promise<FileContextMenuAddResult>;\n removeQuote(id: string, signal: AbortSignal): FileContextMenuRemovalResult | Promise<FileContextMenuRemovalResult>;\n saveShortcut(shortcut: KeyId | null, signal: AbortSignal): void | Promise<void>;\n}\n\ntype Screen = \"main\" | \"selected\" | \"quote\" | \"settings\" | \"shortcut\" | \"status\" | \"help\";\ntype Action = \"add\" | \"review\" | \"remove\" | \"open-shortcut\" | \"set-shortcut\";\n\nexport async function showFileContextMenu(\n ctx: ExtensionCommandContext,\n options: FileContextMenuOptions,\n): Promise<RunMenuResult> {\n const { defineMenu, runMenu } = await import(\"@narumitw/pi-tui-kit\");\n if (!options.isCurrent() || options.signal.aborted) return { kind: \"stale\" };\n\n let addRequested = false;\n let selectedQuoteId: string | undefined;\n const menu = defineMenu<FileContextMenuState, Screen, Action, ExtensionCommandContext>({\n start: options.start === \"remove\" ? \"selected\" : \"main\",\n screens: {\n main: ({ state }) => ({\n kind: \"actions\",\n title: \"File Context\",\n lines: [\n `Next prompt context: ${state.quotes.length}/${state.maximumQuotes} snippets \u00B7 ~${estimateTokens(state.totalBytes)} tokens`,\n `Shortcut: ${formatShortcut(state.shortcut)}`,\n ],\n items: [\n {\n id: \"add\",\n label: \"Add context snippet\",\n description: \"Browse project files and select lines\",\n action: \"add\",\n busyLabel: \"Scanning project files\",\n disabled: addDisabledReason(state) !== undefined,\n disabledReason: addDisabledReason(state),\n },\n {\n id: \"selected\",\n label: `Review selected context (${state.quotes.length})`,\n description: \"Preview exact snapshots before removing them\",\n to: \"selected\",\n disabled: state.quotes.length === 0,\n disabledReason: state.quotes.length === 0 ? \"No context selected for the next prompt\" : undefined,\n },\n {\n id: \"settings\",\n label: \"Settings\",\n description: \"Configure the shortcut used to open File Context\",\n to: \"settings\",\n disabled: state.quotes.length > 0,\n disabledReason:\n state.quotes.length > 0\n ? \"Submit or remove selected context first; applying a shortcut reloads Pi\"\n : undefined,\n },\n {\n id: \"status\",\n label: \"Status\",\n description: \"Review selected context, limits, and active settings\",\n to: \"status\",\n },\n {\n id: \"help\",\n label: \"Help\",\n description: \"Review shortcuts and attachment behavior\",\n to: \"help\",\n },\n ],\n hint: \"close\",\n }),\n selected: ({ state }) =>\n state.quotes.length === 0\n ? {\n kind: \"detail\",\n title: \"Selected context\",\n lines: [\"No context selected for the next prompt.\"],\n hint: \"back\",\n }\n : {\n kind: \"choice\",\n title: \"Selected context\",\n lines: [`${state.quotes.length} snippets \u00B7 Enter reviews the exact snapshot before removal`],\n items: state.quotes.map((quote, index) => ({\n id: quote.id,\n label: `${index + 1}. ${quote.path}`,\n description: `lines ${quote.startLine}-${quote.endLine} \u00B7 ~${estimateTokens(Buffer.byteLength(quote.text, \"utf8\"))} tokens`,\n details: [`Lines ${quote.startLine}-${quote.endLine} \u00B7 Preview: ${singleLinePreview(quote.text)}`],\n })),\n action: \"review\",\n viewportSize: 8,\n hint: \"back\",\n },\n quote: ({ state }) => {\n const quote = state.quotes.find((candidate) => candidate.id === selectedQuoteId);\n if (!quote) {\n return {\n kind: \"detail\",\n title: \"Review context snippet\",\n lines: [\"That snippet is no longer selected. Go back to refresh the list.\"],\n hint: \"back\",\n };\n }\n return {\n kind: \"review\",\n title: \"Review context snippet\",\n lines: [\n quote.path,\n `Lines ${quote.startLine}-${quote.endLine} \u00B7 ~${estimateTokens(Buffer.byteLength(quote.text, \"utf8\"))} tokens`,\n ],\n content: quote.text,\n format: { kind: \"text\" },\n viewportSize: \"adaptive\",\n confirm: {\n id: \"remove\",\n label: \"Remove from next prompt\",\n action: \"remove\",\n },\n hint: \"back\",\n };\n },\n settings: ({ state }) =>\n state.settingsInvalidReason\n ? {\n kind: \"detail\",\n title: \"File Context Settings \u00B7 Read only\",\n lines: [\n `Invalid settings file. Fix ${safeTerminalText(state.settingsPath)} before saving.`,\n safeTerminalText(state.settingsInvalidReason),\n ],\n hint: \"back\",\n }\n : {\n kind: \"settings\",\n title: \"File Context Settings\",\n lines: [\n `User settings \u00B7 ${safeTerminalText(state.settingsPath)}`,\n \"Saving reloads Pi so the new shortcut becomes active.\",\n ],\n items: [\n {\n id: \"openShortcut\",\n label: \"Open shortcut\",\n description: \"Set the global shortcut used to open the file browser.\",\n currentValue: state.shortcut ?? \"none\",\n action: \"open-shortcut\",\n },\n ],\n hint: \"back\",\n },\n shortcut: ({ state }) => ({\n kind: \"input\",\n title: \"File Context shortcut\",\n lines: [\n `Configured: ${state.shortcut ?? \"none\"}`,\n \"Use a Pi key identifier such as ctrl+shift+x or f8.\",\n \"Submit an empty value to disable the shortcut.\",\n ],\n placeholder: state.shortcut ?? \"\",\n action: \"set-shortcut\",\n hint: \"back\",\n }),\n status: ({ state }) => ({\n kind: \"detail\",\n title: \"File Context Status\",\n lines: [\n `Next prompt context: ${state.quotes.length}/${state.maximumQuotes} snippets`,\n `Estimated context: ~${estimateTokens(state.totalBytes)} tokens \u00B7 ${state.totalBytes}/${state.maximumBytes} bytes`,\n `Open shortcut: ${state.shortcut ?? \"none\"}`,\n `User settings: ${safeTerminalText(state.settingsPath)}`,\n ...(state.settingsInvalidReason\n ? [`Settings warning: ${safeTerminalText(state.settingsInvalidReason)}`]\n : []),\n ],\n hint: \"back\",\n }),\n help: () => ({\n kind: \"detail\",\n title: \"File Context help\",\n lines: [\n \"Add context snippet opens the project browser. Select lines and press Enter to add and close, or A to add and keep browsing.\",\n \"Selected context is attached in order to your next prompt, then cleared together.\",\n \"Review selected context opens each exact snapshot before offering removal.\",\n \"The configured shortcut and /file-context browse open the browser directly.\",\n \"Settings saves the shortcut and reloads Pi; Status shows active limits and configuration.\",\n \"Escape goes back. Ctrl+C closes File Context. Cancelling never changes selected context.\",\n ],\n hint: \"back\",\n }),\n },\n actions: {\n add: () => {\n addRequested = true;\n return { kind: \"close\" };\n },\n review: ({ itemId }) => {\n selectedQuoteId = itemId;\n return { kind: \"to\", screen: \"quote\" };\n },\n \"open-shortcut\": async () => ({ kind: \"to\", screen: \"shortcut\" }),\n \"set-shortcut\": async ({ ctx: actionCtx, value, signal }) => {\n const raw = value?.trim() || null;\n const normalized = raw ? normalizeKeyId(raw) : undefined;\n if (raw && !normalized) {\n actionCtx.ui.notify(\n `Invalid key identifier: ${safeTerminalText(raw)}. Use a Pi key identifier like ctrl+shift+x.`,\n \"warning\",\n );\n return { kind: \"stay\" };\n }\n const shortcut = normalized ?? null;\n try {\n await options.saveShortcut(shortcut, signal);\n } catch (error: unknown) {\n if (!signal.aborted && options.isCurrent()) {\n actionCtx.ui.notify(\n `Could not save File Context settings; the previous shortcut remains: ${safeTerminalText(formatError(error))}`,\n \"error\",\n );\n }\n return { kind: \"stay\" };\n }\n if (signal.aborted || !options.isCurrent()) return { kind: \"close\" };\n actionCtx.ui.notify(\n shortcut\n ? `Saved File Context shortcut ${safeTerminalText(shortcut)}. Reloading Pi\u2026`\n : \"Disabled the File Context shortcut. Reloading Pi\u2026\",\n \"info\",\n );\n try {\n await actionCtx.reload();\n return { kind: \"close\" };\n } catch (error: unknown) {\n if (!signal.aborted && options.isCurrent()) {\n actionCtx.ui.notify(\n `File Context settings were saved, but Pi could not reload; run /reload to apply them: ${safeTerminalText(formatError(error))}`,\n \"error\",\n );\n }\n return { kind: \"close\" };\n }\n },\n remove: async ({ signal }) => {\n const itemId = selectedQuoteId;\n if (!itemId) return { kind: \"back\" };\n const result = await options.removeQuote(itemId, signal);\n if (signal.aborted || !options.isCurrent()) return { kind: \"close\" };\n if (result.kind === \"missing\") {\n ctx.ui.notify(\"That snippet is no longer selected. The list was refreshed.\", \"warning\");\n return { kind: \"stay\" };\n }\n ctx.ui.notify(\n `Removed from next prompt context: ${safeTerminalText(result.quote.path)} \u00B7 lines ${result.quote.startLine}-${result.quote.endLine}.`,\n \"info\",\n );\n selectedQuoteId = undefined;\n return { kind: \"back\" };\n },\n },\n });\n\n while (options.isCurrent() && !options.signal.aborted) {\n addRequested = false;\n const result = await runMenu(ctx, menu, {\n getState: () => options.getState(),\n signal: options.signal,\n isCurrent: options.isCurrent,\n onError: (_menuContext, error) => {\n ctx.ui.notify(\n `File Context menu failed: ${safeTerminalText(formatError(error))}. Selected context was kept; try again.`,\n \"error\",\n );\n },\n onUnsupportedMode: (_menuContext, mode) => {\n ctx.ui.notify(`File Context is unavailable in ${mode} mode.`, \"warning\");\n },\n });\n if (!addRequested || result.kind !== \"closed\") return result;\n const addResult = await options.addQuote(options.signal);\n if (!options.isCurrent() || options.signal.aborted) return { kind: \"stale\" };\n if (addResult === \"close\") return { kind: \"closed\", reason: \"close\" };\n }\n return { kind: \"stale\" };\n}\n\nfunction addDisabledReason(state: FileContextMenuState): string | undefined {\n if (state.quotes.length >= state.maximumQuotes) {\n return `The ${state.maximumQuotes}-snippet limit is reached; review selected context first`;\n }\n if (state.totalBytes >= state.maximumBytes) {\n return `The ${formatBytes(state.maximumBytes)} context limit is reached; review selected context first`;\n }\n return undefined;\n}\n\nfunction formatShortcut(shortcut: string | null): string {\n return shortcut ? shortcut.toUpperCase() : \"Disabled (use /file-context browse)\";\n}\n\nfunction singleLinePreview(text: string): string {\n const normalized = text.replaceAll(\"\\r\\n\", \"\\n\").replaceAll(\"\\r\", \"\\n\").split(\"\\n\").join(\" \u21B5 \");\n const characters = [...normalized];\n if (characters.length === 0) return \"(empty)\";\n return characters.length <= 200 ? normalized : `${characters.slice(0, 199).join(\"\")}\u2026`;\n}\n\nfunction estimateTokens(bytes: number): number {\n return bytes === 0 ? 0 : Math.max(1, Math.ceil(bytes / 4));\n}\n\nfunction formatBytes(bytes: number): string {\n return bytes % 1_000 === 0 ? `${bytes / 1_000} KB` : `${bytes} bytes`;\n}\n\nfunction safeTerminalText(text: string): string {\n return [...text]\n .map((character) => {\n const code = character.charCodeAt(0);\n return code <= 31 || (code >= 127 && code <= 159) ? `\\\\x${code.toString(16).padStart(2, \"0\")}` : character;\n })\n .join(\"\");\n}\n\nfunction formatError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// @generated by scripts/build-runtime.mjs; do not edit.
|
|
2
|
+
// @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
|
|
3
|
+
import {
|
|
4
|
+
displayPath
|
|
5
|
+
} from "./chunk-AXW73HDJ.ts";
|
|
6
|
+
|
|
7
|
+
// src/files-menu.ts
|
|
8
|
+
import { defineMenu, runMenu } from "@narumitw/pi-tui-kit";
|
|
9
|
+
async function showFilesMenu(ctx, context, scope) {
|
|
10
|
+
if (ctx.mode !== "tui" || !ctx.hasUI) throw new Error("/files requires Pi TUI mode");
|
|
11
|
+
const signal = AbortSignal.any([context.signal(ctx), scope.signal(ctx), ...ctx.signal ? [ctx.signal] : []]);
|
|
12
|
+
const current = () => !signal.aborted;
|
|
13
|
+
const state = () => ({ context: context.getState(ctx), scope: scope.getView(ctx) });
|
|
14
|
+
let next;
|
|
15
|
+
const menu = defineMenu({
|
|
16
|
+
start: "main",
|
|
17
|
+
screens: {
|
|
18
|
+
main: ({ state: state2 }) => ({
|
|
19
|
+
kind: "actions",
|
|
20
|
+
title: "Files",
|
|
21
|
+
lines: [
|
|
22
|
+
`Next prompt context: ${state2.context.quotes.length} snippets`,
|
|
23
|
+
state2.scope.state === "inactive" ? "Edit scope: unrestricted" : state2.scope.state === "blocked" ? "Edit scope: unavailable \xB7 edits blocked" : `Edit scope: ${state2.scope.targets.length} allowed files`
|
|
24
|
+
],
|
|
25
|
+
items: [
|
|
26
|
+
{
|
|
27
|
+
id: "browse",
|
|
28
|
+
label: "Browse files",
|
|
29
|
+
description: "Preview files and attach context; does not grant edit permission",
|
|
30
|
+
action: "browse"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: "review",
|
|
34
|
+
label: `Review queued context (${state2.context.quotes.length})`,
|
|
35
|
+
description: "Inspect exact snapshots and remove snippets",
|
|
36
|
+
action: "review",
|
|
37
|
+
disabled: !state2.context.quotes.length
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: "scope",
|
|
41
|
+
label: "Manage edit scope",
|
|
42
|
+
description: "Select allowed targets, reserve a new file, or end the restriction",
|
|
43
|
+
action: "scope"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: "settings",
|
|
47
|
+
label: "Settings",
|
|
48
|
+
description: "Browser shortcut and hidden-file discovery preference",
|
|
49
|
+
action: "settings"
|
|
50
|
+
},
|
|
51
|
+
{ id: "status", label: "Status", description: "Review context and every allowed target", to: "status" },
|
|
52
|
+
{ id: "help", label: "Help", description: "Read state lifetimes and the protection boundary", to: "help" }
|
|
53
|
+
],
|
|
54
|
+
hint: "close"
|
|
55
|
+
}),
|
|
56
|
+
status: ({ state: state2 }) => ({
|
|
57
|
+
kind: "detail",
|
|
58
|
+
title: "Files Status",
|
|
59
|
+
lines: [
|
|
60
|
+
`Next prompt context: ${state2.context.quotes.length}/${state2.context.maximumQuotes} snippets \xB7 ${state2.context.totalBytes}/${state2.context.maximumBytes} bytes`,
|
|
61
|
+
`Browser shortcut: ${state2.context.shortcut ?? "disabled"}`,
|
|
62
|
+
`Edit scope: ${state2.scope.state}`,
|
|
63
|
+
...state2.scope.error ? [state2.scope.error] : [],
|
|
64
|
+
...state2.scope.targets.map(
|
|
65
|
+
(target) => `${displayPath(target.path)}${target.canonical !== target.path ? ` \u2192 ${displayPath(target.canonical)}` : ""}`
|
|
66
|
+
),
|
|
67
|
+
...state2.scope.state === "inactive" ? [] : ["/files off explicitly ends the editing restriction."],
|
|
68
|
+
"Reserved new-file targets are created only by a normal write call."
|
|
69
|
+
],
|
|
70
|
+
hint: "back"
|
|
71
|
+
}),
|
|
72
|
+
help: () => ({
|
|
73
|
+
kind: "detail",
|
|
74
|
+
title: "Files Help",
|
|
75
|
+
lines: [
|
|
76
|
+
"Context snapshots attach once to the next ordinary prompt; selecting context never grants edit permission.",
|
|
77
|
+
"Edit scope lasts until explicitly ended and is restored from the active session branch; selecting a target never loads its content.",
|
|
78
|
+
"An active restriction allows only stock readers and target-checked native write/edit. Shell, codemode, delegation, and custom tools are blocked.",
|
|
79
|
+
"This is an in-process guard, not an OS sandbox. The user-controlled external editor is outside native-tool enforcement.",
|
|
80
|
+
"/file-context [browse|remove] and /edit-scope or /scope [off] keep their existing workflows.",
|
|
81
|
+
"Do not load predecessor extensions with Files. Remove or disable their user and project resources, then reload.",
|
|
82
|
+
"Escape goes back. Ctrl+C closes transient UI without ending an active restriction."
|
|
83
|
+
],
|
|
84
|
+
hint: "back"
|
|
85
|
+
})
|
|
86
|
+
},
|
|
87
|
+
actions: {
|
|
88
|
+
browse: () => {
|
|
89
|
+
next = "browse";
|
|
90
|
+
return { kind: "close" };
|
|
91
|
+
},
|
|
92
|
+
review: () => {
|
|
93
|
+
next = "review";
|
|
94
|
+
return { kind: "close" };
|
|
95
|
+
},
|
|
96
|
+
scope: () => {
|
|
97
|
+
next = "scope";
|
|
98
|
+
return { kind: "close" };
|
|
99
|
+
},
|
|
100
|
+
settings: () => {
|
|
101
|
+
next = "settings";
|
|
102
|
+
return { kind: "close" };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
const result = await runMenu(ctx, menu, { signal, isCurrent: current, getState: state });
|
|
107
|
+
if (!current() || result.kind !== "closed") return;
|
|
108
|
+
if (next === "browse") await context.browse(ctx);
|
|
109
|
+
else if (next === "review") await context.open(ctx, "remove");
|
|
110
|
+
else if (next === "scope") await scope.open(ctx, signal);
|
|
111
|
+
else if (next === "settings") {
|
|
112
|
+
const { showFilesSettings } = await import("./files-settings-NYXQU2HV.ts");
|
|
113
|
+
if (current()) await showFilesSettings(ctx, context, scope);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export {
|
|
117
|
+
showFilesMenu
|
|
118
|
+
};
|
|
119
|
+
//# sourceMappingURL=files-menu-MES4MQ4F.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/files-menu.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ExtensionCommandContext } from \"@earendil-works/pi-coding-agent\";\nimport { defineMenu, runMenu } from \"@narumitw/pi-tui-kit\";\nimport type { ContextService } from \"./context-service.js\";\nimport { displayPath } from \"./scope/display.js\";\nimport type { ScopeService } from \"./scope/edit-scope.js\";\n\ntype Screen = \"main\" | \"status\" | \"help\";\ntype Action = \"browse\" | \"review\" | \"scope\" | \"settings\";\n\nexport async function showFilesMenu(\n ctx: ExtensionCommandContext,\n context: ContextService,\n scope: ScopeService,\n): Promise<void> {\n if (ctx.mode !== \"tui\" || !ctx.hasUI) throw new Error(\"/files requires Pi TUI mode\");\n const signal = AbortSignal.any([context.signal(ctx), scope.signal(ctx), ...(ctx.signal ? [ctx.signal] : [])]);\n const current = () => !signal.aborted;\n const state = () => ({ context: context.getState(ctx), scope: scope.getView(ctx) });\n let next: Action | undefined;\n const menu = defineMenu<ReturnType<typeof state>, Screen, Action, ExtensionCommandContext>({\n start: \"main\",\n screens: {\n main: ({ state }) => ({\n kind: \"actions\",\n title: \"Files\",\n lines: [\n `Next prompt context: ${state.context.quotes.length} snippets`,\n state.scope.state === \"inactive\"\n ? \"Edit scope: unrestricted\"\n : state.scope.state === \"blocked\"\n ? \"Edit scope: unavailable \u00B7 edits blocked\"\n : `Edit scope: ${state.scope.targets.length} allowed files`,\n ],\n items: [\n {\n id: \"browse\",\n label: \"Browse files\",\n description: \"Preview files and attach context; does not grant edit permission\",\n action: \"browse\",\n },\n {\n id: \"review\",\n label: `Review queued context (${state.context.quotes.length})`,\n description: \"Inspect exact snapshots and remove snippets\",\n action: \"review\",\n disabled: !state.context.quotes.length,\n },\n {\n id: \"scope\",\n label: \"Manage edit scope\",\n description: \"Select allowed targets, reserve a new file, or end the restriction\",\n action: \"scope\",\n },\n {\n id: \"settings\",\n label: \"Settings\",\n description: \"Browser shortcut and hidden-file discovery preference\",\n action: \"settings\",\n },\n { id: \"status\", label: \"Status\", description: \"Review context and every allowed target\", to: \"status\" },\n { id: \"help\", label: \"Help\", description: \"Read state lifetimes and the protection boundary\", to: \"help\" },\n ],\n hint: \"close\",\n }),\n status: ({ state }) => ({\n kind: \"detail\",\n title: \"Files Status\",\n lines: [\n `Next prompt context: ${state.context.quotes.length}/${state.context.maximumQuotes} snippets \u00B7 ${state.context.totalBytes}/${state.context.maximumBytes} bytes`,\n `Browser shortcut: ${state.context.shortcut ?? \"disabled\"}`,\n `Edit scope: ${state.scope.state}`,\n ...(state.scope.error ? [state.scope.error] : []),\n ...state.scope.targets.map(\n (target) =>\n `${displayPath(target.path)}${target.canonical !== target.path ? ` \u2192 ${displayPath(target.canonical)}` : \"\"}`,\n ),\n ...(state.scope.state === \"inactive\" ? [] : [\"/files off explicitly ends the editing restriction.\"]),\n \"Reserved new-file targets are created only by a normal write call.\",\n ],\n hint: \"back\",\n }),\n help: () => ({\n kind: \"detail\",\n title: \"Files Help\",\n lines: [\n \"Context snapshots attach once to the next ordinary prompt; selecting context never grants edit permission.\",\n \"Edit scope lasts until explicitly ended and is restored from the active session branch; selecting a target never loads its content.\",\n \"An active restriction allows only stock readers and target-checked native write/edit. Shell, codemode, delegation, and custom tools are blocked.\",\n \"This is an in-process guard, not an OS sandbox. The user-controlled external editor is outside native-tool enforcement.\",\n \"/file-context [browse|remove] and /edit-scope or /scope [off] keep their existing workflows.\",\n \"Do not load predecessor extensions with Files. Remove or disable their user and project resources, then reload.\",\n \"Escape goes back. Ctrl+C closes transient UI without ending an active restriction.\",\n ],\n hint: \"back\",\n }),\n },\n actions: {\n browse: () => {\n next = \"browse\";\n return { kind: \"close\" };\n },\n review: () => {\n next = \"review\";\n return { kind: \"close\" };\n },\n scope: () => {\n next = \"scope\";\n return { kind: \"close\" };\n },\n settings: () => {\n next = \"settings\";\n return { kind: \"close\" };\n },\n },\n });\n const result = await runMenu(ctx, menu, { signal, isCurrent: current, getState: state });\n if (!current() || result.kind !== \"closed\") return;\n if (next === \"browse\") await context.browse(ctx);\n else if (next === \"review\") await context.open(ctx, \"remove\");\n else if (next === \"scope\") await scope.open(ctx, signal);\n else if (next === \"settings\") {\n const { showFilesSettings } = await import(\"./files-settings.js\");\n if (current()) await showFilesSettings(ctx, context, scope);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["state"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
// @generated by scripts/build-runtime.mjs; do not edit.
|
|
2
|
+
// @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
|
|
3
|
+
import {
|
|
4
|
+
normalizeKeyId
|
|
5
|
+
} from "./chunk-OPGTWNJI.ts";
|
|
6
|
+
import {
|
|
7
|
+
ScopeInput
|
|
8
|
+
} from "./chunk-3GQ7VS22.ts";
|
|
9
|
+
import "./chunk-F5QFC255.ts";
|
|
10
|
+
import "./chunk-AXW73HDJ.ts";
|
|
11
|
+
|
|
12
|
+
// src/files-settings.ts
|
|
13
|
+
import { stripVTControlCharacters } from "node:util";
|
|
14
|
+
import { getSettingsListTheme } from "@earendil-works/pi-coding-agent";
|
|
15
|
+
import { matchesKey, SettingsList } from "@earendil-works/pi-tui";
|
|
16
|
+
import { HorizontalRule, renderBoundedFrameLayout, sanitizeTerminalText } from "@narumitw/pi-tui-kit";
|
|
17
|
+
async function showFilesSettings(ctx, context, scope) {
|
|
18
|
+
if (ctx.mode !== "tui" || !ctx.hasUI) throw new Error("Files settings require Pi TUI mode");
|
|
19
|
+
const ownerSignal = AbortSignal.any([context.signal(ctx), scope.signal(ctx), ...ctx.signal ? [ctx.signal] : []]);
|
|
20
|
+
if (ownerSignal.aborted) return;
|
|
21
|
+
const controller = new AbortController();
|
|
22
|
+
const signal = AbortSignal.any([ownerSignal, controller.signal]);
|
|
23
|
+
let pending = Promise.resolve();
|
|
24
|
+
let shortcut = context.getState(ctx).shortcut;
|
|
25
|
+
let hidden = scope.getSettings(ctx).showHiddenFiles;
|
|
26
|
+
let requestedHidden = hidden;
|
|
27
|
+
let hiddenSaves = 0;
|
|
28
|
+
let shortcutSaving = false;
|
|
29
|
+
let accepting = true;
|
|
30
|
+
let dispose;
|
|
31
|
+
let resolveDisposal;
|
|
32
|
+
const disposal = new Promise((resolve) => {
|
|
33
|
+
resolveDisposal = resolve;
|
|
34
|
+
});
|
|
35
|
+
let redraw = () => {
|
|
36
|
+
};
|
|
37
|
+
const live = () => !signal.aborted;
|
|
38
|
+
const notify = (message, type) => {
|
|
39
|
+
if (live()) ctx.ui.notify(sanitizeTerminalText(message), type);
|
|
40
|
+
};
|
|
41
|
+
const shortcutBlock = () => {
|
|
42
|
+
const state = context.getState(ctx);
|
|
43
|
+
if (state.settingsInvalidReason) {
|
|
44
|
+
return `Repair ${sanitizeTerminalText(state.settingsPath)} first: ${sanitizeTerminalText(state.settingsInvalidReason)}`;
|
|
45
|
+
}
|
|
46
|
+
if (state.quotes.length) return "Submit or remove queued context before changing the shortcut.";
|
|
47
|
+
if (shortcutSaving) return "Saving the shortcut\u2026";
|
|
48
|
+
return void 0;
|
|
49
|
+
};
|
|
50
|
+
const enqueue = (save) => {
|
|
51
|
+
pending = pending.then(async () => {
|
|
52
|
+
if (!live()) return;
|
|
53
|
+
try {
|
|
54
|
+
await save();
|
|
55
|
+
} catch (error) {
|
|
56
|
+
notify(`Could not save Files settings; the previous value remains: ${errorMessage(error)}`, "error");
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
let reload = false;
|
|
61
|
+
try {
|
|
62
|
+
const interaction = ctx.ui.custom((tui, theme, keybindings, done) => {
|
|
63
|
+
let ended = false;
|
|
64
|
+
let focused = false;
|
|
65
|
+
let editor;
|
|
66
|
+
let submenuDone;
|
|
67
|
+
let mouseRows = /* @__PURE__ */ new Map();
|
|
68
|
+
const rule = new HorizontalRule({ ruleStyle: (text) => theme.fg("border", text) });
|
|
69
|
+
const listTheme = getSettingsListTheme();
|
|
70
|
+
listTheme.label = (text, selected) => selected ? theme.fg("accent", text) : text;
|
|
71
|
+
listTheme.value = (text, selected) => theme.fg(selected ? "accent" : "muted", text);
|
|
72
|
+
listTheme.description = (text) => theme.fg("dim", text);
|
|
73
|
+
listTheme.hint = (text) => theme.fg("dim", text);
|
|
74
|
+
const release = () => {
|
|
75
|
+
if (ended) return false;
|
|
76
|
+
ended = true;
|
|
77
|
+
accepting = false;
|
|
78
|
+
focused = false;
|
|
79
|
+
if (editor) editor.input.focused = false;
|
|
80
|
+
signal.removeEventListener("abort", onAbort);
|
|
81
|
+
controller.abort();
|
|
82
|
+
return true;
|
|
83
|
+
};
|
|
84
|
+
const finish = (shouldReload) => {
|
|
85
|
+
if (release()) done(shouldReload);
|
|
86
|
+
};
|
|
87
|
+
const onAbort = () => finish(false);
|
|
88
|
+
dispose = () => {
|
|
89
|
+
if (release()) resolveDisposal(false);
|
|
90
|
+
};
|
|
91
|
+
redraw = () => {
|
|
92
|
+
if (live() && !ended) tui.requestRender();
|
|
93
|
+
};
|
|
94
|
+
const leaveSubmenu = () => {
|
|
95
|
+
if (editor) editor.input.focused = false;
|
|
96
|
+
editor = void 0;
|
|
97
|
+
const complete = submenuDone;
|
|
98
|
+
submenuDone = void 0;
|
|
99
|
+
complete?.();
|
|
100
|
+
};
|
|
101
|
+
const openShortcut = (_value, complete) => {
|
|
102
|
+
submenuDone = () => complete();
|
|
103
|
+
const input = new ScopeInput(shortcut ?? "", (text) => theme.fg("muted", text));
|
|
104
|
+
editor = input;
|
|
105
|
+
input.input.focused = focused;
|
|
106
|
+
input.input.onEscape = leaveSubmenu;
|
|
107
|
+
input.input.onSubmit = (value) => {
|
|
108
|
+
if (!accepting || !live()) return;
|
|
109
|
+
const blocked = shortcutBlock();
|
|
110
|
+
if (blocked) {
|
|
111
|
+
notify(blocked, "warning");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const raw = value.trim();
|
|
115
|
+
const normalized = raw ? normalizeKeyId(raw) : null;
|
|
116
|
+
if (normalized === void 0) {
|
|
117
|
+
notify("Invalid Pi key identifier. Use a value such as ctrl+shift+x or f8.", "warning");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
leaveSubmenu();
|
|
121
|
+
if (normalized === shortcut) return;
|
|
122
|
+
shortcutSaving = true;
|
|
123
|
+
enqueue(async () => {
|
|
124
|
+
try {
|
|
125
|
+
const state = context.getState(ctx);
|
|
126
|
+
if (state.quotes.length || state.settingsInvalidReason) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
"Submit or remove queued context and repair invalid context settings before changing the shortcut"
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
await context.saveShortcut(ctx, normalized, signal);
|
|
132
|
+
if (!live()) return;
|
|
133
|
+
shortcut = normalized;
|
|
134
|
+
accepting = false;
|
|
135
|
+
void pending.then(() => {
|
|
136
|
+
if (live()) finish(true);
|
|
137
|
+
});
|
|
138
|
+
} finally {
|
|
139
|
+
shortcutSaving = false;
|
|
140
|
+
redraw();
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
return {
|
|
145
|
+
render(width) {
|
|
146
|
+
return [
|
|
147
|
+
theme.fg("accent", "Open shortcut"),
|
|
148
|
+
...input.render(width),
|
|
149
|
+
theme.fg("dim", "Use a Pi key identifier. An empty value disables the shortcut.")
|
|
150
|
+
];
|
|
151
|
+
},
|
|
152
|
+
invalidate: () => input.input.invalidate(),
|
|
153
|
+
handleInput(data) {
|
|
154
|
+
input.handleInput(data, (key) => {
|
|
155
|
+
if (matchesKey(key, "ctrl+c")) {
|
|
156
|
+
finish(false);
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
});
|
|
161
|
+
},
|
|
162
|
+
handleMouse(event) {
|
|
163
|
+
return event.y === 1 ? input.handleMouse({ ...event, y: 0 }) : void 0;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
const items = [
|
|
168
|
+
{ id: "shortcut", label: "Open shortcut", currentValue: shortcut ?? "disabled" },
|
|
169
|
+
{
|
|
170
|
+
id: "hidden",
|
|
171
|
+
label: "Show hidden files",
|
|
172
|
+
description: "Include hidden files in the edit-scope picker. Applies immediately.",
|
|
173
|
+
currentValue: String(hidden),
|
|
174
|
+
values: ["false", "true"]
|
|
175
|
+
}
|
|
176
|
+
];
|
|
177
|
+
const list = new SettingsList(
|
|
178
|
+
items,
|
|
179
|
+
items.length,
|
|
180
|
+
listTheme,
|
|
181
|
+
(id) => {
|
|
182
|
+
if (id !== "hidden") return;
|
|
183
|
+
list.updateValue("hidden", String(hidden));
|
|
184
|
+
if (!accepting || !live()) return;
|
|
185
|
+
requestedHidden = !requestedHidden;
|
|
186
|
+
const value = requestedHidden;
|
|
187
|
+
hiddenSaves += 1;
|
|
188
|
+
enqueue(async () => {
|
|
189
|
+
try {
|
|
190
|
+
await scope.saveShowHiddenFiles(ctx, value);
|
|
191
|
+
if (!live()) return;
|
|
192
|
+
hidden = value;
|
|
193
|
+
} finally {
|
|
194
|
+
hiddenSaves -= 1;
|
|
195
|
+
if (!hiddenSaves) requestedHidden = hidden;
|
|
196
|
+
redraw();
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
},
|
|
200
|
+
() => finish(false)
|
|
201
|
+
);
|
|
202
|
+
const component = {
|
|
203
|
+
capturesViewportInput: true,
|
|
204
|
+
get focused() {
|
|
205
|
+
return focused;
|
|
206
|
+
},
|
|
207
|
+
set focused(value) {
|
|
208
|
+
focused = value;
|
|
209
|
+
if (editor) editor.input.focused = value;
|
|
210
|
+
},
|
|
211
|
+
render(width) {
|
|
212
|
+
if (!live()) return [];
|
|
213
|
+
const renderWidth = Number.isFinite(width) ? Math.max(0, Math.floor(width)) : 0;
|
|
214
|
+
const blocked = shortcutBlock();
|
|
215
|
+
const shortcutItem = items[0];
|
|
216
|
+
if (shortcutItem) {
|
|
217
|
+
shortcutItem.description = blocked ?? "Open the context browser. Saving reloads Pi.";
|
|
218
|
+
shortcutItem.submenu = blocked ? void 0 : openShortcut;
|
|
219
|
+
}
|
|
220
|
+
list.updateValue("shortcut", shortcut ?? "disabled");
|
|
221
|
+
list.updateValue("hidden", String(hidden));
|
|
222
|
+
listTheme.cursor = theme.fg("accent", "\u2192 ");
|
|
223
|
+
const native = list.render(editor ? renderWidth : Math.max(8, renderWidth));
|
|
224
|
+
const content = editor ? native : native.slice(0, -2);
|
|
225
|
+
const focusedRow = editor ? 1 : content.findIndex((line) => stripVTControlCharacters(line).startsWith("\u2192"));
|
|
226
|
+
const terminalRows = Number.isFinite(tui.terminal.rows) ? Math.floor(tui.terminal.rows) : 24;
|
|
227
|
+
const layout = renderBoundedFrameLayout({
|
|
228
|
+
width: renderWidth,
|
|
229
|
+
maxRows: Math.max(1, terminalRows - 3),
|
|
230
|
+
rule: rule.render(renderWidth)[0] ?? "",
|
|
231
|
+
title: [theme.fg("accent", theme.bold("Files Settings"))],
|
|
232
|
+
content,
|
|
233
|
+
priorityRows: focusedRow < 0 ? [] : [focusedRow],
|
|
234
|
+
focusedRow
|
|
235
|
+
});
|
|
236
|
+
mouseRows = new Map(layout.contentRows.map(({ frameIndex, contentIndex }) => [frameIndex, contentIndex]));
|
|
237
|
+
return layout.lines;
|
|
238
|
+
},
|
|
239
|
+
invalidate() {
|
|
240
|
+
rule.invalidate();
|
|
241
|
+
list.invalidate();
|
|
242
|
+
},
|
|
243
|
+
handleInput(data) {
|
|
244
|
+
if (!live()) return;
|
|
245
|
+
if (editor) list.handleInput(data);
|
|
246
|
+
else if (matchesKey(data, "ctrl+c")) finish(false);
|
|
247
|
+
else if (accepting) list.handleInput(data);
|
|
248
|
+
else if (keybindings.matches(data, "tui.select.cancel")) finish(false);
|
|
249
|
+
redraw();
|
|
250
|
+
},
|
|
251
|
+
handleMouse(event) {
|
|
252
|
+
if (!live() || !accepting) return void 0;
|
|
253
|
+
const row = mouseRows.get(event.y);
|
|
254
|
+
if (row === void 0) return void 0;
|
|
255
|
+
const result = list.handleMouse({ ...event, y: row });
|
|
256
|
+
redraw();
|
|
257
|
+
return result;
|
|
258
|
+
},
|
|
259
|
+
dispose
|
|
260
|
+
};
|
|
261
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
262
|
+
if (signal.aborted) finish(false);
|
|
263
|
+
return component;
|
|
264
|
+
});
|
|
265
|
+
reload = await Promise.race([interaction, disposal]);
|
|
266
|
+
} finally {
|
|
267
|
+
dispose?.();
|
|
268
|
+
controller.abort();
|
|
269
|
+
await pending;
|
|
270
|
+
}
|
|
271
|
+
if (!reload || ownerSignal.aborted) return;
|
|
272
|
+
if (context.getState(ctx).quotes.length) {
|
|
273
|
+
ctx.ui.notify("The shortcut was saved. Submit or remove queued context, then run /reload to apply it.", "warning");
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
await ctx.reload();
|
|
278
|
+
return;
|
|
279
|
+
} catch (error) {
|
|
280
|
+
if (ownerSignal.aborted) return;
|
|
281
|
+
ctx.ui.notify(
|
|
282
|
+
sanitizeTerminalText(
|
|
283
|
+
`The shortcut was saved, but Pi could not reload. Run /reload to apply it: ${errorMessage(error)}`
|
|
284
|
+
),
|
|
285
|
+
"error"
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function errorMessage(error) {
|
|
290
|
+
return error instanceof Error ? error.message : String(error);
|
|
291
|
+
}
|
|
292
|
+
export {
|
|
293
|
+
showFilesSettings
|
|
294
|
+
};
|
|
295
|
+
//# sourceMappingURL=files-settings-NYXQU2HV.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/files-settings.ts"],
|
|
4
|
+
"sourcesContent": ["import { stripVTControlCharacters } from \"node:util\";\nimport { type ExtensionCommandContext, getSettingsListTheme } from \"@earendil-works/pi-coding-agent\";\nimport { type Focusable, matchesKey, type SettingItem, SettingsList, type TuiMouseEvent } from \"@earendil-works/pi-tui\";\nimport { HorizontalRule, renderBoundedFrameLayout, sanitizeTerminalText } from \"@narumitw/pi-tui-kit\";\nimport type { ContextService } from \"./context-service.js\";\nimport { normalizeKeyId } from \"./file-context-settings.js\";\nimport type { ScopeService } from \"./scope/edit-scope.js\";\nimport { ScopeInput } from \"./scope/input.js\";\n\n/** Edit the two retained settings stores without changing context or edit permission. */\nexport async function showFilesSettings(\n ctx: ExtensionCommandContext,\n context: ContextService,\n scope: ScopeService,\n): Promise<void> {\n if (ctx.mode !== \"tui\" || !ctx.hasUI) throw new Error(\"Files settings require Pi TUI mode\");\n\n const ownerSignal = AbortSignal.any([context.signal(ctx), scope.signal(ctx), ...(ctx.signal ? [ctx.signal] : [])]);\n if (ownerSignal.aborted) return;\n const controller = new AbortController();\n const signal = AbortSignal.any([ownerSignal, controller.signal]);\n let pending = Promise.resolve();\n let shortcut = context.getState(ctx).shortcut;\n let hidden = scope.getSettings(ctx).showHiddenFiles;\n let requestedHidden = hidden;\n let hiddenSaves = 0;\n let shortcutSaving = false;\n let accepting = true;\n let dispose: (() => void) | undefined;\n let resolveDisposal!: (reload: boolean) => void;\n const disposal = new Promise<boolean>((resolve) => {\n resolveDisposal = resolve;\n });\n let redraw = () => {};\n const live = () => !signal.aborted;\n const notify = (message: string, type: \"warning\" | \"error\") => {\n if (live()) ctx.ui.notify(sanitizeTerminalText(message), type);\n };\n const shortcutBlock = (): string | undefined => {\n const state = context.getState(ctx);\n if (state.settingsInvalidReason) {\n return `Repair ${sanitizeTerminalText(state.settingsPath)} first: ${sanitizeTerminalText(state.settingsInvalidReason)}`;\n }\n if (state.quotes.length) return \"Submit or remove queued context before changing the shortcut.\";\n if (shortcutSaving) return \"Saving the shortcut\u2026\";\n return undefined;\n };\n // Every action joins one queue. A rejected store write cannot poison later actions.\n const enqueue = (save: () => Promise<void>) => {\n pending = pending.then(async () => {\n if (!live()) return;\n try {\n await save();\n } catch (error) {\n notify(`Could not save Files settings; the previous value remains: ${errorMessage(error)}`, \"error\");\n }\n });\n };\n\n let reload = false;\n try {\n const interaction = ctx.ui.custom<boolean>((tui, theme, keybindings, done) => {\n let ended = false;\n let focused = false;\n let editor: ScopeInput | undefined;\n let submenuDone: (() => void) | undefined;\n let mouseRows = new Map<number, number>();\n const rule = new HorizontalRule({ ruleStyle: (text) => theme.fg(\"border\", text) });\n // Keep native SettingsList styling and use the callback theme, including after invalidation.\n const listTheme = getSettingsListTheme();\n listTheme.label = (text, selected) => (selected ? theme.fg(\"accent\", text) : text);\n listTheme.value = (text, selected) => theme.fg(selected ? \"accent\" : \"muted\", text);\n listTheme.description = (text) => theme.fg(\"dim\", text);\n listTheme.hint = (text) => theme.fg(\"dim\", text);\n const release = () => {\n if (ended) return false;\n ended = true;\n accepting = false;\n focused = false;\n if (editor) editor.input.focused = false;\n signal.removeEventListener(\"abort\", onAbort);\n controller.abort();\n return true;\n };\n const finish = (shouldReload: boolean) => {\n if (release()) done(shouldReload);\n };\n const onAbort = () => finish(false);\n dispose = () => {\n // Pi resets its live dialog array through dispose(). Do not call done():\n // it would splice that array and restore a draft from the previous session.\n if (release()) resolveDisposal(false);\n };\n redraw = () => {\n if (live() && !ended) tui.requestRender();\n };\n const leaveSubmenu = () => {\n if (editor) editor.input.focused = false;\n editor = undefined;\n const complete = submenuDone;\n submenuDone = undefined;\n complete?.();\n };\n const openShortcut: NonNullable<SettingItem[\"submenu\"]> = (_value, complete) => {\n submenuDone = () => complete();\n const input = new ScopeInput(shortcut ?? \"\", (text) => theme.fg(\"muted\", text));\n editor = input;\n input.input.focused = focused;\n input.input.onEscape = leaveSubmenu;\n input.input.onSubmit = (value) => {\n if (!accepting || !live()) return;\n const blocked = shortcutBlock();\n if (blocked) {\n notify(blocked, \"warning\");\n return;\n }\n const raw = value.trim();\n const normalized = raw ? normalizeKeyId(raw) : null;\n if (normalized === undefined) {\n notify(\"Invalid Pi key identifier. Use a value such as ctrl+shift+x or f8.\", \"warning\");\n return;\n }\n leaveSubmenu();\n if (normalized === shortcut) return;\n shortcutSaving = true;\n enqueue(async () => {\n try {\n // Context can change while an earlier hidden-file save is pending.\n const state = context.getState(ctx);\n if (state.quotes.length || state.settingsInvalidReason) {\n throw new Error(\n \"Submit or remove queued context and repair invalid context settings before changing the shortcut\",\n );\n }\n await context.saveShortcut(ctx, normalized, signal);\n if (!live()) return;\n shortcut = normalized;\n accepting = false;\n // Drain all already accepted saves before closing and reloading Pi.\n void pending.then(() => {\n if (live()) finish(true);\n });\n } finally {\n shortcutSaving = false;\n redraw();\n }\n });\n };\n return {\n render(width) {\n return [\n theme.fg(\"accent\", \"Open shortcut\"),\n ...input.render(width),\n theme.fg(\"dim\", \"Use a Pi key identifier. An empty value disables the shortcut.\"),\n ];\n },\n invalidate: () => input.input.invalidate(),\n handleInput(data) {\n input.handleInput(data, (key) => {\n if (matchesKey(key, \"ctrl+c\")) {\n finish(false);\n return true;\n }\n // Input owns configured cancellation, editing, and submission priority.\n return false;\n });\n },\n handleMouse(event) {\n return event.y === 1 ? input.handleMouse({ ...event, y: 0 }) : undefined;\n },\n };\n };\n const items: SettingItem[] = [\n { id: \"shortcut\", label: \"Open shortcut\", currentValue: shortcut ?? \"disabled\" },\n {\n id: \"hidden\",\n label: \"Show hidden files\",\n description: \"Include hidden files in the edit-scope picker. Applies immediately.\",\n currentValue: String(hidden),\n values: [\"false\", \"true\"],\n },\n ];\n const list = new SettingsList(\n items,\n items.length,\n listTheme,\n (id) => {\n if (id !== \"hidden\") return;\n // Native toggles are synchronous. Display only durable values while retaining\n // each requested toggle separately, including rapid toggles during a write.\n list.updateValue(\"hidden\", String(hidden));\n if (!accepting || !live()) return;\n requestedHidden = !requestedHidden;\n const value = requestedHidden;\n hiddenSaves += 1;\n enqueue(async () => {\n try {\n await scope.saveShowHiddenFiles(ctx, value);\n if (!live()) return;\n hidden = value;\n } finally {\n hiddenSaves -= 1;\n if (!hiddenSaves) requestedHidden = hidden;\n redraw();\n }\n });\n },\n () => finish(false),\n );\n const component: Focusable & {\n capturesViewportInput: boolean;\n render(width: number): string[];\n invalidate(): void;\n handleInput(data: string): void;\n handleMouse(event: TuiMouseEvent): ReturnType<SettingsList[\"handleMouse\"]>;\n dispose(): void;\n } = {\n capturesViewportInput: true,\n get focused() {\n return focused;\n },\n set focused(value) {\n focused = value;\n if (editor) editor.input.focused = value;\n },\n render(width) {\n if (!live()) return [];\n const renderWidth = Number.isFinite(width) ? Math.max(0, Math.floor(width)) : 0;\n const blocked = shortcutBlock();\n const shortcutItem = items[0];\n if (shortcutItem) {\n shortcutItem.description = blocked ?? \"Open the context browser. Saving reloads Pi.\";\n shortcutItem.submenu = blocked ? undefined : openShortcut;\n }\n list.updateValue(\"shortcut\", shortcut ?? \"disabled\");\n list.updateValue(\"hidden\", String(hidden));\n listTheme.cursor = theme.fg(\"accent\", \"\u2192 \");\n // Native hints contain fixed Enter/Escape labels. Omit them rather than\n // advertise keys that can be remapped. Native input dispatch is unchanged.\n // Keep the native cursor marker measurable in narrow lists. Inputs must\n // receive their actual width so horizontal scrolling retains the IME cursor.\n const native = list.render(editor ? renderWidth : Math.max(8, renderWidth));\n const content = editor ? native : native.slice(0, -2);\n const focusedRow = editor ? 1 : content.findIndex((line) => stripVTControlCharacters(line).startsWith(\"\u2192\"));\n const terminalRows = Number.isFinite(tui.terminal.rows) ? Math.floor(tui.terminal.rows) : 24;\n const layout = renderBoundedFrameLayout({\n width: renderWidth,\n maxRows: Math.max(1, terminalRows - 3),\n rule: rule.render(renderWidth)[0] ?? \"\",\n title: [theme.fg(\"accent\", theme.bold(\"Files Settings\"))],\n content,\n priorityRows: focusedRow < 0 ? [] : [focusedRow],\n focusedRow,\n });\n mouseRows = new Map(layout.contentRows.map(({ frameIndex, contentIndex }) => [frameIndex, contentIndex]));\n return layout.lines;\n },\n invalidate() {\n rule.invalidate();\n list.invalidate();\n },\n handleInput(data) {\n if (!live()) return;\n if (editor) list.handleInput(data);\n else if (matchesKey(data, \"ctrl+c\")) finish(false);\n else if (accepting) list.handleInput(data);\n else if (keybindings.matches(data, \"tui.select.cancel\")) finish(false);\n redraw();\n },\n handleMouse(event) {\n if (!live() || !accepting) return undefined;\n const row = mouseRows.get(event.y);\n if (row === undefined) return undefined;\n const result = list.handleMouse({ ...event, y: row });\n redraw();\n return result;\n },\n dispose,\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n if (signal.aborted) finish(false);\n return component;\n });\n // Host reset can leave custom() pending. Release this flow independently.\n reload = await Promise.race([interaction, disposal]);\n } finally {\n dispose?.();\n controller.abort();\n // A hidden-file write cannot be interrupted through the service API. Do not\n // let command completion, session replacement, or shutdown race its durability.\n await pending;\n }\n if (!reload || ownerSignal.aborted) return;\n if (context.getState(ctx).quotes.length) {\n ctx.ui.notify(\"The shortcut was saved. Submit or remove queued context, then run /reload to apply it.\", \"warning\");\n return;\n }\n try {\n await ctx.reload();\n return;\n } catch (error) {\n if (ownerSignal.aborted) return;\n ctx.ui.notify(\n sanitizeTerminalText(\n `The shortcut was saved, but Pi could not reload. Run /reload to apply it: ${errorMessage(error)}`,\n ),\n \"error\",\n );\n }\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|