Files

50 lines
2.3 KiB
JavaScript

// Prompt autocomplete: while typing in a prompt box, suggest the shortcut triggers that fit the word under the caret.
// Attaches to the prompt boxes by what they are called, when they are first focused; no other feature knows about it.
import { composer as c } from "../../composer/composer.js";
import { accept, suggestionsAt } from "./match.js";
const BOXES = new Set(["Story", "Prompt", "Anchor", "Postfix"]); // aria-labels of the prompt fields
export default {
id: "autocomplete",
mount: "menubar.menus",
needs: ["project", "api", "dom"],
setup({ app }) {
const p = app.project;
let library = [], fetched = 0, span = null;
const refresh = () => { if (Date.now() - fetched > 10000) { fetched = Date.now(); app.api.shortcuts().then((r) => { library = r.shortcuts || []; }).catch(() => {}); } };
const live = new WeakMap();
function attach(box) {
if (live.has(box)) return;
const source = () => {
span = null;
if (!p.pref("autocomplete", true) || box.selectionStart !== box.selectionEnd) return [];
const found = suggestionsAt(library, box.value, box.selectionStart);
if (!found) return [];
span = found.span;
return found.items.map((e) => ({ label: e.trigger, hint: (e.sc.replacements || [])[0] || [e.sc.category, e.sc.sub_category].filter(Boolean).join(" · "), trigger: e.trigger }));
};
const onPick = (item) => {
if (!span) return;
const done = accept(box.value, span, item.trigger);
box.value = done.text; box.setSelectionRange(done.caret, done.caret);
box.dispatchEvent(new Event("input", { bubbles: true }));
box.focus();
};
live.set(box, c.autocomplete.default({ input: box, source, onPick }));
}
const onFocus = (e) => {
const t = e.target;
if (t instanceof HTMLTextAreaElement && BOXES.has(t.getAttribute("aria-label"))) { refresh(); attach(t); }
};
const offFocus = app.dom.onPage("focusin", onFocus);
app.editorSettings.push(() => c.region.stack({ gap: "sm", children: [
c.toggle.default({ label: "Prompt autocomplete", hint: "Suggest matching shortcuts while you type in the Story and scene prompts. Shows the trigger and its prompt.",
checked: p.pref("autocomplete", true), onChange: (v) => p.setPref("autocomplete", v) })] }));
return offFocus;
},
};