@leavepulse/ui 0.73.4 → 0.74.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.
@@ -5,34 +5,34 @@
5
5
  * pane from its right edge as it grows. Animating the columns themselves made
6
6
  * every line of the list and the pane re-wrap on every frame.
7
7
  */
8
- .lp-cmd-dialog[data-v-8d42f8db] {
8
+ .lp-cmd-dialog[data-v-2880ef37] {
9
9
  --lp-cmd-list: min(92vw, 40rem);
10
10
  --lp-cmd-pane: max(0px, min(26rem, 96vw - var(--lp-cmd-list)));
11
11
  width: var(--lp-cmd-list);
12
12
  }
13
- .lp-cmd-split[data-v-8d42f8db] {
13
+ .lp-cmd-split[data-v-2880ef37] {
14
14
  transition:
15
15
  width 300ms var(--ease-emphasized, ease),
16
16
  min-height 300ms var(--ease-emphasized, ease);
17
17
  }
18
18
  @media (min-width: 768px) {
19
- .lp-cmd-split .lp-cmd-list[data-v-8d42f8db] {
19
+ .lp-cmd-split .lp-cmd-list[data-v-2880ef37] {
20
20
  flex: 0 0 auto;
21
21
  /* the dialog's 1px border on each side */
22
22
  width: calc(var(--lp-cmd-list) - 2px);
23
23
  }
24
- .lp-cmd-dialog[data-pane="open"][data-v-8d42f8db] {
24
+ .lp-cmd-dialog[data-pane="open"][data-v-2880ef37] {
25
25
  width: calc(var(--lp-cmd-list) + var(--lp-cmd-pane));
26
26
  min-height: min(76vh, 36rem);
27
27
  }
28
28
  }
29
- .lp-cmd-pane[data-v-8d42f8db] {
29
+ .lp-cmd-pane[data-v-2880ef37] {
30
30
  width: var(--lp-cmd-pane);
31
31
  }
32
- .lp-cmd-dialog[data-editing] .lp-cmd-pane[data-v-8d42f8db] {
32
+ .lp-cmd-dialog[data-editing] .lp-cmd-pane[data-v-2880ef37] {
33
33
  width: 100%;
34
34
  }
35
- .lp-cmd-dialog[data-editing][data-v-8d42f8db] {
35
+ .lp-cmd-dialog[data-editing][data-v-2880ef37] {
36
36
  width: var(--lp-cmd-list);
37
37
  min-height: 0;
38
38
  }
@@ -40,43 +40,43 @@
40
40
  * The pane rides in from the right edge while the dialog opens up to it, and
41
41
  * leaves the same way, for as long as the dialog takes to close over it.
42
42
  */
43
- .lp-cmd-pane-enter-active[data-v-8d42f8db],
44
- .lp-cmd-pane-leave-active[data-v-8d42f8db] {
43
+ .lp-cmd-pane-enter-active[data-v-2880ef37],
44
+ .lp-cmd-pane-leave-active[data-v-2880ef37] {
45
45
  transition:
46
46
  transform 300ms var(--ease-emphasized, ease),
47
47
  opacity 300ms ease;
48
48
  }
49
- .lp-cmd-pane-enter-from[data-v-8d42f8db],
50
- .lp-cmd-pane-leave-to[data-v-8d42f8db] {
49
+ .lp-cmd-pane-enter-from[data-v-2880ef37],
50
+ .lp-cmd-pane-leave-to[data-v-2880ef37] {
51
51
  opacity: 0;
52
52
  transform: translateX(40%);
53
53
  }
54
- .lp-cmd-para[data-v-8d42f8db] {
54
+ .lp-cmd-para[data-v-2880ef37] {
55
55
  --rise: 4px;
56
56
  animation: rise-in 260ms ease-out both;
57
57
  }
58
58
  @media (prefers-reduced-motion: reduce) {
59
- .lp-cmd-split[data-v-8d42f8db],
60
- .lp-cmd-pane-enter-active[data-v-8d42f8db],
61
- .lp-cmd-pane-leave-active[data-v-8d42f8db] {
59
+ .lp-cmd-split[data-v-2880ef37],
60
+ .lp-cmd-pane-enter-active[data-v-2880ef37],
61
+ .lp-cmd-pane-leave-active[data-v-2880ef37] {
62
62
  transition: none;
63
63
  }
64
- .lp-cmd-para[data-v-8d42f8db] {
64
+ .lp-cmd-para[data-v-2880ef37] {
65
65
  animation: none;
66
66
  }
67
67
  }
68
- .lp-cmd-excerpt[data-v-8d42f8db] {
68
+ .lp-cmd-excerpt[data-v-2880ef37] {
69
69
  display: -webkit-box;
70
70
  -webkit-box-orient: vertical;
71
71
  overflow: hidden;
72
72
  }
73
73
  /* Each result eases in; honours reduced motion. */
74
- .lp-cmd-item[data-v-8d42f8db] {
74
+ .lp-cmd-item[data-v-2880ef37] {
75
75
  --rise: 3px;
76
76
  animation: rise-in 140ms ease-out both;
77
77
  }
78
78
  @media (prefers-reduced-motion: reduce) {
79
- .lp-cmd-item[data-v-8d42f8db] {
79
+ .lp-cmd-item[data-v-2880ef37] {
80
80
  animation: none;
81
81
  }
82
82
  }
@@ -1,11 +1,12 @@
1
1
  import { defineComponent, computed, useModel, ref, useId, onMounted, watch, openBlock, createElementBlock, mergeProps, unref, createElementVNode, withModifiers, toDisplayString, createVNode, createSlots, withCtx, renderSlot, normalizeProps, guardReactiveProps, normalizeClass, withDirectives, vShow, createBlock, Teleport, createCommentVNode, mergeModels } from "vue";
2
2
  import { useLabels } from "../composables/useLpConfig.js";
3
3
  import { useScrollMemory } from "../composables/scrollMemory.js";
4
- import { _ as _sfc_main$6 } from "./LpBottomNav-Dik-ZcHv.js";
5
- import { _ as _sfc_main$2 } from "./LpButton-BgdewXxF.js";
6
- import { _ as _sfc_main$3 } from "./LpIcon--5WSb3jJ.js";
7
- import { _ as _sfc_main$5 } from "./LpScrollArea-Hz3-JWUG.js";
8
- import { _ as _sfc_main$4 } from "./LpShift-kKUeG9NS.js";
4
+ import { _ as _sfc_main$7 } from "./LpBottomNav-Dik-ZcHv.js";
5
+ import { _ as _sfc_main$2 } from "./LpContextSwitcher-Dwh6dyID.js";
6
+ import { _ as _sfc_main$3 } from "./LpButton-BgdewXxF.js";
7
+ import { _ as _sfc_main$4 } from "./LpIcon--5WSb3jJ.js";
8
+ import { _ as _sfc_main$6 } from "./LpScrollArea-Hz3-JWUG.js";
9
+ import { _ as _sfc_main$5 } from "./LpShift-kKUeG9NS.js";
9
10
  import { _ as _sfc_main$1 } from "./LpSidebar-CR_WKcn2.js";
10
11
  import { useMergedAttrs, mergeClass } from "../composables/useMergedClass.js";
11
12
  const _hoisted_1 = ["href"];
@@ -39,6 +40,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
39
40
  persist: {},
40
41
  previewDelay: {},
41
42
  compactHotkey: { type: [String, Boolean] },
43
+ contexts: {},
44
+ context: {},
42
45
  environment: {},
43
46
  hints: {},
44
47
  bottomNav: { type: [Boolean, Number, Array] }
@@ -46,10 +49,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
46
49
  "open": { type: Boolean, ...{ default: false } },
47
50
  "openModifiers": {}
48
51
  }),
49
- emits: /* @__PURE__ */ mergeModels(["update:modelValue", "update:collapsed", "select", "update:compact", "update:pinned"], ["update:open"]),
52
+ emits: /* @__PURE__ */ mergeModels(["update:modelValue", "update:collapsed", "select", "update:compact", "update:pinned", "update:context"], ["update:open"]),
50
53
  setup(__props, { expose: __expose, emit: __emit }) {
51
54
  const props = __props;
52
55
  const emit = __emit;
56
+ const switching = computed(() => (props.contexts?.length ?? 0) > 1);
53
57
  const flatItems = computed(
54
58
  () => (props.sections ? props.sections.flatMap((s) => s.items) : props.items ?? []).flatMap((i) => [i, ...i.children ?? []])
55
59
  );
@@ -161,60 +165,85 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
161
165
  hints: __props.hints,
162
166
  class: "shrink-0",
163
167
  "onUpdate:modelValue": onModel,
164
- "onUpdate:compact": _cache[0] || (_cache[0] = (v) => emit("update:compact", v)),
165
- "onUpdate:pinned": _cache[1] || (_cache[1] = (v) => emit("update:pinned", v)),
166
- "onUpdate:open": _cache[2] || (_cache[2] = (v) => drawerOpen.value = v),
167
- "onUpdate:collapsed": _cache[3] || (_cache[3] = (v) => emit("update:collapsed", v)),
168
+ "onUpdate:compact": _cache[1] || (_cache[1] = (v) => emit("update:compact", v)),
169
+ "onUpdate:pinned": _cache[2] || (_cache[2] = (v) => emit("update:pinned", v)),
170
+ "onUpdate:open": _cache[3] || (_cache[3] = (v) => drawerOpen.value = v),
171
+ "onUpdate:collapsed": _cache[4] || (_cache[4] = (v) => emit("update:collapsed", v)),
168
172
  onSelect
169
173
  }, createSlots({ _: 2 }, [
170
- _ctx.$slots.logo ? {
174
+ switching.value ? {
171
175
  name: "header",
172
176
  fn: withCtx(({ compact: folded }) => [
173
- renderSlot(_ctx.$slots, "logo", { compact: folded })
177
+ createVNode(_sfc_main$2, {
178
+ contexts: __props.contexts,
179
+ "model-value": __props.context,
180
+ compact: folded,
181
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = (id) => emit("update:context", id))
182
+ }, createSlots({ _: 2 }, [
183
+ _ctx.$slots.logo ? {
184
+ name: "default",
185
+ fn: withCtx(({ context: shown }) => [
186
+ renderSlot(_ctx.$slots, "logo", {
187
+ compact: folded,
188
+ context: shown
189
+ })
190
+ ]),
191
+ key: "0"
192
+ } : void 0
193
+ ]), 1032, ["contexts", "model-value", "compact"])
174
194
  ]),
175
195
  key: "0"
196
+ } : _ctx.$slots.logo ? {
197
+ name: "header",
198
+ fn: withCtx(({ compact: folded }) => [
199
+ renderSlot(_ctx.$slots, "logo", {
200
+ compact: folded,
201
+ context: __props.contexts?.[0]
202
+ })
203
+ ]),
204
+ key: "1"
176
205
  } : void 0,
177
206
  _ctx.$slots["sidebar-actions"] ? {
178
207
  name: "actions",
179
208
  fn: withCtx(() => [
180
209
  renderSlot(_ctx.$slots, "sidebar-actions")
181
210
  ]),
182
- key: "1"
211
+ key: "2"
183
212
  } : void 0,
184
213
  _ctx.$slots["sidebar-footer"] ? {
185
214
  name: "footer",
186
215
  fn: withCtx(({ compact: folded }) => [
187
216
  renderSlot(_ctx.$slots, "sidebar-footer", { compact: folded })
188
217
  ]),
189
- key: "2"
218
+ key: "3"
190
219
  } : void 0,
191
220
  _ctx.$slots["sidebar-item"] ? {
192
221
  name: "item",
193
222
  fn: withCtx((slotProps) => [
194
223
  renderSlot(_ctx.$slots, "sidebar-item", normalizeProps(guardReactiveProps(slotProps)))
195
224
  ]),
196
- key: "3"
225
+ key: "4"
197
226
  } : void 0
198
227
  ]), 1032, ["model-value", "items", "sections", "is-active", "open", "mobile-breakpoint", "divider", "loading", "collapsed", "auto-collapse-after", "compact", "resizable", "pinnable", "pinned", "persist", "preview-delay", "compact-hotkey", "environment", "hints"]),
199
228
  createElementVNode("div", _hoisted_2, [
200
229
  createElementVNode("header", _hoisted_3, [
201
- createVNode(_sfc_main$2, {
230
+ createVNode(_sfc_main$3, {
202
231
  class: normalizeClass(burgerClass.value),
203
232
  variant: "ghost",
204
233
  size: "sm",
205
234
  square: "",
206
235
  "aria-label": unref(text).openMenu,
207
- onClick: _cache[4] || (_cache[4] = ($event) => drawerOpen.value = true)
236
+ onClick: _cache[5] || (_cache[5] = ($event) => drawerOpen.value = true)
208
237
  }, {
209
238
  default: withCtx(() => [
210
- createVNode(_sfc_main$3, {
239
+ createVNode(_sfc_main$4, {
211
240
  name: "lucide:menu",
212
241
  size: 18
213
242
  })
214
243
  ]),
215
244
  _: 1
216
245
  }, 8, ["class", "aria-label"]),
217
- createVNode(_sfc_main$4, {
246
+ createVNode(_sfc_main$5, {
218
247
  axis: "height",
219
248
  presence: "",
220
249
  class: "flex min-w-0 flex-1 items-center"
@@ -226,7 +255,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
226
255
  ]),
227
256
  _: 3
228
257
  }),
229
- createVNode(_sfc_main$4, {
258
+ createVNode(_sfc_main$5, {
230
259
  axis: "width",
231
260
  anchor: "end",
232
261
  presence: "",
@@ -249,7 +278,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
249
278
  }, null, 512), [
250
279
  [vShow, bleeding.value]
251
280
  ]),
252
- withDirectives(createVNode(_sfc_main$5, {
281
+ withDirectives(createVNode(_sfc_main$6, {
253
282
  ref_key: "scrollArea",
254
283
  ref: scrollArea,
255
284
  class: "min-h-0 flex-1",
@@ -269,7 +298,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
269
298
  [vShow, !bleeding.value]
270
299
  ])
271
300
  ]),
272
- __props.bottomNav ? (openBlock(), createBlock(_sfc_main$6, {
301
+ __props.bottomNav ? (openBlock(), createBlock(_sfc_main$7, {
273
302
  key: 0,
274
303
  class: normalizeClass(burgerHidden[__props.mobileBreakpoint]),
275
304
  items: bottomItems.value,
@@ -277,7 +306,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
277
306
  more: "",
278
307
  "more-active": moreActive.value,
279
308
  onSelect: onBottom,
280
- onMore: _cache[5] || (_cache[5] = ($event) => drawerOpen.value = true)
309
+ onMore: _cache[6] || (_cache[6] = ($event) => drawerOpen.value = true)
281
310
  }, null, 8, ["class", "items", "is-active", "more-active"])) : createCommentVNode("", true)
282
311
  ]),
283
312
  renderSlot(_ctx.$slots, "overlays")
@@ -0,0 +1,81 @@
1
+ import { defineComponent, computed, openBlock, createBlock, unref, withCtx, createElementVNode, normalizeClass, renderSlot, createElementBlock, Fragment, createTextVNode, toDisplayString, createCommentVNode } from "vue";
2
+ import { useLabels } from "../composables/useLpConfig.js";
3
+ import { _ as _sfc_main$1 } from "./LpDropdownMenu-Dl7Z4ZhS.js";
4
+ import { _ as _sfc_main$2 } from "./LpIcon--5WSb3jJ.js";
5
+ const _hoisted_1 = ["aria-label"];
6
+ const _hoisted_2 = { class: "truncate" };
7
+ const _sfc_main = /* @__PURE__ */ defineComponent({
8
+ __name: "LpContextSwitcher",
9
+ props: {
10
+ contexts: {},
11
+ modelValue: {},
12
+ compact: { type: Boolean, default: false },
13
+ labels: {}
14
+ },
15
+ emits: ["update:modelValue"],
16
+ setup(__props, { emit: __emit }) {
17
+ const props = __props;
18
+ const emit = __emit;
19
+ const text = useLabels("contextSwitcher", { switch: "{label}, switch", title: "Switch to" }, () => props.labels);
20
+ const current = computed(() => props.contexts.find((c) => c.id === props.modelValue) ?? props.contexts[0]);
21
+ const initial = computed(() => [...current.value?.label ?? ""][0] ?? "");
22
+ const items = computed(
23
+ () => props.contexts.map((c) => ({
24
+ label: c.label,
25
+ icon: c.icon,
26
+ checked: c.id === current.value?.id ? true : void 0,
27
+ onSelect: () => {
28
+ if (c.id !== current.value?.id) emit("update:modelValue", c.id);
29
+ }
30
+ }))
31
+ );
32
+ return (_ctx, _cache) => {
33
+ return openBlock(), createBlock(_sfc_main$1, {
34
+ items: items.value,
35
+ title: unref(text).title,
36
+ palette: false
37
+ }, {
38
+ trigger: withCtx(() => [
39
+ createElementVNode("button", {
40
+ type: "button",
41
+ class: normalizeClass(["flex min-w-0 items-center gap-2 rounded-control text-base font-semibold text-ink outline-none transition-colors hover:bg-surface-soft focus-visible:ring-2 focus-visible:ring-ring", __props.compact ? "grid size-9 shrink-0 place-items-center bg-surface-soft" : "max-w-full px-2 py-1"]),
42
+ "aria-label": unref(text).switch.replace("{label}", current.value?.label ?? "")
43
+ }, [
44
+ renderSlot(_ctx.$slots, "default", {
45
+ context: current.value,
46
+ compact: __props.compact
47
+ }, () => [
48
+ __props.compact ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
49
+ current.value?.icon ? (openBlock(), createBlock(_sfc_main$2, {
50
+ key: 0,
51
+ name: current.value.icon,
52
+ size: 17
53
+ }, null, 8, ["name"])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
54
+ createTextVNode(toDisplayString(initial.value), 1)
55
+ ], 64))
56
+ ], 64)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
57
+ current.value?.icon ? (openBlock(), createBlock(_sfc_main$2, {
58
+ key: 0,
59
+ name: current.value.icon,
60
+ size: 17,
61
+ class: "shrink-0 text-brand"
62
+ }, null, 8, ["name"])) : createCommentVNode("", true),
63
+ createElementVNode("span", _hoisted_2, toDisplayString(current.value?.label), 1)
64
+ ], 64))
65
+ ]),
66
+ !__props.compact ? (openBlock(), createBlock(_sfc_main$2, {
67
+ key: 0,
68
+ name: "lucide:chevrons-up-down",
69
+ size: 14,
70
+ class: "shrink-0 text-muted"
71
+ })) : createCommentVNode("", true)
72
+ ], 10, _hoisted_1)
73
+ ]),
74
+ _: 3
75
+ }, 8, ["items", "title"]);
76
+ };
77
+ }
78
+ });
79
+ export {
80
+ _sfc_main as _
81
+ };
@@ -1 +1 @@
1
- export declare const COMPONENT_NAMES: readonly ["LayoutCanvas", "LpActionBar", "LpActivityLine", "LpAddressInput", "LpAlert", "LpAppShell", "LpAutocomplete", "LpAvatar", "LpBadge", "LpBottomNav", "LpBreadcrumbs", "LpButton", "LpCalendar", "LpCard", "LpChart", "LpCheckbox", "LpCodeBlock", "LpCodeDisplay", "LpCollapse", "LpColorPicker", "LpCommandPalette", "LpCommandScope", "LpConfirmDialog", "LpConfirmHost", "LpContextMenu", "LpCopyButton", "LpCountdown", "LpDataList", "LpDatePicker", "LpDateRangePicker", "LpDisclosure", "LpDivider", "LpDock", "LpDrawer", "LpDropdownMenu", "LpDropzone", "LpEditable", "LpEmptyState", "LpFieldNotice", "LpFileTree", "LpFormField", "LpFrame", "LpGauge", "LpGlowBorder", "LpHintExample", "LpHintLayer", "LpHints", "LpHoverCard", "LpIcon", "LpIdenticon", "LpImageEditor", "LpInfraNode", "LpInput", "LpKeys", "LpLightbox", "LpLink", "LpLoader", "LpLogViewer", "LpMagnetic", "LpMapPicker", "LpMasonry", "LpMediaGallery", "LpModal", "LpModalNav", "LpNotificationBell", "LpNumberField", "LpNumberFlow", "LpOtpInput", "LpPagination", "LpPaletteFacts", "LpPaletteHeader", "LpPalettePreview", "LpPaletteStats", "LpPaletteSwatches", "LpPaletteTheme", "LpPasswordInput", "LpPhoneInput", "LpPie", "LpPipeline", "LpPopover", "LpProgress", "LpQuota", "LpRadio", "LpRadioGroup", "LpRangeCalendar", "LpRangeSlider", "LpRelativeTime", "LpRepeater", "LpRevealOnScroll", "LpRollingText", "LpScramble", "LpScrollArea", "LpSecretField", "LpSection", "LpSegmented", "LpSelect", "LpServiceNode", "LpShaderBackdrop", "LpShift", "LpShinyText", "LpShortcutsDialog", "LpSidebar", "LpSkeleton", "LpSlider", "LpSparkline", "LpSpotlight", "LpStat", "LpStatus", "LpStepMark", "LpStepper", "LpSwap", "LpSwitch", "LpTable", "LpTableOfContents", "LpTabs", "LpTagsInput", "LpTextReveal", "LpTextarea", "LpThemeProvider", "LpThemeStudio", "LpThemeStudioSection", "LpThemeSwitcher", "LpTilt", "LpTimeField", "LpToaster", "LpTooltip", "LpTooltipProvider", "LpTopologyCanvas", "LpTour", "LpTruncate", "LpTypewriter", "LpUptimeBar", "LpVideoBackdrop"];
1
+ export declare const COMPONENT_NAMES: readonly ["LayoutCanvas", "LpActionBar", "LpActivityLine", "LpAddressInput", "LpAlert", "LpAppShell", "LpAutocomplete", "LpAvatar", "LpBadge", "LpBottomNav", "LpBreadcrumbs", "LpButton", "LpCalendar", "LpCard", "LpChart", "LpCheckbox", "LpCodeBlock", "LpCodeDisplay", "LpCollapse", "LpColorPicker", "LpCommandPalette", "LpCommandScope", "LpConfirmDialog", "LpConfirmHost", "LpContextMenu", "LpContextSwitcher", "LpCopyButton", "LpCountdown", "LpDataList", "LpDatePicker", "LpDateRangePicker", "LpDisclosure", "LpDivider", "LpDock", "LpDrawer", "LpDropdownMenu", "LpDropzone", "LpEditable", "LpEmptyState", "LpFieldNotice", "LpFileTree", "LpFormField", "LpFrame", "LpGauge", "LpGlowBorder", "LpHintExample", "LpHintLayer", "LpHints", "LpHoverCard", "LpIcon", "LpIdenticon", "LpImageEditor", "LpInfraNode", "LpInput", "LpKeys", "LpLightbox", "LpLink", "LpLoader", "LpLogViewer", "LpMagnetic", "LpMapPicker", "LpMasonry", "LpMediaGallery", "LpModal", "LpModalNav", "LpNotificationBell", "LpNumberField", "LpNumberFlow", "LpOtpInput", "LpPagination", "LpPaletteFacts", "LpPaletteHeader", "LpPalettePreview", "LpPaletteStats", "LpPaletteSwatches", "LpPaletteTheme", "LpPasswordInput", "LpPhoneInput", "LpPie", "LpPipeline", "LpPopover", "LpProgress", "LpQuota", "LpRadio", "LpRadioGroup", "LpRangeCalendar", "LpRangeSlider", "LpRelativeTime", "LpRepeater", "LpRevealOnScroll", "LpRollingText", "LpScramble", "LpScrollArea", "LpSecretField", "LpSection", "LpSegmented", "LpSelect", "LpServiceNode", "LpShaderBackdrop", "LpShift", "LpShinyText", "LpShortcutsDialog", "LpSidebar", "LpSkeleton", "LpSlider", "LpSparkline", "LpSpotlight", "LpStat", "LpStatus", "LpStepMark", "LpStepper", "LpSwap", "LpSwitch", "LpTable", "LpTableOfContents", "LpTabs", "LpTagsInput", "LpTextReveal", "LpTextarea", "LpThemeProvider", "LpThemeStudio", "LpThemeStudioSection", "LpThemeSwitcher", "LpTilt", "LpTimeField", "LpToaster", "LpTooltip", "LpTooltipProvider", "LpTopologyCanvas", "LpTour", "LpTruncate", "LpTypewriter", "LpUptimeBar", "LpVideoBackdrop"];
@@ -1,4 +1,4 @@
1
- import type { SidebarItem, SidebarSection } from "./sidebar";
1
+ import type { AppContext, SidebarItem, SidebarSection } from "./sidebar";
2
2
  export type { SidebarItem, SidebarSection } from "./sidebar";
3
3
  declare const _default: typeof __VLS_export;
4
4
  export default _default;
@@ -66,6 +66,14 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
66
66
  previewDelay?: number;
67
67
  /** The key that folds the rail; false turns it off. Forwarded to LpSidebar. */
68
68
  compactHotkey?: string | false;
69
+ /**
70
+ * The app's contexts — a server, a workspace, a part of the product — each
71
+ * with its own rail. With more than one, the rail's header becomes the
72
+ * switcher between them (v-model:context), and the `logo` slot draws the
73
+ * switcher's face with the current `context` in its props.
74
+ */
75
+ contexts?: AppContext[];
76
+ context?: string;
69
77
  /** Marks a non-production copy along the top of the rail. Forwarded to LpSidebar. */
70
78
  environment?: string | {
71
79
  label: string;
@@ -93,6 +101,7 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
93
101
  "update:pinned": (ids: string[]) => any;
94
102
  "update:collapsed": (keys: string[]) => any;
95
103
  "update:compact": (value: boolean) => any;
104
+ "update:context": (id: string) => any;
96
105
  }, string, import("vue").PublicProps, Readonly<{
97
106
  /** Flat items, or sections for grouped nav. Forwarded to LpSidebar. */
98
107
  items?: SidebarItem[];
@@ -157,6 +166,14 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
157
166
  previewDelay?: number;
158
167
  /** The key that folds the rail; false turns it off. Forwarded to LpSidebar. */
159
168
  compactHotkey?: string | false;
169
+ /**
170
+ * The app's contexts — a server, a workspace, a part of the product — each
171
+ * with its own rail. With more than one, the rail's header becomes the
172
+ * switcher between them (v-model:context), and the `logo` slot draws the
173
+ * switcher's face with the current `context` in its props.
174
+ */
175
+ contexts?: AppContext[];
176
+ context?: string;
160
177
  /** Marks a non-production copy along the top of the rail. Forwarded to LpSidebar. */
161
178
  environment?: string | {
162
179
  label: string;
@@ -181,6 +198,7 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
181
198
  "onUpdate:pinned"?: ((ids: string[]) => any) | undefined;
182
199
  "onUpdate:collapsed"?: ((keys: string[]) => any) | undefined;
183
200
  "onUpdate:compact"?: ((value: boolean) => any) | undefined;
201
+ "onUpdate:context"?: ((id: string) => any) | undefined;
184
202
  }>, {
185
203
  contentClass: string;
186
204
  compact: boolean;
@@ -189,6 +207,12 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
189
207
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
190
208
  logo?: (props: {
191
209
  compact: boolean | undefined;
210
+ context: AppContext;
211
+ }) => any;
212
+ } & {
213
+ logo?: (props: {
214
+ compact: boolean | undefined;
215
+ context: AppContext | undefined;
192
216
  }) => any;
193
217
  } & {
194
218
  'sidebar-actions'?: (props: {}) => any;
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpAppShell-B0Yu9MVs.js";
1
+ import { _ as _sfc_main } from "../chunks/LpAppShell-D-NtjYlw.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,7 +1,7 @@
1
1
  declare const _default: typeof __VLS_export;
2
2
  export default _default;
3
3
  declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
4
- tone?: "info" | "error" | "action" | "muted" | "outline" | "success" | "warning" | "neutral" | "brand" | "danger" | "ok" | "accent" | undefined;
4
+ tone?: "info" | "error" | "action" | "muted" | "outline" | "success" | "warning" | "accent" | "neutral" | "brand" | "danger" | "ok" | undefined;
5
5
  /** `sm` is the one that does not disturb a text-sm line it sits on. */
6
6
  size?: "md" | "sm" | "lg" | undefined;
7
7
  dot?: boolean;
@@ -12,7 +12,7 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
12
12
  }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
13
13
  remove: () => any;
14
14
  }, string, import("vue").PublicProps, Readonly<{
15
- tone?: "info" | "error" | "action" | "muted" | "outline" | "success" | "warning" | "neutral" | "brand" | "danger" | "ok" | "accent" | undefined;
15
+ tone?: "info" | "error" | "action" | "muted" | "outline" | "success" | "warning" | "accent" | "neutral" | "brand" | "danger" | "ok" | undefined;
16
16
  /** `sm` is the one that does not disturb a text-sm line it sits on. */
17
17
  size?: "md" | "sm" | "lg" | undefined;
18
18
  dot?: boolean;
@@ -23,7 +23,7 @@ import { parseCommandQuery, searchCommandItems, applyQueryFilters, parsePaletteQ
23
23
  import { PALETTE_MODES, fallbackRows, readRecent, resolveRecentItems, recentVisible, commandHasPreview, matchModePrefix, modeChipLabel, pushRecent, toRecentItem } from "./commandPalette.js";
24
24
  import { usePortalTarget, useOutsideGuard } from "../composables/useView.js";
25
25
  import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
26
- import '../assets/LpCommandPalette-Rtbg669l.css';const _hoisted_1 = { class: "flex shrink-0 items-center gap-2.5 border-b border-line px-4" };
26
+ import '../assets/LpCommandPalette-DOIZHw-g.css';const _hoisted_1 = { class: "flex shrink-0 items-center gap-2.5 border-b border-line px-4" };
27
27
  const _hoisted_2 = ["title", "onClick"];
28
28
  const _hoisted_3 = ["readonly", "aria-activedescendant", "placeholder", "onKeydown"];
29
29
  const _hoisted_4 = { class: "shrink-0 rounded-small border border-line px-1.5 py-0.5 text-[10px] text-muted" };
@@ -677,6 +677,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
677
677
  run(c);
678
678
  }
679
679
  async function run(row) {
680
+ if (!props.open) return;
680
681
  if (!row || row.disabled || executing.value) return;
681
682
  const c = fallbackOf.get(row) ?? row;
682
683
  const group = moreOf.get(row);
@@ -1291,7 +1292,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1291
1292
  };
1292
1293
  }
1293
1294
  });
1294
- const LpCommandPalette = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-8d42f8db"]]);
1295
+ const LpCommandPalette = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-2880ef37"]]);
1295
1296
  export {
1296
1297
  LpCommandPalette as default
1297
1298
  };
@@ -0,0 +1,34 @@
1
+ import type { AppContext } from "./sidebar";
2
+ type __VLS_Props = {
3
+ contexts: AppContext[];
4
+ /** The current context's id (v-model). */
5
+ modelValue?: string;
6
+ /** The icon rail: the trigger is a square with the icon or an initial. */
7
+ compact?: boolean;
8
+ labels?: Partial<{
9
+ switch: string;
10
+ title: string;
11
+ }>;
12
+ };
13
+ declare var __VLS_9: {
14
+ context: AppContext;
15
+ compact: boolean;
16
+ };
17
+ type __VLS_Slots = {} & {
18
+ default?: (props: typeof __VLS_9) => any;
19
+ };
20
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
21
+ "update:modelValue": (id: string) => any;
22
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
23
+ "onUpdate:modelValue"?: ((id: string) => any) | undefined;
24
+ }>, {
25
+ compact: boolean;
26
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
27
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
28
+ declare const _default: typeof __VLS_export;
29
+ export default _default;
30
+ type __VLS_WithSlots<T, S> = T & {
31
+ new (): {
32
+ $slots: S;
33
+ };
34
+ };
@@ -0,0 +1,4 @@
1
+ import { _ as _sfc_main } from "../chunks/LpContextSwitcher-Dwh6dyID.js";
2
+ export {
3
+ _sfc_main as default
4
+ };
@@ -42,9 +42,9 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
42
42
  }>, {
43
43
  disabled: boolean;
44
44
  align: "start" | "center" | "end";
45
+ touch: "hold" | "none";
45
46
  side: "top" | "right" | "bottom" | "left";
46
47
  sideOffset: number;
47
- touch: "hold" | "none";
48
48
  openDelay: number;
49
49
  closeDelay: number;
50
50
  arrow: boolean;
@@ -1,5 +1,11 @@
1
1
  import type { Component } from "vue";
2
2
  import type { ContextMenuItemDef } from "./LpContextMenu.vue";
3
+ /** One of an app's contexts in the rail header switcher (LpContextSwitcher). */
4
+ export interface AppContext {
5
+ id: string;
6
+ label: string;
7
+ icon?: string;
8
+ }
3
9
  export interface SidebarItem {
4
10
  id: string;
5
11
  label: string;
@@ -1,8 +1,22 @@
1
1
  import { useAttrs, computed, toValue } from "vue";
2
- import { cn } from "tailwind-variants";
2
+ import { extendTailwindMerge } from "tailwind-merge";
3
+ import { cx } from "tailwind-variants";
4
+ const kitMerge = extendTailwindMerge({
5
+ extend: {
6
+ theme: {
7
+ radius: ["control", "card", "pill", "inner", "small"],
8
+ shadow: ["panel"],
9
+ ease: ["emphasized", "travel", "settle"]
10
+ }
11
+ }
12
+ });
13
+ function merge(...classes) {
14
+ const joined = cx(...classes);
15
+ return joined ? kitMerge(joined) : "";
16
+ }
3
17
  function useMergedClass(own) {
4
18
  const attrs = useAttrs();
5
- return computed(() => cn(toValue(own), attrs.class) ?? "");
19
+ return computed(() => merge(toValue(own), attrs.class));
6
20
  }
7
21
  function useMergedAttrs(own) {
8
22
  const attrs = useAttrs();
@@ -15,7 +29,7 @@ function useMergedAttrs(own) {
15
29
  };
16
30
  }
17
31
  function mergeClass(...classes) {
18
- return cn(...classes) ?? "";
32
+ return merge(...classes);
19
33
  }
20
34
  export {
21
35
  mergeClass,
@@ -53,6 +53,7 @@ function useShift(options = {}) {
53
53
  const tops = readScroll();
54
54
  if (wantsHeight()) node.style.height = "";
55
55
  if (wantsWidth()) node.style.width = "";
56
+ node.style.removeProperty(TARGET_WIDTH);
56
57
  const toHeight = wantsHeight() ? node.offsetHeight : 0;
57
58
  const toWidth = wantsWidth() ? node.offsetWidth : 0;
58
59
  const exactWidth = wantsWidth() ? Math.ceil(node.getBoundingClientRect().width) : 0;
package/dist/index.d.ts CHANGED
@@ -137,6 +137,8 @@ export { default as LpHintExample } from "./components/LpHintExample.vue";
137
137
  export { default as LpShortcutsDialog } from "./components/LpShortcutsDialog.vue";
138
138
  export { default as LpSidebar } from "./components/LpSidebar.vue";
139
139
  export type { SidebarItem, SidebarSection } from "./components/LpSidebar.vue";
140
+ export type { AppContext } from "./components/sidebar";
141
+ export { default as LpContextSwitcher } from "./components/LpContextSwitcher.vue";
140
142
  export { default as LpSkeleton } from "./components/LpSkeleton.vue";
141
143
  export { default as LpSlider } from "./components/LpSlider.vue";
142
144
  export { default as LpRangeSlider } from "./components/LpRangeSlider.vue";
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { _ as _3 } from "./chunks/LpMapPicker-DeJRMfFN.js";
4
4
  import { OSM_TILES, createNominatimProvider, emptyAddress, formatAddress, project, tileUrl, unproject } from "./components/address.js";
5
5
  import { _ as _4 } from "./chunks/LpActionBar-B55msXTr.js";
6
6
  import { _ as _5 } from "./chunks/LpAlert-Cg7oIx1B.js";
7
- import { _ as _6 } from "./chunks/LpAppShell-B0Yu9MVs.js";
7
+ import { _ as _6 } from "./chunks/LpAppShell-D-NtjYlw.js";
8
8
  import { _ as _7 } from "./chunks/LpAutocomplete-DOu7FN5P.js";
9
9
  import { _ as _8 } from "./chunks/LpAvatar-Cg6Az3wz.js";
10
10
  import { _ as _9 } from "./chunks/LpBadge-0A6sPks3.js";
@@ -87,44 +87,45 @@ import { _ as _61 } from "./chunks/LpSwap-KIS24N8W.js";
87
87
  import { _ as _62 } from "./chunks/LpHintExample-9Py0wc-L.js";
88
88
  import { default as default11 } from "./components/LpShortcutsDialog.vue.js";
89
89
  import { _ as _63 } from "./chunks/LpSidebar-CR_WKcn2.js";
90
- import { _ as _64 } from "./chunks/LpSkeleton-BB6XIV-w.js";
91
- import { _ as _65 } from "./chunks/LpSlider-DxYoCJJO.js";
92
- import { _ as _66 } from "./chunks/LpRangeSlider-CM-0ZITZ.js";
93
- import { _ as _67 } from "./chunks/LpVideoBackdrop-DS1wGv-X.js";
90
+ import { _ as _64 } from "./chunks/LpContextSwitcher-Dwh6dyID.js";
91
+ import { _ as _65 } from "./chunks/LpSkeleton-BB6XIV-w.js";
92
+ import { _ as _66 } from "./chunks/LpSlider-DxYoCJJO.js";
93
+ import { _ as _67 } from "./chunks/LpRangeSlider-CM-0ZITZ.js";
94
+ import { _ as _68 } from "./chunks/LpVideoBackdrop-DS1wGv-X.js";
94
95
  import { formatClipTime, isNativeLoop, planLoop, seamProgress, seamReached } from "./components/videoBackdrop.js";
95
- import { _ as _68 } from "./chunks/LpSparkline-CExIRDb3.js";
96
- import { _ as _69 } from "./chunks/LpStat-DXqKD1h5.js";
97
- import { _ as _70 } from "./chunks/LpStepper-CKSTFzHE.js";
98
- import { _ as _71 } from "./chunks/LpSwitch-DlZHmj-M.js";
96
+ import { _ as _69 } from "./chunks/LpSparkline-CExIRDb3.js";
97
+ import { _ as _70 } from "./chunks/LpStat-DXqKD1h5.js";
98
+ import { _ as _71 } from "./chunks/LpStepper-CKSTFzHE.js";
99
+ import { _ as _72 } from "./chunks/LpSwitch-DlZHmj-M.js";
99
100
  import { default as default12 } from "./components/LpTable.vue.js";
100
101
  import { compareValues } from "./components/tableSort.js";
101
102
  import { resolveRowMenu } from "./components/tableRowMenu.js";
102
- import { _ as _72 } from "./chunks/LpTableOfContents-BN5zvpnx.js";
103
+ import { _ as _73 } from "./chunks/LpTableOfContents-BN5zvpnx.js";
103
104
  import { default as default13 } from "./components/LpTabs.vue.js";
104
- import { _ as _73 } from "./chunks/LpBottomNav-Dik-ZcHv.js";
105
- import { _ as _74 } from "./chunks/LpTagsInput-CHoiid8u.js";
105
+ import { _ as _74 } from "./chunks/LpBottomNav-Dik-ZcHv.js";
106
+ import { _ as _75 } from "./chunks/LpTagsInput-CHoiid8u.js";
106
107
  import { addTags, splitTags } from "./components/tags.js";
107
- import { _ as _75 } from "./chunks/LpTextarea-CU7C1pLO.js";
108
- import { _ as _76 } from "./chunks/LpThemeSwitcher-BXJhQYGD.js";
109
- import { _ as _77 } from "./chunks/LpTilt-B7KHX1zH.js";
110
- import { _ as _78 } from "./chunks/LpToaster-C1D35SKk.js";
111
- import { _ as _79 } from "./chunks/LpTooltip-BcBvIx4z.js";
112
- import { _ as _80 } from "./chunks/LpTour-XshJslGg.js";
108
+ import { _ as _76 } from "./chunks/LpTextarea-CU7C1pLO.js";
109
+ import { _ as _77 } from "./chunks/LpThemeSwitcher-BXJhQYGD.js";
110
+ import { _ as _78 } from "./chunks/LpTilt-B7KHX1zH.js";
111
+ import { _ as _79 } from "./chunks/LpToaster-C1D35SKk.js";
112
+ import { _ as _80 } from "./chunks/LpTooltip-BcBvIx4z.js";
113
+ import { _ as _81 } from "./chunks/LpTour-XshJslGg.js";
113
114
  import { holeOf, openContextMenu, panelsAround } from "./components/tour.js";
114
- import { _ as _81 } from "./chunks/LpHintLayer-Db_s9_DL.js";
115
- import { _ as _82 } from "./chunks/LpHints-Cxf5GXsx.js";
115
+ import { _ as _82 } from "./chunks/LpHintLayer-Db_s9_DL.js";
116
+ import { _ as _83 } from "./chunks/LpHints-Cxf5GXsx.js";
116
117
  import { HINT_SIGNALS, contextMenuTip, createHintSystem, dropzoneTip, hintKey, hintSummary, hotkeyTip, localHintStorage, noteDropzone, noteHotkeyTarget, paletteTip, provideHintSystem, runningHintSystem, useHintSystem } from "./composables/useHintSystem.js";
117
- import { _ as _83 } from "./chunks/LpTooltipProvider-CftjV7Xc.js";
118
- import { _ as _84 } from "./chunks/LpTopologyCanvas-1OufbBtQ.js";
119
- import { _ as _85 } from "./chunks/LpUptimeBar-qhvvmmoI.js";
118
+ import { _ as _84 } from "./chunks/LpTooltipProvider-CftjV7Xc.js";
119
+ import { _ as _85 } from "./chunks/LpTopologyCanvas-1OufbBtQ.js";
120
+ import { _ as _86 } from "./chunks/LpUptimeBar-qhvvmmoI.js";
120
121
  import { deserializeLayout, serializeLayout } from "./layout/tree.js";
121
122
  import { addLeaf, addTab, countBlocks, countLeaves, makeLayout, moveLeaf, removeBlock, removeLeaf, reorderTab, resizeAt, setActiveTab } from "./layout/tree.js";
122
123
  import { blockTitle, defineBlocks } from "./layout/registry.js";
123
124
  import { layoutController, useLayout } from "./layout/useLayout.js";
124
125
  import { a as parseTheme, s as serializeTheme } from "./chunks/LpThemeProvider-8MG6yqU9.js";
125
- import { D, _ as _86, T, b as b2, c as c2, d, e, f, g, l, h, n, i as i2, j, p, r, k, t, m, o, q, u, v } from "./chunks/LpThemeProvider-8MG6yqU9.js";
126
+ import { D, _ as _87, T, b as b2, c as c2, d, e, f, g, l, h, n, i as i2, j, p, r, k, t, m, o, q, u, v } from "./chunks/LpThemeProvider-8MG6yqU9.js";
126
127
  import { G, N, d as d2, i as i3, s } from "./chunks/backdrop-Dkf2vM81.js";
127
- import { _ as _87, a as a2, b as b3, c as c3, d as d3, S, e as e2, u as u2, w } from "./chunks/LpThemeStudio-zyOH3dF2.js";
128
+ import { _ as _88, a as a2, b as b3, c as c3, d as d3, S, e as e2, u as u2, w } from "./chunks/LpThemeStudio-zyOH3dF2.js";
128
129
  import { useClipboard } from "./composables/useClipboard.js";
129
130
  import { activeShortcuts, appleModifierLabel, findHotkeyConflicts, hotkeyConflicts, hotkeySteps, isAppleKeyboard, matchesHotkey, mergeShortcutGroups, parseCombo, pendingHotkey, sequenceProgress, useAppleKeyboard, useHotkeys } from "./composables/useHotkeys.js";
130
131
  import { anyModalOpen, topModalElement, useModalLayer } from "./components/modalDepth.js";
@@ -160,64 +161,64 @@ import { useShift } from "./composables/useShift.js";
160
161
  import { useTilt } from "./composables/useTilt.js";
161
162
  import { useZoomPan } from "./composables/useZoomPan.js";
162
163
  import { useToast } from "./composables/useToast.js";
163
- import { _ as _88 } from "./chunks/LpFrame-BVQCQLDI.js";
164
- import { _ as _89 } from "./chunks/LpThemeStudioSection-B7bt8Iyg.js";
164
+ import { _ as _89 } from "./chunks/LpFrame-BVQCQLDI.js";
165
+ import { _ as _90 } from "./chunks/LpThemeStudioSection-B7bt8Iyg.js";
165
166
  import { isInternalHref, resolveLink } from "./components/link.js";
166
- import { _ as _90 } from "./chunks/LpRepeater-DghUJ3bc.js";
167
- import { _ as _91 } from "./chunks/LpGauge-KZaQ_FDR.js";
168
- import { _ as _92 } from "./chunks/LpQuota-Dk2MyI2Z.js";
169
- import { _ as _93 } from "./chunks/LpStatus-DfZREVe0.js";
170
- import { _ as _94 } from "./chunks/LpRelativeTime-DwgCb5-8.js";
171
- import { _ as _95 } from "./chunks/LpActivityLine-CJvzrNpj.js";
167
+ import { _ as _91 } from "./chunks/LpRepeater-DghUJ3bc.js";
168
+ import { _ as _92 } from "./chunks/LpGauge-KZaQ_FDR.js";
169
+ import { _ as _93 } from "./chunks/LpQuota-Dk2MyI2Z.js";
170
+ import { _ as _94 } from "./chunks/LpStatus-DfZREVe0.js";
171
+ import { _ as _95 } from "./chunks/LpRelativeTime-DwgCb5-8.js";
172
+ import { _ as _96 } from "./chunks/LpActivityLine-CJvzrNpj.js";
172
173
  import { TONE_COLOR, TONE_SOFT, arcPath, capacityThresholds, meterFraction, toneAt } from "./components/meter.js";
173
174
  import { TONES, TONE_ALIASES, toneLook, toneOf } from "./components/tone.js";
174
175
  import { formatAmount, quotaSegments, quotaState } from "./components/quota.js";
175
176
  import { compactNumber, decimalMark, formatBytes } from "./components/format.js";
176
177
  import { STATUS_LABELS, STATUS_LOOK } from "./components/status.js";
177
178
  import { formatRelativeTime } from "./components/relativeTime.js";
178
- import { _ as _96 } from "./chunks/LpPipeline-QCZWWB0G.js";
179
- import { _ as _97 } from "./chunks/LpStepMark-TaAyuNsH.js";
179
+ import { _ as _97 } from "./chunks/LpPipeline-QCZWWB0G.js";
180
+ import { _ as _98 } from "./chunks/LpStepMark-TaAyuNsH.js";
180
181
  import { childProgress, connectorFilled, currentStepIndex, isSettled, overallStatus, stepElapsed } from "./components/pipeline.js";
181
- import { _ as _98 } from "./chunks/LpLoader-DPTNhqLu.js";
182
+ import { _ as _99 } from "./chunks/LpLoader-DPTNhqLu.js";
182
183
  import { useLoadingGate } from "./composables/useLoadingGate.js";
183
184
  import { useNow } from "./composables/useNow.js";
184
- import { _ as _99 } from "./chunks/LpCountdown-BNNhnQAK.js";
185
+ import { _ as _100 } from "./chunks/LpCountdown-BNNhnQAK.js";
185
186
  import { DEFAULT_MILESTONES, DEFAULT_UNIT_LABELS, ceilToSecond, crossedMilestone, formatElapsed, formatTime, msToNextSecond, splitTime, timeParts } from "./components/timeSpan.js";
186
- import { _ as _100 } from "./chunks/LpCopyButton-Cw-lF7Q3.js";
187
- import { _ as _101 } from "./chunks/LpSecretField-osKPNdNT.js";
188
- import { _ as _102 } from "./chunks/LpDataList-CcAA-rD9.js";
189
- import { _ as _103 } from "./chunks/LpEditable-CSuJIPbG.js";
190
- import { _ as _104 } from "./chunks/LpTruncate-qgKA-3K3.js";
191
- import { _ as _105 } from "./chunks/LpCodeDisplay-BCghfBFC.js";
187
+ import { _ as _101 } from "./chunks/LpCopyButton-Cw-lF7Q3.js";
188
+ import { _ as _102 } from "./chunks/LpSecretField-osKPNdNT.js";
189
+ import { _ as _103 } from "./chunks/LpDataList-CcAA-rD9.js";
190
+ import { _ as _104 } from "./chunks/LpEditable-CSuJIPbG.js";
191
+ import { _ as _105 } from "./chunks/LpTruncate-qgKA-3K3.js";
192
+ import { _ as _106 } from "./chunks/LpCodeDisplay-BCghfBFC.js";
192
193
  import { formatRemaining, groupCode, middleTruncate, normalizeCode } from "./components/fieldText.js";
193
- import { _ as _106 } from "./chunks/LpHoverCard-spGxyw7T.js";
194
- import { _ as _107 } from "./chunks/LpIdenticon-BzdC_qz9.js";
194
+ import { _ as _107 } from "./chunks/LpHoverCard-spGxyw7T.js";
195
+ import { _ as _108 } from "./chunks/LpIdenticon-BzdC_qz9.js";
195
196
  import { hashString, identicon, identiconRects, identiconSvg } from "./components/identicon.js";
196
- import { _ as _108 } from "./chunks/LpShaderBackdrop-Bote0iuy.js";
197
+ import { _ as _109 } from "./chunks/LpShaderBackdrop-Bote0iuy.js";
197
198
  import { PRESET_NAMES } from "./components/shaderBackdrop.js";
198
- import { _ as _109 } from "./chunks/LpCollapse-CWS9M_lm.js";
199
+ import { _ as _110 } from "./chunks/LpCollapse-CWS9M_lm.js";
199
200
  import { pseudoLocalize } from "./composables/pseudoLocale.js";
200
- import { _ as _110 } from "./chunks/LpTextReveal-CtAnOUkp.js";
201
- import { _ as _111 } from "./chunks/LpShinyText-DX2ajtWq.js";
202
- import { _ as _112 } from "./chunks/LpTypewriter-BCL_a6wE.js";
203
- import { _ as _113 } from "./chunks/LpScramble-DeT_D36q.js";
201
+ import { _ as _111 } from "./chunks/LpTextReveal-CtAnOUkp.js";
202
+ import { _ as _112 } from "./chunks/LpShinyText-DX2ajtWq.js";
203
+ import { _ as _113 } from "./chunks/LpTypewriter-BCL_a6wE.js";
204
+ import { _ as _114 } from "./chunks/LpScramble-DeT_D36q.js";
204
205
  import { useScramble } from "./composables/useScramble.js";
205
206
  import { useInView } from "./composables/useInView.js";
206
207
  import { SCRAMBLE_GLYPHS, graphemes, planScramble, randomGlyph, scrambleFrame, seededRandom, settledCells } from "./components/scramble.js";
207
208
  import { default as default14 } from "./components/LpDock.vue.js";
208
209
  import { dockSize, magneticOffset } from "./components/dock.js";
209
- import { _ as _114 } from "./chunks/LpMasonry--k0571qG.js";
210
+ import { _ as _115 } from "./chunks/LpMasonry--k0571qG.js";
210
211
  import { masonryColumnCount, placeMasonry } from "./components/masonry.js";
211
- import { _ as _115 } from "./chunks/LpRevealOnScroll-DcQjexLy.js";
212
+ import { _ as _116 } from "./chunks/LpRevealOnScroll-DcQjexLy.js";
212
213
  import { revealFrom, revealTo, staggerDelay } from "./components/reveal.js";
213
- import { _ as _116 } from "./chunks/LpSpotlight-4F3NeUMY.js";
214
- import { _ as _117 } from "./chunks/LpGlowBorder-B8V-QzIl.js";
215
- import { _ as _118 } from "./chunks/LpMagnetic-CtXKLa2Y.js";
214
+ import { _ as _117 } from "./chunks/LpSpotlight-4F3NeUMY.js";
215
+ import { _ as _118 } from "./chunks/LpGlowBorder-B8V-QzIl.js";
216
+ import { _ as _119 } from "./chunks/LpMagnetic-CtXKLa2Y.js";
216
217
  import { spotlightPoint, useSpotlight } from "./composables/useSpotlight.js";
217
- import { _ as _119 } from "./chunks/LpCommandScope-CtUhJe8T.js";
218
+ import { _ as _120 } from "./chunks/LpCommandScope-CtUhJe8T.js";
218
219
  import { createPaletteRegistry, mergePaletteCommands, usePaletteCommands, vPalette } from "./composables/usePaletteCommands.js";
219
220
  import { u as u3 } from "./chunks/palette-Co8tSASo.js";
220
- import { _ as _120 } from "./chunks/LpPaletteTheme-CWAIjBCi.js";
221
+ import { _ as _121 } from "./chunks/LpPaletteTheme-CWAIjBCi.js";
221
222
  const UI_CONFIG_VERSION = 1;
222
223
  function serializeConfig(config) {
223
224
  const out = {
@@ -250,14 +251,14 @@ export {
250
251
  LP_FIELD,
251
252
  _ as LayoutCanvas,
252
253
  _4 as LpActionBar,
253
- _95 as LpActivityLine,
254
+ _96 as LpActivityLine,
254
255
  _2 as LpAddressInput,
255
256
  _5 as LpAlert,
256
257
  _6 as LpAppShell,
257
258
  _7 as LpAutocomplete,
258
259
  _8 as LpAvatar,
259
260
  _9 as LpBadge,
260
- _73 as LpBottomNav,
261
+ _74 as LpBottomNav,
261
262
  _10 as LpBreadcrumbs,
262
263
  _11 as LpButton,
263
264
  _12 as LpCalendar,
@@ -265,17 +266,18 @@ export {
265
266
  default2 as LpChart,
266
267
  _14 as LpCheckbox,
267
268
  _15 as LpCodeBlock,
268
- _105 as LpCodeDisplay,
269
- _109 as LpCollapse,
269
+ _106 as LpCodeDisplay,
270
+ _110 as LpCollapse,
270
271
  _16 as LpColorPicker,
271
272
  default3 as LpCommandPalette,
272
- _119 as LpCommandScope,
273
+ _120 as LpCommandScope,
273
274
  _18 as LpConfirmDialog,
274
275
  _19 as LpConfirmHost,
275
276
  _20 as LpContextMenu,
276
- _100 as LpCopyButton,
277
- _99 as LpCountdown,
278
- _102 as LpDataList,
277
+ _64 as LpContextSwitcher,
278
+ _101 as LpCopyButton,
279
+ _100 as LpCountdown,
280
+ _103 as LpDataList,
279
281
  _21 as LpDatePicker,
280
282
  _22 as LpDateRangePicker,
281
283
  _24 as LpDisclosure,
@@ -284,31 +286,31 @@ export {
284
286
  _26 as LpDrawer,
285
287
  _27 as LpDropdownMenu,
286
288
  default9 as LpDropzone,
287
- _103 as LpEditable,
289
+ _104 as LpEditable,
288
290
  _28 as LpEmptyState,
289
291
  _30 as LpFieldNotice,
290
292
  _29 as LpFileTree,
291
293
  _31 as LpFormField,
292
- _88 as LpFrame,
293
- _91 as LpGauge,
294
- _117 as LpGlowBorder,
294
+ _89 as LpFrame,
295
+ _92 as LpGauge,
296
+ _118 as LpGlowBorder,
295
297
  _62 as LpHintExample,
296
- _81 as LpHintLayer,
297
- _82 as LpHints,
298
- _106 as LpHoverCard,
298
+ _82 as LpHintLayer,
299
+ _83 as LpHints,
300
+ _107 as LpHoverCard,
299
301
  _32 as LpIcon,
300
- _107 as LpIdenticon,
302
+ _108 as LpIdenticon,
301
303
  default10 as LpImageEditor,
302
304
  _33 as LpInfraNode,
303
305
  _34 as LpInput,
304
306
  default8 as LpKeys,
305
307
  _35 as LpLightbox,
306
308
  _36 as LpLink,
307
- _98 as LpLoader,
309
+ _99 as LpLoader,
308
310
  _37 as LpLogViewer,
309
- _118 as LpMagnetic,
311
+ _119 as LpMagnetic,
310
312
  _3 as LpMapPicker,
311
- _114 as LpMasonry,
313
+ _115 as LpMasonry,
312
314
  _38 as LpMediaGallery,
313
315
  _39 as LpModal,
314
316
  _40 as LpModalNav,
@@ -322,65 +324,65 @@ export {
322
324
  _17 as LpPalettePreview,
323
325
  default6 as LpPaletteStats,
324
326
  default7 as LpPaletteSwatches,
325
- _120 as LpPaletteTheme,
327
+ _121 as LpPaletteTheme,
326
328
  _47 as LpPasswordInput,
327
329
  _49 as LpPhoneInput,
328
330
  _48 as LpPie,
329
- _96 as LpPipeline,
331
+ _97 as LpPipeline,
330
332
  _50 as LpPopover,
331
333
  _51 as LpProgress,
332
- _92 as LpQuota,
334
+ _93 as LpQuota,
333
335
  _52 as LpRadio,
334
336
  _53 as LpRadioGroup,
335
337
  _23 as LpRangeCalendar,
336
- _66 as LpRangeSlider,
337
- _94 as LpRelativeTime,
338
- _90 as LpRepeater,
339
- _115 as LpRevealOnScroll,
338
+ _67 as LpRangeSlider,
339
+ _95 as LpRelativeTime,
340
+ _91 as LpRepeater,
341
+ _116 as LpRevealOnScroll,
340
342
  _54 as LpRollingText,
341
- _113 as LpScramble,
343
+ _114 as LpScramble,
342
344
  _55 as LpScrollArea,
343
- _101 as LpSecretField,
345
+ _102 as LpSecretField,
344
346
  _57 as LpSection,
345
347
  _56 as LpSegmented,
346
348
  _58 as LpSelect,
347
349
  _59 as LpServiceNode,
348
- _108 as LpShaderBackdrop,
350
+ _109 as LpShaderBackdrop,
349
351
  _60 as LpShift,
350
- _111 as LpShinyText,
352
+ _112 as LpShinyText,
351
353
  default11 as LpShortcutsDialog,
352
354
  _63 as LpSidebar,
353
- _64 as LpSkeleton,
354
- _65 as LpSlider,
355
- _68 as LpSparkline,
356
- _116 as LpSpotlight,
357
- _69 as LpStat,
358
- _93 as LpStatus,
359
- _97 as LpStepMark,
360
- _70 as LpStepper,
355
+ _65 as LpSkeleton,
356
+ _66 as LpSlider,
357
+ _69 as LpSparkline,
358
+ _117 as LpSpotlight,
359
+ _70 as LpStat,
360
+ _94 as LpStatus,
361
+ _98 as LpStepMark,
362
+ _71 as LpStepper,
361
363
  _61 as LpSwap,
362
- _71 as LpSwitch,
364
+ _72 as LpSwitch,
363
365
  default12 as LpTable,
364
- _72 as LpTableOfContents,
366
+ _73 as LpTableOfContents,
365
367
  default13 as LpTabs,
366
- _74 as LpTagsInput,
367
- _110 as LpTextReveal,
368
- _75 as LpTextarea,
369
- _86 as LpThemeProvider,
370
- _87 as LpThemeStudio,
371
- _89 as LpThemeStudioSection,
372
- _76 as LpThemeSwitcher,
373
- _77 as LpTilt,
368
+ _75 as LpTagsInput,
369
+ _111 as LpTextReveal,
370
+ _76 as LpTextarea,
371
+ _87 as LpThemeProvider,
372
+ _88 as LpThemeStudio,
373
+ _90 as LpThemeStudioSection,
374
+ _77 as LpThemeSwitcher,
375
+ _78 as LpTilt,
374
376
  _43 as LpTimeField,
375
- _78 as LpToaster,
376
- _79 as LpTooltip,
377
- _83 as LpTooltipProvider,
378
- _84 as LpTopologyCanvas,
379
- _80 as LpTour,
380
- _104 as LpTruncate,
381
- _112 as LpTypewriter,
382
- _85 as LpUptimeBar,
383
- _67 as LpVideoBackdrop,
377
+ _79 as LpToaster,
378
+ _80 as LpTooltip,
379
+ _84 as LpTooltipProvider,
380
+ _85 as LpTopologyCanvas,
381
+ _81 as LpTour,
382
+ _105 as LpTruncate,
383
+ _113 as LpTypewriter,
384
+ _86 as LpUptimeBar,
385
+ _68 as LpVideoBackdrop,
384
386
  N as NO_BACKDROP_ATTR,
385
387
  OSM_TILES,
386
388
  PALETTE_MODES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leavepulse/ui",
3
- "version": "0.73.4",
3
+ "version": "0.74.0",
4
4
  "type": "module",
5
5
  "description": "LeavePulse token-driven Vue component kit.",
6
6
  "repository": {
@@ -27,6 +27,7 @@ export const COMPONENT_NAMES = [
27
27
  "LpConfirmDialog",
28
28
  "LpConfirmHost",
29
29
  "LpContextMenu",
30
+ "LpContextSwitcher",
30
31
  "LpCopyButton",
31
32
  "LpCountdown",
32
33
  "LpDataList",
@@ -35,12 +35,13 @@ import { computed, onMounted, ref, useId, watch } from "vue"
35
35
  import { useLabels } from "../composables/useLpConfig"
36
36
  import { useScrollMemory } from "../composables/scrollMemory"
37
37
  import LpBottomNav from "./LpBottomNav.vue"
38
+ import LpContextSwitcher from "./LpContextSwitcher.vue"
38
39
  import LpButton from "./LpButton.vue"
39
40
  import LpIcon from "./LpIcon.vue"
40
41
  import LpScrollArea from "./LpScrollArea.vue"
41
42
  import LpShift from "./LpShift.vue"
42
43
  import LpSidebar from "./LpSidebar.vue"
43
- import type { SidebarItem, SidebarSection } from "./sidebar"
44
+ import type { AppContext, SidebarItem, SidebarSection } from "./sidebar"
44
45
  import { mergeClass, useMergedAttrs } from "../composables/useMergedClass"
45
46
 
46
47
  export type { SidebarItem, SidebarSection } from "./sidebar"
@@ -107,6 +108,14 @@ const props = withDefaults(
107
108
  previewDelay?: number
108
109
  /** The key that folds the rail; false turns it off. Forwarded to LpSidebar. */
109
110
  compactHotkey?: string | false
111
+ /**
112
+ * The app's contexts — a server, a workspace, a part of the product — each
113
+ * with its own rail. With more than one, the rail's header becomes the
114
+ * switcher between them (v-model:context), and the `logo` slot draws the
115
+ * switcher's face with the current `context` in its props.
116
+ */
117
+ contexts?: AppContext[]
118
+ context?: string
110
119
  /** Marks a non-production copy along the top of the rail. Forwarded to LpSidebar. */
111
120
  environment?: string | { label: string; tone?: "warning" | "danger" | "action" | "brand" }
112
121
  /** The app's hint system; rows with a `hintPage` show unread news as a dot. Forwarded to LpSidebar. */
@@ -134,8 +143,11 @@ const emit = defineEmits<{
134
143
  (e: "select", item: SidebarItem): void
135
144
  (e: "update:compact", value: boolean): void
136
145
  (e: "update:pinned", ids: string[]): void
146
+ (e: "update:context", id: string): void
137
147
  }>()
138
148
 
149
+ const switching = computed(() => (props.contexts?.length ?? 0) > 1)
150
+
139
151
  // Normalise items/sections to a flat list for the title computation.
140
152
  const flatItems = computed<SidebarItem[]>(() =>
141
153
  (props.sections ? props.sections.flatMap((s) => s.items) : (props.items ?? [])).flatMap((i) => [i, ...(i.children ?? [])]),
@@ -297,8 +309,20 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
297
309
  @update:collapsed="(v) => emit('update:collapsed', v)"
298
310
  @select="onSelect"
299
311
  >
300
- <template v-if="$slots.logo" #header="{ compact: folded }">
301
- <slot name="logo" :compact="folded" />
312
+ <template v-if="switching" #header="{ compact: folded }">
313
+ <LpContextSwitcher
314
+ :contexts="contexts!"
315
+ :model-value="context"
316
+ :compact="folded"
317
+ @update:model-value="(id) => emit('update:context', id)"
318
+ >
319
+ <template v-if="$slots.logo" #default="{ context: shown }">
320
+ <slot name="logo" :compact="folded" :context="shown" />
321
+ </template>
322
+ </LpContextSwitcher>
323
+ </template>
324
+ <template v-else-if="$slots.logo" #header="{ compact: folded }">
325
+ <slot name="logo" :compact="folded" :context="contexts?.[0]" />
302
326
  </template>
303
327
  <template v-if="$slots['sidebar-actions']" #actions>
304
328
  <slot name="sidebar-actions" />
@@ -1250,6 +1250,10 @@ function onRowClick(event: MouseEvent, c: Command) {
1250
1250
  }
1251
1251
 
1252
1252
  async function run(row: Command | undefined) {
1253
+ // A closing palette has already reset to its first rows, where the command
1254
+ // just run sits again as recent: an Enter that lands during the fade ran it
1255
+ // a second time (two snapshots for one pick).
1256
+ if (!props.open) return
1253
1257
  if (!row || row.disabled || executing.value) return
1254
1258
  const c = fallbackOf.get(row) ?? row
1255
1259
  // The highlight stays on the same index, which is now the first row that
@@ -0,0 +1,74 @@
1
+ <script setup lang="ts">
2
+ /*
3
+ * Which of an app's contexts the rail is showing — a server, a workspace, a
4
+ * machine, a part of the product — and the menu to change it. Every app used
5
+ * to draw this into the rail's header slot itself, each a little differently;
6
+ * LpAppShell renders it from its `contexts` prop, and a standalone LpSidebar
7
+ * can put it in its own header.
8
+ *
9
+ * The trigger shows the current context's icon and label, or whatever the
10
+ * default slot draws (a brand mark for one context, a plain word for
11
+ * another). Folded to icons it is the icon alone, or the label's first letter.
12
+ */
13
+ import { computed } from "vue"
14
+ import { useLabels } from "../composables/useLpConfig"
15
+ import LpDropdownMenu from "./LpDropdownMenu.vue"
16
+ import LpIcon from "./LpIcon.vue"
17
+ import type { ContextMenuItemDef } from "./LpContextMenu.vue"
18
+ import type { AppContext } from "./sidebar"
19
+
20
+ const props = withDefaults(
21
+ defineProps<{
22
+ contexts: AppContext[]
23
+ /** The current context's id (v-model). */
24
+ modelValue?: string
25
+ /** The icon rail: the trigger is a square with the icon or an initial. */
26
+ compact?: boolean
27
+ labels?: Partial<{ switch: string; title: string }>
28
+ }>(),
29
+ { compact: false },
30
+ )
31
+
32
+ const emit = defineEmits<{ (e: "update:modelValue", id: string): void }>()
33
+
34
+ const text = useLabels("contextSwitcher", { switch: "{label}, switch", title: "Switch to" }, () => props.labels)
35
+
36
+ const current = computed(() => props.contexts.find((c) => c.id === props.modelValue) ?? props.contexts[0])
37
+ const initial = computed(() => [...(current.value?.label ?? "")][0] ?? "")
38
+
39
+ const items = computed<ContextMenuItemDef[]>(() =>
40
+ props.contexts.map((c) => ({
41
+ label: c.label,
42
+ icon: c.icon,
43
+ checked: c.id === current.value?.id ? true : undefined,
44
+ onSelect: () => {
45
+ if (c.id !== current.value?.id) emit("update:modelValue", c.id)
46
+ },
47
+ })),
48
+ )
49
+ </script>
50
+
51
+ <template>
52
+ <LpDropdownMenu :items="items" :title="text.title" :palette="false">
53
+ <template #trigger>
54
+ <button
55
+ type="button"
56
+ class="flex min-w-0 items-center gap-2 rounded-control text-base font-semibold text-ink outline-none transition-colors hover:bg-surface-soft focus-visible:ring-2 focus-visible:ring-ring"
57
+ :class="compact ? 'grid size-9 shrink-0 place-items-center bg-surface-soft' : 'max-w-full px-2 py-1'"
58
+ :aria-label="text.switch.replace('{label}', current?.label ?? '')"
59
+ >
60
+ <slot :context="current" :compact="compact">
61
+ <template v-if="compact">
62
+ <LpIcon v-if="current?.icon" :name="current.icon" :size="17" />
63
+ <template v-else>{{ initial }}</template>
64
+ </template>
65
+ <template v-else>
66
+ <LpIcon v-if="current?.icon" :name="current.icon" :size="17" class="shrink-0 text-brand" />
67
+ <span class="truncate">{{ current?.label }}</span>
68
+ </template>
69
+ </slot>
70
+ <LpIcon v-if="!compact" name="lucide:chevrons-up-down" :size="14" class="shrink-0 text-muted" />
71
+ </button>
72
+ </template>
73
+ </LpDropdownMenu>
74
+ </template>
@@ -7,6 +7,13 @@
7
7
  import type { Component } from "vue"
8
8
  import type { ContextMenuItemDef } from "./LpContextMenu.vue"
9
9
 
10
+ /** One of an app's contexts in the rail header switcher (LpContextSwitcher). */
11
+ export interface AppContext {
12
+ id: string
13
+ label: string
14
+ icon?: string
15
+ }
16
+
10
17
  export interface SidebarItem {
11
18
  id: string
12
19
  label: string
@@ -1,5 +1,6 @@
1
1
  import { computed, useAttrs, type ComputedRef, type MaybeRefOrGetter, toValue } from "vue"
2
- import { cn } from "tailwind-variants"
2
+ import { extendTailwindMerge } from "tailwind-merge"
3
+ import { cx } from "tailwind-variants"
3
4
 
4
5
  /*
5
6
  * "The class the consumer passed should win."
@@ -23,6 +24,28 @@ import { cn } from "tailwind-variants"
23
24
  * the component said nothing about shadows.
24
25
  */
25
26
 
27
+ /*
28
+ * tailwind-merge knows Tailwind's own scales only, so the kit's named tokens
29
+ * (`rounded-card`, `shadow-panel`, `ease-emphasized`) looked like unrelated
30
+ * classes to it: `class="rounded-card"` on a box that rounds itself
31
+ * `rounded-control` kept both, and stylesheet order picked the control radius.
32
+ * Named here from tokens.css, so a consumer's token wins like any utility.
33
+ */
34
+ const kitMerge = extendTailwindMerge({
35
+ extend: {
36
+ theme: {
37
+ radius: ["control", "card", "pill", "inner", "small"],
38
+ shadow: ["panel"],
39
+ ease: ["emphasized", "travel", "settle"],
40
+ },
41
+ },
42
+ })
43
+
44
+ function merge(...classes: (string | undefined | null | false)[]): string {
45
+ const joined = cx(...classes)
46
+ return joined ? kitMerge(joined) : ""
47
+ }
48
+
26
49
  /**
27
50
  * Merges the component's own classes with the `class` attribute the consumer
28
51
  * passed, resolving Tailwind conflicts in the consumer's favour.
@@ -43,7 +66,7 @@ export function useMergedClass(
43
66
  const attrs = useAttrs()
44
67
  // `cn` returns undefined when everything it was given was empty; a class
45
68
  // binding is happier with "" than with undefined.
46
- return computed(() => cn(toValue(own), attrs.class as string | undefined) ?? "")
69
+ return computed(() => merge(toValue(own), attrs.class as string | undefined))
47
70
  }
48
71
 
49
72
  /**
@@ -82,5 +105,5 @@ export function useMergedAttrs(own: MaybeRefOrGetter<string | undefined>): {
82
105
  * component merging a class onto a child rather than its own root.
83
106
  */
84
107
  export function mergeClass(...classes: (string | undefined | null | false)[]): string {
85
- return cn(...classes) ?? ""
108
+ return merge(...classes)
86
109
  }
@@ -191,6 +191,10 @@ export function useShift(
191
191
  // its bar re-animated some sixty times a second).
192
192
  if (wantsHeight()) node.style.height = ""
193
193
  if (wantsWidth()) node.style.width = ""
194
+ // The content's own width, not the one the last tween held it at: left in
195
+ // place, the rounded-up figure fed back into the next measurement and the
196
+ // box grew a pixel a frame for good.
197
+ node.style.removeProperty(TARGET_WIDTH)
194
198
  const toHeight = wantsHeight() ? node.offsetHeight : 0
195
199
  const toWidth = wantsWidth() ? node.offsetWidth : 0
196
200
  // offsetWidth is rounded, and content that needs 406.4px wraps at 406.
package/src/index.ts CHANGED
@@ -202,6 +202,8 @@ export { default as LpHintExample } from "./components/LpHintExample.vue"
202
202
  export { default as LpShortcutsDialog } from "./components/LpShortcutsDialog.vue"
203
203
  export { default as LpSidebar } from "./components/LpSidebar.vue"
204
204
  export type { SidebarItem, SidebarSection } from "./components/LpSidebar.vue"
205
+ export type { AppContext } from "./components/sidebar"
206
+ export { default as LpContextSwitcher } from "./components/LpContextSwitcher.vue"
205
207
  export { default as LpSkeleton } from "./components/LpSkeleton.vue"
206
208
  export { default as LpSlider } from "./components/LpSlider.vue"
207
209
  export { default as LpRangeSlider } from "./components/LpRangeSlider.vue"