feat(journal): improve command completion behavior
- Scroll the selected completion item into view - Filter commands based on prefix when typing a slash command - Keep completion menu open while typing a command - Add data attributes to track completion indices for scrolling
This commit is contained in:
parent
e0d61cf91e
commit
276241f3b0
|
|
@ -12,6 +12,7 @@
|
|||
import {
|
||||
Component,
|
||||
createSignal,
|
||||
createEffect,
|
||||
onMount,
|
||||
onCleanup,
|
||||
Show,
|
||||
|
|
@ -126,18 +127,34 @@ export const JournalInput: Component = () => {
|
|||
revertLatest();
|
||||
}
|
||||
|
||||
// ---- Scroll selected completion into view ----
|
||||
|
||||
createEffect(() => {
|
||||
const idx = selectedIdx();
|
||||
if (!showCompletions() || !completionsRef) return;
|
||||
const el = completionsRef.querySelector(`[data-comp-idx="${idx}"]`);
|
||||
if (el) el.scrollIntoView({ block: "nearest" });
|
||||
});
|
||||
|
||||
// ---- Completions ----
|
||||
|
||||
function buildCompletions(): CompletionItem[] {
|
||||
const raw = text().trim();
|
||||
const data = comp.data;
|
||||
|
||||
// Show commands when user types /
|
||||
if (raw === "" || raw === "/") {
|
||||
return [
|
||||
{ label: "/roll", kind: "command", insertText: "/roll " },
|
||||
{ label: "/link", kind: "command", insertText: "/link " },
|
||||
const commands = [
|
||||
{ label: "/roll", kind: "command" as const, insertText: "/roll " },
|
||||
{ label: "/link", kind: "command" as const, insertText: "/link " },
|
||||
];
|
||||
|
||||
// Show commands when user types / or starts typing a command name
|
||||
if (raw.startsWith("/") && !raw.includes(" ")) {
|
||||
const prefix = raw.toLowerCase();
|
||||
const matches = commands.filter((c) =>
|
||||
c.label.toLowerCase().startsWith(prefix),
|
||||
);
|
||||
return matches.length > 0
|
||||
? matches
|
||||
: [{ label: "Unknown command", kind: "no-results", insertText: "" }];
|
||||
}
|
||||
|
||||
// After /roll — show dice suggestions
|
||||
|
|
@ -226,9 +243,9 @@ export const JournalInput: Component = () => {
|
|||
}
|
||||
return;
|
||||
}
|
||||
// If not open and typing /, open completions
|
||||
// If not open and starts with /, open completions
|
||||
const raw = text();
|
||||
if (raw === "/" || raw.startsWith("/roll") || raw.startsWith("/link")) {
|
||||
if (raw.startsWith("/")) {
|
||||
e.preventDefault();
|
||||
setShowCompletions(true);
|
||||
setSelectedIdx(0);
|
||||
|
|
@ -271,14 +288,8 @@ export const JournalInput: Component = () => {
|
|||
const raw = input.value;
|
||||
setText(raw);
|
||||
|
||||
// Completions visibility
|
||||
if (
|
||||
raw === "/" ||
|
||||
raw.startsWith("/roll ") ||
|
||||
raw.startsWith("/link ") ||
|
||||
raw === "/roll" ||
|
||||
raw === "/link"
|
||||
) {
|
||||
// Completions visibility — keep open while user types any /
|
||||
if (raw.startsWith("/")) {
|
||||
setShowCompletions(true);
|
||||
setSelectedIdx(0);
|
||||
} else {
|
||||
|
|
@ -317,6 +328,7 @@ export const JournalInput: Component = () => {
|
|||
<For each={comps}>
|
||||
{(item, idx) => (
|
||||
<div
|
||||
data-comp-idx={idx()}
|
||||
class={`flex items-center gap-2 px-3 py-1.5 cursor-pointer text-sm ${
|
||||
item.kind === "no-results"
|
||||
? "text-gray-400 italic cursor-default"
|
||||
|
|
|
|||
Loading…
Reference in New Issue