@leavepulse/ui 0.37.11 → 0.38.0

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-C0yStVNy.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,6 +452,7 @@ 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();
@@ -292,26 +473,26 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
292
473
  return (_ctx, _cache) => {
293
474
  return openBlock(), createBlock(unref(DialogRoot), {
294
475
  open: __props.open,
295
- "onUpdate:open": _cache[2] || (_cache[2] = (v) => emit("update:open", v))
476
+ "onUpdate:open": _cache[3] || (_cache[3] = (v) => emit("update:open", v))
296
477
  }, {
297
478
  default: withCtx(() => [
298
479
  createVNode(unref(DialogPortal), null, {
299
480
  default: withCtx(() => [
300
481
  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
482
  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(() => {
483
+ 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)]"]),
484
+ onOpenAutoFocus: _cache[2] || (_cache[2] = withModifiers(() => {
304
485
  }, ["prevent"]))
305
486
  }, {
306
487
  default: withCtx(() => [
307
488
  createVNode(unref(DialogTitle), { class: "sr-only" }, {
308
- default: withCtx(() => [..._cache[3] || (_cache[3] = [
489
+ default: withCtx(() => [..._cache[4] || (_cache[4] = [
309
490
  createTextVNode("Command palette", -1)
310
491
  ])]),
311
492
  _: 1
312
493
  }),
313
494
  createVNode(unref(DialogDescription), { class: "sr-only" }, {
314
- default: withCtx(() => [..._cache[4] || (_cache[4] = [
495
+ default: withCtx(() => [..._cache[5] || (_cache[5] = [
315
496
  createTextVNode("Search and run a command.", -1)
316
497
  ])]),
317
498
  _: 1
@@ -354,6 +535,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
354
535
  ref: inputRef,
355
536
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => query.value = $event),
356
537
  type: "text",
538
+ role: "combobox",
539
+ "aria-autocomplete": "list",
540
+ "aria-expanded": "true",
541
+ "aria-controls": listId,
542
+ "aria-activedescendant": activeId.value,
357
543
  placeholder: mode.value?.childPlaceholder ?? __props.placeholder,
358
544
  class: "h-12 min-w-0 flex-1 bg-transparent text-sm text-ink outline-none placeholder:text-muted",
359
545
  onKeydown: [
@@ -361,111 +547,214 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
361
547
  withKeys(withModifiers(moveUp, ["prevent"]), ["up"]),
362
548
  withKeys(withModifiers(runSelected, ["prevent"]), ["enter"]),
363
549
  withKeys(onBackspace, ["backspace"]),
364
- withKeys(onEscape, ["esc"])
550
+ withKeys(onEscape, ["esc"]),
551
+ withKeys(onTab, ["tab"])
365
552
  ]
366
553
  }, null, 40, _hoisted_3), [
367
554
  [vModelText, query.value]
368
555
  ]),
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))
556
+ _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
557
  ]),
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
558
+ createElementVNode("div", _hoisted_4, [
559
+ createVNode(_sfc_main$2, {
560
+ ref_key: "scrollRef",
561
+ ref: scrollRef,
562
+ class: "min-h-0 min-w-0 flex-1",
563
+ "content-class": "p-2"
564
+ }, {
565
+ default: withCtx(() => [
566
+ flat.value.length === 0 && !failed.value ? (openBlock(), createElementBlock("p", _hoisted_5, toDisplayString(unref(searching) ? text.value.searching : __props.emptyText), 1)) : createCommentVNode("", true),
567
+ createElementVNode("div", {
568
+ id: listId,
569
+ role: "listbox"
382
570
  }, [
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)
571
+ (openBlock(true), createElementBlock(Fragment, null, renderList(grouped.value, (group, gi) => {
572
+ return openBlock(), createElementBlock("div", {
573
+ key: group.name,
574
+ role: "group",
575
+ "aria-labelledby": group.name ? `${listId}-g${gi}` : void 0
393
576
  }, [
394
- c.icon ? (openBlock(), createBlock(_sfc_main$1, {
577
+ group.name ? (openBlock(), createElementBlock("div", {
578
+ key: 0,
579
+ id: `${listId}-g${gi}`,
580
+ class: "px-2 pb-1 pt-3 text-[11px] font-semibold uppercase tracking-[0.08em] text-muted"
581
+ }, toDisplayString(group.name), 9, _hoisted_7)) : createCommentVNode("", true),
582
+ (openBlock(true), createElementBlock(Fragment, null, renderList(group.items, (c) => {
583
+ return openBlock(), createBlock(resolveDynamicComponent(c.href ? "a" : "button"), {
584
+ id: optionId(flatIndexOf(c)),
585
+ key: c.id,
586
+ href: c.disabled ? void 0 : c.href,
587
+ type: c.href ? void 0 : "button",
588
+ role: "option",
589
+ tabindex: "-1",
590
+ "aria-selected": flatIndexOf(c) === selectedIndex.value,
591
+ "aria-disabled": c.disabled || void 0,
592
+ "data-cmd-index": flatIndexOf(c),
593
+ disabled: c.href ? void 0 : c.disabled,
594
+ 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", [
595
+ c.excerpt ? "items-start" : "items-center",
596
+ 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"
597
+ ]]),
598
+ onClick: ($event) => onRowClick($event, c),
599
+ onPointerenter: ($event) => selectedIndex.value = flatIndexOf(c)
600
+ }, {
601
+ default: withCtx(() => [
602
+ c.icon ? (openBlock(), createBlock(_sfc_main$1, {
603
+ key: 0,
604
+ name: c.icon,
605
+ size: 16,
606
+ class: normalizeClass(["shrink-0", { "mt-0.5": c.excerpt }])
607
+ }, null, 8, ["name", "class"])) : createCommentVNode("", true),
608
+ createElementVNode("span", _hoisted_8, [
609
+ createElementVNode("span", _hoisted_9, [
610
+ (openBlock(true), createElementBlock(Fragment, null, renderList(highlight(c.label, c.highlight), (p, pi) => {
611
+ return openBlock(), createElementBlock(Fragment, {
612
+ key: `l${pi}`
613
+ }, [
614
+ p.hit ? (openBlock(), createElementBlock("mark", _hoisted_10, toDisplayString(p.text), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
615
+ createTextVNode(toDisplayString(p.text), 1)
616
+ ], 64))
617
+ ], 64);
618
+ }), 128))
619
+ ]),
620
+ c.description ? (openBlock(), createElementBlock("span", _hoisted_11, [
621
+ (openBlock(true), createElementBlock(Fragment, null, renderList(highlight(c.description, c.highlight), (p, pi) => {
622
+ return openBlock(), createElementBlock(Fragment, {
623
+ key: `d${pi}`
624
+ }, [
625
+ p.hit ? (openBlock(), createElementBlock("mark", _hoisted_12, toDisplayString(p.text), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
626
+ createTextVNode(toDisplayString(p.text), 1)
627
+ ], 64))
628
+ ], 64);
629
+ }), 128))
630
+ ])) : createCommentVNode("", true),
631
+ c.excerpt ? (openBlock(), createElementBlock("span", {
632
+ key: 1,
633
+ class: "lp-cmd-excerpt mt-1 block text-[13px] leading-snug text-ink/80",
634
+ style: normalizeStyle({ "-webkit-line-clamp": __props.excerptLines })
635
+ }, [
636
+ (openBlock(true), createElementBlock(Fragment, null, renderList(highlight(c.excerpt, c.highlight), (p, pi) => {
637
+ return openBlock(), createElementBlock(Fragment, {
638
+ key: `e${pi}`
639
+ }, [
640
+ p.hit ? (openBlock(), createElementBlock("mark", _hoisted_13, toDisplayString(p.text), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
641
+ createTextVNode(toDisplayString(p.text), 1)
642
+ ], 64))
643
+ ], 64);
644
+ }), 128))
645
+ ], 4)) : createCommentVNode("", true)
646
+ ]),
647
+ c.shortcut ? (openBlock(), createElementBlock("kbd", _hoisted_14, toDisplayString(c.shortcut), 1)) : c.children ? (openBlock(), createBlock(_sfc_main$1, {
648
+ key: 2,
649
+ name: "lucide:chevron-right",
650
+ size: 14,
651
+ class: "shrink-0 text-muted"
652
+ })) : createCommentVNode("", true)
653
+ ]),
654
+ _: 2
655
+ }, 1064, ["id", "href", "type", "aria-selected", "aria-disabled", "data-cmd-index", "disabled", "class", "onClick", "onPointerenter"]);
656
+ }), 128))
657
+ ], 8, _hoisted_6);
658
+ }), 128))
659
+ ]),
660
+ failed.value ? (openBlock(), createElementBlock("div", {
661
+ key: 1,
662
+ role: "alert",
663
+ class: normalizeClass(["flex items-center justify-center gap-2 px-2 text-sm text-muted", flat.value.length ? "pb-1 pt-3" : "py-10"])
664
+ }, [
665
+ createVNode(_sfc_main$1, {
666
+ name: "lucide:circle-alert",
667
+ size: 15,
668
+ class: "shrink-0"
669
+ }),
670
+ createElementVNode("span", null, toDisplayString(text.value.searchFailed), 1),
671
+ createElementVNode("button", {
672
+ type: "button",
673
+ 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",
674
+ onClick: _cache[1] || (_cache[1] = ($event) => {
675
+ refresh();
676
+ inputRef.value?.focus();
677
+ })
678
+ }, toDisplayString(text.value.retry), 1)
679
+ ], 2)) : createCommentVNode("", true)
680
+ ]),
681
+ _: 1
682
+ }, 512),
683
+ __props.previewPane ? (openBlock(), createElementBlock("aside", _hoisted_15, [
684
+ createVNode(_sfc_main$2, {
685
+ ref_key: "previewRef",
686
+ ref: previewRef,
687
+ class: "min-h-0 flex-1",
688
+ "content-class": "px-5 py-4"
689
+ }, {
690
+ default: withCtx(() => [
691
+ active.value ? renderSlot(_ctx.$slots, "preview", {
692
+ key: 0,
693
+ command: active.value
694
+ }, () => [
695
+ createElementVNode("div", _hoisted_16, [
696
+ active.value.icon ? (openBlock(), createBlock(_sfc_main$1, {
395
697
  key: 0,
396
- name: c.icon,
698
+ name: active.value.icon,
397
699
  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
700
  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)),
701
+ }, null, 8, ["name"])) : createCommentVNode("", true),
702
+ createElementVNode("h3", _hoisted_17, toDisplayString(active.value.label), 1)
703
+ ]),
704
+ active.value.description ? (openBlock(), createElementBlock("p", _hoisted_18, toDisplayString(active.value.description), 1)) : createCommentVNode("", true),
705
+ active.value.preview || active.value.excerpt ? (openBlock(), createElementBlock("p", _hoisted_19, [
706
+ (openBlock(true), createElementBlock(Fragment, null, renderList(highlight(active.value.preview ?? active.value.excerpt ?? "", active.value.highlight), (p, pi) => {
707
+ return openBlock(), createElementBlock(Fragment, {
708
+ key: `p${pi}`
709
+ }, [
710
+ p.hit ? (openBlock(), createElementBlock("mark", _hoisted_20, toDisplayString(p.text), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
711
+ createTextVNode(toDisplayString(p.text), 1)
712
+ ], 64))
713
+ ], 64);
714
+ }), 128))
715
+ ])) : (openBlock(), createElementBlock("p", _hoisted_21, toDisplayString(text.value.noPreview), 1))
716
+ ], true) : createCommentVNode("", true)
717
+ ]),
718
+ _: 3
719
+ }, 512)
720
+ ])) : createCommentVNode("", true)
721
+ ]),
722
+ createElementVNode("div", _hoisted_22, [
723
+ createElementVNode("span", _hoisted_23, [
724
+ _cache[7] || (_cache[7] = createElementVNode("kbd", { class: "rounded border border-line px-1 text-[10px]" }, "↑", -1)),
725
+ _cache[8] || (_cache[8] = createElementVNode("kbd", { class: "rounded border border-line px-1 text-[10px]" }, "↓", -1)),
441
726
  createTextVNode(" " + toDisplayString(text.value.navigate), 1)
442
727
  ]),
443
- createElementVNode("span", _hoisted_15, [
444
- _cache[8] || (_cache[8] = createElementVNode("kbd", { class: "rounded border border-line px-1 text-[10px]" }, "↵", -1)),
728
+ createElementVNode("span", _hoisted_24, [
729
+ _cache[9] || (_cache[9] = createElementVNode("kbd", { class: "rounded border border-line px-1 text-[10px]" }, "↵", -1)),
445
730
  createTextVNode(" " + toDisplayString(text.value.select), 1)
446
731
  ]),
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)),
732
+ selectedActions.value.length ? (openBlock(), createElementBlock("span", _hoisted_25, [
733
+ _cache[10] || (_cache[10] = createElementVNode("kbd", { class: "rounded border border-line px-1 text-[10px]" }, "tab", -1)),
734
+ createTextVNode(" " + toDisplayString(text.value.actions), 1)
735
+ ])) : createCommentVNode("", true),
736
+ trail.value.length ? (openBlock(), createElementBlock("span", _hoisted_26, [
737
+ _cache[11] || (_cache[11] = createElementVNode("kbd", { class: "rounded border border-line px-1 text-[10px]" }, "⌫", -1)),
449
738
  createTextVNode(" " + toDisplayString(text.value.back), 1)
450
739
  ])) : 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)),
740
+ createElementVNode("span", _hoisted_27, [
741
+ _cache[12] || (_cache[12] = createElementVNode("kbd", { class: "rounded border border-line px-1 text-[10px]" }, "esc", -1)),
453
742
  createTextVNode(" " + toDisplayString(text.value.close), 1)
454
743
  ])
455
744
  ])
456
745
  ]),
457
- _: 1
458
- })
746
+ _: 3
747
+ }, 8, ["class"])
459
748
  ]),
460
- _: 1
749
+ _: 3
461
750
  })
462
751
  ]),
463
- _: 1
752
+ _: 3
464
753
  }, 8, ["open"]);
465
754
  };
466
755
  }
467
756
  });
468
- const LpCommandPalette = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-dbc1130a"]]);
757
+ const LpCommandPalette = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-2a5d3558"]]);
469
758
  export {
470
759
  LpCommandPalette as default
471
760
  };