feat: ai's attempt

This commit is contained in:
2026-03-01 09:36:09 +08:00
parent 8693b28c75
commit f7eabbdeba
14 changed files with 757 additions and 76 deletions
+174 -66
View File
@@ -1,75 +1,39 @@
import { createSignal } from "solid-js";
import { createSignal, onCleanup, onMount } from "solid-js";
import {
MdCommanderCommand,
MdCommanderCommandMap,
CommanderEntry,
CompletionItem,
TrackerItem,
TrackerAttribute,
TrackerCommand,
TrackerViewMode,
} from "../types";
import { rollSimple } from "./useDiceRoller";
import { helpCommand, setupHelpCommand, clearCommand, rollCommand, trackCommand, untrackCommand, listTrackCommand } from "../commands";
// ==================== 默认命令 ====================
export const defaultCommands: Record<string, MdCommanderCommand> = {
help: {
command: "help",
description: "显示帮助信息或特定命令的帮助",
parameters: [
{
name: "cmd",
description: "要查询的命令名",
type: "enum",
values: [], // 运行时填充
required: false,
},
],
handler: (args) => {
const cmdName = args.params.cmd;
if (cmdName) {
return {
message: `命令:${cmdName}\n描述:${defaultCommands[cmdName]?.description || "无描述"}`,
type: "info",
};
}
const cmdList = Object.keys(defaultCommands).join(", ");
return {
message: `可用命令:${cmdList}`,
type: "info",
};
},
},
clear: {
command: "clear",
description: "清空命令历史",
handler: () => ({
message: "命令历史已清空",
type: "success",
}),
},
roll: {
command: "roll",
description: "掷骰子 - 支持骰池、修饰符和组合",
parameters: [
{
name: "formula",
description: "骰子公式,如 3d6+{d4,d8}kh2-5",
type: "string",
required: true,
},
],
handler: (args) => {
const formula = args.params.formula || "1d6";
const result = rollSimple(formula);
return {
message: result.text,
isHtml: result.isHtml,
type: result.text.startsWith("错误") ? "error" : "success",
};
},
},
export const defaultCommands: MdCommanderCommandMap = {
help: setupHelpCommand({}),
clear: clearCommand,
roll: rollCommand,
track: trackCommand,
untrack: untrackCommand,
list: listTrackCommand,
};
// 初始化默认命令
Object.keys(defaultCommands).forEach(key => {
if (key === "help") {
const help = setupHelpCommand(defaultCommands);
defaultCommands.help = help;
}
});
// ==================== 工具函数 ====================
export function parseInput(input: string, commands?: Record<string, MdCommanderCommand>): {
export function parseInput(input: string, commands?: MdCommanderCommandMap): {
command?: string;
params: Record<string, string>;
options: Record<string, string>;
@@ -156,7 +120,7 @@ export function parseInput(input: string, commands?: Record<string, MdCommanderC
export function getCompletions(
input: string,
commands: Record<string, MdCommanderCommand>,
commands: MdCommanderCommandMap,
): CompletionItem[] {
const trimmed = input.trim();
@@ -264,15 +228,29 @@ export interface UseCommanderReturn {
handleCommand: () => void;
updateCompletions: () => void;
acceptCompletion: () => void;
commands: Record<string, MdCommanderCommand>;
commands: MdCommanderCommandMap;
historyIndex: () => number;
setHistoryIndex: (v: number) => void;
commandHistory: () => string[];
navigateHistory: (direction: 'up' | 'down') => void;
// Tracker 相关
viewMode: () => TrackerViewMode;
setViewMode: (mode: TrackerViewMode) => void;
trackerItems: () => TrackerItem[];
setTrackerItems: (updater: (prev: TrackerItem[]) => TrackerItem[]) => void;
trackerHistory: () => TrackerCommand[];
addTrackerItem: (item: Omit<TrackerItem, 'id'>) => void;
removeTrackerItem: (itemId: string) => void;
updateTrackerAttribute: (itemId: string, attrName: string, attr: TrackerAttribute) => void;
moveTrackerItem: (itemId: string, direction: 'up' | 'down') => void;
removeTrackerClass: (itemId: string, className: string) => void;
recordTrackerCommand: (cmd: Omit<TrackerCommand, 'timestamp'>) => void;
}
export function useCommander(
customCommands?: Record<string, MdCommanderCommand>,
customCommands?: MdCommanderCommandMap,
element?: HTMLElement,
): UseCommanderReturn {
const [inputValue, setInputValue] = createSignal("");
const [entries, setEntries] = createSignal<CommanderEntry[]>([]);
@@ -280,11 +258,16 @@ export function useCommander(
const [completions, setCompletions] = createSignal<CompletionItem[]>([]);
const [selectedCompletion, setSelectedCompletion] = createSignal(0);
const [isFocused, setIsFocused] = createSignal(false);
// 命令历史
const [commandHistory, setCommandHistory] = createSignal<string[]>([]);
const [historyIndex, setHistoryIndex] = createSignal(-1);
// Tracker 相关
const [viewMode, setViewMode] = createSignal<TrackerViewMode>("history");
const [trackerItems, setTrackerItemsState] = createSignal<TrackerItem[]>([]);
const [trackerHistory, setTrackerHistory] = createSignal<TrackerCommand[]>([]);
const commands = { ...defaultCommands, ...customCommands };
// 更新 help 命令的参数值
@@ -294,6 +277,38 @@ export function useCommander(
);
}
// 设置事件监听器
if (element) {
const handleTrack = (e: Event) => {
const detail = (e as CustomEvent).detail as { tag: string; classes: string[]; attributes: Record<string, TrackerAttribute> };
addTrackerItem({
tag: detail.tag,
classes: detail.classes || [],
attributes: detail.attributes || {},
});
};
const handleUntrack = (e: Event) => {
const detail = (e as CustomEvent).detail as { id: string };
removeTrackerItem(detail.id);
};
const handleListTrack = () => {
// 切换到 tracker 视图
setViewMode("tracker");
};
element.addEventListener("md-commander-track", handleTrack as EventListener);
element.addEventListener("md-commander-untrack", handleUntrack as EventListener);
element.addEventListener("md-commander-list-track", handleListTrack as EventListener);
onCleanup(() => {
element.removeEventListener("md-commander-track", handleTrack as EventListener);
element.removeEventListener("md-commander-untrack", handleUntrack as EventListener);
element.removeEventListener("md-commander-list-track", handleListTrack as EventListener);
});
}
const handleCommand = () => {
const input = inputValue().trim();
if (!input) return;
@@ -308,7 +323,7 @@ export function useCommander(
result = { message: `未知命令:${commandName}`, type: "error" };
} else if (cmd.handler) {
try {
result = cmd.handler({ params: parsed.params, options: parsed.options });
result = cmd.handler({ params: parsed.params, options: parsed.options }, commands);
} catch (e) {
result = {
message: `执行错误:${e instanceof Error ? e.message : String(e)}`,
@@ -328,11 +343,11 @@ export function useCommander(
};
setEntries((prev) => [...prev, newEntry]);
// 添加到命令历史
setCommandHistory((prev) => [...prev, input]);
setHistoryIndex(-1); // 重置历史索引
setInputValue("");
setShowCompletions(false);
@@ -418,6 +433,88 @@ export function useCommander(
setInputValue(history[history.length - 1 - newIndex]);
};
// ==================== Tracker 方法 ====================
const recordTrackerCommand = (cmd: Omit<TrackerCommand, 'timestamp'>) => {
setTrackerHistory((prev) => [
...prev,
{ ...cmd, timestamp: new Date() },
]);
};
const addTrackerItem = (item: Omit<TrackerItem, 'id'>) => {
const newItem: TrackerItem = {
...item,
id: Date.now().toString() + Math.random().toString(36).slice(2),
};
setTrackerItemsState((prev) => [...prev, newItem]);
recordTrackerCommand({
type: "add",
itemId: newItem.id,
data: newItem,
});
};
const removeTrackerItem = (itemId: string) => {
const item = trackerItems().find((i) => i.id === itemId);
setTrackerItemsState((prev) => prev.filter((i) => i.id !== itemId));
recordTrackerCommand({
type: "remove",
itemId,
data: item,
});
};
const updateTrackerAttribute = (itemId: string, attrName: string, attr: TrackerAttribute) => {
setTrackerItemsState((prev) =>
prev.map((i) =>
i.id === itemId
? { ...i, attributes: { ...i.attributes, [attrName]: attr } }
: i
)
);
recordTrackerCommand({
type: "update",
itemId,
attributeUpdates: { [attrName]: attr },
});
};
const moveTrackerItem = (itemId: string, direction: 'up' | 'down') => {
const items = trackerItems();
const index = items.findIndex((i) => i.id === itemId);
if (index === -1) return;
const newIndex = direction === 'up' ? index - 1 : index + 1;
if (newIndex < 0 || newIndex >= items.length) return;
const newItems = [...items];
[newItems[index], newItems[newIndex]] = [newItems[newIndex], newItems[index]];
setTrackerItemsState(newItems);
recordTrackerCommand({
type: "reorder",
data: newItems,
});
};
const removeTrackerClass = (itemId: string, className: string) => {
setTrackerItemsState((prev) =>
prev.map((i) =>
i.id === itemId
? { ...i, classes: i.classes.filter((c) => c !== className) }
: i
)
);
recordTrackerCommand({
type: "update",
itemId,
});
};
const setTrackerItems = (updater: (prev: TrackerItem[]) => TrackerItem[]) => {
setTrackerItemsState(updater);
};
return {
inputValue,
entries,
@@ -438,5 +535,16 @@ export function useCommander(
setHistoryIndex,
commandHistory,
navigateHistory,
viewMode,
setViewMode,
trackerItems,
setTrackerItems,
trackerHistory,
addTrackerItem,
removeTrackerItem,
updateTrackerAttribute,
moveTrackerItem,
removeTrackerClass,
recordTrackerCommand,
};
}