Files
ttrpg-tools/src/components/md-commander/stores/commandsStore.ts
T
hypercross 256c685f6c refactor: remove implicit content sniffing and table conversion
- Split loadCSV into parseCSVString (content) and loadCSVFromPath
  (path); drop isCSV/looksLikeCsv heuristics
- Delete coerceSparkTables and markedTable label-header magic;
  plain markdown tables now render as plain tables
- Add explicit markdown role=spark-table fence syntax that converts
  pipe tables to CSV at scan time, with dice-header validation
- Map ESM-only github-slugger and csv-parse browser build to CJS in
  jest config; add content-registry tests
2026-09-08 21:33:12 +08:00

169 lines
4.3 KiB
TypeScript

import { createStore } from "solid-js/store";
import type { MdCommanderCommand, MdCommanderCommandMap } from "../types";
import { setupHelpCommand, clearCommand, rollCommand, trackCommand, untrackCommand, listTrackCommand } from "../commands";
import {resolvePath} from "../../utils/path";
import {loadCSVFromPath} from "../../utils/csv-loader";
const defaultCommands: MdCommanderCommandMap = {
help: setupHelpCommand({}),
clear: clearCommand,
roll: rollCommand,
track: trackCommand,
untrack: untrackCommand,
list: listTrackCommand,
};
export interface CommandsStoreState {
commands: MdCommanderCommandMap;
initialized: boolean;
loading: boolean;
error?: string;
}
const [commandsStore, setCommandsStore] = createStore<CommandsStoreState>({
commands: { ...defaultCommands },
initialized: false,
loading: false,
});
/**
* 初始化命令(包括 help 命令的动态更新)
*/
export function initializeCommands(customCommands?: MdCommanderCommandMap): MdCommanderCommandMap {
const commands = { ...defaultCommands, ...customCommands };
// 更新 help 命令的命令列表
commands.help = setupHelpCommand(commands);
return commands;
}
/**
* 注册命令到 store
*/
export function registerCommands(customCommands?: MdCommanderCommandMap): void {
const commands = initializeCommands(customCommands);
setCommandsStore({ commands, initialized: true, loading: false });
}
/**
* 从 store 获取命令
*/
export function getCommands(): MdCommanderCommandMap {
return commandsStore.commands;
}
/**
* 检查命令是否已初始化
*/
export function isCommandsInitialized(): boolean {
return commandsStore.initialized;
}
/**
* 获取单个命令
*/
export function getCommand(name: string): MdCommanderCommand | undefined {
return commandsStore.commands[name];
}
/**
* 获取加载状态
*/
export function getCommandsLoading(): boolean {
return commandsStore.loading;
}
/**
* 获取错误信息
*/
export function getCommandsError(): string | undefined {
return commandsStore.error;
}
/**
* 设置加载状态
*/
export function setCommandsLoading(loading: boolean): void {
setCommandsStore("loading", loading);
}
/**
* 设置错误信息
*/
export function setCommandsError(error?: string): void {
setCommandsStore("error", error);
}
/**
* 更新命令
*/
export function updateCommands(updater: (prev: MdCommanderCommandMap) => MdCommanderCommandMap): void {
setCommandsStore("commands", (prev) => updater(prev));
}
/**
* 从 CSV 文件加载命令模板并更新命令定义
*/
export async function loadCommandTemplatesFromCSV(
path: string,
articlePath: string
): Promise<void> {
setCommandsLoading(true);
setCommandsError(undefined);
try {
const csv = await loadCSVFromPath<CommandTemplateRow>(resolvePath(articlePath, path));
// 按命令分组模板
const templatesByCommand = new Map<string, CommandTemplateRow[]>();
for (const row of csv) {
if (!row.command || !row.label || !row.insertedText) continue;
if (!templatesByCommand.has(row.command)) {
templatesByCommand.set(row.command, []);
}
templatesByCommand.get(row.command)!.push(row);
}
// 为每个命令添加模板
updateCommands((prev) => {
const updated = { ...prev };
for (const [commandName, rows] of templatesByCommand.entries()) {
const cmd = updated[commandName];
if (!cmd || !cmd.parameters) continue;
const templates = rows.map((row) => ({
label: row.label,
description: row.description || "",
insertText: row.insertedText,
}));
// 为每个参数添加模板
updated[commandName] = {
...cmd,
parameters: cmd.parameters.map((param) => ({
...param,
templates: param.templates ? [...param.templates, ...templates] : templates,
})),
};
}
return updated;
});
setCommandsStore("initialized", true);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
setCommandsError(`加载命令模板失败:${errorMessage}`);
console.warn(`Error loading command templates from ${path}:`, error);
} finally {
setCommandsLoading(false);
}
}
interface CommandTemplateRow {
command: string;
parameter: string;
label: string;
description: string;
insertedText: string;
}