feat: less completions
This commit is contained in:
parent
a55f55505c
commit
c23bff5a89
|
|
@ -39,8 +39,8 @@ export const CommanderInput: Component<CommanderInputProps> = (props) => {
|
||||||
|
|
||||||
{/* 自动补全下拉框 - 向上弹出 */}
|
{/* 自动补全下拉框 - 向上弹出 */}
|
||||||
<Show when={props.showCompletions() && props.completions().length > 0}>
|
<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">
|
<div class="absolute left-0 bottom-full w-full bg-white border border-gray-300 shadow-lg mb-1">
|
||||||
<For each={props.completions()}>{(comp, idx) => (
|
<For each={props.completions().slice(0, 3)}>{(comp, idx) => (
|
||||||
<div
|
<div
|
||||||
class={`px-3 py-2 cursor-pointer flex justify-between items-center ${
|
class={`px-3 py-2 cursor-pointer flex justify-between items-center ${
|
||||||
idx() === props.selectedCompletion()
|
idx() === props.selectedCompletion()
|
||||||
|
|
@ -71,6 +71,11 @@ export const CommanderInput: Component<CommanderInputProps> = (props) => {
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
)}</For>
|
)}</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>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -34,11 +34,14 @@ export const TrackerView: Component<TrackerViewProps> = (props) => {
|
||||||
<For each={props.items()}>
|
<For each={props.items()}>
|
||||||
{(item) => (
|
{(item) => (
|
||||||
<div class="border border-gray-200 rounded-lg p-3 bg-gray-50">
|
<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 justify-between mb-2">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="font-bold text-gray-800">{item.tag}</span>
|
<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>
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,13 @@ export const trackCommand: MdCommanderCommand = {
|
||||||
// 直接添加追踪项目
|
// 直接添加追踪项目
|
||||||
addTrackerItem(parsed);
|
addTrackerItem(parsed);
|
||||||
|
|
||||||
|
const idStr = parsed.id ? ` #${parsed.id}` : "";
|
||||||
const classStr = parsed.classes.length > 0 ? `.${parsed.classes.join(".")}` : "";
|
const classStr = parsed.classes.length > 0 ? `.${parsed.classes.join(".")}` : "";
|
||||||
const attrCount = Object.keys(parsed.attributes).length;
|
const attrCount = Object.keys(parsed.attributes).length;
|
||||||
const attrStr = attrCount > 0 ? `[${attrCount} 个属性]` : "";
|
const attrStr = attrCount > 0 ? `[${attrCount} 个属性]` : "";
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: `已添加追踪项目:${parsed.tag}${classStr}${attrStr}`,
|
message: `已添加追踪项目:${parsed.tag}${idStr}${classStr}${attrStr}`,
|
||||||
type: "success",
|
type: "success",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
@ -71,7 +72,7 @@ export const listTrackCommand: MdCommanderCommand = {
|
||||||
return { message: "暂无追踪项目", type: "info" };
|
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" };
|
return { message: `追踪项目:\n${list}`, type: "info" };
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -264,7 +264,7 @@ export function useCommander(
|
||||||
const [entries, setEntries] = createSignal<CommanderEntry[]>([]);
|
const [entries, setEntries] = createSignal<CommanderEntry[]>([]);
|
||||||
const [showCompletions, setShowCompletions] = createSignal(false);
|
const [showCompletions, setShowCompletions] = createSignal(false);
|
||||||
const [completions, setCompletions] = createSignal<CompletionItem[]>([]);
|
const [completions, setCompletions] = createSignal<CompletionItem[]>([]);
|
||||||
const [selectedCompletion, setSelectedCompletion] = createSignal(0);
|
const [selectedCompletion, setSelectedCompletionState] = createSignal(0);
|
||||||
const [isFocused, setIsFocused] = createSignal(false);
|
const [isFocused, setIsFocused] = createSignal(false);
|
||||||
|
|
||||||
// 命令历史
|
// 命令历史
|
||||||
|
|
@ -335,12 +335,30 @@ export function useCommander(
|
||||||
const comps = getCompletions(input, commands);
|
const comps = getCompletions(input, commands);
|
||||||
setCompletions(comps);
|
setCompletions(comps);
|
||||||
setShowCompletions(comps.length > 0 && isFocused());
|
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 acceptCompletion = () => {
|
||||||
const idx = selectedCompletion();
|
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;
|
if (!comp) return;
|
||||||
|
|
||||||
const input = inputValue();
|
const input = inputValue();
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@ const [tracker, setTracker] = createStore<TrackerStore>({
|
||||||
history: [],
|
history: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
export function addTrackerItem(item: Omit<TrackerItem, "id">): TrackerItem {
|
export function addTrackerItem(item: Omit<TrackerItem, "uuid">): TrackerItem {
|
||||||
const newItem: TrackerItem = {
|
const newItem: TrackerItem = {
|
||||||
...item,
|
...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]);
|
setTracker("items", (prev) => [...prev, newItem]);
|
||||||
|
|
@ -32,9 +32,9 @@ export function addTrackerItem(item: Omit<TrackerItem, "id">): TrackerItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeTrackerItem(itemId: string) {
|
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) => [
|
setTracker("history", (prev) => [
|
||||||
...prev,
|
...prev,
|
||||||
{
|
{
|
||||||
|
|
@ -53,7 +53,7 @@ export function updateTrackerAttribute(
|
||||||
) {
|
) {
|
||||||
setTracker("items", (prev) =>
|
setTracker("items", (prev) =>
|
||||||
prev.map((i) =>
|
prev.map((i) =>
|
||||||
i.id === itemId
|
i.uuid === itemId
|
||||||
? { ...i, attributes: { ...i.attributes, [attrName]: attr } }
|
? { ...i, attributes: { ...i.attributes, [attrName]: attr } }
|
||||||
: i
|
: i
|
||||||
)
|
)
|
||||||
|
|
@ -70,7 +70,7 @@ export function updateTrackerAttribute(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function moveTrackerItem(itemId: string, direction: "up" | "down") {
|
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;
|
if (index === -1) return;
|
||||||
|
|
||||||
const newIndex = direction === "up" ? index - 1 : index + 1;
|
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) {
|
export function removeTrackerClass(itemId: string, className: string) {
|
||||||
setTracker("items", (prev) =>
|
setTracker("items", (prev) =>
|
||||||
prev.map((i) =>
|
prev.map((i) =>
|
||||||
i.id === itemId
|
i.uuid === itemId
|
||||||
? { ...i, classes: i.classes.filter((c) => c !== className) }
|
? { ...i, classes: i.classes.filter((c) => c !== className) }
|
||||||
: i
|
: i
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,9 @@ export interface TrackerAttribute {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TrackerItem {
|
export interface TrackerItem {
|
||||||
id: string;
|
uuid: string; // 内部唯一标识符
|
||||||
tag: string;
|
tag: string;
|
||||||
|
id?: string; // Emmet ID (#id),可选的用户定义 ID
|
||||||
classes: string[];
|
classes: string[];
|
||||||
attributes: Record<string, TrackerAttribute>;
|
attributes: Record<string, TrackerAttribute>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue