@leavepulse/ui 0.37.11 → 0.38.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,48 +1,80 @@
1
- import { defineComponent, computed, ref, shallowRef, watch, nextTick, openBlock, createBlock, unref, withCtx, createVNode, withModifiers, createTextVNode, createElementVNode, createElementBlock, Fragment, renderList, createCommentVNode, toDisplayString, withDirectives, withKeys, vModelText, normalizeClass } from "vue";
1
+ import { defineComponent, computed, ref, useId, shallowRef, watch, nextTick, openBlock, createBlock, unref, withCtx, createVNode, withModifiers, normalizeClass, createTextVNode, createElementVNode, createElementBlock, Fragment, renderList, createCommentVNode, toDisplayString, withDirectives, withKeys, vModelText, resolveDynamicComponent, normalizeStyle, renderSlot } from "vue";
2
2
  import { DialogRoot, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription } from "reka-ui";
3
3
  import { _ as _sfc_main$1 } from "../chunks/LpIcon-CCnX5_2j.js";
4
4
  import { _ as _sfc_main$2 } from "../chunks/LpScrollArea-D8xyKXcF.js";
5
5
  import { useAsyncTask } from "../composables/useAsyncTask.js";
6
+ import { useClipboard } from "../composables/useClipboard.js";
6
7
  import { useHotkeys } from "../composables/useHotkeys.js";
7
8
  import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
8
- import '../assets/LpCommandPalette-B-g1fa8r.css';const _hoisted_1 = { class: "flex shrink-0 items-center gap-2.5 border-b border-line px-4" };
9
+ import '../assets/LpCommandPalette-BV6-oPGZ.css';const _hoisted_1 = { class: "flex shrink-0 items-center gap-2.5 border-b border-line px-4" };
9
10
  const _hoisted_2 = ["title", "onClick"];
10
- const _hoisted_3 = ["placeholder", "onKeydown"];
11
- const _hoisted_4 = {
12
- key: 0,
13
- class: "px-2 py-10 text-center text-sm text-muted"
14
- };
11
+ const _hoisted_3 = ["aria-activedescendant", "placeholder", "onKeydown"];
12
+ const _hoisted_4 = { class: "flex min-h-0 flex-1" };
15
13
  const _hoisted_5 = {
16
14
  key: 0,
17
- class: "px-2 pb-1 pt-3 text-[11px] font-semibold uppercase tracking-[0.08em] text-muted"
15
+ class: "px-2 py-10 text-center text-sm text-muted"
18
16
  };
19
- const _hoisted_6 = ["data-cmd-index", "disabled", "onClick", "onPointerenter"];
20
- const _hoisted_7 = { class: "min-w-0 flex-1" };
21
- const _hoisted_8 = { class: "block truncate font-medium" };
22
- const _hoisted_9 = {
17
+ const _hoisted_6 = ["aria-labelledby"];
18
+ const _hoisted_7 = ["id"];
19
+ const _hoisted_8 = { class: "min-w-0 flex-1" };
20
+ const _hoisted_9 = { class: "block truncate font-medium" };
21
+ const _hoisted_10 = {
23
22
  key: 0,
24
23
  class: "rounded-xs bg-brand/25 text-ink"
25
24
  };
26
- const _hoisted_10 = {
25
+ const _hoisted_11 = {
27
26
  key: 0,
28
27
  class: "mt-0.5 block truncate text-xs text-muted"
29
28
  };
30
- const _hoisted_11 = {
29
+ const _hoisted_12 = {
31
30
  key: 0,
32
31
  class: "rounded-xs bg-brand/20 text-ink"
33
32
  };
34
- const _hoisted_12 = {
33
+ const _hoisted_13 = {
34
+ key: 0,
35
+ class: "rounded-xs bg-brand/20 text-ink"
36
+ };
37
+ const _hoisted_14 = {
35
38
  key: 1,
36
39
  class: "shrink-0 text-xs text-muted"
37
40
  };
38
- const _hoisted_13 = { class: "flex shrink-0 items-center gap-4 border-t border-line px-4 py-2.5 text-[11px] text-muted" };
39
- const _hoisted_14 = { class: "flex items-center gap-1" };
40
- const _hoisted_15 = { class: "flex items-center gap-1" };
41
- const _hoisted_16 = {
41
+ const _hoisted_15 = {
42
+ key: 0,
43
+ class: "hidden min-h-0 w-[56%] shrink-0 flex-col border-l border-line md:flex"
44
+ };
45
+ const _hoisted_16 = { class: "flex items-center gap-2 text-ink" };
46
+ const _hoisted_17 = { class: "min-w-0 text-base font-semibold" };
47
+ const _hoisted_18 = {
48
+ key: 0,
49
+ class: "mt-0.5 text-xs text-muted"
50
+ };
51
+ const _hoisted_19 = {
52
+ key: 1,
53
+ class: "mt-3 whitespace-pre-line text-sm leading-relaxed text-ink/90"
54
+ };
55
+ const _hoisted_20 = {
56
+ key: 0,
57
+ class: "rounded-xs bg-brand/25 text-ink"
58
+ };
59
+ const _hoisted_21 = {
60
+ key: 2,
61
+ class: "mt-3 text-sm text-muted"
62
+ };
63
+ const _hoisted_22 = { class: "flex shrink-0 items-center gap-4 border-t border-line px-4 py-2.5 text-[11px] text-muted" };
64
+ const _hoisted_23 = { class: "flex items-center gap-1" };
65
+ const _hoisted_24 = { class: "flex items-center gap-1" };
66
+ const _hoisted_25 = {
42
67
  key: 0,
43
68
  class: "flex items-center gap-1"
44
69
  };
45
- const _hoisted_17 = { class: "ml-auto flex items-center gap-1" };
70
+ const _hoisted_26 = {
71
+ key: 1,
72
+ class: "flex items-center gap-1"
73
+ };
74
+ const _hoisted_27 = { class: "ml-auto flex items-center gap-1" };
75
+ const LATIN = "qwertyuiop[]asdfghjkl;'zxcvbnm,.`";
76
+ const CYRILLIC = "йцукенгшщзхъфывапролджэячсмитьбюё";
77
+ const TYPO_MIN = 5;
46
78
  const _sfc_main = /* @__PURE__ */ defineComponent({
47
79
  __name: "LpCommandPalette",
48
80
  props: {
@@ -58,7 +90,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
58
90
  recentLimit: { default: 5 },
59
91
  recentLabel: { default: "Recent" },
60
92
  resultsLabel: { default: "Results" },
61
- labels: {}
93
+ labels: {},
94
+ excerptLines: { default: 4 },
95
+ groupLimit: { default: 0 },
96
+ layoutFallback: { type: Boolean, default: true },
97
+ previewPane: { type: Boolean, default: false }
62
98
  },
63
99
  emits: ["update:open", "select", "run"],
64
100
  setup(__props, { emit: __emit }) {
@@ -68,17 +104,63 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
68
104
  select: "select",
69
105
  close: "close",
70
106
  back: "back",
71
- searching: "Searching…"
107
+ searching: "Searching…",
108
+ searchFailed: "Search is unavailable right now.",
109
+ retry: "Retry",
110
+ showMore: "Show {count} more",
111
+ actions: "actions",
112
+ openInNewTab: "Open in new tab",
113
+ copyLink: "Copy link",
114
+ linkCopied: "Link copied",
115
+ noPreview: "No preview"
72
116
  };
73
117
  const text = computed(() => ({ ...DEFAULT_LABELS, ...props.labels }));
74
118
  const emit = __emit;
75
119
  const query = ref("");
120
+ const listId = `lp-cmd-${useId()}`;
121
+ function optionId(index) {
122
+ return `${listId}-${index}`;
123
+ }
124
+ const activeId = computed(() => flat.value.length ? optionId(selectedIndex.value) : void 0);
76
125
  const selectedIndex = ref(0);
77
126
  const inputRef = ref(null);
78
127
  const scrollRef = ref(null);
79
128
  const trail = shallowRef([]);
80
129
  const mode = computed(() => trail.value[trail.value.length - 1] ?? null);
81
130
  const modeItems = shallowRef([]);
131
+ const { copy } = useClipboard();
132
+ function actionsOf(c) {
133
+ if (!c || c.disabled || moreOf.has(c)) return [];
134
+ const own = c.actions ?? [];
135
+ const href = c.href;
136
+ if (!href) return own;
137
+ const link = new URL(href, window.location.href).href;
138
+ return [
139
+ ...own,
140
+ {
141
+ id: `${c.id}:open-in-new-tab`,
142
+ label: text.value.openInNewTab,
143
+ icon: "lucide:external-link",
144
+ onSelect: () => window.open(href, "_blank", "noopener")
145
+ },
146
+ {
147
+ id: `${c.id}:copy-link`,
148
+ label: text.value.copyLink,
149
+ icon: "lucide:link",
150
+ onSelect: () => void copy(link, { toast: text.value.linkCopied || false })
151
+ }
152
+ ];
153
+ }
154
+ const selectedActions = computed(() => actionsOf(flat.value[selectedIndex.value]));
155
+ function onTab(event) {
156
+ if (event.shiftKey) return;
157
+ const c = flat.value[selectedIndex.value];
158
+ const actions = selectedActions.value;
159
+ if (!c || !actions.length) return;
160
+ event.preventDefault();
161
+ event.stopPropagation();
162
+ enter({ id: `${c.id}:actions`, label: c.label, icon: c.icon, children: actions });
163
+ }
82
164
  function enter(c) {
83
165
  trail.value = [...trail.value, c];
84
166
  query.value = "";
@@ -115,9 +197,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
115
197
  function cancel() {
116
198
  task.cancel();
117
199
  }
200
+ const failed = ref(false);
118
201
  function refresh() {
119
202
  cancel();
120
203
  found.value = [];
204
+ failed.value = false;
121
205
  const run2 = fetcher();
122
206
  if (!run2) return;
123
207
  const q = query.value.trim();
@@ -126,21 +210,60 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
126
210
  task.run((signal) => run2(q, signal), {
127
211
  delay: props.debounce,
128
212
  apply: (hits) => found.value = hits,
129
- fail: () => found.value = []
213
+ fail: () => {
214
+ found.value = [];
215
+ failed.value = true;
216
+ }
130
217
  });
131
218
  }
132
219
  const source = computed(() => mode.value ? modeItems.value : props.commands);
133
- const ranked = computed(() => {
134
- const q = query.value.trim().toLowerCase();
135
- if (!q) return source.value.filter((c) => !c.hidden);
136
- return source.value.map((c) => {
137
- const label = c.label.toLowerCase().includes(q);
138
- const kw = (c.keywords ?? []).some((k) => k.toLowerCase().includes(q));
139
- const desc = c.description?.toLowerCase().includes(q) ?? false;
140
- const score = (label ? 4 : 0) + (kw ? 3 : 0) + (desc ? 2 : 0);
141
- return { c, score };
142
- }).filter((e) => e.score > 0).sort((a, b) => b.score - a.score || a.c.label.localeCompare(b.c.label)).map((e) => e.c);
220
+ const SWAP = /* @__PURE__ */ new Map();
221
+ for (let i = 0; i < LATIN.length; i++) {
222
+ SWAP.set(LATIN[i], CYRILLIC[i]);
223
+ SWAP.set(CYRILLIC[i], LATIN[i]);
224
+ }
225
+ function swapLayout(term) {
226
+ return [...term].map((ch) => SWAP.get(ch) ?? ch).join("");
227
+ }
228
+ function withinOne(a, b) {
229
+ if (Math.abs(a.length - b.length) > 1) return false;
230
+ let i = 0;
231
+ while (i < a.length && i < b.length && a[i] === b[i]) i++;
232
+ if (i === a.length || i === b.length) return true;
233
+ const rest = (x, y) => a.slice(i + x) === b.slice(i + y);
234
+ return rest(1, 1) || rest(1, 0) || rest(0, 1) || a[i] === b[i + 1] && a[i + 1] === b[i] && rest(2, 2);
235
+ }
236
+ function nearWord(term, label) {
237
+ if (term.length < TYPO_MIN) return false;
238
+ return label.split(/[^\p{L}\p{N}]+/u).some((word) => withinOne(term, word) || word.length > term.length && withinOne(term, word.slice(0, term.length)));
239
+ }
240
+ function score(c, terms) {
241
+ const label = c.label.toLowerCase();
242
+ const keywords = (c.keywords ?? []).map((k) => k.toLowerCase());
243
+ const description = c.description?.toLowerCase() ?? "";
244
+ let total = 0;
245
+ for (const term of terms) {
246
+ let hit = (label.includes(term) ? 4 : 0) + (keywords.some((k) => k.includes(term)) ? 3 : 0) + (description.includes(term) ? 2 : 0);
247
+ if (!hit && nearWord(term, label)) hit = 1;
248
+ if (!hit) return 0;
249
+ total += hit;
250
+ }
251
+ return total;
252
+ }
253
+ function rank(items, terms) {
254
+ return items.map((c) => ({ c, score: score(c, terms) })).filter((e) => e.score > 0).sort((a, b) => b.score - a.score || a.c.label.localeCompare(b.c.label)).map((e) => e.c);
255
+ }
256
+ const typedTerms = computed(() => query.value.trim().toLowerCase().split(/\s+/).filter(Boolean));
257
+ const local = computed(() => {
258
+ const terms = typedTerms.value;
259
+ if (!terms.length) return { items: source.value.filter((c) => !c.hidden), terms };
260
+ const items = rank(source.value, terms);
261
+ if (items.length || !props.layoutFallback) return { items, terms };
262
+ const swapped = terms.map(swapLayout);
263
+ const other = rank(source.value, swapped);
264
+ return other.length ? { items: other, terms: swapped } : { items, terms };
143
265
  });
266
+ const ranked = computed(() => local.value.items);
144
267
  const fetched = computed(() => {
145
268
  if (!found.value.length) return [];
146
269
  const seen = new Set(ranked.value.map((c) => c.id));
@@ -171,14 +294,45 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
171
294
  for (const c of recent) push(c, props.recentLabel);
172
295
  for (const c of ranked.value) if (!recent.includes(c)) push(c, c.group ?? "");
173
296
  for (const c of fetched.value) push(c, c.group ?? "");
174
- return order.map((name) => ({ name, items: map.get(name) }));
297
+ return order.map((name) => ({ name, items: folded(name, map.get(name)) }));
175
298
  });
299
+ const expanded = ref(/* @__PURE__ */ new Set());
300
+ const moreOf = /* @__PURE__ */ new WeakMap();
301
+ function folded(name, items) {
302
+ const limit = props.groupLimit;
303
+ if (!limit || items.length <= limit + 1 || expanded.value.has(name)) return items;
304
+ const more = {
305
+ id: `lp-cmd-more:${name}`,
306
+ label: text.value.showMore.replace("{count}", String(items.length - limit)),
307
+ icon: "lucide:chevron-down",
308
+ keepOpen: true
309
+ };
310
+ moreOf.set(more, name);
311
+ return [...items.slice(0, limit), more];
312
+ }
176
313
  const flat = computed(() => grouped.value.flatMap((g) => g.items));
314
+ const active = computed(() => {
315
+ const c = flat.value[selectedIndex.value];
316
+ return c && !moreOf.has(c) ? c : void 0;
317
+ });
318
+ const previewRef = ref(null);
319
+ watch(
320
+ () => active.value?.id,
321
+ async () => {
322
+ if (!props.previewPane) return;
323
+ await nextTick();
324
+ const viewport = previewRef.value?.viewportEl;
325
+ if (!viewport) return;
326
+ const mark = viewport.querySelector("mark");
327
+ if (mark) mark.scrollIntoView({ block: "center" });
328
+ else viewport.scrollTop = 0;
329
+ }
330
+ );
177
331
  function flatIndexOf(c) {
178
332
  return flat.value.indexOf(c);
179
333
  }
180
- function highlight(value) {
181
- const terms = [...new Set(query.value.toLowerCase().trim().split(/\s+/).filter((t) => t.length >= 2))].sort(
334
+ function highlight(value, extra = []) {
335
+ const terms = [...new Set([...local.value.terms, ...extra.map((t) => t.toLowerCase())].filter((t) => t.length >= 2))].sort(
182
336
  (a, b) => b.length - a.length
183
337
  );
184
338
  if (!value || terms.length === 0) return value ? [{ text: value, hit: false }] : [];
@@ -209,6 +363,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
209
363
  selectedIndex.value = 0;
210
364
  refresh();
211
365
  });
366
+ watch([query, trail, () => props.open], () => {
367
+ expanded.value = /* @__PURE__ */ new Set();
368
+ });
212
369
  watch(
213
370
  () => flat.value.length,
214
371
  (len) => {
@@ -233,13 +390,36 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
233
390
  const len = flat.value.length;
234
391
  if (len) selectedIndex.value = (selectedIndex.value - 1 + len) % len;
235
392
  }
236
- function runSelected() {
237
- run(flat.value[selectedIndex.value]);
393
+ function runSelected(event) {
394
+ const c = flat.value[selectedIndex.value];
395
+ if (c?.href && !c.disabled && (event?.ctrlKey || event?.metaKey)) {
396
+ openInNewTab(c);
397
+ return;
398
+ }
399
+ run(c);
400
+ }
401
+ function openInNewTab(c) {
402
+ if (!c.href) return;
403
+ window.open(c.href, "_blank", "noopener");
404
+ emit("run", c.id, c);
405
+ emit("update:open", false);
406
+ }
407
+ function onRowClick(event, c) {
408
+ if (c.href && (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey || event.button !== 0)) return;
409
+ event.preventDefault();
410
+ run(c);
238
411
  }
239
412
  function run(c) {
240
413
  if (!c || c.disabled) return;
414
+ const group = moreOf.get(c);
415
+ if (group !== void 0) {
416
+ expanded.value = /* @__PURE__ */ new Set([...expanded.value, group]);
417
+ inputRef.value?.focus();
418
+ return;
419
+ }
241
420
  emit("select", c);
242
421
  c.onSelect?.();
422
+ if (!c.onSelect && c.href && !c.children) window.location.assign(c.href);
243
423
  if (c.children) {
244
424
  enter(c);
245
425
  return;
@@ -272,11 +452,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
272
452
  trail.value = [];
273
453
  modeItems.value = [];
274
454
  found.value = [];
455
+ failed.value = false;
275
456
  if (o) {
276
457
  await nextTick();
277
458
  inputRef.value?.focus();
278
459
  }
279
- }
460
+ },
461
+ { immediate: true }
280
462
  );
281
463
  useHotkeys(
282
464
  () => props.hotkey ? [
@@ -292,26 +474,26 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
292
474
  return (_ctx, _cache) => {
293
475
  return openBlock(), createBlock(unref(DialogRoot), {
294
476
  open: __props.open,
295
- "onUpdate:open": _cache[2] || (_cache[2] = (v) => emit("update:open", v))
477
+ "onUpdate:open": _cache[3] || (_cache[3] = (v) => emit("update:open", v))
296
478
  }, {
297
479
  default: withCtx(() => [
298
480
  createVNode(unref(DialogPortal), null, {
299
481
  default: withCtx(() => [
300
482
  createVNode(unref(DialogOverlay), { class: "lp-scrim fixed inset-0 z-(--z-overlay) data-[state=open]:animate-[fade-in_150ms_ease] data-[state=closed]:animate-[fade-out_130ms_ease]" }),
301
483
  createVNode(unref(DialogContent), {
302
- class: "fixed left-1/2 top-[12vh] z-(--z-modal) flex max-h-[76vh] w-[min(92vw,40rem)] -translate-x-1/2 flex-col overflow-hidden rounded-card border border-line bg-surface-raised shadow-panel outline-none data-[state=open]:animate-[pop-in_160ms_var(--ease-emphasized)] data-[state=closed]:animate-[pop-out_130ms_ease]",
303
- onOpenAutoFocus: _cache[1] || (_cache[1] = withModifiers(() => {
484
+ class: normalizeClass(["fixed left-1/2 top-[12vh] z-(--z-modal) flex max-h-[76vh] w-[min(92vw,40rem)] -translate-x-1/2 flex-col overflow-hidden rounded-card border border-line lp-glass-overlay shadow-panel outline-none data-[state=open]:animate-[pop-in_160ms_var(--ease-emphasized)] data-[state=closed]:animate-[pop-out_130ms_ease]", __props.previewPane && "md:h-[min(76vh,36rem)] md:w-[min(92vw,64rem)]"]),
485
+ onOpenAutoFocus: _cache[2] || (_cache[2] = withModifiers(() => {
304
486
  }, ["prevent"]))
305
487
  }, {
306
488
  default: withCtx(() => [
307
489
  createVNode(unref(DialogTitle), { class: "sr-only" }, {
308
- default: withCtx(() => [..._cache[3] || (_cache[3] = [
490
+ default: withCtx(() => [..._cache[4] || (_cache[4] = [
309
491
  createTextVNode("Command palette", -1)
310
492
  ])]),
311
493
  _: 1
312
494
  }),
313
495
  createVNode(unref(DialogDescription), { class: "sr-only" }, {
314
- default: withCtx(() => [..._cache[4] || (_cache[4] = [
496
+ default: withCtx(() => [..._cache[5] || (_cache[5] = [
315
497
  createTextVNode("Search and run a command.", -1)
316
498
  ])]),
317
499
  _: 1
@@ -354,6 +536,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
354
536
  ref: inputRef,
355
537
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => query.value = $event),
356
538
  type: "text",
539
+ role: "combobox",
540
+ "aria-autocomplete": "list",
541
+ "aria-expanded": "true",
542
+ "aria-controls": listId,
543
+ "aria-activedescendant": activeId.value,
357
544
  placeholder: mode.value?.childPlaceholder ?? __props.placeholder,
358
545
  class: "h-12 min-w-0 flex-1 bg-transparent text-sm text-ink outline-none placeholder:text-muted",
359
546
  onKeydown: [
@@ -361,111 +548,214 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
361
548
  withKeys(withModifiers(moveUp, ["prevent"]), ["up"]),
362
549
  withKeys(withModifiers(runSelected, ["prevent"]), ["enter"]),
363
550
  withKeys(onBackspace, ["backspace"]),
364
- withKeys(onEscape, ["esc"])
551
+ withKeys(onEscape, ["esc"]),
552
+ withKeys(onTab, ["tab"])
365
553
  ]
366
554
  }, null, 40, _hoisted_3), [
367
555
  [vModelText, query.value]
368
556
  ]),
369
- _cache[5] || (_cache[5] = createElementVNode("kbd", { class: "shrink-0 rounded border border-line px-1.5 py-0.5 text-[10px] text-muted" }, "esc", -1))
557
+ _cache[6] || (_cache[6] = createElementVNode("kbd", { class: "shrink-0 rounded border border-line px-1.5 py-0.5 text-[10px] text-muted" }, "esc", -1))
370
558
  ]),
371
- createVNode(_sfc_main$2, {
372
- ref_key: "scrollRef",
373
- ref: scrollRef,
374
- class: "min-h-0 flex-1",
375
- "content-class": "p-2"
376
- }, {
377
- default: withCtx(() => [
378
- flat.value.length === 0 ? (openBlock(), createElementBlock("p", _hoisted_4, toDisplayString(unref(searching) ? text.value.searching : __props.emptyText), 1)) : createCommentVNode("", true),
379
- (openBlock(true), createElementBlock(Fragment, null, renderList(grouped.value, (group) => {
380
- return openBlock(), createElementBlock(Fragment, {
381
- key: group.name
559
+ createElementVNode("div", _hoisted_4, [
560
+ createVNode(_sfc_main$2, {
561
+ ref_key: "scrollRef",
562
+ ref: scrollRef,
563
+ class: "min-h-0 min-w-0 flex-1",
564
+ "content-class": "p-2"
565
+ }, {
566
+ default: withCtx(() => [
567
+ flat.value.length === 0 && !failed.value ? (openBlock(), createElementBlock("p", _hoisted_5, toDisplayString(unref(searching) ? text.value.searching : __props.emptyText), 1)) : createCommentVNode("", true),
568
+ createElementVNode("div", {
569
+ id: listId,
570
+ role: "listbox"
382
571
  }, [
383
- group.name ? (openBlock(), createElementBlock("div", _hoisted_5, toDisplayString(group.name), 1)) : createCommentVNode("", true),
384
- (openBlock(true), createElementBlock(Fragment, null, renderList(group.items, (c) => {
385
- return openBlock(), createElementBlock("button", {
386
- key: c.id,
387
- type: "button",
388
- "data-cmd-index": flatIndexOf(c),
389
- disabled: c.disabled,
390
- class: normalizeClass(["lp-cmd-item flex w-full items-center gap-3 rounded-md px-2.5 py-2 text-left text-sm outline-none transition-colors duration-[var(--duration-fast)] disabled:pointer-events-none disabled:opacity-45", flatIndexOf(c) === selectedIndex.value ? "bg-brand-soft text-brand" : "text-ink hover:bg-surface-soft/60"]),
391
- onClick: ($event) => run(c),
392
- onPointerenter: ($event) => selectedIndex.value = flatIndexOf(c)
572
+ (openBlock(true), createElementBlock(Fragment, null, renderList(grouped.value, (group, gi) => {
573
+ return openBlock(), createElementBlock("div", {
574
+ key: group.name,
575
+ role: "group",
576
+ "aria-labelledby": group.name ? `${listId}-g${gi}` : void 0
393
577
  }, [
394
- c.icon ? (openBlock(), createBlock(_sfc_main$1, {
578
+ group.name ? (openBlock(), createElementBlock("div", {
579
+ key: 0,
580
+ id: `${listId}-g${gi}`,
581
+ class: "px-2 pb-1 pt-3 text-[11px] font-semibold uppercase tracking-[0.08em] text-muted"
582
+ }, toDisplayString(group.name), 9, _hoisted_7)) : createCommentVNode("", true),
583
+ (openBlock(true), createElementBlock(Fragment, null, renderList(group.items, (c) => {
584
+ return openBlock(), createBlock(resolveDynamicComponent(c.href ? "a" : "button"), {
585
+ id: optionId(flatIndexOf(c)),
586
+ key: c.id,
587
+ href: c.disabled ? void 0 : c.href,
588
+ type: c.href ? void 0 : "button",
589
+ role: "option",
590
+ tabindex: "-1",
591
+ "aria-selected": flatIndexOf(c) === selectedIndex.value,
592
+ "aria-disabled": c.disabled || void 0,
593
+ "data-cmd-index": flatIndexOf(c),
594
+ disabled: c.href ? void 0 : c.disabled,
595
+ class: normalizeClass(["lp-cmd-item flex w-full gap-3 rounded-md px-2.5 py-2 text-left text-sm outline-none transition-colors duration-[var(--duration-fast)] disabled:pointer-events-none disabled:opacity-45 aria-disabled:pointer-events-none aria-disabled:opacity-45", [
596
+ c.excerpt ? "items-start" : "items-center",
597
+ flatIndexOf(c) === selectedIndex.value ? "bg-brand-soft text-brand" : unref(moreOf).has(c) ? "text-muted hover:bg-surface-soft/60" : "text-ink hover:bg-surface-soft/60"
598
+ ]]),
599
+ onClick: ($event) => onRowClick($event, c),
600
+ onPointerenter: ($event) => selectedIndex.value = flatIndexOf(c)
601
+ }, {
602
+ default: withCtx(() => [
603
+ c.icon ? (openBlock(), createBlock(_sfc_main$1, {
604
+ key: 0,
605
+ name: c.icon,
606
+ size: 16,
607
+ class: normalizeClass(["shrink-0", { "mt-0.5": c.excerpt }])
608
+ }, null, 8, ["name", "class"])) : createCommentVNode("", true),
609
+ createElementVNode("span", _hoisted_8, [
610
+ createElementVNode("span", _hoisted_9, [
611
+ (openBlock(true), createElementBlock(Fragment, null, renderList(highlight(c.label, c.highlight), (p, pi) => {
612
+ return openBlock(), createElementBlock(Fragment, {
613
+ key: `l${pi}`
614
+ }, [
615
+ p.hit ? (openBlock(), createElementBlock("mark", _hoisted_10, toDisplayString(p.text), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
616
+ createTextVNode(toDisplayString(p.text), 1)
617
+ ], 64))
618
+ ], 64);
619
+ }), 128))
620
+ ]),
621
+ c.description ? (openBlock(), createElementBlock("span", _hoisted_11, [
622
+ (openBlock(true), createElementBlock(Fragment, null, renderList(highlight(c.description, c.highlight), (p, pi) => {
623
+ return openBlock(), createElementBlock(Fragment, {
624
+ key: `d${pi}`
625
+ }, [
626
+ p.hit ? (openBlock(), createElementBlock("mark", _hoisted_12, toDisplayString(p.text), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
627
+ createTextVNode(toDisplayString(p.text), 1)
628
+ ], 64))
629
+ ], 64);
630
+ }), 128))
631
+ ])) : createCommentVNode("", true),
632
+ c.excerpt ? (openBlock(), createElementBlock("span", {
633
+ key: 1,
634
+ class: "lp-cmd-excerpt mt-1 block text-[13px] leading-snug text-ink/80",
635
+ style: normalizeStyle({ "-webkit-line-clamp": __props.excerptLines })
636
+ }, [
637
+ (openBlock(true), createElementBlock(Fragment, null, renderList(highlight(c.excerpt, c.highlight), (p, pi) => {
638
+ return openBlock(), createElementBlock(Fragment, {
639
+ key: `e${pi}`
640
+ }, [
641
+ p.hit ? (openBlock(), createElementBlock("mark", _hoisted_13, toDisplayString(p.text), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
642
+ createTextVNode(toDisplayString(p.text), 1)
643
+ ], 64))
644
+ ], 64);
645
+ }), 128))
646
+ ], 4)) : createCommentVNode("", true)
647
+ ]),
648
+ c.shortcut ? (openBlock(), createElementBlock("kbd", _hoisted_14, toDisplayString(c.shortcut), 1)) : c.children ? (openBlock(), createBlock(_sfc_main$1, {
649
+ key: 2,
650
+ name: "lucide:chevron-right",
651
+ size: 14,
652
+ class: "shrink-0 text-muted"
653
+ })) : createCommentVNode("", true)
654
+ ]),
655
+ _: 2
656
+ }, 1064, ["id", "href", "type", "aria-selected", "aria-disabled", "data-cmd-index", "disabled", "class", "onClick", "onPointerenter"]);
657
+ }), 128))
658
+ ], 8, _hoisted_6);
659
+ }), 128))
660
+ ]),
661
+ failed.value ? (openBlock(), createElementBlock("div", {
662
+ key: 1,
663
+ role: "alert",
664
+ class: normalizeClass(["flex items-center justify-center gap-2 px-2 text-sm text-muted", flat.value.length ? "pb-1 pt-3" : "py-10"])
665
+ }, [
666
+ createVNode(_sfc_main$1, {
667
+ name: "lucide:circle-alert",
668
+ size: 15,
669
+ class: "shrink-0"
670
+ }),
671
+ createElementVNode("span", null, toDisplayString(text.value.searchFailed), 1),
672
+ createElementVNode("button", {
673
+ type: "button",
674
+ class: "rounded-md px-1.5 py-0.5 font-medium text-brand outline-none transition-colors hover:bg-brand-soft focus-visible:ring-2 focus-visible:ring-ring",
675
+ onClick: _cache[1] || (_cache[1] = ($event) => {
676
+ refresh();
677
+ inputRef.value?.focus();
678
+ })
679
+ }, toDisplayString(text.value.retry), 1)
680
+ ], 2)) : createCommentVNode("", true)
681
+ ]),
682
+ _: 1
683
+ }, 512),
684
+ __props.previewPane ? (openBlock(), createElementBlock("aside", _hoisted_15, [
685
+ createVNode(_sfc_main$2, {
686
+ ref_key: "previewRef",
687
+ ref: previewRef,
688
+ class: "min-h-0 flex-1",
689
+ "content-class": "px-5 py-4"
690
+ }, {
691
+ default: withCtx(() => [
692
+ active.value ? renderSlot(_ctx.$slots, "preview", {
693
+ key: 0,
694
+ command: active.value
695
+ }, () => [
696
+ createElementVNode("div", _hoisted_16, [
697
+ active.value.icon ? (openBlock(), createBlock(_sfc_main$1, {
395
698
  key: 0,
396
- name: c.icon,
699
+ name: active.value.icon,
397
700
  size: 16,
398
- class: "shrink-0"
399
- }, null, 8, ["name"])) : createCommentVNode("", true),
400
- createElementVNode("span", _hoisted_7, [
401
- createElementVNode("span", _hoisted_8, [
402
- (openBlock(true), createElementBlock(Fragment, null, renderList(highlight(c.label), (p, pi) => {
403
- return openBlock(), createElementBlock(Fragment, {
404
- key: `l${pi}`
405
- }, [
406
- p.hit ? (openBlock(), createElementBlock("mark", _hoisted_9, toDisplayString(p.text), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
407
- createTextVNode(toDisplayString(p.text), 1)
408
- ], 64))
409
- ], 64);
410
- }), 128))
411
- ]),
412
- c.description ? (openBlock(), createElementBlock("span", _hoisted_10, [
413
- (openBlock(true), createElementBlock(Fragment, null, renderList(highlight(c.description), (p, pi) => {
414
- return openBlock(), createElementBlock(Fragment, {
415
- key: `d${pi}`
416
- }, [
417
- p.hit ? (openBlock(), createElementBlock("mark", _hoisted_11, toDisplayString(p.text), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
418
- createTextVNode(toDisplayString(p.text), 1)
419
- ], 64))
420
- ], 64);
421
- }), 128))
422
- ])) : createCommentVNode("", true)
423
- ]),
424
- c.shortcut ? (openBlock(), createElementBlock("kbd", _hoisted_12, toDisplayString(c.shortcut), 1)) : c.children ? (openBlock(), createBlock(_sfc_main$1, {
425
- key: 2,
426
- name: "lucide:chevron-right",
427
- size: 14,
428
701
  class: "shrink-0 text-muted"
429
- })) : createCommentVNode("", true)
430
- ], 42, _hoisted_6);
431
- }), 128))
432
- ], 64);
433
- }), 128))
434
- ]),
435
- _: 1
436
- }, 512),
437
- createElementVNode("div", _hoisted_13, [
438
- createElementVNode("span", _hoisted_14, [
439
- _cache[6] || (_cache[6] = createElementVNode("kbd", { class: "rounded border border-line px-1 text-[10px]" }, "↑", -1)),
440
- _cache[7] || (_cache[7] = createElementVNode("kbd", { class: "rounded border border-line px-1 text-[10px]" }, "↓", -1)),
702
+ }, null, 8, ["name"])) : createCommentVNode("", true),
703
+ createElementVNode("h3", _hoisted_17, toDisplayString(active.value.label), 1)
704
+ ]),
705
+ active.value.description ? (openBlock(), createElementBlock("p", _hoisted_18, toDisplayString(active.value.description), 1)) : createCommentVNode("", true),
706
+ active.value.preview || active.value.excerpt ? (openBlock(), createElementBlock("p", _hoisted_19, [
707
+ (openBlock(true), createElementBlock(Fragment, null, renderList(highlight(active.value.preview ?? active.value.excerpt ?? "", active.value.highlight), (p, pi) => {
708
+ return openBlock(), createElementBlock(Fragment, {
709
+ key: `p${pi}`
710
+ }, [
711
+ p.hit ? (openBlock(), createElementBlock("mark", _hoisted_20, toDisplayString(p.text), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
712
+ createTextVNode(toDisplayString(p.text), 1)
713
+ ], 64))
714
+ ], 64);
715
+ }), 128))
716
+ ])) : (openBlock(), createElementBlock("p", _hoisted_21, toDisplayString(text.value.noPreview), 1))
717
+ ], true) : createCommentVNode("", true)
718
+ ]),
719
+ _: 3
720
+ }, 512)
721
+ ])) : createCommentVNode("", true)
722
+ ]),
723
+ createElementVNode("div", _hoisted_22, [
724
+ createElementVNode("span", _hoisted_23, [
725
+ _cache[7] || (_cache[7] = createElementVNode("kbd", { class: "rounded border border-line px-1 text-[10px]" }, "↑", -1)),
726
+ _cache[8] || (_cache[8] = createElementVNode("kbd", { class: "rounded border border-line px-1 text-[10px]" }, "↓", -1)),
441
727
  createTextVNode(" " + toDisplayString(text.value.navigate), 1)
442
728
  ]),
443
- createElementVNode("span", _hoisted_15, [
444
- _cache[8] || (_cache[8] = createElementVNode("kbd", { class: "rounded border border-line px-1 text-[10px]" }, "↵", -1)),
729
+ createElementVNode("span", _hoisted_24, [
730
+ _cache[9] || (_cache[9] = createElementVNode("kbd", { class: "rounded border border-line px-1 text-[10px]" }, "↵", -1)),
445
731
  createTextVNode(" " + toDisplayString(text.value.select), 1)
446
732
  ]),
447
- trail.value.length ? (openBlock(), createElementBlock("span", _hoisted_16, [
448
- _cache[9] || (_cache[9] = createElementVNode("kbd", { class: "rounded border border-line px-1 text-[10px]" }, "⌫", -1)),
733
+ selectedActions.value.length ? (openBlock(), createElementBlock("span", _hoisted_25, [
734
+ _cache[10] || (_cache[10] = createElementVNode("kbd", { class: "rounded border border-line px-1 text-[10px]" }, "tab", -1)),
735
+ createTextVNode(" " + toDisplayString(text.value.actions), 1)
736
+ ])) : createCommentVNode("", true),
737
+ trail.value.length ? (openBlock(), createElementBlock("span", _hoisted_26, [
738
+ _cache[11] || (_cache[11] = createElementVNode("kbd", { class: "rounded border border-line px-1 text-[10px]" }, "⌫", -1)),
449
739
  createTextVNode(" " + toDisplayString(text.value.back), 1)
450
740
  ])) : createCommentVNode("", true),
451
- createElementVNode("span", _hoisted_17, [
452
- _cache[10] || (_cache[10] = createElementVNode("kbd", { class: "rounded border border-line px-1 text-[10px]" }, "esc", -1)),
741
+ createElementVNode("span", _hoisted_27, [
742
+ _cache[12] || (_cache[12] = createElementVNode("kbd", { class: "rounded border border-line px-1 text-[10px]" }, "esc", -1)),
453
743
  createTextVNode(" " + toDisplayString(text.value.close), 1)
454
744
  ])
455
745
  ])
456
746
  ]),
457
- _: 1
458
- })
747
+ _: 3
748
+ }, 8, ["class"])
459
749
  ]),
460
- _: 1
750
+ _: 3
461
751
  })
462
752
  ]),
463
- _: 1
753
+ _: 3
464
754
  }, 8, ["open"]);
465
755
  };
466
756
  }
467
757
  });
468
- const LpCommandPalette = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-dbc1130a"]]);
758
+ const LpCommandPalette = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-cc5b91ea"]]);
469
759
  export {
470
760
  LpCommandPalette as default
471
761
  };