fix: issues
This commit is contained in:
parent
c23bff5a89
commit
fdaf79e7ed
|
|
@ -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<TrackerViewProps> = (props) => {
|
||||
|
|
@ -32,7 +32,7 @@ export const TrackerView: Component<TrackerViewProps> = (props) => {
|
|||
>
|
||||
<div class="space-y-4">
|
||||
<For each={props.items()}>
|
||||
{(item) => (
|
||||
{(item, index) => (
|
||||
<div class="border border-gray-200 rounded-lg p-3 bg-gray-50">
|
||||
{/* 头部:tag、id 和操作按钮 */}
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
|
|
@ -41,27 +41,26 @@ export const TrackerView: Component<TrackerViewProps> = (props) => {
|
|||
<Show when={item.id}>
|
||||
<span class="text-xs text-purple-600 font-mono">#{item.id}</span>
|
||||
</Show>
|
||||
<span class="text-xs text-gray-500 font-mono">• {item.uuid.slice(-6)}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<button
|
||||
class="p-1 text-gray-500 hover:text-blue-600 hover:bg-blue-50 rounded"
|
||||
title="上移"
|
||||
onClick={() => props.onMoveUp?.(item.id)}
|
||||
onClick={() => props.onMoveUp?.(index())}
|
||||
>
|
||||
↑
|
||||
</button>
|
||||
<button
|
||||
class="p-1 text-gray-500 hover:text-blue-600 hover:bg-blue-50 rounded"
|
||||
title="下移"
|
||||
onClick={() => props.onMoveDown?.(item.id)}
|
||||
onClick={() => props.onMoveDown?.(index())}
|
||||
>
|
||||
↓
|
||||
</button>
|
||||
<button
|
||||
class="p-1 text-gray-500 hover:text-red-600 hover:bg-red-50 rounded"
|
||||
title="移除"
|
||||
onClick={() => props.onRemove?.(item.id)}
|
||||
onClick={() => props.onRemove?.(index())}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
|
|
@ -71,20 +70,20 @@ export const TrackerView: Component<TrackerViewProps> = (props) => {
|
|||
{/* Classes */}
|
||||
<Show when={item.classes.length > 0}>
|
||||
<div class="flex flex-wrap gap-1 mb-2">
|
||||
<For each={item.classes}>
|
||||
<Index each={item.classes}>
|
||||
{(cls) => (
|
||||
<span class="inline-flex items-center gap-1 px-2 py-0.5 bg-blue-100 text-blue-700 text-xs rounded">
|
||||
{cls}
|
||||
{cls()}
|
||||
<button
|
||||
class="hover:text-red-600"
|
||||
onClick={() => props.onRemoveClass?.(item.id, cls)}
|
||||
onClick={() => props.onRemoveClass?.(index(), cls())}
|
||||
title="移除类"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
</Index>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
|
|
@ -94,7 +93,7 @@ export const TrackerView: Component<TrackerViewProps> = (props) => {
|
|||
{(attr: TrackerAttribute) => (
|
||||
<div
|
||||
class="flex items-center justify-between px-2 py-1 bg-white rounded border border-gray-200 cursor-pointer hover:border-blue-400"
|
||||
onClick={() => props.onEditAttribute?.(item.id, attr.name, attr)}
|
||||
onClick={() => props.onEditAttribute?.(index(), attr.name, attr)}
|
||||
>
|
||||
<span class="text-xs text-gray-600">{attr.name}</span>
|
||||
<span class="text-sm font-mono text-gray-800">
|
||||
|
|
|
|||
|
|
@ -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" };
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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<TrackerItem, 'id'>) => 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<TrackerCommand, 'timestamp'>) => 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,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ customElement<MdCommanderProps>(
|
|||
|
||||
const commander = useCommander(props.commands);
|
||||
const [editingAttr, setEditingAttr] = createSignal<{
|
||||
itemId: string;
|
||||
index: number;
|
||||
attrName: string;
|
||||
attr: TrackerAttribute;
|
||||
} | null>(null);
|
||||
|
|
@ -99,13 +99,13 @@ customElement<MdCommanderProps>(
|
|||
fallback={
|
||||
<TrackerView
|
||||
items={commander.trackerItems}
|
||||
onEditAttribute={(itemId, attrName, attr) =>
|
||||
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<MdCommanderProps>(
|
|||
onUpdate={(attr) => {
|
||||
const data = editingAttr();
|
||||
if (data) {
|
||||
commander.updateTrackerAttribute(data.itemId, data.attrName, attr);
|
||||
commander.updateTrackerAttributeByIndex(data.index, data.attrName, attr);
|
||||
}
|
||||
}}
|
||||
onClose={() => setEditingAttr(null)}
|
||||
|
|
|
|||
|
|
@ -7,5 +7,7 @@ export {
|
|||
getTrackerItems,
|
||||
getTrackerHistory,
|
||||
clearTrackerHistory,
|
||||
findTrackerIndex,
|
||||
findTrackerItem,
|
||||
} from "./trackerStore";
|
||||
export type { TrackerStore } from "./trackerStore";
|
||||
|
|
|
|||
|
|
@ -11,10 +11,51 @@ const [tracker, setTracker] = createStore<TrackerStore>({
|
|||
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, "uuid">): 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, "uuid">): 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[] {
|
||||
|
|
|
|||
|
|
@ -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<string, TrackerAttribute>;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue