diff --git a/src/components/index.ts b/src/components/index.ts index c77bdfa..c61fb6d 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -17,4 +17,27 @@ export { FileTreeNode, HeadingNode } from './FileTree'; // 导出数据类型 export type { DiceProps } from './md-dice'; export type { TableProps } from './md-table'; -export type { BgProps } from './md-bg'; \ No newline at end of file +export type { BgProps } from './md-bg'; + +// 导出 md-commander 相关 +export type { + MdCommanderProps, + MdCommanderCommand, + MdCommanderCommandMap, + MdCommanderParameter, + MdCommanderOption, + MdCommanderOptionType, + CommanderEntry, + CompletionItem, + TrackerItem, + TrackerAttribute, + TrackerAttributeType, + TrackerCommand, + TrackerViewMode, +} from './md-commander/types'; +export { TabBar } from './md-commander/TabBar'; +export { TrackerView } from './md-commander/TrackerView'; +export { AttributeEditor } from './md-commander/AttributeEditor'; +export { CommanderEntries } from './md-commander/CommanderEntries'; +export { CommanderInput } from './md-commander/CommanderInput'; +export { useCommander, defaultCommands } from './md-commander/hooks'; \ No newline at end of file diff --git a/src/components/md-commander/AttributeEditor.tsx b/src/components/md-commander/AttributeEditor.tsx new file mode 100644 index 0000000..6f067ae --- /dev/null +++ b/src/components/md-commander/AttributeEditor.tsx @@ -0,0 +1,103 @@ +import { type Component, Show } from "solid-js"; +import type { TrackerAttribute, TrackerAttributeType } from "./types"; + +export interface AttributeEditorProps { + attribute: () => TrackerAttribute; + onUpdate: (attr: TrackerAttribute) => void; + onClose: () => void; +} + +export const AttributeEditor: Component = (props) => { + const updateValue = (value: any) => { + props.onUpdate({ + ...props.attribute(), + value, + }); + }; + + const renderEditor = () => { + const attr = props.attribute(); + + if (attr.type === "progress") { + const val = attr.value as { x: number; y: number }; + return ( +
+ updateValue({ x: parseInt(e.target.value) || 0, y: val.y })} + class="w-16 px-2 py-1 border border-gray-300 rounded text-center" + min="0" + /> + / + updateValue({ x: val.x, y: parseInt(e.target.value) || 0 })} + class="w-16 px-2 py-1 border border-gray-300 rounded text-center" + min="0" + /> +
+ ); + } + + if (attr.type === "count") { + return ( + updateValue(parseInt(e.target.value) || 0)} + class="w-24 px-2 py-1 border border-gray-300 rounded text-center" + min="0" + /> + ); + } + + if (attr.type === "string") { + return ( + updateValue(e.target.value)} + class="w-full px-2 py-1 border border-gray-300 rounded" + /> + ); + } + + return null; + }; + + return ( +
+
+
+

编辑属性:{props.attribute().name}

+ +
+
+ + + {props.attribute().type} + +
+
+ + {renderEditor()} +
+
+ +
+
+
+ ); +}; diff --git a/src/components/md-commander/TabBar.tsx b/src/components/md-commander/TabBar.tsx new file mode 100644 index 0000000..40ba0a8 --- /dev/null +++ b/src/components/md-commander/TabBar.tsx @@ -0,0 +1,34 @@ +import { type Component } from "solid-js"; +import type { TrackerViewMode } from "./types"; + +export interface TabBarProps { + mode: () => TrackerViewMode; + onModeChange: (mode: TrackerViewMode) => void; +} + +export const TabBar: Component = (props) => { + return ( +
+ + +
+ ); +}; diff --git a/src/components/md-commander/TrackerView.tsx b/src/components/md-commander/TrackerView.tsx new file mode 100644 index 0000000..801a710 --- /dev/null +++ b/src/components/md-commander/TrackerView.tsx @@ -0,0 +1,111 @@ +import { type Component, For, Show } from "solid-js"; +import type { TrackerItem, TrackerAttribute } from "./types"; + +export interface TrackerViewProps { + items: () => TrackerItem[]; + onEditAttribute?: (itemId: string, attrName: string, attr: TrackerAttribute) => void; + onRemoveClass?: (itemId: string, className: string) => void; + onMoveUp?: (itemId: string) => void; + onMoveDown?: (itemId: string) => void; + onRemove?: (itemId: string) => void; +} + +export const TrackerView: Component = (props) => { + const formatAttributeValue = (attr: TrackerAttribute): string => { + if (attr.type === "progress") { + const val = attr.value as { x: number; y: number }; + return `${val.x}/${val.y}`; + } + if (attr.type === "count") { + return String(attr.value); + } + return String(attr.value); + }; + + return ( +
+ 0} + fallback={ +
暂无追踪项目
+ } + > +
+ + {(item) => ( +
+ {/* 头部:tag 和操作按钮 */} +
+
+ {item.tag} + #{item.id.slice(-6)} +
+
+ + + +
+
+ + {/* Classes */} + 0}> +
+ + {(cls) => ( + + {cls} + + + )} + +
+
+ + {/* Attributes */} +
+ + {(attr: TrackerAttribute) => ( +
props.onEditAttribute?.(item.id, attr.name, attr)} + > + {attr.name} + + {formatAttributeValue(attr)} + +
+ )} +
+
+
+ )} +
+
+
+
+ ); +}; diff --git a/src/components/md-commander/commands/clear.ts b/src/components/md-commander/commands/clear.ts new file mode 100644 index 0000000..2ceb2a1 --- /dev/null +++ b/src/components/md-commander/commands/clear.ts @@ -0,0 +1,10 @@ +import type { MdCommanderCommand } from "../types"; + +export const clearCommand: MdCommanderCommand = { + command: "clear", + description: "清空命令历史", + handler: () => ({ + message: "命令历史已清空", + type: "success", + }), +}; diff --git a/src/components/md-commander/commands/help.ts b/src/components/md-commander/commands/help.ts new file mode 100644 index 0000000..272bc74 --- /dev/null +++ b/src/components/md-commander/commands/help.ts @@ -0,0 +1,37 @@ +import type { MdCommanderCommand, MdCommanderCommandMap } from "../types"; + +export const helpCommand: MdCommanderCommand = { + command: "help", + description: "显示帮助信息或特定命令的帮助", + parameters: [ + { + name: "cmd", + description: "要查询的命令名", + type: "enum", + values: [], // 运行时填充 + required: false, + }, + ], + handler: (args, commands) => { + const cmdName = args.params.cmd; + if (cmdName && commands?.[cmdName]) { + return { + message: `命令:${cmdName}\n描述:${commands[cmdName]?.description || "无描述"}`, + type: "info", + }; + } + const cmdList = Object.keys(commands || {}).filter(k => k !== "help").join(", "); + return { + message: `可用命令:${cmdList}`, + type: "info", + }; + }, +}; + +export function setupHelpCommand(commands: MdCommanderCommandMap): MdCommanderCommand { + const cmd = { ...helpCommand }; + if (cmd.parameters?.[0]) { + cmd.parameters[0].values = Object.keys(commands).filter(k => k !== "help"); + } + return cmd; +} diff --git a/src/components/md-commander/commands/index.ts b/src/components/md-commander/commands/index.ts new file mode 100644 index 0000000..707a35d --- /dev/null +++ b/src/components/md-commander/commands/index.ts @@ -0,0 +1,4 @@ +export { helpCommand, setupHelpCommand } from "./help"; +export { clearCommand } from "./clear"; +export { rollCommand } from "./roll"; +export { trackCommand, untrackCommand, listTrackCommand } from "./tracker"; diff --git a/src/components/md-commander/commands/roll.ts b/src/components/md-commander/commands/roll.ts new file mode 100644 index 0000000..4650c99 --- /dev/null +++ b/src/components/md-commander/commands/roll.ts @@ -0,0 +1,24 @@ +import type { MdCommanderCommand } from "../types"; +import { rollSimple } from "../hooks/useDiceRoller"; + +export const rollCommand: MdCommanderCommand = { + 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", + }; + }, +}; diff --git a/src/components/md-commander/commands/tracker.ts b/src/components/md-commander/commands/tracker.ts new file mode 100644 index 0000000..a4cb7af --- /dev/null +++ b/src/components/md-commander/commands/tracker.ts @@ -0,0 +1,116 @@ +import type { MdCommanderCommand, MdCommanderCommandMap, TrackerAttributeType } from "../types"; + +export const trackCommand: MdCommanderCommand = { + command: "track", + description: "添加一个新的追踪项目", + parameters: [ + { + name: "tag", + description: "追踪项目的标签", + type: "string", + required: true, + }, + ], + options: { + class: { + option: "class", + description: "添加类(可多次使用)", + type: "string", + required: false, + }, + attr: { + option: "attr", + description: "添加属性,格式:name:type:value (type: progress/count/string)", + type: "string", + required: false, + }, + }, + handler: (args, commands) => { + const tag = args.params.tag; + if (!tag) { + return { message: "错误:缺少 tag 参数", type: "error" }; + } + + // 解析属性 + const attributes: Record = {}; + const attrOption = args.options.attr; + + if (attrOption) { + const attrs = Array.isArray(attrOption) ? attrOption : [attrOption]; + for (const attrStr of attrs) { + const parts = attrStr.split(":"); + if (parts.length >= 3) { + const [name, typeStr, ...valueParts] = parts; + const type = typeStr as TrackerAttributeType; + const valueStr = valueParts.join(":"); + + let value: any; + if (type === "progress") { + const [x, y] = valueStr.split("/").map(Number); + value = { x: x || 0, y: y || 0 }; + } else if (type === "count") { + value = parseInt(valueStr) || 0; + } else { + value = valueStr; + } + + attributes[name] = { name, type, value }; + } + } + } + + // 解析类 + const classes: string[] = []; + const classOption = args.options.class; + if (classOption) { + const cls = Array.isArray(classOption) ? classOption : [classOption]; + classes.push(...cls); + } + + // 通过全局 commander 对象添加追踪项目 + const event = new CustomEvent("md-commander-track", { + detail: { tag, classes, attributes }, + }); + window.dispatchEvent(event); + + return { + message: `已添加追踪项目:${tag}`, + type: "success", + }; + }, +}; + +export const untrackCommand: MdCommanderCommand = { + command: "untrack", + description: "移除一个追踪项目", + parameters: [ + { + name: "id", + description: "追踪项目的 ID", + type: "string", + required: true, + }, + ], + handler: (args) => { + const id = args.params.id; + if (!id) { + return { message: "错误:缺少 id 参数", type: "error" }; + } + + const event = new CustomEvent("md-commander-untrack", { detail: { id } }); + window.dispatchEvent(event); + + return { message: `已移除追踪项目:${id}`, type: "success" }; + }, +}; + +export const listTrackCommand: MdCommanderCommand = { + command: "list", + description: "列出所有追踪项目", + handler: (args, commands) => { + const event = new CustomEvent("md-commander-list-track"); + window.dispatchEvent(event); + + return { message: "追踪列表已更新", type: "info" }; + }, +}; diff --git a/src/components/md-commander/hooks/index.ts b/src/components/md-commander/hooks/index.ts index f39927a..f2b1cd8 100644 --- a/src/components/md-commander/hooks/index.ts +++ b/src/components/md-commander/hooks/index.ts @@ -2,3 +2,12 @@ export { useCommander, defaultCommands, parseInput, getCompletions, getResultCla export type { UseCommanderReturn } from './useCommander'; export { rollFormula, rollSimple } from './useDiceRoller'; export * from './dice-engine'; + +// Tracker 相关导出 +export type { + TrackerItem, + TrackerAttribute, + TrackerAttributeType, + TrackerCommand, + TrackerViewMode, +} from '../types'; diff --git a/src/components/md-commander/hooks/useCommander.ts b/src/components/md-commander/hooks/useCommander.ts index 5fdd5d0..26ce4ba 100644 --- a/src/components/md-commander/hooks/useCommander.ts +++ b/src/components/md-commander/hooks/useCommander.ts @@ -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 = { - 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): { +export function parseInput(input: string, commands?: MdCommanderCommandMap): { command?: string; params: Record; options: Record; @@ -156,7 +120,7 @@ export function parseInput(input: string, commands?: Record, + commands: MdCommanderCommandMap, ): CompletionItem[] { const trimmed = input.trim(); @@ -264,15 +228,29 @@ export interface UseCommanderReturn { handleCommand: () => void; updateCompletions: () => void; acceptCompletion: () => void; - commands: Record; + 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) => 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) => void; } export function useCommander( - customCommands?: Record, + customCommands?: MdCommanderCommandMap, + element?: HTMLElement, ): UseCommanderReturn { const [inputValue, setInputValue] = createSignal(""); const [entries, setEntries] = createSignal([]); @@ -280,11 +258,16 @@ export function useCommander( const [completions, setCompletions] = createSignal([]); const [selectedCompletion, setSelectedCompletion] = createSignal(0); const [isFocused, setIsFocused] = createSignal(false); - + // 命令历史 const [commandHistory, setCommandHistory] = createSignal([]); const [historyIndex, setHistoryIndex] = createSignal(-1); + // Tracker 相关 + const [viewMode, setViewMode] = createSignal("history"); + const [trackerItems, setTrackerItemsState] = createSignal([]); + const [trackerHistory, setTrackerHistory] = createSignal([]); + 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 }; + 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) => { + setTrackerHistory((prev) => [ + ...prev, + { ...cmd, timestamp: new Date() }, + ]); + }; + + const addTrackerItem = (item: Omit) => { + 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, }; } diff --git a/src/components/md-commander/index.tsx b/src/components/md-commander/index.tsx index 6b8b780..e2b2878 100644 --- a/src/components/md-commander/index.tsx +++ b/src/components/md-commander/index.tsx @@ -1,9 +1,12 @@ import { customElement, noShadowDOM } from "solid-element"; -import { onMount, onCleanup } from "solid-js"; +import { onMount, onCleanup, createSignal, Show } from "solid-js"; import { useCommander } from "./hooks"; import { CommanderInput } from "./CommanderInput"; import { CommanderEntries } from "./CommanderEntries"; -import type { MdCommanderProps } from "./types"; +import { TrackerView } from "./TrackerView"; +import { TabBar } from "./TabBar"; +import { AttributeEditor } from "./AttributeEditor"; +import type { MdCommanderProps, TrackerAttribute } from "./types"; customElement( "md-commander", @@ -11,7 +14,12 @@ customElement( (props, { element }) => { noShadowDOM(); - const commander = useCommander(props.commands); + const commander = useCommander(props.commands, element as unknown as HTMLElement); + const [editingAttr, setEditingAttr] = createSignal<{ + itemId: string; + attrName: string; + attr: TrackerAttribute; + } | null>(null); const handleKeyDown = (e: KeyboardEvent) => { if (commander.showCompletions() && commander.completions().length > 0) { @@ -35,7 +43,11 @@ customElement( return; } if (e.key === "Escape") { - commander.setShowCompletions(false); + if (editingAttr()) { + setEditingAttr(null); + } else { + commander.setShowCompletions(false); + } return; } } @@ -75,12 +87,34 @@ customElement( class={`md-commander flex flex-col border border-gray-300 rounded-lg overflow-hidden ${props.class || ""}`} style={{ height: heightStyle() }} > - {/* 命令执行结果 */} - commander.setInputValue(cmd)} + {/* 标签页导航 */} + + {/* 内容区域:历史或追踪 */} + + setEditingAttr({ itemId, attrName, attr }) + } + onRemoveClass={commander.removeTrackerClass} + onMoveUp={(itemId) => commander.moveTrackerItem(itemId, "up")} + onMoveDown={(itemId) => commander.moveTrackerItem(itemId, "down")} + onRemove={commander.removeTrackerItem} + /> + } + > + commander.setInputValue(cmd)} + /> + + {/* 命令输入框 */}
( onAcceptCompletion={commander.acceptCompletion} />
+ + {/* 属性编辑器弹窗 */} + + editingAttr()!.attr} + onUpdate={(attr) => { + const data = editingAttr(); + if (data) { + commander.updateTrackerAttribute(data.itemId, data.attrName, attr); + } + }} + onClose={() => setEditingAttr(null)} + /> + ); }, diff --git a/src/components/md-commander/types.ts b/src/components/md-commander/types.ts index ef1afe4..ec0c1cc 100644 --- a/src/components/md-commander/types.ts +++ b/src/components/md-commander/types.ts @@ -7,6 +7,10 @@ export interface MdCommanderProps { commands?: Record; } +export interface MdCommanderCommandMap { + [key: string]: MdCommanderCommand; +} + export interface MdCommanderCommand { command: string; description?: string; @@ -15,7 +19,7 @@ export interface MdCommanderCommand { handler?: (args: { params: Record; options: Record; - }) => { + }, commands?: MdCommanderCommandMap) => { message: string; type?: "success" | "error" | "info" | "warning"; isHtml?: boolean; @@ -70,3 +74,30 @@ export interface CompletionItem { description?: string; insertText: string; } + +// ==================== Tracker 类型 ==================== + +export type TrackerAttributeType = "progress" | "count" | "string"; + +export interface TrackerAttribute { + name: string; + type: TrackerAttributeType; + value: string | number | { x: number; y: number }; +} + +export interface TrackerItem { + id: string; + tag: string; + classes: string[]; + attributes: Record; +} + +export type TrackerViewMode = "history" | "tracker"; + +export interface TrackerCommand { + type: "add" | "remove" | "update" | "reorder"; + itemId?: string; + data?: Partial | TrackerItem[]; + attributeUpdates?: Record; + timestamp: Date; +} diff --git a/todo.md b/todo.md index e69de29..aa1f840 100644 --- a/todo.md +++ b/todo.md @@ -0,0 +1,23 @@ +# todo + +## md-commander(./components/md-commander) + +- [ ] create a new file for each command +- [ ] add a tab bar to the top of md-commander. it should switch between the current history view and a new tracker view. +- [ ] add a command to update the tracker view. + +the tracker view should show a list of currently tracked information. + +each entry should have a tag, an id, a list of classes, and a list of attributes. + +each attribute can be of the following types: +- progress: represented in x/y format, x y are both ints. +- count: represented as an int. +- string: represented as a string. + +the tracker view should support the following interactions: +- reordering +- updating attributes, by using popup controls that appear when you click on the attribute +- removing classes + +each interaction should be implemented as a command entry in the history view. \ No newline at end of file