feat: less completions

This commit is contained in:
hypercross 2026-03-01 09:52:53 +08:00
parent a55f55505c
commit c23bff5a89
6 changed files with 45 additions and 17 deletions

View File

@ -39,8 +39,8 @@ export const CommanderInput: Component<CommanderInputProps> = (props) => {
{/* 自动补全下拉框 - 向上弹出 */}
<Show when={props.showCompletions() && props.completions().length > 0}>
<div class="absolute left-0 bottom-full w-full bg-white border border-gray-300 shadow-lg max-h-48 overflow-auto mb-1">
<For each={props.completions()}>{(comp, idx) => (
<div class="absolute left-0 bottom-full w-full bg-white border border-gray-300 shadow-lg mb-1">
<For each={props.completions().slice(0, 3)}>{(comp, idx) => (
<div
class={`px-3 py-2 cursor-pointer flex justify-between items-center ${
idx() === props.selectedCompletion()
@ -71,6 +71,11 @@ export const CommanderInput: Component<CommanderInputProps> = (props) => {
</Show>
</div>
)}</For>
<Show when={props.completions().length > 3}>
<div class="px-3 py-1 text-xs text-gray-500 bg-gray-50 border-t border-gray-200">
{props.completions().length - 3} ...
</div>
</Show>
</div>
</Show>
</div>

View File

@ -34,11 +34,14 @@ export const TrackerView: Component<TrackerViewProps> = (props) => {
<For each={props.items()}>
{(item) => (
<div class="border border-gray-200 rounded-lg p-3 bg-gray-50">
{/* 头部tag 和操作按钮 */}
{/* 头部tag、id 和操作按钮 */}
<div class="flex items-center justify-between mb-2">
<div class="flex items-center gap-2">
<span class="font-bold text-gray-800">{item.tag}</span>
<span class="text-xs text-gray-500 font-mono">#{item.id.slice(-6)}</span>
<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

View File

@ -28,12 +28,13 @@ export const trackCommand: MdCommanderCommand = {
// 直接添加追踪项目
addTrackerItem(parsed);
const idStr = parsed.id ? ` #${parsed.id}` : "";
const classStr = parsed.classes.length > 0 ? `.${parsed.classes.join(".")}` : "";
const attrCount = Object.keys(parsed.attributes).length;
const attrStr = attrCount > 0 ? `[${attrCount} 个属性]` : "";
return {
message: `已添加追踪项目:${parsed.tag}${classStr}${attrStr}`,
message: `已添加追踪项目:${parsed.tag}${idStr}${classStr}${attrStr}`,
type: "success",
};
},
@ -71,7 +72,7 @@ export const listTrackCommand: MdCommanderCommand = {
return { message: "暂无追踪项目", type: "info" };
}
const list = items.map((i) => `${i.tag}${i.classes.length > 0 ? "." + i.classes.join(".") : ""}`).join("\n");
const list = items.map((i) => `${i.tag}${i.id ? "#" + i.id : ""}${i.classes.length > 0 ? "." + i.classes.join(".") : ""}`).join("\n");
return { message: `追踪项目:\n${list}`, type: "info" };
},
};

View File

@ -264,7 +264,7 @@ export function useCommander(
const [entries, setEntries] = createSignal<CommanderEntry[]>([]);
const [showCompletions, setShowCompletions] = createSignal(false);
const [completions, setCompletions] = createSignal<CompletionItem[]>([]);
const [selectedCompletion, setSelectedCompletion] = createSignal(0);
const [selectedCompletion, setSelectedCompletionState] = createSignal(0);
const [isFocused, setIsFocused] = createSignal(false);
// 命令历史
@ -335,12 +335,30 @@ export function useCommander(
const comps = getCompletions(input, commands);
setCompletions(comps);
setShowCompletions(comps.length > 0 && isFocused());
setSelectedCompletion(0);
setSelectedCompletionState(0);
};
const setSelectedCompletion = (v: number | ((prev: number) => number)) => {
const comps = completions();
const maxVisible = 3;
const maxIdx = Math.min(comps.length - 1, maxVisible - 1);
if (typeof v === 'function') {
setSelectedCompletionState(prev => {
const next = v(prev);
return Math.max(0, Math.min(next, maxIdx));
});
} else {
setSelectedCompletionState(Math.max(0, Math.min(v, maxIdx)));
}
};
const acceptCompletion = () => {
const idx = selectedCompletion();
const comp = completions()[idx];
const comps = completions();
// 确保索引在有效范围内
const validIdx = Math.min(idx, Math.min(comps.length - 1, 2));
const comp = comps[validIdx];
if (!comp) return;
const input = inputValue();

View File

@ -11,10 +11,10 @@ const [tracker, setTracker] = createStore<TrackerStore>({
history: [],
});
export function addTrackerItem(item: Omit<TrackerItem, "id">): TrackerItem {
export function addTrackerItem(item: Omit<TrackerItem, "uuid">): TrackerItem {
const newItem: TrackerItem = {
...item,
id: Date.now().toString() + Math.random().toString(36).slice(2),
uuid: Date.now().toString() + Math.random().toString(36).slice(2),
};
setTracker("items", (prev) => [...prev, newItem]);
@ -32,9 +32,9 @@ export function addTrackerItem(item: Omit<TrackerItem, "id">): TrackerItem {
}
export function removeTrackerItem(itemId: string) {
const item = tracker.items.find((i) => i.id === itemId);
const item = tracker.items.find((i) => i.uuid === itemId);
setTracker("items", (prev) => prev.filter((i) => i.id !== itemId));
setTracker("items", (prev) => prev.filter((i) => i.uuid !== itemId));
setTracker("history", (prev) => [
...prev,
{
@ -53,7 +53,7 @@ export function updateTrackerAttribute(
) {
setTracker("items", (prev) =>
prev.map((i) =>
i.id === itemId
i.uuid === itemId
? { ...i, attributes: { ...i.attributes, [attrName]: attr } }
: i
)
@ -70,7 +70,7 @@ export function updateTrackerAttribute(
}
export function moveTrackerItem(itemId: string, direction: "up" | "down") {
const index = tracker.items.findIndex((i) => i.id === itemId);
const index = tracker.items.findIndex((i) => i.uuid === itemId);
if (index === -1) return;
const newIndex = direction === "up" ? index - 1 : index + 1;
@ -93,7 +93,7 @@ export function moveTrackerItem(itemId: string, direction: "up" | "down") {
export function removeTrackerClass(itemId: string, className: string) {
setTracker("items", (prev) =>
prev.map((i) =>
i.id === itemId
i.uuid === itemId
? { ...i, classes: i.classes.filter((c) => c !== className) }
: i
)

View File

@ -86,8 +86,9 @@ export interface TrackerAttribute {
}
export interface TrackerItem {
id: string;
uuid: string; // 内部唯一标识符
tag: string;
id?: string; // Emmet ID (#id),可选的用户定义 ID
classes: string[];
attributes: Record<string, TrackerAttribute>;
}