/** * Command parser — parse a raw input string into a typed ParsedInput. * * Pure function with no component dependencies, so it can live in its own * module and keep JournalInput small. */ import type { IconTypes } from "solid-icons"; export interface CompletionItem { label: string; kind: "command" | "value" | "no-results"; insertText: string; /** Optional icon rendered before the label in the completions dropdown. */ icon?: IconTypes; } export interface ParsedInput { type: "chat" | "roll" | "link" | "set" | "rolltag"; payload: Record; error?: string; } export function parseInput(raw: string): ParsedInput { if (raw.startsWith("/roll ")) { const arg = raw.slice("/roll ".length).trim(); if (!arg) return { type: "roll", payload: {}, error: "需要骰子表达式或种子表键名" }; return { type: "roll", payload: { arg } }; } if (raw.startsWith("/link ")) { const arg = raw.slice("/link ".length).trim(); if (!arg) return { type: "link", payload: {}, error: "需要路径" }; const hashIdx = arg.indexOf("#"); const path = hashIdx === -1 ? arg : arg.slice(0, hashIdx); const section = hashIdx === -1 ? undefined : arg.slice(hashIdx + 1) || undefined; return { type: "link", payload: { path, section } }; } if (raw.startsWith("/set ")) { const rest = raw.slice("/set ".length).trim(); if (!rest) return { type: "set", payload: {}, error: "格式: /set $key expression" }; // Split on first space to get key and expression const spaceIdx = rest.indexOf(" "); if (spaceIdx === -1) return { type: "set", payload: {}, error: "格式: /set $key expression" }; const key = rest.slice(0, spaceIdx).trim(); const expr = rest.slice(spaceIdx + 1).trim(); if (!key || !expr) return { type: "set", payload: {}, error: "key 和 expression 不能为空" }; if (!key.startsWith("$")) return { type: "set", payload: {}, error: "key 必须以 $ 开头" }; // Check if expr is a rolltag (e.g. #w|#d|#s) if (expr.includes("|") && expr.split("|").every((t) => t.trim().startsWith("#"))) { const tags = expr.split("|").map((t) => t.trim()); return { type: "rolltag", payload: { key, tags } }; } return { type: "set", payload: { key, expr } }; } if (raw === "/roll" || raw === "/link" || raw === "/set") { return { type: "chat", payload: {}, error: "请补全命令" }; } return { type: "chat", payload: { text: raw } }; }