fix lint: make bun tsc --noEmit actually pass (was a TS crash + latent errors)

tsconfig used jsx:"preserve"+jsxImportSource, which trips a TS 5.9
internal crash ("Expected sourceFile.imports[0] to be the synthesized JSX
runtime import") so tsc could never run clean. Switch to jsx:"react-jsx"
with the package's real jsx-runtime types, and fix the real errors that
surfaced:
- delete dead src/components/Navigation.tsx (imported ./Tab that doesn't
  exist; the component has no importers)
- PlaybackControls: fix relative path to @/utils/audio-player
- SourceBadge: drop dead module-level typeColor (bare 'theme')
- command palette: bind to the real 'command' keybind (:) instead of the
  never-defined 'command_list' action (palette was unreachable)
- yazi-pane-row test: destroy() must return Promise<void> as typed

Also fold in the package.json lint fix (bun tsc --noEmit) and doc polish.
This commit is contained in:
2026-08-07 18:18:25 -04:00
parent 1d3abd53d4
commit 0cc15c8d90
8 changed files with 403 additions and 410 deletions

View File

@@ -1,13 +1,13 @@
import {
createContext,
createMemo,
createSignal,
onCleanup,
useContext,
type Accessor,
type ParentProps,
For,
Show,
createContext,
createMemo,
createSignal,
onCleanup,
useContext,
type Accessor,
type ParentProps,
For,
Show,
} from "solid-js";
import { useKeyboard, useTerminalDimensions } from "@opentui/solid";
import { KeybindsResolved, useKeybinds } from "../context/KeybindContext";
@@ -21,313 +21,319 @@ import { SelectableBox, SelectableText } from "@/components/Selectable";
* Command option for the command palette.
*/
export type CommandOption = {
/** Display title */
title: string;
/** Unique identifier */
value: string;
/** Description shown below title */
description?: string;
/** Category for grouping */
category?: string;
/** Keybind reference */
keybind?: keyof KeybindsResolved;
/** Whether this command is suggested */
suggested?: boolean;
/** Slash command configuration */
slash?: {
name: string;
aliases?: string[];
};
/** Whether to hide from command list */
hidden?: boolean;
/** Whether command is enabled */
enabled?: boolean;
/** Footer text (usually keybind display) */
footer?: string;
/** Handler when command is selected */
onSelect?: (dialog: ReturnType<typeof useDialog>) => void;
/** Display title */
title: string;
/** Unique identifier */
value: string;
/** Description shown below title */
description?: string;
/** Category for grouping */
category?: string;
/** Keybind reference */
keybind?: keyof KeybindsResolved;
/** Whether this command is suggested */
suggested?: boolean;
/** Slash command configuration */
slash?: {
name: string;
aliases?: string[];
};
/** Whether to hide from command list */
hidden?: boolean;
/** Whether command is enabled */
enabled?: boolean;
/** Footer text (usually keybind display) */
footer?: string;
/** Handler when command is selected */
onSelect?: (dialog: ReturnType<typeof useDialog>) => void;
};
type CommandContext = ReturnType<typeof init>;
const ctx = createContext<CommandContext>();
function init() {
const [registrations, setRegistrations] = createSignal<
Accessor<CommandOption[]>[]
>([]);
const [suspendCount, setSuspendCount] = createSignal(0);
const dialog = useDialog();
const keybind = useKeybinds();
const [registrations, setRegistrations] = createSignal<
Accessor<CommandOption[]>[]
>([]);
const [suspendCount, setSuspendCount] = createSignal(0);
const dialog = useDialog();
const keybind = useKeybinds();
const entries = createMemo(() => {
const all = registrations().flatMap((x) => x());
return all.map((x) => ({
...x,
footer: x.keybind ? keybind.print(x.keybind) : undefined,
}));
});
const entries = createMemo(() => {
const all = registrations().flatMap((x) => x());
return all.map((x) => ({
...x,
footer: x.keybind ? keybind.print(x.keybind) : undefined,
}));
});
const isEnabled = (option: CommandOption) => option.enabled !== false;
const isVisible = (option: CommandOption) =>
isEnabled(option) && !option.hidden;
const isEnabled = (option: CommandOption) => option.enabled !== false;
const isVisible = (option: CommandOption) =>
isEnabled(option) && !option.hidden;
const visibleOptions = createMemo(() =>
entries().filter((option) => isVisible(option)),
);
const suggestedOptions = createMemo(() =>
visibleOptions()
.filter((option) => option.suggested)
.map((option) => ({
...option,
value: `suggested:${option.value}`,
category: "Suggested",
})),
);
const suspended = () => suspendCount() > 0;
const visibleOptions = createMemo(() =>
entries().filter((option) => isVisible(option)),
);
const suggestedOptions = createMemo(() =>
visibleOptions()
.filter((option) => option.suggested)
.map((option) => ({
...option,
value: `suggested:${option.value}`,
category: "Suggested",
})),
);
const suspended = () => suspendCount() > 0;
// Handle keybind shortcuts
useKeyboard((evt) => {
if (suspended()) return;
if (dialog.isOpen) return;
for (const option of entries()) {
if (!isEnabled(option)) continue;
if (option.keybind && keybind.match(option.keybind, evt)) {
evt.preventDefault();
option.onSelect?.(dialog);
emit("command.execute", { command: option.value });
return;
}
}
});
// Handle keybind shortcuts
useKeyboard((evt) => {
if (suspended()) return;
if (dialog.isOpen) return;
for (const option of entries()) {
if (!isEnabled(option)) continue;
if (option.keybind && keybind.match(option.keybind, evt)) {
evt.preventDefault();
option.onSelect?.(dialog);
emit("command.execute", { command: option.value });
return;
}
}
});
const result = {
/**
* Trigger a command by its value.
*/
trigger(name: string) {
for (const option of entries()) {
if (option.value === name) {
if (!isEnabled(option)) return;
option.onSelect?.(dialog);
emit("command.execute", { command: name });
return;
}
}
},
/**
* Get all slash commands.
*/
slashes() {
return visibleOptions().flatMap((option) => {
const slash = option.slash;
if (!slash) return [];
return {
display: "/" + slash.name,
description: option.description ?? option.title,
aliases: slash.aliases?.map((alias) => "/" + alias),
onSelect: () => result.trigger(option.value),
};
});
},
/**
* Enable/disable keybinds temporarily.
*/
keybinds(enabled: boolean) {
setSuspendCount((count) => count + (enabled ? -1 : 1));
},
suspended,
/**
* Show the command palette dialog.
*/
show() {
dialog.replace(() => (
<CommandDialog
options={visibleOptions()}
suggestedOptions={suggestedOptions()}
/>
));
},
/**
* Register commands. Returns cleanup function.
*/
register(cb: () => CommandOption[]) {
const results = createMemo(cb);
setRegistrations((arr) => [results, ...arr]);
onCleanup(() => {
setRegistrations((arr) => arr.filter((x) => x !== results));
});
},
/**
* Get all visible options.
*/
get options() {
return visibleOptions();
},
};
return result;
const result = {
/**
* Trigger a command by its value.
*/
trigger(name: string) {
for (const option of entries()) {
if (option.value === name) {
if (!isEnabled(option)) return;
option.onSelect?.(dialog);
emit("command.execute", { command: name });
return;
}
}
},
/**
* Get all slash commands.
*/
slashes() {
return visibleOptions().flatMap((option) => {
const slash = option.slash;
if (!slash) return [];
return {
display: "/" + slash.name,
description: option.description ?? option.title,
aliases: slash.aliases?.map((alias) => "/" + alias),
onSelect: () => result.trigger(option.value),
};
});
},
/**
* Enable/disable keybinds temporarily.
*/
keybinds(enabled: boolean) {
setSuspendCount((count) => count + (enabled ? -1 : 1));
},
suspended,
/**
* Show the command palette dialog.
*/
show() {
dialog.replace(() => (
<CommandDialog
options={visibleOptions()}
suggestedOptions={suggestedOptions()}
/>
));
},
/**
* Register commands. Returns cleanup function.
*/
register(cb: () => CommandOption[]) {
const results = createMemo(cb);
setRegistrations((arr) => [results, ...arr]);
onCleanup(() => {
setRegistrations((arr) => arr.filter((x) => x !== results));
});
},
/**
* Get all visible options.
*/
get options() {
return visibleOptions();
},
};
return result;
}
export function useCommandDialog() {
const value = useContext(ctx);
if (!value) {
throw new Error("useCommandDialog must be used within a CommandProvider");
}
return value;
const value = useContext(ctx);
if (!value) {
throw new Error("useCommandDialog must be used within a CommandProvider");
}
return value;
}
export function CommandProvider(props: ParentProps) {
const value = init();
const dialog = useDialog();
const keybind = useKeybinds();
const value = init();
const dialog = useDialog();
const keybind = useKeybinds();
// Open command palette on ctrl+p or command_list keybind
useKeyboard((evt) => {
if (value.suspended()) return;
if (dialog.isOpen) return;
if (evt.defaultPrevented) return;
if (keybind.match("command_list", evt)) {
evt.preventDefault();
value.show();
return;
}
});
// Open the command palette via the `command` keybind (bound to `:` in
// keybinds.jsonc). The old hardcoded "command_list" name was never a
// canonical action, so the palette was unreachable dead code.
useKeyboard((evt) => {
if (value.suspended()) return;
if (dialog.isOpen) return;
if (evt.defaultPrevented) return;
if (keybind.match("command", evt)) {
evt.preventDefault();
value.show();
return;
}
});
return <ctx.Provider value={value}>{props.children}</ctx.Provider>;
return <ctx.Provider value={value}>{props.children}</ctx.Provider>;
}
/**
* Command palette dialog component.
*/
function CommandDialog(props: {
options: CommandOption[];
suggestedOptions: CommandOption[];
options: CommandOption[];
suggestedOptions: CommandOption[];
}) {
const { theme } = useTheme();
const dialog = useDialog();
const dimensions = useTerminalDimensions();
const [filter, setFilter] = createSignal("");
const [selectedIndex, setSelectedIndex] = createSignal(0);
const { theme } = useTheme();
const dialog = useDialog();
const dimensions = useTerminalDimensions();
const [filter, setFilter] = createSignal("");
const [selectedIndex, setSelectedIndex] = createSignal(0);
const filteredOptions = createMemo(() => {
const query = filter().toLowerCase();
if (!query) {
return [...props.suggestedOptions, ...props.options];
}
return props.options.filter(
(option) =>
option.title.toLowerCase().includes(query) ||
option.description?.toLowerCase().includes(query) ||
option.category?.toLowerCase().includes(query),
);
});
const filteredOptions = createMemo(() => {
const query = filter().toLowerCase();
if (!query) {
return [...props.suggestedOptions, ...props.options];
}
return props.options.filter(
(option) =>
option.title.toLowerCase().includes(query) ||
option.description?.toLowerCase().includes(query) ||
option.category?.toLowerCase().includes(query),
);
});
// Reset selection when filter changes
createMemo(() => {
filter();
setSelectedIndex(0);
});
// Reset selection when filter changes
createMemo(() => {
filter();
setSelectedIndex(0);
});
useKeyboard((evt) => {
if (evt.name === "escape") {
dialog.clear();
evt.preventDefault();
return;
}
useKeyboard((evt) => {
if (evt.name === "escape") {
dialog.clear();
evt.preventDefault();
return;
}
if (evt.name === "return" || evt.name === "enter") {
const option = filteredOptions()[selectedIndex()];
if (option) {
option.onSelect?.(dialog);
dialog.clear();
}
evt.preventDefault();
return;
}
if (evt.name === "return" || evt.name === "enter") {
const option = filteredOptions()[selectedIndex()];
if (option) {
option.onSelect?.(dialog);
dialog.clear();
}
evt.preventDefault();
return;
}
if (evt.name === "up" || (evt.ctrl && evt.name === "p")) {
setSelectedIndex((i) => Math.max(0, i - 1));
evt.preventDefault();
return;
}
if (evt.name === "up" || (evt.ctrl && evt.name === "p")) {
setSelectedIndex((i) => Math.max(0, i - 1));
evt.preventDefault();
return;
}
if (evt.name === "down" || (evt.ctrl && evt.name === "n")) {
setSelectedIndex((i) => Math.min(filteredOptions().length - 1, i + 1));
evt.preventDefault();
return;
}
if (evt.name === "down" || (evt.ctrl && evt.name === "n")) {
setSelectedIndex((i) => Math.min(filteredOptions().length - 1, i + 1));
evt.preventDefault();
return;
}
// Handle text input
if (evt.name && evt.name.length === 1 && !evt.ctrl && !evt.meta) {
setFilter((f) => f + evt.name);
return;
}
// Handle text input
if (evt.name && evt.name.length === 1 && !evt.ctrl && !evt.meta) {
setFilter((f) => f + evt.name);
return;
}
if (evt.name === "backspace") {
setFilter((f) => f.slice(0, -1));
return;
}
});
if (evt.name === "backspace") {
setFilter((f) => f.slice(0, -1));
return;
}
});
const maxHeight = Math.floor(dimensions().height * 0.6);
const maxHeight = Math.floor(dimensions().height * 0.6);
return (
<box flexDirection="column" padding={1} borderColor={theme.border}>
{/* Search input */}
<box marginBottom={1}>
<text fg={theme.textMuted}>{"> "}</text>
<text fg={theme.text}>{filter() || "Type to search commands..."}</text>
</box>
return (
<box flexDirection="column" padding={1} borderColor={theme.border}>
{/* Search input */}
<box marginBottom={1}>
<text fg={theme.textMuted}>{"> "}</text>
<text fg={theme.text}>{filter() || "Type to search commands..."}</text>
</box>
{/* Command list */}
<box flexDirection="column" maxHeight={maxHeight} borderColor={theme.border}>
<For each={filteredOptions().slice(0, 10)}>
{(option, index) => (
<SelectableBox
selected={() => index() === selectedIndex()}
flexDirection="column"
padding={1}
onMouseDown={() => {
setSelectedIndex(index());
const selectedOption = filteredOptions()[index()];
if (selectedOption) {
selectedOption.onSelect?.(dialog);
dialog.clear();
}
}}
>
<box flexDirection="column" flexGrow={1}>
<SelectableText
selected={() => index() === selectedIndex()}
primary
>
{option.title}
</SelectableText>
<Show when={option.footer}>
<SelectableText
selected={() => index() === selectedIndex()}
tertiary
>
{option.footer}
</SelectableText>
</Show>
<Show when={option.description}>
<SelectableText
selected={() => index() === selectedIndex()}
tertiary
>
{option.description}
</SelectableText>
</Show>
</box>
</SelectableBox>
)}
</For>
<Show when={filteredOptions().length === 0}>
<text fg={theme.textMuted} style={{ padding: 1 }}>
No commands found
</text>
</Show>
</box>
</box>
);
{/* Command list */}
<box
flexDirection="column"
maxHeight={maxHeight}
borderColor={theme.border}
>
<For each={filteredOptions().slice(0, 10)}>
{(option, index) => (
<SelectableBox
selected={() => index() === selectedIndex()}
flexDirection="column"
padding={1}
onMouseDown={() => {
setSelectedIndex(index());
const selectedOption = filteredOptions()[index()];
if (selectedOption) {
selectedOption.onSelect?.(dialog);
dialog.clear();
}
}}
>
<box flexDirection="column" flexGrow={1}>
<SelectableText
selected={() => index() === selectedIndex()}
primary
>
{option.title}
</SelectableText>
<Show when={option.footer}>
<SelectableText
selected={() => index() === selectedIndex()}
tertiary
>
{option.footer}
</SelectableText>
</Show>
<Show when={option.description}>
<SelectableText
selected={() => index() === selectedIndex()}
tertiary
>
{option.description}
</SelectableText>
</Show>
</box>
</SelectableBox>
)}
</For>
<Show when={filteredOptions().length === 0}>
<text fg={theme.textMuted} style={{ padding: 1 }}>
No commands found
</text>
</Show>
</box>
</box>
);
}