From fdaf79e7ed78347a40759a111d9ea54ebc2bd587 Mon Sep 17 00:00:00 2001 From: hypercross Date: Sun, 1 Mar 2026 10:01:53 +0800 Subject: [PATCH] fix: issues --- src/components/md-commander/TrackerView.tsx | 31 +++--- .../md-commander/commands/tracker.ts | 30 +++--- .../md-commander/hooks/useCommander.ts | 69 +++++++++++--- src/components/md-commander/index.tsx | 16 ++-- src/components/md-commander/stores/index.ts | 2 + .../md-commander/stores/trackerStore.ts | 95 +++++++++++++++---- src/components/md-commander/types.ts | 3 +- 7 files changed, 178 insertions(+), 68 deletions(-) diff --git a/src/components/md-commander/TrackerView.tsx b/src/components/md-commander/TrackerView.tsx index ba202c0..332ab39 100644 --- a/src/components/md-commander/TrackerView.tsx +++ b/src/components/md-commander/TrackerView.tsx @@ -1,13 +1,13 @@ -import { type Component, For, Show } from "solid-js"; +import { type Component, For, Show, Index } 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; + onEditAttribute?: (index: number, attrName: string, attr: TrackerAttribute) => void; + onRemoveClass?: (index: number, className: string) => void; + onMoveUp?: (index: number) => void; + onMoveDown?: (index: number) => void; + onRemove?: (index: number) => void; } export const TrackerView: Component = (props) => { @@ -32,7 +32,7 @@ export const TrackerView: Component = (props) => { >
- {(item) => ( + {(item, index) => (
{/* 头部:tag、id 和操作按钮 */}
@@ -41,27 +41,26 @@ export const TrackerView: Component = (props) => { #{item.id} - • {item.uuid.slice(-6)}
@@ -71,20 +70,20 @@ export const TrackerView: Component = (props) => { {/* Classes */} 0}>
- + {(cls) => ( - {cls} + {cls()} )} - +
@@ -94,7 +93,7 @@ export const TrackerView: Component = (props) => { {(attr: TrackerAttribute) => (
props.onEditAttribute?.(item.id, attr.name, attr)} + onClick={() => props.onEditAttribute?.(index(), attr.name, attr)} > {attr.name} diff --git a/src/components/md-commander/commands/tracker.ts b/src/components/md-commander/commands/tracker.ts index d4d56f0..403935c 100644 --- a/src/components/md-commander/commands/tracker.ts +++ b/src/components/md-commander/commands/tracker.ts @@ -1,6 +1,6 @@ -import type { MdCommanderCommand, MdCommanderCommandMap } from "../types"; -import { parseEmmet } from "../utils/emmetParser"; -import { addTrackerItem, removeTrackerItem, getTrackerItems } from "../stores"; +import type { MdCommanderCommand } from "../types"; +import { parseEmmet } from "../utils"; +import { addTrackerItem, removeTrackerItem, getTrackerItems, findTrackerItem } from "../stores"; export const trackCommand: MdCommanderCommand = { command: "track", @@ -42,24 +42,32 @@ export const trackCommand: MdCommanderCommand = { export const untrackCommand: MdCommanderCommand = { command: "untrack", - description: "移除一个追踪项目", + description: "移除一个追踪项目 - 支持 Emmet 语法:untrack #g2 或 untrack goblin.warrior", parameters: [ { - name: "id", - description: "追踪项目的 ID", + name: "emmet", + description: "Emmet 格式:#id 或 tag.class", type: "string", required: true, }, ], handler: (args) => { - const id = args.params.id; - if (!id) { - return { message: "错误:缺少 id 参数", type: "error" }; + const emmet = args.params.emmet; + if (!emmet) { + return { message: "错误:缺少 Emmet 参数", type: "error" }; } - removeTrackerItem(id); + const found = findTrackerItem(emmet); + if (!found) { + return { message: `未找到匹配的追踪项目:${emmet}`, type: "error" }; + } - return { message: `已移除追踪项目:${id}`, type: "success" }; + const success = removeTrackerItem(emmet); + if (!success) { + return { message: `移除失败:${emmet}`, type: "error" }; + } + + return { message: `已移除追踪项目:${found.tag}${found.id ? '#' + found.id : ''}`, type: "success" }; }, }; diff --git a/src/components/md-commander/hooks/useCommander.ts b/src/components/md-commander/hooks/useCommander.ts index 25807cb..182d369 100644 --- a/src/components/md-commander/hooks/useCommander.ts +++ b/src/components/md-commander/hooks/useCommander.ts @@ -19,6 +19,7 @@ import { removeTrackerClass as removeClassFromTracker, getTrackerItems, getTrackerHistory, + findTrackerIndex, } from "../stores"; // ==================== 默认命令 ==================== @@ -250,10 +251,14 @@ export interface UseCommanderReturn { 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; - removeTrackerItemClass: (itemId: string, className: string) => void; + removeTrackerItem: (emmet: string) => boolean; + removeTrackerItemByIndex: (index: number) => void; + updateTrackerAttribute: (emmet: string, attrName: string, attr: TrackerAttribute) => boolean; + updateTrackerAttributeByIndex: (index: number, attrName: string, attr: TrackerAttribute) => void; + moveTrackerItem: (emmet: string, direction: 'up' | 'down') => boolean; + moveTrackerItemByIndex: (index: number, direction: 'up' | 'down') => void; + removeTrackerItemClass: (emmet: string, className: string) => boolean; + removeTrackerItemClassByIndex: (index: number, className: string) => void; recordTrackerCommand: (cmd: Omit) => void; } @@ -431,20 +436,56 @@ export function useCommander( return addTracker(item); }; - const removeTrackerItem = (itemId: string) => { - removeTracker(itemId); + const removeTrackerItem = (emmet: string) => { + return removeTracker(emmet); }; - const updateTrackerAttribute = (itemId: string, attrName: string, attr: TrackerAttribute) => { - updateTrackerAttr(itemId, attrName, attr); + const removeTrackerItemByIndex = (index: number) => { + const items = trackerItems(); + if (index >= 0 && index < items.length) { + const item = items[index]; + const emmet = `${item.tag}${item.id ? '#' + item.id : ''}${item.classes.length > 0 ? '.' + item.classes.join('.') : ''}`; + removeTracker(emmet); + } }; - const moveTrackerItem = (itemId: string, direction: 'up' | 'down') => { - moveTracker(itemId, direction); + const updateTrackerAttribute = (emmet: string, attrName: string, attr: TrackerAttribute) => { + return updateTrackerAttr(emmet, attrName, attr); }; - const removeTrackerItemClass = (itemId: string, className: string) => { - removeClassFromTracker(itemId, className); + const updateTrackerAttributeByIndex = (index: number, attrName: string, attr: TrackerAttribute) => { + const items = trackerItems(); + if (index >= 0 && index < items.length) { + const item = items[index]; + const emmet = `${item.tag}${item.id ? '#' + item.id : ''}${item.classes.length > 0 ? '.' + item.classes.join('.') : ''}`; + updateTrackerAttr(emmet, attrName, attr); + } + }; + + const moveTrackerItem = (emmet: string, direction: 'up' | 'down') => { + return moveTracker(emmet, direction); + }; + + const moveTrackerItemByIndex = (index: number, direction: 'up' | 'down') => { + const items = trackerItems(); + if (index >= 0 && index < items.length) { + const item = items[index]; + const emmet = `${item.tag}${item.id ? '#' + item.id : ''}${item.classes.length > 0 ? '.' + item.classes.join('.') : ''}`; + moveTracker(emmet, direction); + } + }; + + const removeTrackerItemClass = (emmet: string, className: string) => { + return removeClassFromTracker(emmet, className); + }; + + const removeTrackerItemClassByIndex = (index: number, className: string) => { + const items = trackerItems(); + if (index >= 0 && index < items.length) { + const item = items[index]; + const emmet = `${item.tag}${item.id ? '#' + item.id : ''}${item.classes.length > 0 ? '.' + item.classes.join('.') : ''}`; + removeClassFromTracker(emmet, className); + } }; const trackerItems = () => getTrackerItems(); @@ -491,9 +532,13 @@ export function useCommander( trackerHistory, addTrackerItem, removeTrackerItem, + removeTrackerItemByIndex, updateTrackerAttribute, + updateTrackerAttributeByIndex, moveTrackerItem, + moveTrackerItemByIndex, removeTrackerItemClass, + removeTrackerItemClassByIndex, recordTrackerCommand, }; } diff --git a/src/components/md-commander/index.tsx b/src/components/md-commander/index.tsx index a47849c..fa2eb30 100644 --- a/src/components/md-commander/index.tsx +++ b/src/components/md-commander/index.tsx @@ -16,7 +16,7 @@ customElement( const commander = useCommander(props.commands); const [editingAttr, setEditingAttr] = createSignal<{ - itemId: string; + index: number; attrName: string; attr: TrackerAttribute; } | null>(null); @@ -99,13 +99,13 @@ customElement( fallback={ - setEditingAttr({ itemId, attrName, attr }) + onEditAttribute={(index, attrName, attr) => + setEditingAttr({ index, attrName, attr }) } - onRemoveClass={commander.removeTrackerItemClass} - onMoveUp={(itemId) => commander.moveTrackerItem(itemId, "up")} - onMoveDown={(itemId) => commander.moveTrackerItem(itemId, "down")} - onRemove={commander.removeTrackerItem} + onRemoveClass={commander.removeTrackerItemClassByIndex} + onMoveUp={(index) => commander.moveTrackerItemByIndex(index, "up")} + onMoveDown={(index) => commander.moveTrackerItemByIndex(index, "down")} + onRemove={(index) => commander.removeTrackerItemByIndex(index)} /> } > @@ -146,7 +146,7 @@ customElement( onUpdate={(attr) => { const data = editingAttr(); if (data) { - commander.updateTrackerAttribute(data.itemId, data.attrName, attr); + commander.updateTrackerAttributeByIndex(data.index, data.attrName, attr); } }} onClose={() => setEditingAttr(null)} diff --git a/src/components/md-commander/stores/index.ts b/src/components/md-commander/stores/index.ts index 82f62d7..6c4f38e 100644 --- a/src/components/md-commander/stores/index.ts +++ b/src/components/md-commander/stores/index.ts @@ -7,5 +7,7 @@ export { getTrackerItems, getTrackerHistory, clearTrackerHistory, + findTrackerIndex, + findTrackerItem, } from "./trackerStore"; export type { TrackerStore } from "./trackerStore"; diff --git a/src/components/md-commander/stores/trackerStore.ts b/src/components/md-commander/stores/trackerStore.ts index 30114f8..b37a9ad 100644 --- a/src/components/md-commander/stores/trackerStore.ts +++ b/src/components/md-commander/stores/trackerStore.ts @@ -11,10 +11,51 @@ const [tracker, setTracker] = createStore({ history: [], }); +/** + * 根据 Emmet 语法查找匹配的项目 + * 支持: + * - #id - 匹配 id + * - tag - 匹配 tag + * - tag.class - 匹配 tag 和 class + * - .class - 匹配 class + */ +export function findTrackerIndex(emmet: string): number { + const trimmed = emmet.trim(); + + // 解析 #id + if (trimmed.startsWith('#')) { + const id = trimmed.slice(1).split('.')[0]; + return tracker.items.findIndex(item => item.id === id); + } + + // 解析 tag 和 classes + const match = trimmed.match(/^([^.#]+)?(?:#([^.#]+))?(?:\.(.+))?$/); + if (!match) return -1; + + const tag = match[1]; + const classes = match[3] ? match[3].split('.') : []; + + return tracker.items.findIndex(item => { + // 匹配 tag + if (tag && item.tag !== tag) return false; + // 匹配所有 classes + if (classes.length > 0) { + for (const cls of classes) { + if (!item.classes.includes(cls)) return false; + } + } + return true; + }); +} + +export function findTrackerItem(emmet: string): TrackerItem | undefined { + const index = findTrackerIndex(emmet); + return index >= 0 ? tracker.items[index] : undefined; +} + export function addTrackerItem(item: Omit): TrackerItem { const newItem: TrackerItem = { ...item, - uuid: Date.now().toString() + Math.random().toString(36).slice(2), }; setTracker("items", (prev) => [...prev, newItem]); @@ -31,29 +72,38 @@ export function addTrackerItem(item: Omit): TrackerItem { return newItem; } -export function removeTrackerItem(itemId: string) { - const item = tracker.items.find((i) => i.uuid === itemId); +export function removeTrackerItem(emmet: string): boolean { + const index = findTrackerIndex(emmet); + if (index === -1) return false; - setTracker("items", (prev) => prev.filter((i) => i.uuid !== itemId)); + const item = tracker.items[index]; + + setTracker("items", (prev) => prev.filter((_, i) => i !== index)); setTracker("history", (prev) => [ ...prev, { type: "remove", - itemId, + itemId: `${item.tag}${item.id ? '#' + item.id : ''}`, data: item, timestamp: new Date(), }, ]); + + return true; } export function updateTrackerAttribute( - itemId: string, + emmet: string, attrName: string, attr: TrackerAttribute -) { +): boolean { + const index = findTrackerIndex(emmet); + if (index === -1) return false; + + const item = tracker.items[index]; setTracker("items", (prev) => - prev.map((i) => - i.uuid === itemId + prev.map((i, idx) => + idx === index ? { ...i, attributes: { ...i.attributes, [attrName]: attr } } : i ) @@ -62,23 +112,24 @@ export function updateTrackerAttribute( ...prev, { type: "update", - itemId, + itemId: `${item.tag}${item.id ? '#' + item.id : ''}`, attributeUpdates: { [attrName]: attr }, timestamp: new Date(), }, ]); + return true; } -export function moveTrackerItem(itemId: string, direction: "up" | "down") { - const index = tracker.items.findIndex((i) => i.uuid === itemId); - if (index === -1) return; +export function moveTrackerItem(emmet: string, direction: "up" | "down"): boolean { + const index = findTrackerIndex(emmet); + if (index === -1) return false; const newIndex = direction === "up" ? index - 1 : index + 1; - if (newIndex < 0 || newIndex >= tracker.items.length) return; + if (newIndex < 0 || newIndex >= tracker.items.length) return false; const newItems = [...tracker.items]; [newItems[index], newItems[newIndex]] = [newItems[newIndex], newItems[index]]; - + setTracker("items", newItems); setTracker("history", (prev) => [ ...prev, @@ -88,12 +139,17 @@ export function moveTrackerItem(itemId: string, direction: "up" | "down") { timestamp: new Date(), }, ]); + return true; } -export function removeTrackerClass(itemId: string, className: string) { +export function removeTrackerClass(emmet: string, className: string): boolean { + const index = findTrackerIndex(emmet); + if (index === -1) return false; + + const item = tracker.items[index]; setTracker("items", (prev) => - prev.map((i) => - i.uuid === itemId + prev.map((i, idx) => + idx === index ? { ...i, classes: i.classes.filter((c) => c !== className) } : i ) @@ -102,10 +158,11 @@ export function removeTrackerClass(itemId: string, className: string) { ...prev, { type: "update", - itemId, + itemId: `${item.tag}${item.id ? '#' + item.id : ''}`, timestamp: new Date(), }, ]); + return true; } export function getTrackerItems(): TrackerItem[] { diff --git a/src/components/md-commander/types.ts b/src/components/md-commander/types.ts index a6b8614..6b9686f 100644 --- a/src/components/md-commander/types.ts +++ b/src/components/md-commander/types.ts @@ -86,9 +86,8 @@ export interface TrackerAttribute { } export interface TrackerItem { - uuid: string; // 内部唯一标识符 tag: string; - id?: string; // Emmet ID (#id),可选的用户定义 ID + id?: string; // Emmet ID (#id) classes: string[]; attributes: Record; }