@leavepulse/ui 0.46.0 → 0.47.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.
Files changed (64) hide show
  1. package/dist/assets/HighlightText-BwC5xbqn.css +6 -0
  2. package/dist/assets/{LpCommandPalette-5miKYsb-.css → LpCommandPalette-DmQUsJqA.css} +21 -21
  3. package/dist/assets/LpKeys-BxRhfQda.css +36 -0
  4. package/dist/assets/LpPaletteFacts-B9FdVmIO.css +93 -0
  5. package/dist/assets/LpPaletteHeader-BVOu1UX5.css +70 -0
  6. package/dist/assets/LpPalettePreview-CJmAKgYW.css +43 -0
  7. package/dist/assets/LpPaletteStats-ClE-37MK.css +33 -0
  8. package/dist/assets/LpPaletteSwatches-CA0B8y2E.css +21 -0
  9. package/dist/assets/{LpShortcutsDialog-CqYX3yy6.css → LpShortcutsDialog-BUp_BX4s.css} +7 -28
  10. package/dist/assets/{LpTable-BoeA_TgK.css → LpTable-xWwI4KPZ.css} +17 -17
  11. package/dist/chunks/HighlightText-BvnYZkhi.js +27 -0
  12. package/dist/chunks/{LpAvatar-DeSb6H0c.js → LpAvatar-Cb1jtOVv.js} +21 -3
  13. package/dist/chunks/{LpLightbox-DSDahF23.js → LpLightbox-EVXzAq62.js} +3 -3
  14. package/dist/chunks/{LpMediaGallery-YKr-cQPN.js → LpMediaGallery-BnlkN_1f.js} +1 -1
  15. package/dist/chunks/LpPalettePreview-CFav_7co.js +19 -0
  16. package/dist/component-names.d.ts +1 -1
  17. package/dist/components/HighlightText.vue.d.ts +7 -0
  18. package/dist/components/HighlightText.vue.js +4 -0
  19. package/dist/components/LpAvatar.vue.d.ts +12 -0
  20. package/dist/components/LpAvatar.vue.js +1 -1
  21. package/dist/components/LpCommandPalette.vue.d.ts +152 -15
  22. package/dist/components/LpCommandPalette.vue.js +178 -92
  23. package/dist/components/LpKeys.vue.d.ts +22 -0
  24. package/dist/components/LpKeys.vue.js +52 -0
  25. package/dist/components/LpLightbox.vue.js +1 -1
  26. package/dist/components/LpMediaGallery.vue.js +1 -1
  27. package/dist/components/LpPaletteFacts.vue.d.ts +25 -0
  28. package/dist/components/LpPaletteFacts.vue.js +85 -0
  29. package/dist/components/LpPaletteHeader.vue.d.ts +32 -0
  30. package/dist/components/LpPaletteHeader.vue.js +103 -0
  31. package/dist/components/LpPalettePreview.vue.d.ts +19 -0
  32. package/dist/components/LpPalettePreview.vue.js +4 -0
  33. package/dist/components/LpPaletteStats.vue.d.ts +11 -0
  34. package/dist/components/LpPaletteStats.vue.js +47 -0
  35. package/dist/components/LpPaletteSwatches.vue.d.ts +10 -0
  36. package/dist/components/LpPaletteSwatches.vue.js +34 -0
  37. package/dist/components/LpShortcutsDialog.vue.js +8 -28
  38. package/dist/components/LpTable.vue.js +5 -3
  39. package/dist/components/commandPalette.d.ts +157 -0
  40. package/dist/components/commandPalette.js +103 -0
  41. package/dist/components/commandSearch.d.ts +32 -0
  42. package/dist/components/commandSearch.js +48 -0
  43. package/dist/components/keys.d.ts +2 -0
  44. package/dist/components/keys.js +11 -0
  45. package/dist/index.d.ts +12 -3
  46. package/dist/index.js +157 -134
  47. package/package.json +1 -1
  48. package/src/component-names.ts +6 -0
  49. package/src/components/HighlightText.vue +28 -0
  50. package/src/components/LpAvatar.vue +21 -2
  51. package/src/components/LpCommandPalette.vue +387 -87
  52. package/src/components/LpKeys.vue +79 -0
  53. package/src/components/LpLightbox.vue +4 -1
  54. package/src/components/LpPaletteFacts.vue +165 -0
  55. package/src/components/LpPaletteHeader.vue +155 -0
  56. package/src/components/LpPalettePreview.vue +77 -0
  57. package/src/components/LpPaletteStats.vue +81 -0
  58. package/src/components/LpPaletteSwatches.vue +56 -0
  59. package/src/components/LpShortcutsDialog.vue +2 -45
  60. package/src/components/LpTable.vue +6 -1
  61. package/src/components/commandPalette.ts +264 -0
  62. package/src/components/commandSearch.ts +79 -0
  63. package/src/components/keys.ts +13 -0
  64. package/src/index.ts +12 -3
@@ -16,12 +16,11 @@
16
16
  * look for keys rather than only in the console.
17
17
  */
18
18
  import { computed } from "vue"
19
+ import LpKeys from "./LpKeys.vue"
19
20
  import LpModal from "./LpModal.vue"
20
21
  import {
21
22
  activeShortcuts,
22
- appleModifierLabel,
23
23
  mergeShortcutGroups,
24
- useAppleKeyboard,
25
24
  useHotkeys,
26
25
  type ShortcutGroup,
27
26
  } from "../composables/useHotkeys"
@@ -49,23 +48,9 @@ const props = withDefaults(
49
48
 
50
49
  const text = useLabels("shortcuts", {
51
50
  title: "Keyboard shortcuts",
52
- then: "then",
53
51
  conflict: "Also bound elsewhere: only one of them runs",
54
52
  group: "General",
55
53
  })
56
- const apple = useAppleKeyboard()
57
-
58
- /*
59
- * One key per "+", except a "+" that is the key itself: "Ctrl++" is Ctrl and
60
- * plus, not Ctrl and two blanks.
61
- */
62
- function keysOf(chord: string): string[] {
63
- return chord.split(/\+(?!$)/)
64
- }
65
-
66
- function stepsOf(combo: string): string[] {
67
- return combo.trim().split(/\s+/)
68
- }
69
54
 
70
55
  const shown = computed(() => (props.live ? mergeShortcutGroups(props.groups, activeShortcuts.value) : props.groups))
71
56
 
@@ -112,12 +97,7 @@ useHotkeys(() =>
112
97
  >!</span>
113
98
  </dt>
114
99
  <dd>
115
- <template v-for="(step, stepIndex) in stepsOf(entry.combo)" :key="`${step}-${stepIndex}`">
116
- <span v-if="stepIndex" class="shortcut-then">{{ text.then }}</span>
117
- <kbd v-for="(key, index) in keysOf(step)" :key="`${key}-${index}`">
118
- {{ appleModifierLabel(key.trim(), apple) }}
119
- </kbd>
120
- </template>
100
+ <LpKeys :combo="entry.combo" />
121
101
  </dd>
122
102
  </div>
123
103
  </dl>
@@ -181,33 +161,10 @@ useHotkeys(() =>
181
161
  cursor: help;
182
162
  }
183
163
 
184
- .shortcut-then {
185
- align-self: center;
186
- padding: 0 2px;
187
- color: var(--color-muted);
188
- font-size: 0.68rem;
189
- }
190
-
191
164
  .shortcut-row dd {
192
165
  display: flex;
193
166
  flex-shrink: 0;
194
167
  gap: 3px;
195
168
  margin: 0;
196
169
  }
197
-
198
- kbd {
199
- display: inline-flex;
200
- min-width: 20px;
201
- align-items: center;
202
- justify-content: center;
203
- padding: 2px 6px;
204
- border: 1px solid var(--color-line-strong);
205
- border-bottom-width: 2px;
206
- border-radius: 5px;
207
- background: color-mix(in srgb, var(--color-ink) 6%, transparent);
208
- color: var(--color-muted-strong);
209
- font-family: inherit;
210
- font-size: 0.7rem;
211
- line-height: 1.5;
212
- }
213
170
  </style>
@@ -909,7 +909,12 @@ const countText = computed(() => {
909
909
  })
910
910
  // A selectable table keeps the bar at all times (the row count until something
911
911
  // is selected): ticking the first row must not push the table down.
912
- const showBar = computed(() => props.summary || !!props.selectable || activeFilters.value.length > 0)
912
+ // As documented on `summary`: the bar shows on its own while filters are
913
+ // active or rows are selected, not merely because rows can be selected, which
914
+ // left a lone "75 rows" over every selectable table.
915
+ const showBar = computed(
916
+ () => props.summary || (!!props.selectable && props.selected.length > 0) || activeFilters.value.length > 0,
917
+ )
913
918
  const filteredOut = computed(() => {
914
919
  if (loading.value || failed.value || displayRows.value.length) return false
915
920
  if (props.load) return Object.values(hiddenValues.value).some((set) => set.size)
@@ -0,0 +1,264 @@
1
+ import { applyQueryFilters, parsePaletteQuery, type CommandSearchTerms, type HighlightPart, type SearchableCommand } from "./commandSearch"
2
+
3
+ /*
4
+ * The parts of LpCommandPalette that are plain logic, kept out of the SFC so
5
+ * they can be tested without mounting a dialog.
6
+ */
7
+
8
+ /** The fields of a command that decide whether the preview pane opens for it. */
9
+ export interface PreviewableCommand {
10
+ preview?: string
11
+ excerpt?: string
12
+ noPreview?: boolean
13
+ }
14
+
15
+ /**
16
+ * Whether the pane has something to show for a row. `noPreview` on the row
17
+ * wins, then the app's `hasPreview`; without either, a row qualifies when the
18
+ * app supplied a `#preview` slot or the row carries text of its own.
19
+ */
20
+ export function commandHasPreview<T extends PreviewableCommand>(
21
+ command: T | undefined,
22
+ options: { slot: boolean; hasPreview?: (command: T) => boolean },
23
+ ): boolean {
24
+ if (!command || command.noPreview) return false
25
+ if (options.hasPreview) return options.hasPreview(command)
26
+ return options.slot || !!(command.preview || command.excerpt)
27
+ }
28
+
29
+ /** One line of LpPaletteFacts: what it is, and what it is set to now. */
30
+ export interface PaletteFact {
31
+ /** Text, or the preview slot's `highlight(text)` to mark the query. */
32
+ label: string | HighlightPart[]
33
+ /** Right-aligned value; a change rolls in rather than swapping. */
34
+ value?: string | number
35
+ /** A second, quieter line under the label. */
36
+ hint?: string | HighlightPart[]
37
+ icon?: string
38
+ /** A colour dot in place of the icon: a role colour, a status. */
39
+ color?: string
40
+ /** Marks the row that is in effect now: the current theme, the open tab. */
41
+ current?: boolean
42
+ /** Draw the value in the monospace face: ids, paths, versions. */
43
+ mono?: boolean
44
+ }
45
+
46
+ /** One tile of LpPaletteStats. */
47
+ export interface PaletteStat {
48
+ label: string
49
+ /** A number counts to its new value; text rolls. */
50
+ value: number | string
51
+ /** Abbreviate a number: 1.2k, 3.4M. */
52
+ compact?: boolean
53
+ suffix?: string
54
+ format?: (value: number) => string
55
+ }
56
+
57
+ /** The fields of a command that let typing enter its mode. */
58
+ export interface PrefixedCommand {
59
+ prefix?: string
60
+ children?: unknown
61
+ disabled?: boolean
62
+ }
63
+
64
+ /**
65
+ * The mode a query opens by its prefix, and the query left after it. Only a
66
+ * command with `children` and a non-empty `prefix` qualifies; letters match
67
+ * in any case, and the longest prefix wins, so "mr " and "m" can live side by
68
+ * side. Leading spaces are ignored: they are never what someone meant.
69
+ */
70
+ export function matchModePrefix<T extends PrefixedCommand>(
71
+ query: string,
72
+ commands: readonly T[],
73
+ ): { command: T; rest: string } | null {
74
+ const typed = query.trimStart().toLowerCase()
75
+ let best: T | null = null
76
+ for (const command of commands) {
77
+ const prefix = command.prefix?.toLowerCase()
78
+ if (!prefix || !command.children || command.disabled || !typed.startsWith(prefix)) continue
79
+ if (!best || prefix.length > (best.prefix?.length ?? 0)) best = command
80
+ }
81
+ if (!best) return null
82
+ return { command: best, rest: query.trimStart().slice(best.prefix!.length) }
83
+ }
84
+
85
+ /** The fields of a command that make it a "search elsewhere" row. */
86
+ export interface FallbackCommand extends SearchableCommand {
87
+ fallback?: boolean
88
+ hidden?: boolean
89
+ }
90
+
91
+ /**
92
+ * The fallback rows to show for a query, with their labels filled in. They
93
+ * appear only while positive words are typed, whatever those words are: the
94
+ * row offers to search somewhere else for them, so matching it against them
95
+ * would hide it exactly when it is needed. Exclusions and `in:` scopes still
96
+ * apply, to the row's own label, keywords and group. `{query}` in the label
97
+ * becomes the positive words, without the operators.
98
+ */
99
+ export function fallbackRows<T extends FallbackCommand>(
100
+ items: readonly T[],
101
+ raw: string,
102
+ ): { command: T; label: string }[] {
103
+ const parsed = parsePaletteQuery(raw.trim())
104
+ if (!parsed.text) return []
105
+ return applyQueryFilters(
106
+ items.filter((c) => c.fallback),
107
+ parsed,
108
+ ).map((command) => ({ command, label: command.label.split("{query}").join(parsed.text) }))
109
+ }
110
+
111
+ /**
112
+ * A remembered pick that is not a root command: one chosen inside a mode, or
113
+ * a result a search fetched. It keeps what the row needs to be drawn again
114
+ * and where it was found, so the palette can find the live command next time
115
+ * (`path` through static children, or the app's `resolveRecent`), and fall
116
+ * back to its `href` when neither can.
117
+ */
118
+ export interface RecentEntry {
119
+ id: string
120
+ label: string
121
+ description?: string
122
+ icon?: string
123
+ image?: string | { src?: string | null; alt?: string; fallback?: string; icon?: string; shape?: "circle" | "soft" | "square" }
124
+ href?: string
125
+ /** Ids of the modes it was picked in, outermost first. */
126
+ path: string[]
127
+ /** The command's `recentData`, kept as given for `resolveRecent`. */
128
+ data?: unknown
129
+ }
130
+
131
+ /** A stored history item: a root command id (the old format), or an entry. */
132
+ export type RecentItem = string | RecentEntry
133
+
134
+ /** The fields of a command a history entry is built from and resolved to. */
135
+ export interface RecentSource {
136
+ id: string
137
+ label: string
138
+ description?: string
139
+ icon?: string
140
+ image?: RecentEntry["image"]
141
+ href?: string
142
+ recentData?: unknown
143
+ disabled?: boolean
144
+ children?: unknown
145
+ }
146
+
147
+ function isEntry(value: unknown): value is RecentEntry {
148
+ if (!value || typeof value !== "object") return false
149
+ const v = value as Record<string, unknown>
150
+ return (
151
+ typeof v.id === "string"
152
+ && typeof v.label === "string"
153
+ && Array.isArray(v.path)
154
+ && v.path.every((step) => typeof step === "string")
155
+ )
156
+ }
157
+
158
+ /** Stored history, with anything malformed dropped rather than trusted. */
159
+ export function readRecent(stored: unknown): RecentItem[] {
160
+ if (!Array.isArray(stored)) return []
161
+ return stored.filter((v): v is RecentItem => typeof v === "string" || isEntry(v))
162
+ }
163
+
164
+ function recentKey(item: RecentItem): string {
165
+ return typeof item === "string" ? item : [...item.path, item.id].join("\u0000")
166
+ }
167
+
168
+ /**
169
+ * The history item for a pick. A root pick stays a bare id, the format older
170
+ * versions wrote and still read; anything deeper becomes an entry with only
171
+ * the fields that survive JSON.
172
+ */
173
+ export function toRecentItem(command: RecentSource, path: readonly string[]): RecentItem {
174
+ if (!path.length) return command.id
175
+ const entry: RecentEntry = { id: command.id, label: command.label, path: [...path] }
176
+ if (command.description) entry.description = command.description
177
+ if (command.icon) entry.icon = command.icon
178
+ if (typeof command.image === "string") entry.image = command.image
179
+ else if (command.image) {
180
+ const { src, alt, fallback, icon, shape } = command.image
181
+ entry.image = { src, alt, fallback, icon, shape }
182
+ }
183
+ if (command.href) entry.href = command.href
184
+ if (command.recentData !== undefined) entry.data = command.recentData
185
+ return entry
186
+ }
187
+
188
+ /** The history with `item` first and any earlier copy of it dropped. */
189
+ export function pushRecent(list: readonly RecentItem[], item: RecentItem, keep: number): RecentItem[] {
190
+ const key = recentKey(item)
191
+ return [item, ...list.filter((v) => recentKey(v) !== key)].slice(0, keep)
192
+ }
193
+
194
+ /**
195
+ * The live commands a history stands for, newest first. A bare id is looked
196
+ * up among the root commands; an entry is followed down its path through
197
+ * static children, then offered to `resolve` (for results a fetcher found),
198
+ * and last rebuilt from its own fields when it has an `href` to follow.
199
+ * Whatever none of these can find is skipped, so a stored history never
200
+ * resurrects something that cannot run.
201
+ */
202
+ export function resolveRecentItems<T extends RecentSource>(
203
+ items: readonly RecentItem[],
204
+ commands: readonly T[],
205
+ options: { limit: number; resolve?: (entry: RecentEntry) => T | null | undefined; fromEntry: (entry: RecentEntry) => T },
206
+ ): { command: T; path: string[] }[] {
207
+ const out: { command: T; path: string[] }[] = []
208
+ const seen = new Set<string>()
209
+ for (const item of items) {
210
+ if (out.length >= options.limit) break
211
+ const key = recentKey(item)
212
+ if (seen.has(key)) continue
213
+ const found = typeof item === "string" ? commands.find((c) => c.id === item) : findEntry(item, commands, options)
214
+ if (!found || found.disabled || found.children) continue
215
+ seen.add(key)
216
+ out.push({ command: found, path: typeof item === "string" ? [] : [...item.path] })
217
+ }
218
+ return out
219
+ }
220
+
221
+ function findEntry<T extends RecentSource>(
222
+ entry: RecentEntry,
223
+ commands: readonly T[],
224
+ options: { resolve?: (entry: RecentEntry) => T | null | undefined; fromEntry: (entry: RecentEntry) => T },
225
+ ): T | undefined {
226
+ let level: readonly T[] | null = commands
227
+ for (const step of entry.path) {
228
+ const mode: T | undefined = level?.find((c) => c.id === step)
229
+ level = mode && Array.isArray(mode.children) ? (mode.children as T[]) : null
230
+ }
231
+ const live = level?.find((c) => c.id === entry.id)
232
+ if (live) return live
233
+ const resolved = options.resolve?.(entry)
234
+ if (resolved) return resolved
235
+ return entry.href ? options.fromEntry(entry) : undefined
236
+ }
237
+
238
+ /**
239
+ * Whether a recent row survives the operators typed so far. Recent rows show
240
+ * while no positive words are typed, so "-theme" or "in:recent" filters them
241
+ * like any other row; an `in:` scope matches the row's own group or the
242
+ * Recent heading it sits under.
243
+ */
244
+ export function recentVisible(command: SearchableCommand, terms: CommandSearchTerms, recentLabel: string): boolean {
245
+ if (!applyQueryFilters([{ ...command, group: undefined }], { ...terms, groups: [] }).length) return false
246
+ return terms.groups.every((term) =>
247
+ [command.group, recentLabel].some((group) => group?.toLowerCase().includes(term)),
248
+ )
249
+ }
250
+
251
+ /**
252
+ * The name a mode's chip shows. `modeLabel` when given; otherwise the label,
253
+ * with a `{query}` placeholder and the quotes around it taken out, since the
254
+ * words it stood for are in the box now ("Search mods for "{query}"" reads
255
+ * "Search mods").
256
+ */
257
+ export function modeChipLabel(command: { label: string; modeLabel?: string }): string {
258
+ if (command.modeLabel) return command.modeLabel
259
+ if (!command.label.includes("{query}")) return command.label
260
+ return command.label
261
+ .replace(/\s*["'“”«»]?\{query\}["'“”«»]?/g, "")
262
+ .replace(/\s+(for|in|on|:)$/i, "")
263
+ .trim()
264
+ }
@@ -79,3 +79,82 @@ export function rankCommandItems<T extends SearchableCommand>(items: T[], terms:
79
79
  .sort((a, b) => b.score - a.score || a.command.label.localeCompare(b.command.label))
80
80
  .map((entry) => entry.command)
81
81
  }
82
+
83
+ /**
84
+ * A query as a fetcher receives it. `text` is what a remote search should be
85
+ * sent: the positive words and phrases, in the case they were typed, without
86
+ * the operators a server would not understand. The operators come alongside
87
+ * as data, so a provider that can scope or exclude on the server may.
88
+ */
89
+ export interface PaletteQuery extends CommandSearchTerms {
90
+ /** The box as typed, operators and all. */
91
+ raw: string
92
+ /** Positive terms joined by spaces, case kept, quotes dropped. */
93
+ text: string
94
+ }
95
+
96
+ export function parsePaletteQuery(raw: string): PaletteQuery {
97
+ const terms = parseCommandQuery(raw)
98
+ const words: string[] = []
99
+ QUERY_TOKEN.lastIndex = 0
100
+ for (let match; (match = QUERY_TOKEN.exec(raw)); ) {
101
+ const text = (match[3] ?? match[4] ?? "").trim()
102
+ if (text && !match[1] && !match[2]) words.push(text)
103
+ }
104
+ return { ...terms, raw, text: words.join(" ") }
105
+ }
106
+
107
+ /**
108
+ * The operators applied to results someone else matched: a server answered
109
+ * the positive words, and `-word` and `in:group` still have to hold for what
110
+ * it sent back. Positive terms are not re-checked here; the server may match
111
+ * word forms the text does not contain.
112
+ */
113
+ export function applyQueryFilters<T extends SearchableCommand>(items: T[], terms: CommandSearchTerms): T[] {
114
+ if (!terms.exclude.length && !terms.groups.length) return items
115
+ return items.filter((command) => {
116
+ const group = command.group?.toLowerCase() ?? ""
117
+ return !terms.exclude.some((term) => matches(command, term)) && terms.groups.every((term) => group.includes(term))
118
+ })
119
+ }
120
+
121
+ /** A run of text, and whether it matched the query. */
122
+ export interface HighlightPart {
123
+ text: string
124
+ hit: boolean
125
+ }
126
+
127
+ /**
128
+ * Text split around the terms that matched, for <mark> without v-html. Terms
129
+ * shorter than two characters are skipped (one letter would mark half the
130
+ * text), longer ones win where two start at the same place, and case is
131
+ * ignored. Pass positive terms only: an excluded word is never a match.
132
+ */
133
+ export function highlightParts(value: string, terms: readonly string[]): HighlightPart[] {
134
+ const sorted = [...new Set(terms.map((t) => t.toLowerCase()).filter((t) => t.length >= 2))].sort(
135
+ (a, b) => b.length - a.length,
136
+ )
137
+ if (!value || sorted.length === 0) return value ? [{ text: value, hit: false }] : []
138
+ const lower = value.toLowerCase()
139
+ const parts: HighlightPart[] = []
140
+ let i = 0
141
+ while (i < value.length) {
142
+ let at = -1
143
+ let term = ""
144
+ for (const t of sorted) {
145
+ const found = lower.indexOf(t, i)
146
+ if (found !== -1 && (at === -1 || found < at || (found === at && t.length > term.length))) {
147
+ at = found
148
+ term = t
149
+ }
150
+ }
151
+ if (at === -1) {
152
+ parts.push({ text: value.slice(i), hit: false })
153
+ break
154
+ }
155
+ if (at > i) parts.push({ text: value.slice(i, at), hit: false })
156
+ parts.push({ text: value.slice(at, at + term.length), hit: true })
157
+ i = at + term.length
158
+ }
159
+ return parts
160
+ }
@@ -0,0 +1,13 @@
1
+ /*
2
+ * How a written combo splits into caps. One key per "+", except a "+" that is
3
+ * the key itself: "Ctrl++" is Ctrl and plus, not Ctrl and two blanks. Spaces
4
+ * separate the steps of a sequence: "G I" reads "G then I".
5
+ */
6
+ export function comboKeys(chord: string): string[] {
7
+ return chord.split(/\+(?!$)/)
8
+ }
9
+
10
+ export function comboSteps(combo: string): string[][] {
11
+ const trimmed = combo.trim()
12
+ return trimmed ? trimmed.split(/\s+/).map(comboKeys) : []
13
+ }
package/src/index.ts CHANGED
@@ -71,9 +71,18 @@ export {
71
71
  } from "./components/color"
72
72
  export type { ColorFormat, Hsv, Rgb } from "./components/color"
73
73
  export { default as LpCommandPalette } from "./components/LpCommandPalette.vue"
74
- export type { Command, PaletteLabels } from "./components/LpCommandPalette.vue"
75
- export { parseCommandQuery, rankCommandItems } from "./components/commandSearch"
76
- export type { CommandSearchTerms, SearchableCommand } from "./components/commandSearch"
74
+ export type { Command, CommandFetcher, CommandImage, PaletteLabels } from "./components/LpCommandPalette.vue"
75
+ export { applyQueryFilters, highlightParts, parseCommandQuery, parsePaletteQuery, rankCommandItems } from "./components/commandSearch"
76
+ export { commandHasPreview, pushRecent, readRecent, toRecentItem } from "./components/commandPalette"
77
+ export type { PaletteFact, PaletteStat, PreviewableCommand, RecentEntry, RecentItem } from "./components/commandPalette"
78
+ export { default as LpPalettePreview } from "./components/LpPalettePreview.vue"
79
+ export { default as LpPaletteHeader } from "./components/LpPaletteHeader.vue"
80
+ export { default as LpPaletteFacts } from "./components/LpPaletteFacts.vue"
81
+ export { default as LpPaletteStats } from "./components/LpPaletteStats.vue"
82
+ export { default as LpPaletteSwatches } from "./components/LpPaletteSwatches.vue"
83
+ export { default as LpKeys } from "./components/LpKeys.vue"
84
+ export { comboKeys, comboSteps } from "./components/keys"
85
+ export type { CommandSearchTerms, HighlightPart, PaletteQuery, SearchableCommand } from "./components/commandSearch"
77
86
  export { default as LpConfirmDialog } from "./components/LpConfirmDialog.vue"
78
87
  export type { ConfirmLabels } from "./components/LpConfirmDialog.vue"
79
88
  export { default as LpConfirmHost } from "./components/LpConfirmHost.vue"