terminal-pilot 0.0.49 → 0.0.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/jetbrains-mono-400-italic.ttf +0 -0
- package/dist/assets/jetbrains-mono-400-normal.ttf +0 -0
- package/dist/assets/jetbrains-mono-700-italic.ttf +0 -0
- package/dist/assets/jetbrains-mono-700-normal.ttf +0 -0
- package/dist/cli.js +64 -32
- package/dist/cli.js.map +4 -4
- package/dist/commands/close-session.js +26 -0
- package/dist/commands/close-session.js.map +2 -2
- package/dist/commands/create-session.js +26 -0
- package/dist/commands/create-session.js.map +2 -2
- package/dist/commands/daemon-runtime.js.map +1 -1
- package/dist/commands/fill.js +26 -0
- package/dist/commands/fill.js.map +2 -2
- package/dist/commands/get-session.js +26 -0
- package/dist/commands/get-session.js.map +2 -2
- package/dist/commands/index.js +39 -6
- package/dist/commands/index.js.map +4 -4
- package/dist/commands/install.js +42 -3
- package/dist/commands/install.js.map +4 -4
- package/dist/commands/installer.js +16 -3
- package/dist/commands/installer.js.map +4 -4
- package/dist/commands/list-sessions.js +26 -0
- package/dist/commands/list-sessions.js.map +2 -2
- package/dist/commands/press-key.js +26 -0
- package/dist/commands/press-key.js.map +2 -2
- package/dist/commands/read-history.js +26 -0
- package/dist/commands/read-history.js.map +2 -2
- package/dist/commands/read-screen.js +26 -0
- package/dist/commands/read-screen.js.map +2 -2
- package/dist/commands/resize.js +26 -0
- package/dist/commands/resize.js.map +2 -2
- package/dist/commands/runtime.js.map +1 -1
- package/dist/commands/screenshot.js +26 -0
- package/dist/commands/screenshot.js.map +2 -2
- package/dist/commands/send-signal.js +26 -0
- package/dist/commands/send-signal.js.map +2 -2
- package/dist/commands/type.js +26 -0
- package/dist/commands/type.js.map +2 -2
- package/dist/commands/uninstall.js +42 -3
- package/dist/commands/uninstall.js.map +4 -4
- package/dist/commands/wait-for-exit.js +26 -0
- package/dist/commands/wait-for-exit.js.map +2 -2
- package/dist/commands/wait-for.js +26 -0
- package/dist/commands/wait-for.js.map +2 -2
- package/dist/composition.json +1 -1
- package/dist/testing/cli-repl.js +64 -32
- package/dist/testing/cli-repl.js.map +4 -4
- package/dist/testing/qa-cli.js +64 -32
- package/dist/testing/qa-cli.js.map +4 -4
- package/node_modules/@poe-code/user-error/README.md +56 -0
- package/node_modules/@poe-code/user-error/dist/index.d.ts +17 -0
- package/node_modules/@poe-code/user-error/dist/index.js +21 -0
- package/node_modules/@poe-code/user-error/package.json +24 -0
- package/node_modules/toolcraft-design/README.md +30 -0
- package/node_modules/toolcraft-design/dist/dashboard/terminal-width.d.ts +1 -0
- package/node_modules/toolcraft-design/dist/dashboard/terminal-width.js +18 -1
- package/node_modules/toolcraft-design/dist/explorer/actions.d.ts +3 -2
- package/node_modules/toolcraft-design/dist/explorer/actions.js +50 -4
- package/node_modules/toolcraft-design/dist/explorer/events.d.ts +7 -2
- package/node_modules/toolcraft-design/dist/explorer/filter.js +1 -1
- package/node_modules/toolcraft-design/dist/explorer/index.d.ts +2 -4
- package/node_modules/toolcraft-design/dist/explorer/index.js +1 -2
- package/node_modules/toolcraft-design/dist/explorer/jobs.d.ts +2 -2
- package/node_modules/toolcraft-design/dist/explorer/jobs.js +3 -5
- package/node_modules/toolcraft-design/dist/explorer/keymap.d.ts +13 -3
- package/node_modules/toolcraft-design/dist/explorer/keymap.js +89 -348
- package/node_modules/toolcraft-design/dist/explorer/layout.d.ts +1 -0
- package/node_modules/toolcraft-design/dist/explorer/layout.js +14 -4
- package/node_modules/toolcraft-design/dist/explorer/reducer.js +116 -16
- package/node_modules/toolcraft-design/dist/explorer/render/detail.d.ts +1 -1
- package/node_modules/toolcraft-design/dist/explorer/render/detail.js +45 -7
- package/node_modules/toolcraft-design/dist/explorer/render/footer.d.ts +1 -1
- package/node_modules/toolcraft-design/dist/explorer/render/footer.js +18 -3
- package/node_modules/toolcraft-design/dist/explorer/render/header.d.ts +1 -1
- package/node_modules/toolcraft-design/dist/explorer/render/header.js +6 -2
- package/node_modules/toolcraft-design/dist/explorer/render/index.d.ts +1 -1
- package/node_modules/toolcraft-design/dist/explorer/render/index.js +2 -1
- package/node_modules/toolcraft-design/dist/explorer/render/list.d.ts +16 -2
- package/node_modules/toolcraft-design/dist/explorer/render/list.js +13 -34
- package/node_modules/toolcraft-design/dist/explorer/render/modal.d.ts +1 -1
- package/node_modules/toolcraft-design/dist/explorer/render/modal.js +11 -11
- package/node_modules/toolcraft-design/dist/explorer/render/pane.d.ts +5 -2
- package/node_modules/toolcraft-design/dist/explorer/render/pane.js +9 -6
- package/node_modules/toolcraft-design/dist/explorer/render/test-fixtures.js +2 -3
- package/node_modules/toolcraft-design/dist/explorer/runtime.js +115 -73
- package/node_modules/toolcraft-design/dist/explorer/runtime.test-helpers.d.ts +23 -37
- package/node_modules/toolcraft-design/dist/explorer/runtime.test-helpers.js +32 -83
- package/node_modules/toolcraft-design/dist/explorer/state.d.ts +79 -8
- package/node_modules/toolcraft-design/dist/explorer/state.js +70 -18
- package/node_modules/toolcraft-design/dist/index.d.ts +8 -2
- package/node_modules/toolcraft-design/dist/index.js +5 -1
- package/node_modules/toolcraft-design/dist/screen/ansi-text.d.ts +2 -0
- package/node_modules/toolcraft-design/dist/screen/ansi-text.js +35 -0
- package/node_modules/toolcraft-design/dist/screen/screen.d.ts +40 -0
- package/node_modules/toolcraft-design/dist/screen/screen.js +122 -0
- package/node_modules/toolcraft-design/dist/screen/style.d.ts +16 -0
- package/node_modules/toolcraft-design/dist/screen/style.js +55 -0
- package/node_modules/toolcraft-design/dist/terminal/driver.d.ts +34 -0
- package/node_modules/toolcraft-design/dist/terminal/driver.js +65 -0
- package/node_modules/toolcraft-design/dist/terminal/input.d.ts +25 -0
- package/node_modules/toolcraft-design/dist/terminal/input.js +181 -0
- package/node_modules/toolcraft-design/dist/terminal/output.d.ts +10 -0
- package/node_modules/toolcraft-design/dist/terminal/output.js +44 -0
- package/package.json +2 -1
- package/node_modules/toolcraft-design/dist/explorer/two-pane.d.ts +0 -102
- package/node_modules/toolcraft-design/dist/explorer/two-pane.js +0 -510
- package/node_modules/toolcraft-design/dist/run-two-pane-explorer.d.ts +0 -2
- package/node_modules/toolcraft-design/dist/run-two-pane-explorer.js +0 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# @poe-code/user-error
|
|
2
|
+
|
|
3
|
+
The shared user-error type every workspace package throws for expected user
|
|
4
|
+
mistakes, and the guard the CLI renders them with.
|
|
5
|
+
|
|
6
|
+
`src/cli/errors.ts` cannot be imported from `packages/*`, so package code used to
|
|
7
|
+
throw a plain `Error` for recoverable conditions. `src/cli/bootstrap.ts` then
|
|
8
|
+
treated each one as a crash: an `Error:` prefix plus a `See logs at
|
|
9
|
+
~/.poe-code/logs/errors.log` pointer to a log that adds nothing for a typo. This
|
|
10
|
+
package gives packages a classification the CLI honours.
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { UserError, isUserError } from "@poe-code/user-error";
|
|
16
|
+
|
|
17
|
+
throw new UserError('Unknown agent "clyde". Try: claude, codex, gemini.');
|
|
18
|
+
|
|
19
|
+
throw new UserError("No API key found.", {
|
|
20
|
+
hint: "Create one at https://poe.com/api_key",
|
|
21
|
+
cause: readError
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (isUserError(error)) {
|
|
25
|
+
// render the message as guidance: no stack trace, no log pointer
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Throw a `UserError` when the user can fix the condition themselves, and say how:
|
|
30
|
+
name the value that was rejected and the valid ones, the path that was searched,
|
|
31
|
+
or the URL that issues the key. Keep plain `Error` for genuine failures — those
|
|
32
|
+
should reach the log.
|
|
33
|
+
|
|
34
|
+
## Public API
|
|
35
|
+
|
|
36
|
+
- `UserError`: `Error` subclass with `name === "UserError"` and an optional
|
|
37
|
+
`hint` for the recovery step. Accepts the standard `cause` option.
|
|
38
|
+
- `isUserError(error)`: true for a `UserError` **or** any `Error` whose `name` is
|
|
39
|
+
`"UserError"`.
|
|
40
|
+
|
|
41
|
+
## Cross-bundle recognition
|
|
42
|
+
|
|
43
|
+
`isUserError` matches on `error.name`, not `instanceof`. `toolcraft` publishes its
|
|
44
|
+
own `UserError` (`packages/toolcraft/src/user-error.ts`) as a separately released
|
|
45
|
+
framework, and an instance crossing a bundle boundary fails `instanceof` against
|
|
46
|
+
this package's class. Name matching recognises both, and mirrors the existing
|
|
47
|
+
`isSilentError()` precedent in `src/cli/errors.ts`. toolcraft deliberately does
|
|
48
|
+
not depend on this package — the coupling would run the wrong way.
|
|
49
|
+
|
|
50
|
+
## Config Options
|
|
51
|
+
|
|
52
|
+
This package reads no configuration.
|
|
53
|
+
|
|
54
|
+
## Environment Variables
|
|
55
|
+
|
|
56
|
+
This package reads no environment variables.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An expected user mistake: bad input, a missing file, an unknown id, absent
|
|
3
|
+
* credentials. Thrown where the condition is detected so the CLI can render the
|
|
4
|
+
* message as guidance instead of dressing it in system-failure chrome.
|
|
5
|
+
*/
|
|
6
|
+
export declare class UserError extends Error {
|
|
7
|
+
readonly hint?: string;
|
|
8
|
+
constructor(message: string, options?: ErrorOptions & {
|
|
9
|
+
hint?: string;
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Detects user errors by name as well as identity, so an instance created in
|
|
14
|
+
* another bundle (toolcraft publishes its own `UserError`) is still recognised
|
|
15
|
+
* where `instanceof` would fail.
|
|
16
|
+
*/
|
|
17
|
+
export declare function isUserError(error: unknown): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An expected user mistake: bad input, a missing file, an unknown id, absent
|
|
3
|
+
* credentials. Thrown where the condition is detected so the CLI can render the
|
|
4
|
+
* message as guidance instead of dressing it in system-failure chrome.
|
|
5
|
+
*/
|
|
6
|
+
export class UserError extends Error {
|
|
7
|
+
hint;
|
|
8
|
+
constructor(message, options) {
|
|
9
|
+
super(message, options);
|
|
10
|
+
this.name = "UserError";
|
|
11
|
+
this.hint = options?.hint;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Detects user errors by name as well as identity, so an instance created in
|
|
16
|
+
* another bundle (toolcraft publishes its own `UserError`) is still recognised
|
|
17
|
+
* where `instanceof` would fail.
|
|
18
|
+
*/
|
|
19
|
+
export function isUserError(error) {
|
|
20
|
+
return error instanceof Error && error.name === "UserError";
|
|
21
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"private": true,
|
|
3
|
+
"license": "MIT",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "node ../../scripts/guard-package-dist.mjs && tsc"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/poe-platform/poe-code.git",
|
|
22
|
+
"directory": "packages/user-error"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -83,6 +83,36 @@ import { renderTable } from "toolcraft-design/render-table";
|
|
|
83
83
|
- Root-level types: `Row`, `DetailItem`, `Detail`, `DetailCtx`, `Action`, `ActionContext`, `ExplorerConfig`, `ReorderContext`, `Tone`.
|
|
84
84
|
- The namespace also exports `createInitialState`, `resolveBindings`, and the remaining explorer event, binding, layout, size, state, and effect types.
|
|
85
85
|
|
|
86
|
+
Explorers declare one to three list or detail panes. Printable input always filters; actions use optional Ctrl accelerators:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
await runExplorer({
|
|
90
|
+
title: "Plans",
|
|
91
|
+
panes: [
|
|
92
|
+
{ id: "plans", kind: "list", title: "Plans", rows: loadPlans },
|
|
93
|
+
{ id: "preview", kind: "detail", title: "Preview", render: renderPlan }
|
|
94
|
+
],
|
|
95
|
+
actions: [
|
|
96
|
+
{ id: "edit", label: "Edit", accelerator: "e", handler: editPlan },
|
|
97
|
+
{ id: "remove", label: "Remove", destructive: true, handler: removePlan }
|
|
98
|
+
]
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
| Input | Behavior |
|
|
103
|
+
| --- | --- |
|
|
104
|
+
| Printable text / paste | Filter the focused list |
|
|
105
|
+
| Up, Down, PageUp, PageDown, Home, End | Navigate |
|
|
106
|
+
| Ctrl+U, Ctrl+D | Move half a page |
|
|
107
|
+
| Enter | Open the action menu |
|
|
108
|
+
| Ctrl+P | Open the command palette |
|
|
109
|
+
| Tab | Cycle pane focus |
|
|
110
|
+
| Space | Toggle selection |
|
|
111
|
+
| Escape | Dismiss, clear the filter, then quit |
|
|
112
|
+
| Ctrl+C | Quit immediately |
|
|
113
|
+
|
|
114
|
+
Set `POE_CODE_TUI_TRACE` to a file path to write JSONL input and frame diagnostics. It is unset and disabled by default.
|
|
115
|
+
|
|
86
116
|
### Terminal Markdown
|
|
87
117
|
|
|
88
118
|
- `parse`: parses Markdown into `MdNode` values.
|
|
@@ -2,3 +2,4 @@ export declare function graphemes(value: string): string[];
|
|
|
2
2
|
export declare function displayWidth(value: string, startColumn?: number): number;
|
|
3
3
|
export declare function expandTabs(value: string, startColumn?: number): string;
|
|
4
4
|
export declare function graphemeWidth(segment: string): number;
|
|
5
|
+
export declare function truncateToWidth(text: string, width: number): string;
|
|
@@ -33,7 +33,24 @@ export function graphemeWidth(segment) {
|
|
|
33
33
|
if (codePoint === undefined || isZeroWidthCodePoint(codePoint)) {
|
|
34
34
|
return 0;
|
|
35
35
|
}
|
|
36
|
-
return isWideCodePoint(codePoint) || isFlagSegment(segment) ? 2 : 1;
|
|
36
|
+
return isWideCodePoint(codePoint) || isFlagSegment(segment) || segment.includes("\ufe0f") ? 2 : 1;
|
|
37
|
+
}
|
|
38
|
+
export function truncateToWidth(text, width) {
|
|
39
|
+
if (width <= 0)
|
|
40
|
+
return "";
|
|
41
|
+
if (displayWidth(text) <= width)
|
|
42
|
+
return text;
|
|
43
|
+
const target = Math.max(0, width - 1);
|
|
44
|
+
let result = "";
|
|
45
|
+
let used = 0;
|
|
46
|
+
for (const segment of graphemes(text)) {
|
|
47
|
+
const segmentWidth = graphemeWidth(segment);
|
|
48
|
+
if (used + segmentWidth > target)
|
|
49
|
+
break;
|
|
50
|
+
result += segment;
|
|
51
|
+
used += segmentWidth;
|
|
52
|
+
}
|
|
53
|
+
return `${result}…`;
|
|
37
54
|
}
|
|
38
55
|
function isZeroWidthCodePoint(codePoint) {
|
|
39
56
|
return (codePoint >= 0x0300 && codePoint <= 0x036f)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ExplorerEvent } from "./events.js";
|
|
2
2
|
import type { Action, ActionContext, ExplorerState, Row, Tone } from "./state.js";
|
|
3
|
-
export type ActionSource = "row" | "detail";
|
|
3
|
+
export type ActionSource = "row" | "detail" | "both";
|
|
4
4
|
type ExplorerKeypressEvent = Extract<ExplorerEvent, {
|
|
5
5
|
type: "key";
|
|
6
6
|
}>["key"];
|
|
@@ -13,7 +13,8 @@ export type ActionRuntimeHandles = {
|
|
|
13
13
|
content: string;
|
|
14
14
|
}) => void;
|
|
15
15
|
toast: (msg: string, tone?: Tone) => void;
|
|
16
|
-
confirm:
|
|
16
|
+
confirm: ActionContext<unknown>["confirm"];
|
|
17
|
+
promptText: ActionContext<unknown>["promptText"];
|
|
17
18
|
exit: (after?: () => void | Promise<void>) => void;
|
|
18
19
|
};
|
|
19
20
|
export declare function resolveAction<R>(state: ExplorerState, keyEvent: ExplorerKeypressEvent): Action<R> | null;
|
|
@@ -12,11 +12,14 @@ export function resolveAction(state, keyEvent) {
|
|
|
12
12
|
return actionState.action;
|
|
13
13
|
}
|
|
14
14
|
export function buildActionContext(state, _action, source, runtimeHandles, rowsOverride) {
|
|
15
|
-
const
|
|
15
|
+
const detailActive = source === "both" && state.focused === "detail";
|
|
16
|
+
const row = rowsOverride?.[0] ??
|
|
17
|
+
(detailActive ? currentDetailRow(state) : currentRow(state)) ?? { id: "", title: "" };
|
|
18
|
+
const panes = runtimePanes(state);
|
|
16
19
|
return {
|
|
17
20
|
row,
|
|
18
21
|
rows: rowsOverride ?? selectedRows(state, row),
|
|
19
|
-
item: source === "detail" ? currentDetailItem(state) : undefined,
|
|
22
|
+
item: source === "detail" || detailActive ? currentDetailItem(state) : undefined,
|
|
20
23
|
filter: state.filter,
|
|
21
24
|
refresh: runtimeHandles.refresh,
|
|
22
25
|
reloadDetail: runtimeHandles.reloadDetail,
|
|
@@ -24,7 +27,10 @@ export function buildActionContext(state, _action, source, runtimeHandles, rowsO
|
|
|
24
27
|
openModal: runtimeHandles.openModal,
|
|
25
28
|
toast: runtimeHandles.toast,
|
|
26
29
|
confirm: runtimeHandles.confirm,
|
|
27
|
-
|
|
30
|
+
promptText: runtimeHandles.promptText,
|
|
31
|
+
exit: runtimeHandles.exit,
|
|
32
|
+
activePane: state.focused === "detail" ? panes[1] : panes[0],
|
|
33
|
+
inactivePane: state.focused === "detail" ? panes[0] : panes[1]
|
|
28
34
|
};
|
|
29
35
|
}
|
|
30
36
|
function currentRow(state) {
|
|
@@ -33,9 +39,49 @@ function currentRow(state) {
|
|
|
33
39
|
function currentDetailItem(state) {
|
|
34
40
|
return state.detail.items?.[state.detail.cursor];
|
|
35
41
|
}
|
|
42
|
+
function currentDetailRow(state) {
|
|
43
|
+
const item = currentDetailItem(state);
|
|
44
|
+
return item === undefined
|
|
45
|
+
? undefined
|
|
46
|
+
: { id: item.id, title: item.title ?? item.id, subtitle: item.subtitle, badge: item.badge };
|
|
47
|
+
}
|
|
36
48
|
function selectedRows(state, fallback) {
|
|
37
49
|
if (!state.multiSelect || state.selected.size === 0) {
|
|
38
50
|
return fallback.id === "" ? [] : [fallback];
|
|
39
51
|
}
|
|
40
|
-
|
|
52
|
+
const source = state.focused === "detail"
|
|
53
|
+
? (state.detail.items ?? []).map((item) => ({
|
|
54
|
+
id: item.id,
|
|
55
|
+
title: item.title ?? item.id,
|
|
56
|
+
subtitle: item.subtitle,
|
|
57
|
+
badge: item.badge
|
|
58
|
+
}))
|
|
59
|
+
: state.rows;
|
|
60
|
+
return source.filter((row) => state.selected.has(row.id));
|
|
61
|
+
}
|
|
62
|
+
function runtimePanes(state) {
|
|
63
|
+
const definitions = state.paneDefinitions;
|
|
64
|
+
return [
|
|
65
|
+
{
|
|
66
|
+
id: definitions[0]?.id ?? "list",
|
|
67
|
+
title: definitions[0]?.title ?? state.title,
|
|
68
|
+
rows: state.rows,
|
|
69
|
+
cursor: state.cursor,
|
|
70
|
+
selected: state.selected,
|
|
71
|
+
filter: state.filter
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: definitions[1]?.id ?? "detail",
|
|
75
|
+
title: definitions[1]?.title ?? "Preview",
|
|
76
|
+
rows: (state.detail.items ?? []).map((item) => ({
|
|
77
|
+
id: item.id,
|
|
78
|
+
title: item.title ?? item.id,
|
|
79
|
+
subtitle: item.subtitle,
|
|
80
|
+
badge: item.badge
|
|
81
|
+
})),
|
|
82
|
+
cursor: state.detail.cursor,
|
|
83
|
+
selected: state.selected,
|
|
84
|
+
filter: ""
|
|
85
|
+
}
|
|
86
|
+
];
|
|
41
87
|
}
|
|
@@ -65,7 +65,12 @@ export type ExplorerEvent = {
|
|
|
65
65
|
};
|
|
66
66
|
export type ConfirmModal = {
|
|
67
67
|
kind: "confirm";
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
title: string;
|
|
69
|
+
message: string;
|
|
70
|
+
confirmLabel: string;
|
|
71
|
+
cancelLabel: string;
|
|
72
|
+
destructive: boolean;
|
|
73
|
+
action?: Action<unknown>;
|
|
74
|
+
rows?: Row[];
|
|
70
75
|
resolver: (ok: boolean) => void;
|
|
71
76
|
};
|
|
@@ -3,7 +3,7 @@ const CONSECUTIVE_BONUS = 14;
|
|
|
3
3
|
const WORD_START_BONUS = 10;
|
|
4
4
|
const EARLY_MATCH_BONUS = 3;
|
|
5
5
|
export function filterRows(query, rows, opts = {}) {
|
|
6
|
-
if (query.length === 0) {
|
|
6
|
+
if (query.trim().length === 0) {
|
|
7
7
|
return rows.map((_, index) => ({ index, score: 0, positions: [] }));
|
|
8
8
|
}
|
|
9
9
|
const preparedQuery = opts.caseSensitive === true ? query : query.toLocaleLowerCase();
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import type { Detail, DetailCtx, Row } from "./state.js";
|
|
2
2
|
export { runExplorer } from "./runtime.js";
|
|
3
|
-
export {
|
|
4
|
-
export { createInitialState } from "./state.js";
|
|
3
|
+
export { createInitialState, normalizeExplorerConfig } from "./state.js";
|
|
5
4
|
export { resolveBindings } from "./keymap.js";
|
|
6
5
|
export type { Effect, ExplorerEvent } from "./events.js";
|
|
7
6
|
export type { BindingTarget, ExplorerBindingDefaults, ExplorerBuiltinCommand, ResolvedBindings } from "./keymap.js";
|
|
8
|
-
export type { Action, ActionContext, Detail, DetailCtx, DetailItem, Dirty, ExplorerConfig, ExplorerLayoutMode, ExplorerSize, ExplorerState, ReorderContext, Row, Tone } from "./state.js";
|
|
9
|
-
export type { TwoPaneAction, TwoPaneActionContext, TwoPaneDefinition, TwoPaneExplorerConfig, TwoPaneExplorerState, TwoPanePaneState, TwoPaneRow } from "./two-pane.js";
|
|
7
|
+
export type { Action, ActionContext, ConfirmPromptOptions, Detail, DetailCtx, DetailItem, Dirty, ExplorerConfig, PaneConfig, ListPaneConfig, DetailPaneConfig, PaneRuntimeState, ExplorerLayoutMode, ExplorerSize, ExplorerState, ReorderContext, Row, Tone } from "./state.js";
|
|
10
8
|
export declare function singleDetail<R>(fn: (row: Row, ctx: DetailCtx) => string | Promise<string>): Detail<R>;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export { runExplorer } from "./runtime.js";
|
|
2
|
-
export {
|
|
3
|
-
export { createInitialState } from "./state.js";
|
|
2
|
+
export { createInitialState, normalizeExplorerConfig } from "./state.js";
|
|
4
3
|
export { resolveBindings } from "./keymap.js";
|
|
5
4
|
export function singleDetail(fn) {
|
|
6
5
|
return {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ExplorerEvent } from "./events.js";
|
|
2
2
|
import type { DetailCtx, DetailItem } from "./state.js";
|
|
3
3
|
export declare const LOADING_INDICATOR_MS = 150;
|
|
4
|
-
export declare const DETAIL_DEBOUNCE_MS =
|
|
4
|
+
export declare const DETAIL_DEBOUNCE_MS = 30;
|
|
5
5
|
export declare function createDetailJobs(emit: (event: ExplorerEvent) => void): {
|
|
6
|
-
schedule: (rowId: string, items: (ctx: DetailCtx) => Promise<DetailItem[]>, ctx: DetailCtx) => Promise<void>;
|
|
6
|
+
schedule: (rowId: string, token: number, items: (ctx: DetailCtx) => Promise<DetailItem[]>, ctx: DetailCtx) => Promise<void>;
|
|
7
7
|
abort: () => void;
|
|
8
8
|
};
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
export const LOADING_INDICATOR_MS = 150;
|
|
2
|
-
export const DETAIL_DEBOUNCE_MS =
|
|
2
|
+
export const DETAIL_DEBOUNCE_MS = 30;
|
|
3
3
|
export function createDetailJobs(emit) {
|
|
4
|
-
let token = 0;
|
|
5
4
|
let lastScheduleAt = 0;
|
|
6
5
|
let current = null;
|
|
7
6
|
const abortedTokens = new Set();
|
|
8
7
|
return {
|
|
9
|
-
async schedule(rowId, items, ctx) {
|
|
8
|
+
async schedule(rowId, nextToken, items, ctx) {
|
|
10
9
|
if (current !== null) {
|
|
11
10
|
current.controller.abort();
|
|
12
11
|
clearTimeout(current.loadingTimer);
|
|
@@ -14,7 +13,6 @@ export function createDetailJobs(emit) {
|
|
|
14
13
|
const scheduledAt = Date.now();
|
|
15
14
|
const debounce = scheduledAt - lastScheduleAt < DETAIL_DEBOUNCE_MS;
|
|
16
15
|
lastScheduleAt = scheduledAt;
|
|
17
|
-
const nextToken = ++token;
|
|
18
16
|
const controller = new AbortController();
|
|
19
17
|
let finished = false;
|
|
20
18
|
const loadingTimer = setTimeout(() => {
|
|
@@ -26,7 +24,7 @@ export function createDetailJobs(emit) {
|
|
|
26
24
|
try {
|
|
27
25
|
if (debounce) {
|
|
28
26
|
await waitUnlessAborted(DETAIL_DEBOUNCE_MS, controller.signal);
|
|
29
|
-
if (controller.signal.aborted ||
|
|
27
|
+
if (controller.signal.aborted || current?.token !== nextToken) {
|
|
30
28
|
return;
|
|
31
29
|
}
|
|
32
30
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ExplorerEvent } from "./events.js";
|
|
2
2
|
import type { ExplorerConfig } from "./state.js";
|
|
3
|
-
export type ExplorerBuiltinCommand = "quit" | "filter" | "help" | "palette" | "cursorUp" | "cursorDown" | "top" | "bottom" | "pageUp" | "pageDown" | "focusNext" | "escape" | "confirm" | "toggleSelect" | "selectAll" | "clearSelection" | "detailScrollDown" | "detailScrollUp" | "extendSelectionUp" | "extendSelectionDown" | "reorderUp" | "reorderDown";
|
|
3
|
+
export type ExplorerBuiltinCommand = "quit" | "filter" | "help" | "palette" | "cursorUp" | "cursorDown" | "top" | "bottom" | "pageUp" | "pageDown" | "focusNext" | "escape" | "confirm" | "halfPageUp" | "halfPageDown" | "toggleSelect" | "selectAll" | "clearSelection" | "detailScrollDown" | "detailScrollUp" | "extendSelectionUp" | "extendSelectionDown" | "reorderUp" | "reorderDown";
|
|
4
4
|
export type BindingTarget = {
|
|
5
5
|
type: "builtin";
|
|
6
6
|
id: ExplorerBuiltinCommand;
|
|
@@ -8,14 +8,24 @@ export type BindingTarget = {
|
|
|
8
8
|
type: "action";
|
|
9
9
|
id: string;
|
|
10
10
|
};
|
|
11
|
-
type
|
|
11
|
+
type Key = Extract<ExplorerEvent, {
|
|
12
12
|
type: "key";
|
|
13
13
|
}>["key"];
|
|
14
14
|
export interface ResolvedBindings {
|
|
15
15
|
bindings: ReadonlyMap<string, BindingTarget>;
|
|
16
16
|
keysByTarget: ReadonlyMap<string, readonly string[]>;
|
|
17
|
-
resolve: (event:
|
|
17
|
+
resolve: (event: Key) => BindingTarget | undefined;
|
|
18
18
|
}
|
|
19
19
|
export type ExplorerBindingDefaults = Partial<Record<ExplorerBuiltinCommand, string[]>>;
|
|
20
|
+
export interface HelpSection {
|
|
21
|
+
title: string;
|
|
22
|
+
entries: Array<{
|
|
23
|
+
key: string;
|
|
24
|
+
label: string;
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
20
27
|
export declare function resolveBindings<R>(config: ExplorerConfig<R>, defaults?: ExplorerBindingDefaults): ResolvedBindings;
|
|
28
|
+
export declare function assertNoBareLetterBindings<R>(config: ExplorerConfig<R>): void;
|
|
29
|
+
export declare function assertAcceleratorsFree<R>(config: ExplorerConfig<R>): void;
|
|
30
|
+
export declare function keymapToHelp<R>(config: ExplorerConfig<R>): HelpSection[];
|
|
21
31
|
export {};
|