@leavepulse/ui 0.38.1 → 0.39.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/assets/{LpCommandPalette-BV6-oPGZ.css → LpCommandPalette-DKL4O4JP.css} +5 -5
  2. package/dist/assets/{LpShortcutsDialog-B7H-RZLQ.css → LpShortcutsDialog-Cap_ALQM.css} +7 -7
  3. package/dist/chunks/{LpAddressInput-DOVpUONY.js → LpAddressInput-H5NdhK6c.js} +1 -1
  4. package/dist/chunks/{LpAppShell-CRDw1Du5.js → LpAppShell-BfqSA1ba.js} +1 -1
  5. package/dist/chunks/{LpConfirmDialog-BH7az_E7.js → LpConfirmDialog-BBRnMkV4.js} +5 -3
  6. package/dist/chunks/{LpDrawer-E67O8fFP.js → LpDrawer-B8vBnCWF.js} +9 -7
  7. package/dist/chunks/{LpModal-Dq_qXN8-.js → LpModal-EyEdRuqF.js} +6 -5
  8. package/dist/chunks/{LpModalNav-CqxXbTkl.js → LpModalNav-BrDtP7b_.js} +6 -4
  9. package/dist/chunks/{LpSidebar-B1wLj-B2.js → LpSidebar-DZ20iBxY.js} +1 -1
  10. package/dist/components/LpAddressInput.vue.js +1 -1
  11. package/dist/components/LpAppShell.vue.js +1 -1
  12. package/dist/components/LpCommandPalette.vue.d.ts +12 -0
  13. package/dist/components/LpCommandPalette.vue.js +12 -6
  14. package/dist/components/LpConfirmDialog.vue.d.ts +2 -0
  15. package/dist/components/LpConfirmDialog.vue.js +1 -1
  16. package/dist/components/LpDrawer.vue.d.ts +13 -0
  17. package/dist/components/LpDrawer.vue.js +1 -1
  18. package/dist/components/LpModal.vue.d.ts +7 -0
  19. package/dist/components/LpModal.vue.js +1 -1
  20. package/dist/components/LpModalNav.vue.d.ts +2 -0
  21. package/dist/components/LpModalNav.vue.js +1 -1
  22. package/dist/components/LpShortcutsDialog.vue.d.ts +2 -0
  23. package/dist/components/LpShortcutsDialog.vue.js +7 -5
  24. package/dist/components/LpSidebar.vue.js +1 -1
  25. package/dist/index.js +7 -7
  26. package/package.json +1 -1
  27. package/src/components/LpCommandPalette.vue +18 -2
  28. package/src/components/LpConfirmDialog.vue +3 -0
  29. package/src/components/LpDrawer.vue +11 -1
  30. package/src/components/LpModal.vue +10 -3
  31. package/src/components/LpModalNav.vue +3 -0
  32. package/src/components/LpShortcutsDialog.vue +3 -0
  33. package/src/tokens/tokens.css +21 -7
@@ -1,21 +1,21 @@
1
1
 
2
- .lp-cmd-excerpt[data-v-cc5b91ea] {
2
+ .lp-cmd-excerpt[data-v-fcb38dbd] {
3
3
  display: -webkit-box;
4
4
  -webkit-box-orient: vertical;
5
5
  overflow: hidden;
6
6
  }
7
7
  /* Each result eases in; honours reduced motion. */
8
- .lp-cmd-item[data-v-cc5b91ea] {
9
- animation: lp-cmd-in-cc5b91ea 140ms ease-out both;
8
+ .lp-cmd-item[data-v-fcb38dbd] {
9
+ animation: lp-cmd-in-fcb38dbd 140ms ease-out both;
10
10
  }
11
- @keyframes lp-cmd-in-cc5b91ea {
11
+ @keyframes lp-cmd-in-fcb38dbd {
12
12
  from {
13
13
  opacity: 0;
14
14
  transform: translateY(3px);
15
15
  }
16
16
  }
17
17
  @media (prefers-reduced-motion: reduce) {
18
- .lp-cmd-item[data-v-cc5b91ea] {
18
+ .lp-cmd-item[data-v-fcb38dbd] {
19
19
  animation: none;
20
20
  }
21
21
  }
@@ -1,12 +1,12 @@
1
1
 
2
2
  /* Two columns where there is room: the list is long enough that one column
3
3
  pushes the last group off a laptop screen. */
4
- .shortcut-groups[data-v-d86b724b] {
4
+ .shortcut-groups[data-v-7b1cdfeb] {
5
5
  display: grid;
6
6
  gap: 18px;
7
7
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
8
8
  }
9
- .shortcut-group h3[data-v-d86b724b] {
9
+ .shortcut-group h3[data-v-7b1cdfeb] {
10
10
  margin: 0 0 6px;
11
11
  color: var(--color-muted);
12
12
  font-size: 0.7rem;
@@ -14,31 +14,31 @@
14
14
  letter-spacing: 0.06em;
15
15
  text-transform: uppercase;
16
16
  }
17
- .shortcut-group dl[data-v-d86b724b] {
17
+ .shortcut-group dl[data-v-7b1cdfeb] {
18
18
  display: flex;
19
19
  flex-direction: column;
20
20
  gap: 1px;
21
21
  margin: 0;
22
22
  }
23
- .shortcut-row[data-v-d86b724b] {
23
+ .shortcut-row[data-v-7b1cdfeb] {
24
24
  display: flex;
25
25
  align-items: center;
26
26
  justify-content: space-between;
27
27
  gap: 12px;
28
28
  padding: 5px 0;
29
29
  }
30
- .shortcut-row dt[data-v-d86b724b] {
30
+ .shortcut-row dt[data-v-7b1cdfeb] {
31
31
  color: var(--color-ink);
32
32
  font-size: 0.82rem;
33
33
  min-width: 0;
34
34
  }
35
- .shortcut-row dd[data-v-d86b724b] {
35
+ .shortcut-row dd[data-v-7b1cdfeb] {
36
36
  display: flex;
37
37
  flex-shrink: 0;
38
38
  gap: 3px;
39
39
  margin: 0;
40
40
  }
41
- kbd[data-v-d86b724b] {
41
+ kbd[data-v-7b1cdfeb] {
42
42
  display: inline-flex;
43
43
  min-width: 20px;
44
44
  align-items: center;
@@ -6,7 +6,7 @@ import { _ as _sfc_main$3 } from "./LpButton-5VXXCjwF.js";
6
6
  import { _ as _sfc_main$1 } from "./LpFormField-CazBc2-a.js";
7
7
  import { _ as _sfc_main$4 } from "./LpIcon-CCnX5_2j.js";
8
8
  import { _ as _sfc_main$5 } from "./LpInput-9EXWUM9B.js";
9
- import { _ as _sfc_main$7 } from "./LpModal-Dq_qXN8-.js";
9
+ import { _ as _sfc_main$7 } from "./LpModal-EyEdRuqF.js";
10
10
  import { _ as _sfc_main$6 } from "./LpSelect-ANIVtrpM.js";
11
11
  import { useMergedAttrs } from "../composables/useMergedClass.js";
12
12
  const _hoisted_1 = { class: "flex items-stretch gap-2" };
@@ -3,7 +3,7 @@ import { _ as _sfc_main$2 } from "./LpButton-5VXXCjwF.js";
3
3
  import { _ as _sfc_main$3 } from "./LpIcon-CCnX5_2j.js";
4
4
  import { _ as _sfc_main$5 } from "./LpScrollArea-D8xyKXcF.js";
5
5
  import { _ as _sfc_main$4 } from "./LpShift-DXNsauQZ.js";
6
- import { _ as _sfc_main$1 } from "./LpSidebar-B1wLj-B2.js";
6
+ import { _ as _sfc_main$1 } from "./LpSidebar-DZ20iBxY.js";
7
7
  import { useMergedAttrs } from "../composables/useMergedClass.js";
8
8
  const _hoisted_1 = { class: "flex min-w-0 flex-1 flex-col" };
9
9
  const _hoisted_2 = { class: "relative flex h-16 shrink-0 items-center gap-3 border-b border-line bg-surface px-4 md:px-6" };
@@ -1,7 +1,7 @@
1
1
  import { defineComponent, computed, openBlock, createBlock, createSlots, withCtx, renderSlot, createVNode, createTextVNode, toDisplayString, createElementVNode, normalizeClass } from "vue";
2
2
  import { _ as _sfc_main$2 } from "./LpButton-5VXXCjwF.js";
3
3
  import { _ as _sfc_main$3 } from "./LpIcon-CCnX5_2j.js";
4
- import { _ as _sfc_main$1 } from "./LpModal-Dq_qXN8-.js";
4
+ import { _ as _sfc_main$1 } from "./LpModal-EyEdRuqF.js";
5
5
  const _hoisted_1 = { class: "inline-flex items-center gap-2.5" };
6
6
  const _sfc_main = /* @__PURE__ */ defineComponent({
7
7
  __name: "LpConfirmDialog",
@@ -14,7 +14,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
14
14
  danger: { type: Boolean },
15
15
  icon: {},
16
16
  loading: { type: Boolean },
17
- loadingLabel: { default: "Working…" }
17
+ loadingLabel: { default: "Working…" },
18
+ glass: { type: Boolean }
18
19
  },
19
20
  emits: ["update:open", "confirm", "cancel"],
20
21
  setup(__props, { emit: __emit }) {
@@ -34,6 +35,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
34
35
  }
35
36
  return (_ctx, _cache) => {
36
37
  return openBlock(), createBlock(_sfc_main$1, {
38
+ glass: __props.glass,
37
39
  open: __props.open,
38
40
  title: __props.title,
39
41
  description: __props.description,
@@ -86,7 +88,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
86
88
  ]),
87
89
  key: "0"
88
90
  } : void 0
89
- ]), 1032, ["open", "title", "description"]);
91
+ ]), 1032, ["glass", "open", "title", "description"]);
90
92
  };
91
93
  }
92
94
  });
@@ -1,4 +1,4 @@
1
- import { ref, defineComponent, computed, watch, onBeforeUnmount, useSlots, openBlock, createElementBlock, Fragment, withDirectives, createElementVNode, normalizeStyle, unref, vShow, normalizeClass, toDisplayString, createCommentVNode, renderSlot, createVNode, withCtx, createBlock, createTextVNode } from "vue";
1
+ import { ref, defineComponent, computed, watch, onBeforeUnmount, useSlots, openBlock, createElementBlock, Fragment, withDirectives, createElementVNode, normalizeStyle, unref, normalizeClass, vShow, toDisplayString, createCommentVNode, renderSlot, createVNode, withCtx, createBlock, createTextVNode } from "vue";
2
2
  import { DrawerRoot, DrawerPortal, DrawerOverlay, DrawerContent, DrawerHandle, DrawerTitle, DrawerDescription, DrawerClose } from "vaul-vue";
3
3
  import { CLOSE_ICON } from "../components/dropdown.js";
4
4
  import { _ as _sfc_main$1 } from "./LpIcon-CCnX5_2j.js";
@@ -36,7 +36,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
36
36
  edgeOpenThreshold: { default: 0.3 },
37
37
  resizable: { type: Boolean },
38
38
  minSize: { default: "22rem" },
39
- maxSize: { default: "96vw" }
39
+ maxSize: { default: "96vw" },
40
+ glass: { type: Boolean, default: false }
40
41
  },
41
42
  emits: ["update:open"],
42
43
  setup(__props, { emit: __emit }) {
@@ -51,7 +52,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
51
52
  watch(() => props.open, (open) => open ? claim() : release(), { immediate: true });
52
53
  onBeforeUnmount(release);
53
54
  const contentClass = computed(() => {
54
- const base = "fixed z-(--z-modal) flex flex-col border-line bg-surface-raised shadow-panel outline-none";
55
+ const surface = props.glass ? "lp-glass-overlay" : "bg-surface-raised";
56
+ const base = `fixed z-(--z-modal) flex flex-col border-line ${surface} shadow-panel outline-none`;
55
57
  const map = {
56
58
  left: "inset-y-0 left-0 border-r rounded-r-card",
57
59
  right: "inset-y-0 right-0 border-l rounded-l-card",
@@ -266,9 +268,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
266
268
  return openBlock(), createElementBlock(Fragment, null, [
267
269
  __props.edgeOpen && isHorizontal.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
268
270
  withDirectives(createElementVNode("div", {
269
- class: "lp-scrim pointer-events-none fixed inset-0",
271
+ class: normalizeClass(["lp-scrim pointer-events-none fixed inset-0", { "lp-scrim--light": __props.glass }]),
270
272
  style: normalizeStyle({ zIndex: unref(layer).scrim, opacity: peek.value, transition: peeking.value || reducedMotion() ? "none" : "opacity 0.18s ease-out" })
271
- }, null, 4), [
273
+ }, null, 6), [
272
274
  [vShow, peeking.value || peek.value > 0]
273
275
  ]),
274
276
  withDirectives(createElementVNode("div", {
@@ -307,8 +309,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
307
309
  default: withCtx(() => [
308
310
  createVNode(unref(DrawerOverlay), {
309
311
  style: normalizeStyle({ zIndex: unref(layer).scrim }),
310
- 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]"
311
- }, null, 8, ["style"]),
312
+ class: normalizeClass(["lp-scrim fixed inset-0 z-(--z-overlay) data-[state=open]:animate-[fade-in_150ms_ease] data-[state=closed]:animate-[fade-out_130ms_ease]", { "lp-scrim--light": __props.glass }])
313
+ }, null, 8, ["style", "class"]),
312
314
  createVNode(unref(DrawerContent), {
313
315
  class: normalizeClass(contentClass.value),
314
316
  style: normalizeStyle([sizeStyle.value, { zIndex: unref(layer).panel }]),
@@ -1,4 +1,4 @@
1
- import { defineComponent, computed, useSlots, watch, onBeforeUnmount, ref, openBlock, createBlock, unref, withCtx, createVNode, normalizeStyle, createElementVNode, mergeProps, createElementBlock, renderSlot, createCommentVNode, normalizeClass, createTextVNode, toDisplayString } from "vue";
1
+ import { defineComponent, computed, useSlots, watch, onBeforeUnmount, ref, openBlock, createBlock, unref, withCtx, createVNode, normalizeStyle, normalizeClass, createElementVNode, mergeProps, createElementBlock, renderSlot, createCommentVNode, createTextVNode, toDisplayString } from "vue";
2
2
  import { DialogRoot, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription, DialogClose } from "reka-ui";
3
3
  import { useShift } from "../composables/useShift.js";
4
4
  import { useModalLayer } from "../components/modalDepth.js";
@@ -33,7 +33,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
33
33
  size: { default: "md" },
34
34
  width: {},
35
35
  fillBody: { type: Boolean },
36
- autoFocus: { default: "auto" }
36
+ autoFocus: { default: "auto" },
37
+ glass: { type: Boolean, default: false }
37
38
  },
38
39
  emits: ["update:open"],
39
40
  setup(__props) {
@@ -123,9 +124,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
123
124
  createVNode(unref(DialogPortal), null, {
124
125
  default: withCtx(() => [
125
126
  createVNode(unref(DialogOverlay), {
126
- class: "lp-scrim fixed inset-0 data-[state=open]:animate-[fade-in_var(--duration-medium)_var(--ease-emphasized)] data-[state=closed]:animate-[fade-out_120ms_ease]",
127
+ class: normalizeClass([{ "lp-scrim--light": __props.glass }, "lp-scrim fixed inset-0 data-[state=open]:animate-[fade-in_var(--duration-medium)_var(--ease-emphasized)] data-[state=closed]:animate-[fade-out_120ms_ease]"]),
127
128
  style: normalizeStyle({ zIndex: unref(layer).scrim })
128
- }, null, 8, ["style"]),
129
+ }, null, 8, ["class", "style"]),
129
130
  createElementVNode("div", {
130
131
  class: "fixed inset-0 flex items-center justify-center pointer-events-none",
131
132
  style: normalizeStyle({ zIndex: unref(layer).panel })
@@ -145,7 +146,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
145
146
  createElementVNode("div", {
146
147
  ref_key: "panelRef",
147
148
  ref: panelRef,
148
- class: normalizeClass(["flex max-h-[min(90vh,calc(100dvh-2rem))] min-h-0 flex-col overflow-hidden rounded-card border border-line bg-surface-raised shadow-panel", [widthClass.value, unref(tweening) ? "transition-[height] duration-fast ease-[var(--ease-emphasized)] motion-reduce:transition-none" : ""]]),
149
+ class: normalizeClass(["flex max-h-[min(90vh,calc(100dvh-2rem))] min-h-0 flex-col overflow-hidden rounded-card border border-line shadow-panel", [__props.glass ? "lp-glass-overlay" : "bg-surface-raised", widthClass.value, unref(tweening) ? "transition-[height] duration-fast ease-[var(--ease-emphasized)] motion-reduce:transition-none" : ""]]),
149
150
  style: normalizeStyle({ ...__props.width ? { width: __props.width } : {}, ...hasAside.value ? { alignSelf: "stretch" } : {} })
150
151
  }, [
151
152
  __props.title || _ctx.$slots.title ? (openBlock(), createElementBlock("header", _hoisted_2, [
@@ -1,8 +1,8 @@
1
1
  import { defineComponent, ref, computed, openBlock, createBlock, createSlots, withCtx, renderSlot, createElementVNode, normalizeClass, createVNode, createCommentVNode, createTextVNode, toDisplayString } from "vue";
2
2
  import { _ as _sfc_main$2 } from "./LpButton-5VXXCjwF.js";
3
3
  import { _ as _sfc_main$3 } from "./LpIcon-CCnX5_2j.js";
4
- import { _ as _sfc_main$1 } from "./LpModal-Dq_qXN8-.js";
5
- import { _ as _sfc_main$4 } from "./LpSidebar-B1wLj-B2.js";
4
+ import { _ as _sfc_main$1 } from "./LpModal-EyEdRuqF.js";
5
+ import { _ as _sfc_main$4 } from "./LpSidebar-DZ20iBxY.js";
6
6
  const _hoisted_1 = { class: "flex items-center gap-2" };
7
7
  const _sfc_main = /* @__PURE__ */ defineComponent({
8
8
  __name: "LpModalNav",
@@ -22,7 +22,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
22
22
  mobileBreakpoint: { default: "lg" },
23
23
  navLabel: { default: "Sections" },
24
24
  navSide: { default: "start" },
25
- fillBody: { type: Boolean }
25
+ fillBody: { type: Boolean },
26
+ glass: { type: Boolean }
26
27
  },
27
28
  emits: ["update:open", "update:modelValue"],
28
29
  setup(__props, { emit: __emit }) {
@@ -59,6 +60,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
59
60
  const railStyle = { maxHeight: "min(90vh, calc(100dvh - 2rem))" };
60
61
  return (_ctx, _cache) => {
61
62
  return openBlock(), createBlock(_sfc_main$1, {
63
+ glass: __props.glass,
62
64
  open: __props.open,
63
65
  title: __props.title,
64
66
  description: __props.description,
@@ -157,7 +159,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
157
159
  ]),
158
160
  key: "2"
159
161
  } : void 0
160
- ]), 1032, ["open", "title", "description", "size", "width", "fill-body"]);
162
+ ]), 1032, ["glass", "open", "title", "description", "size", "width", "fill-body"]);
161
163
  };
162
164
  }
163
165
  });
@@ -1,5 +1,5 @@
1
1
  import { defineComponent, computed, ref, useId, openBlock, createElementBlock, Fragment, createElementVNode, mergeProps, normalizeClass, renderSlot, createCommentVNode, createBlock, createVNode, createSlots, withCtx, normalizeProps, guardReactiveProps } from "vue";
2
- import { _ as _sfc_main$3 } from "./LpDrawer-E67O8fFP.js";
2
+ import { _ as _sfc_main$3 } from "./LpDrawer-B8vBnCWF.js";
3
3
  import { _ as _sfc_main$1 } from "./LpInput-9EXWUM9B.js";
4
4
  import { _ as _sfc_main$2 } from "./LpSidebarNav-llOcs_Fn.js";
5
5
  const _hoisted_1 = {
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpAddressInput-DOVpUONY.js";
1
+ import { _ as _sfc_main } from "../chunks/LpAddressInput-H5NdhK6c.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpAppShell-CRDw1Du5.js";
1
+ import { _ as _sfc_main } from "../chunks/LpAppShell-BfqSA1ba.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -149,8 +149,19 @@ type __VLS_Props = {
149
149
  * pane lets it be read without leaving the page. Opt-in and fixed while
150
150
  * open, so the dialog does not change width as results come and go.
151
151
  * Hidden below the md breakpoint, where there is no room beside the list.
152
+ *
153
+ * The dialog takes one width class or the other, never a base width plus
154
+ * an `md:` override: a consumer's Tailwind build can emit the two in an
155
+ * order where the base wins, and the pane opened squeezed into 40rem.
156
+ * Both widths are 92vw on a narrow screen, so no breakpoint is needed.
152
157
  */
153
158
  previewPane?: boolean;
159
+ /**
160
+ * Frosted glass instead of a solid panel: the page shows through,
161
+ * blurred, under a lighter scrim (.lp-glass-overlay, .lp-scrim--light).
162
+ * A look, not a behaviour, so it is the app's call and off by default.
163
+ */
164
+ glass?: boolean;
154
165
  };
155
166
  type __VLS_Slots = {
156
167
  /** Body of the preview pane for the highlighted command. */
@@ -170,6 +181,7 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {},
170
181
  placeholder: string;
171
182
  emptyText: string;
172
183
  minChars: number;
184
+ glass: boolean;
173
185
  debounce: number;
174
186
  recentLimit: number;
175
187
  hotkey: boolean;
@@ -1,4 +1,4 @@
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";
1
+ import { defineComponent, computed, ref, useId, shallowRef, watch, nextTick, openBlock, createBlock, unref, withCtx, createVNode, normalizeClass, withModifiers, 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";
@@ -6,7 +6,7 @@ import { useAsyncTask } from "../composables/useAsyncTask.js";
6
6
  import { useClipboard } from "../composables/useClipboard.js";
7
7
  import { useHotkeys } from "../composables/useHotkeys.js";
8
8
  import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
9
- import '../assets/LpCommandPalette-BV6-oPGZ.css';const _hoisted_1 = { class: "flex shrink-0 items-center gap-2.5 border-b border-line px-4" };
9
+ import '../assets/LpCommandPalette-DKL4O4JP.css';const _hoisted_1 = { class: "flex shrink-0 items-center gap-2.5 border-b border-line px-4" };
10
10
  const _hoisted_2 = ["title", "onClick"];
11
11
  const _hoisted_3 = ["aria-activedescendant", "placeholder", "onKeydown"];
12
12
  const _hoisted_4 = { class: "flex min-h-0 flex-1" };
@@ -94,7 +94,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
94
94
  excerptLines: { default: 4 },
95
95
  groupLimit: { default: 0 },
96
96
  layoutFallback: { type: Boolean, default: true },
97
- previewPane: { type: Boolean, default: false }
97
+ previewPane: { type: Boolean, default: false },
98
+ glass: { type: Boolean, default: false }
98
99
  },
99
100
  emits: ["update:open", "select", "run"],
100
101
  setup(__props, { emit: __emit }) {
@@ -479,9 +480,14 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
479
480
  default: withCtx(() => [
480
481
  createVNode(unref(DialogPortal), null, {
481
482
  default: withCtx(() => [
482
- createVNode(unref(DialogOverlay), { class: "lp-scrim fixed inset-0 z-(--z-overlay) data-[state=open]:animate-[fade-in_150ms_ease] data-[state=closed]:animate-[fade-out_130ms_ease]" }),
483
+ createVNode(unref(DialogOverlay), {
484
+ class: normalizeClass([{ "lp-scrim--light": __props.glass }, "lp-scrim fixed inset-0 z-(--z-overlay) data-[state=open]:animate-[fade-in_150ms_ease] data-[state=closed]:animate-[fade-out_130ms_ease]"])
485
+ }, null, 8, ["class"]),
483
486
  createVNode(unref(DialogContent), {
484
- class: normalizeClass(["fixed left-1/2 top-[12vh] z-(--z-modal) flex max-h-[76vh] w-[min(92vw,40rem)] -translate-x-1/2 flex-col overflow-hidden rounded-card border border-line lp-glass-overlay shadow-panel outline-none data-[state=open]:animate-[pop-in_160ms_var(--ease-emphasized)] data-[state=closed]:animate-[pop-out_130ms_ease]", __props.previewPane && "md:h-[min(76vh,36rem)] md:w-[min(92vw,64rem)]"]),
487
+ class: normalizeClass(["fixed left-1/2 top-[12vh] z-(--z-modal) flex max-h-[76vh] -translate-x-1/2 flex-col overflow-hidden rounded-card border border-line shadow-panel outline-none data-[state=open]:animate-[pop-in_160ms_var(--ease-emphasized)] data-[state=closed]:animate-[pop-out_130ms_ease]", [
488
+ __props.glass ? "lp-glass-overlay" : "bg-surface-raised",
489
+ __props.previewPane ? "w-[min(92vw,64rem)] md:h-[min(76vh,36rem)]" : "w-[min(92vw,40rem)]"
490
+ ]]),
485
491
  onOpenAutoFocus: _cache[2] || (_cache[2] = withModifiers(() => {
486
492
  }, ["prevent"]))
487
493
  }, {
@@ -755,7 +761,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
755
761
  };
756
762
  }
757
763
  });
758
- const LpCommandPalette = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-cc5b91ea"]]);
764
+ const LpCommandPalette = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-fcb38dbd"]]);
759
765
  export {
760
766
  LpCommandPalette as default
761
767
  };
@@ -28,6 +28,8 @@ type __VLS_Props = {
28
28
  */
29
29
  loading?: boolean;
30
30
  loadingLabel?: string;
31
+ /** Frosted glass panel, passed to LpModal. Off by default. */
32
+ glass?: boolean;
31
33
  };
32
34
  declare var __VLS_16: {};
33
35
  type __VLS_Slots = {} & {
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpConfirmDialog-BH7az_E7.js";
1
+ import { _ as _sfc_main } from "../chunks/LpConfirmDialog-BBRnMkV4.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -63,6 +63,12 @@ declare const __VLS_export: __VLS_WithSlots<import('vue').DefineComponent<{
63
63
  minSize?: string;
64
64
  /** Max main-axis size while resizing (any CSS length). Default 96vw. */
65
65
  maxSize?: string;
66
+ /**
67
+ * Frosted glass instead of a solid panel: the page shows through,
68
+ * blurred, under a lighter scrim (.lp-glass-overlay, .lp-scrim--light).
69
+ * A look, not a behaviour, so it is the app's call and off by default.
70
+ */
71
+ glass?: boolean;
66
72
  }, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {} & {
67
73
  "update:open": (value: boolean) => any;
68
74
  }, string, import('vue').PublicProps, Readonly<{
@@ -128,11 +134,18 @@ declare const __VLS_export: __VLS_WithSlots<import('vue').DefineComponent<{
128
134
  minSize?: string;
129
135
  /** Max main-axis size while resizing (any CSS length). Default 96vw. */
130
136
  maxSize?: string;
137
+ /**
138
+ * Frosted glass instead of a solid panel: the page shows through,
139
+ * blurred, under a lighter scrim (.lp-glass-overlay, .lp-scrim--light).
140
+ * A look, not a behaviour, so it is the app's call and off by default.
141
+ */
142
+ glass?: boolean;
131
143
  }> & Readonly<{
132
144
  "onUpdate:open"?: ((value: boolean) => any) | undefined;
133
145
  }>, {
134
146
  size: "sm" | "md" | "lg" | "xl";
135
147
  side: "left" | "right";
148
+ glass: boolean;
136
149
  dismissible: boolean;
137
150
  noDragControls: boolean;
138
151
  edgeSize: number;
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpDrawer-E67O8fFP.js";
1
+ import { _ as _sfc_main } from "../chunks/LpDrawer-B8vBnCWF.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -25,6 +25,12 @@ type __VLS_Props = {
25
25
  * the element yourself.
26
26
  */
27
27
  autoFocus?: "auto" | "none" | string;
28
+ /**
29
+ * Frosted glass instead of a solid panel: the page shows through,
30
+ * blurred, under a lighter scrim (.lp-glass-overlay, .lp-scrim--light).
31
+ * A look, not a behaviour, so it is the app's call and off by default.
32
+ */
33
+ glass?: boolean;
28
34
  };
29
35
  declare var __VLS_31: {}, __VLS_39: {}, __VLS_58: {}, __VLS_66: {}, __VLS_68: {}, __VLS_70: {};
30
36
  type __VLS_Slots = {} & {
@@ -47,6 +53,7 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {},
47
53
  }>, {
48
54
  size: "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "full";
49
55
  autoFocus: "auto" | "none" | string;
56
+ glass: boolean;
50
57
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
51
58
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
52
59
  declare const _default: typeof __VLS_export;
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpModal-Dq_qXN8-.js";
1
+ import { _ as _sfc_main } from "../chunks/LpModal-EyEdRuqF.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -31,6 +31,8 @@ type __VLS_Props = {
31
31
  * column inside the body, and the rail is no longer in the body.
32
32
  */
33
33
  fillBody?: boolean;
34
+ /** Frosted glass panel, passed to LpModal. Off by default. */
35
+ glass?: boolean;
34
36
  };
35
37
  declare var __VLS_36: {}, __VLS_38: {}, __VLS_41: {};
36
38
  type __VLS_Slots = {} & {
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpModalNav-CqxXbTkl.js";
1
+ import { _ as _sfc_main } from "../chunks/LpModalNav-BrDtP7b_.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -12,6 +12,8 @@ type __VLS_Props = {
12
12
  groups: ShortcutGroup[];
13
13
  title?: string;
14
14
  description?: string;
15
+ /** Frosted glass panel, passed to LpModal. Off by default. */
16
+ glass?: boolean;
15
17
  };
16
18
  declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {} & {
17
19
  "update:open": (value: boolean) => any;
@@ -1,15 +1,16 @@
1
1
  import { defineComponent, openBlock, createBlock, withCtx, createElementVNode, createElementBlock, Fragment, renderList, toDisplayString, unref } from "vue";
2
- import { _ as _sfc_main$1 } from "../chunks/LpModal-Dq_qXN8-.js";
2
+ import { _ as _sfc_main$1 } from "../chunks/LpModal-EyEdRuqF.js";
3
3
  import { appleModifierLabel } from "../composables/useHotkeys.js";
4
4
  import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
5
- import '../assets/LpShortcutsDialog-B7H-RZLQ.css';const _hoisted_1 = { class: "shortcut-groups" };
5
+ import '../assets/LpShortcutsDialog-Cap_ALQM.css';const _hoisted_1 = { class: "shortcut-groups" };
6
6
  const _sfc_main = /* @__PURE__ */ defineComponent({
7
7
  __name: "LpShortcutsDialog",
8
8
  props: {
9
9
  open: { type: Boolean, default: false },
10
10
  groups: {},
11
11
  title: { default: "Keyboard shortcuts" },
12
- description: {}
12
+ description: {},
13
+ glass: { type: Boolean }
13
14
  },
14
15
  emits: ["update:open"],
15
16
  setup(__props, { emit: __emit }) {
@@ -17,6 +18,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
17
18
  const emit = __emit;
18
19
  return (_ctx, _cache) => {
19
20
  return openBlock(), createBlock(_sfc_main$1, {
21
+ glass: props.glass,
20
22
  open: props.open,
21
23
  size: "lg",
22
24
  title: props.title,
@@ -53,11 +55,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
53
55
  ])
54
56
  ]),
55
57
  _: 1
56
- }, 8, ["open", "title", "description"]);
58
+ }, 8, ["glass", "open", "title", "description"]);
57
59
  };
58
60
  }
59
61
  });
60
- const LpShortcutsDialog = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-d86b724b"]]);
62
+ const LpShortcutsDialog = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-7b1cdfeb"]]);
61
63
  export {
62
64
  LpShortcutsDialog as default
63
65
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpSidebar-B1wLj-B2.js";
1
+ import { _ as _sfc_main } from "../chunks/LpSidebar-DZ20iBxY.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { _ } from "./chunks/LayoutCanvas-Dd20boUG.js";
2
- import { _ as _2 } from "./chunks/LpAddressInput-DOVpUONY.js";
2
+ import { _ as _2 } from "./chunks/LpAddressInput-H5NdhK6c.js";
3
3
  import { _ as _3 } from "./chunks/LpMapPicker-ul4oYno6.js";
4
4
  import { OSM_TILES, createNominatimProvider, emptyAddress, formatAddress, project, tileUrl, unproject } from "./components/address.js";
5
5
  import { _ as _4 } from "./chunks/LpActionBar-BlvtGB2P.js";
6
6
  import { _ as _5 } from "./chunks/LpAlert-BAYHDSS1.js";
7
- import { _ as _6 } from "./chunks/LpAppShell-CRDw1Du5.js";
7
+ import { _ as _6 } from "./chunks/LpAppShell-BfqSA1ba.js";
8
8
  import { _ as _7 } from "./chunks/LpAutocomplete-DExlIZe_.js";
9
9
  import { _ as _8 } from "./chunks/LpAvatar-B3-xAOH3.js";
10
10
  import { _ as _9 } from "./chunks/LpBadge-Fe0D1bYx.js";
@@ -19,12 +19,12 @@ import { _ as _15 } from "./chunks/LpCodeBlock-Bo0B4TFR.js";
19
19
  import { _ as _16 } from "./chunks/LpColorPicker-BlXea7LM.js";
20
20
  import { DEFAULT_SWATCHES, clampHsv, formatColor, hsvToHsl, hsvToRgb, isLight, normalizeHue, opaqueCss, parseColor, parseHex, rgbToHex, rgbToHsv } from "./components/color.js";
21
21
  import { default as default3 } from "./components/LpCommandPalette.vue.js";
22
- import { _ as _17 } from "./chunks/LpConfirmDialog-BH7az_E7.js";
22
+ import { _ as _17 } from "./chunks/LpConfirmDialog-BBRnMkV4.js";
23
23
  import { _ as _18 } from "./chunks/LpContextMenu-CwW1QXe_.js";
24
24
  import { _ as _19 } from "./chunks/LpDatePicker-CwOToxGq.js";
25
25
  import { _ as _20 } from "./chunks/LpDisclosure-ChQYmjlR.js";
26
26
  import { _ as _21 } from "./chunks/LpDivider-CUX46KR0.js";
27
- import { _ as _22 } from "./chunks/LpDrawer-E67O8fFP.js";
27
+ import { _ as _22 } from "./chunks/LpDrawer-B8vBnCWF.js";
28
28
  import { _ as _23 } from "./chunks/LpDropdownMenu-DJpoqRCb.js";
29
29
  import { default as default4 } from "./components/LpDropzone.vue.js";
30
30
  import { _ as _24 } from "./chunks/LpEmptyState-Dsdqz1Wj.js";
@@ -40,8 +40,8 @@ import { fileNameOf } from "./components/lightbox.js";
40
40
  import { _ as _31 } from "./chunks/LpLink-DCND-zyu.js";
41
41
  import { _ as _32 } from "./chunks/LpLogViewer-CaRy5MeS.js";
42
42
  import { _ as _33 } from "./chunks/LpMediaGallery-Cid7PGYx.js";
43
- import { _ as _34 } from "./chunks/LpModal-Dq_qXN8-.js";
44
- import { _ as _35 } from "./chunks/LpModalNav-CqxXbTkl.js";
43
+ import { _ as _34 } from "./chunks/LpModal-EyEdRuqF.js";
44
+ import { _ as _35 } from "./chunks/LpModalNav-BrDtP7b_.js";
45
45
  import { _ as _36 } from "./chunks/LpNotificationBell-DyGGsMAM.js";
46
46
  import { _ as _37 } from "./chunks/LpNumberField-CWuX08iX.js";
47
47
  import { _ as _38 } from "./chunks/LpTimeField-K6nAGboX.js";
@@ -64,7 +64,7 @@ import { _ as _53 } from "./chunks/LpSelect-ANIVtrpM.js";
64
64
  import { _ as _54 } from "./chunks/LpServiceNode-D8uJ8sGk.js";
65
65
  import { _ as _55 } from "./chunks/LpShift-DXNsauQZ.js";
66
66
  import { default as default6 } from "./components/LpShortcutsDialog.vue.js";
67
- import { _ as _56 } from "./chunks/LpSidebar-B1wLj-B2.js";
67
+ import { _ as _56 } from "./chunks/LpSidebar-DZ20iBxY.js";
68
68
  import { _ as _57 } from "./chunks/LpSkeleton-CTI-vBgW.js";
69
69
  import { _ as _58 } from "./chunks/LpSlider-BwNIBkAK.js";
70
70
  import { _ as _59 } from "./chunks/LpSparkline-C6xQ2dwv.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leavepulse/ui",
3
- "version": "0.38.1",
3
+ "version": "0.39.1",
4
4
  "type": "module",
5
5
  "description": "LeavePulse token-driven Vue component kit.",
6
6
  "repository": {
@@ -197,8 +197,19 @@ const props = withDefaults(
197
197
  * pane lets it be read without leaving the page. Opt-in and fixed while
198
198
  * open, so the dialog does not change width as results come and go.
199
199
  * Hidden below the md breakpoint, where there is no room beside the list.
200
+ *
201
+ * The dialog takes one width class or the other, never a base width plus
202
+ * an `md:` override: a consumer's Tailwind build can emit the two in an
203
+ * order where the base wins, and the pane opened squeezed into 40rem.
204
+ * Both widths are 92vw on a narrow screen, so no breakpoint is needed.
200
205
  */
201
206
  previewPane?: boolean
207
+ /**
208
+ * Frosted glass instead of a solid panel: the page shows through,
209
+ * blurred, under a lighter scrim (.lp-glass-overlay, .lp-scrim--light).
210
+ * A look, not a behaviour, so it is the app's call and off by default.
211
+ */
212
+ glass?: boolean
202
213
  }>(),
203
214
  {
204
215
  placeholder: "Type a command or search…",
@@ -213,6 +224,7 @@ const props = withDefaults(
213
224
  groupLimit: 0,
214
225
  layoutFallback: true,
215
226
  previewPane: false,
227
+ glass: false,
216
228
  },
217
229
  )
218
230
 
@@ -825,11 +837,15 @@ useHotkeys(() =>
825
837
  <DialogRoot :open="open" @update:open="(v) => emit('update:open', v)">
826
838
  <DialogPortal>
827
839
  <DialogOverlay
840
+ :class="{ 'lp-scrim--light': glass }"
828
841
  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]"
829
842
  />
830
843
  <DialogContent
831
- 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 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]"
832
- :class="previewPane && 'md:h-[min(76vh,36rem)] md:w-[min(92vw,64rem)]'"
844
+ class="fixed left-1/2 top-[12vh] z-(--z-modal) flex max-h-[76vh] -translate-x-1/2 flex-col overflow-hidden rounded-card border border-line shadow-panel outline-none data-[state=open]:animate-[pop-in_160ms_var(--ease-emphasized)] data-[state=closed]:animate-[pop-out_130ms_ease]"
845
+ :class="[
846
+ glass ? 'lp-glass-overlay' : 'bg-surface-raised',
847
+ previewPane ? 'w-[min(92vw,64rem)] md:h-[min(76vh,36rem)]' : 'w-[min(92vw,40rem)]',
848
+ ]"
833
849
  @open-auto-focus.prevent
834
850
  >
835
851
  <DialogTitle class="sr-only">Command palette</DialogTitle>
@@ -36,6 +36,8 @@ const props = withDefaults(
36
36
  */
37
37
  loading?: boolean
38
38
  loadingLabel?: string
39
+ /** Frosted glass panel, passed to LpModal. Off by default. */
40
+ glass?: boolean
39
41
  }>(),
40
42
  { confirmLabel: "Confirm", cancelLabel: "Cancel", loadingLabel: "Working…" },
41
43
  )
@@ -76,6 +78,7 @@ function cancel() {
76
78
 
77
79
  <template>
78
80
  <LpModal
81
+ :glass="glass"
79
82
  :open="open"
80
83
  :title="title"
81
84
  :description="description"
@@ -101,6 +101,12 @@ const props = withDefaults(
101
101
  minSize?: string
102
102
  /** Max main-axis size while resizing (any CSS length). Default 96vw. */
103
103
  maxSize?: string
104
+ /**
105
+ * Frosted glass instead of a solid panel: the page shows through,
106
+ * blurred, under a lighter scrim (.lp-glass-overlay, .lp-scrim--light).
107
+ * A look, not a behaviour, so it is the app's call and off by default.
108
+ */
109
+ glass?: boolean
104
110
  }>(),
105
111
  {
106
112
  side: "right",
@@ -111,6 +117,7 @@ const props = withDefaults(
111
117
  edgeOpenThreshold: 0.3,
112
118
  minSize: "22rem",
113
119
  maxSize: "96vw",
120
+ glass: false,
114
121
  },
115
122
  )
116
123
 
@@ -142,7 +149,8 @@ watch(() => props.open, (open) => (open ? claim() : release()), { immediate: tru
142
149
  onBeforeUnmount(release)
143
150
 
144
151
  const contentClass = computed(() => {
145
- const base = "fixed z-(--z-modal) flex flex-col border-line bg-surface-raised shadow-panel outline-none"
152
+ const surface = props.glass ? "lp-glass-overlay" : "bg-surface-raised"
153
+ const base = `fixed z-(--z-modal) flex flex-col border-line ${surface} shadow-panel outline-none`
146
154
  const map: Record<Direction, string> = {
147
155
  left: "inset-y-0 left-0 border-r rounded-r-card",
148
156
  right: "inset-y-0 right-0 border-l rounded-l-card",
@@ -431,6 +439,7 @@ const previewStyle = computed(() => {
431
439
  <div
432
440
  v-show="peeking || peek > 0"
433
441
  class="lp-scrim pointer-events-none fixed inset-0"
442
+ :class="{ 'lp-scrim--light': glass }"
434
443
  :style="{ zIndex: layer.scrim, opacity: peek, transition: peeking || reducedMotion() ? 'none' : 'opacity 0.18s ease-out' }"
435
444
  />
436
445
  <div
@@ -463,6 +472,7 @@ const previewStyle = computed(() => {
463
472
  <DrawerOverlay
464
473
  :style="{ zIndex: layer.scrim }"
465
474
  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]"
475
+ :class="{ 'lp-scrim--light': glass }"
466
476
  />
467
477
  <DrawerContent
468
478
  :class="contentClass"
@@ -62,8 +62,14 @@ const props = withDefaults(
62
62
  * the element yourself.
63
63
  */
64
64
  autoFocus?: "auto" | "none" | string
65
+ /**
66
+ * Frosted glass instead of a solid panel: the page shows through,
67
+ * blurred, under a lighter scrim (.lp-glass-overlay, .lp-scrim--light).
68
+ * A look, not a behaviour, so it is the app's call and off by default.
69
+ */
70
+ glass?: boolean
65
71
  }>(),
66
- { size: "md", autoFocus: "auto" },
72
+ { size: "md", autoFocus: "auto", glass: false },
67
73
  )
68
74
 
69
75
  defineEmits<{ (e: "update:open", value: boolean): void }>()
@@ -222,6 +228,7 @@ watch(
222
228
  <DialogRoot :open="open" @update:open="(v) => $emit('update:open', v)">
223
229
  <DialogPortal>
224
230
  <DialogOverlay
231
+ :class="{ 'lp-scrim--light': glass }"
225
232
  class="lp-scrim fixed inset-0 data-[state=open]:animate-[fade-in_var(--duration-medium)_var(--ease-emphasized)] data-[state=closed]:animate-[fade-out_120ms_ease]"
226
233
  :style="{ zIndex: layer.scrim }"
227
234
  />
@@ -290,8 +297,8 @@ watch(
290
297
  </aside>
291
298
  <div
292
299
  ref="panelRef"
293
- class="flex max-h-[min(90vh,calc(100dvh-2rem))] min-h-0 flex-col overflow-hidden rounded-card border border-line bg-surface-raised shadow-panel"
294
- :class="[widthClass, tweening ? 'transition-[height] duration-fast ease-[var(--ease-emphasized)] motion-reduce:transition-none' : '']"
300
+ class="flex max-h-[min(90vh,calc(100dvh-2rem))] min-h-0 flex-col overflow-hidden rounded-card border border-line shadow-panel"
301
+ :class="[glass ? 'lp-glass-overlay' : 'bg-surface-raised', widthClass, tweening ? 'transition-[height] duration-fast ease-[var(--ease-emphasized)] motion-reduce:transition-none' : '']"
295
302
  :style="{ ...(width ? { width } : {}), ...(hasAside ? { alignSelf: 'stretch' } : {}) }"
296
303
  >
297
304
  <header v-if="title || $slots.title" class="flex shrink-0 items-start justify-between gap-4 p-5 pb-3">
@@ -55,6 +55,8 @@ const props = withDefaults(
55
55
  * column inside the body, and the rail is no longer in the body.
56
56
  */
57
57
  fillBody?: boolean
58
+ /** Frosted glass panel, passed to LpModal. Off by default. */
59
+ glass?: boolean
58
60
  }>(),
59
61
  {
60
62
  size: "2xl",
@@ -125,6 +127,7 @@ const railStyle = { maxHeight: "min(90vh, calc(100dvh - 2rem))" }
125
127
 
126
128
  <template>
127
129
  <LpModal
130
+ :glass="glass"
128
131
  :open="open"
129
132
  :title="title"
130
133
  :description="description"
@@ -30,6 +30,8 @@ const props = withDefaults(
30
30
  groups: ShortcutGroup[]
31
31
  title?: string
32
32
  description?: string
33
+ /** Frosted glass panel, passed to LpModal. Off by default. */
34
+ glass?: boolean
33
35
  }>(),
34
36
  { open: false, title: "Keyboard shortcuts" },
35
37
  )
@@ -39,6 +41,7 @@ const emit = defineEmits<{ (e: "update:open", value: boolean): void }>()
39
41
 
40
42
  <template>
41
43
  <LpModal
44
+ :glass="props.glass"
42
45
  :open="props.open"
43
46
  size="lg"
44
47
  :title="props.title"
@@ -522,23 +522,37 @@
522
522
  --scrim-opacity: 0.78;
523
523
  }
524
524
 
525
+ /*
526
+ * Under a glass panel (.lp-glass-overlay) the usual tint leaves nothing but
527
+ * grey to see through it, so the scrim dims less and the panel's own blur
528
+ * does the separating. Without a working blur the no-backdrop rule above is
529
+ * more specific and puts the full tint back.
530
+ */
531
+ .lp-scrim--light {
532
+ --scrim-opacity: 0.32;
533
+ }
534
+
525
535
  /*
526
536
  * Frosted overlay: a floating panel that lets the page show through, softened.
527
537
  * For surfaces that float over everything and are read briefly (the command
528
538
  * palette), where a hint of what lies beneath says "this is on top, the page
529
- * is still there" better than a solid slab. The fill keeps enough opacity for
530
- * text to read without the blur; where the blur does not composite
531
- * (WebKitGTK, see above) the fill turns solid instead of leaving the page
532
- * readable through it.
539
+ * is still there" better than a solid slab. It sits on a dimmed scrim, so the
540
+ * fill has to be well below opaque for anything to show through at all — at
541
+ * 80 % the panel read as solid; the blur is what keeps text legible at 45 %.
542
+ * The faint top-down sheen is the edge light of glass. Where the blur does
543
+ * not composite (WebKitGTK, see above) the fill turns solid instead of
544
+ * leaving the page readable through it.
533
545
  */
534
546
  .lp-glass-overlay {
535
- background-color: color-mix(in srgb, var(--color-surface-raised) var(--glass-overlay-fill, 82%), transparent);
536
- -webkit-backdrop-filter: blur(var(--glass-overlay-blur, 24px)) saturate(140%);
537
- backdrop-filter: blur(var(--glass-overlay-blur, 24px)) saturate(140%);
547
+ background-color: color-mix(in srgb, var(--color-surface-raised) var(--glass-overlay-fill, 45%), transparent);
548
+ background-image: linear-gradient(180deg, rgb(255 255 255 / 0.05), transparent 45%);
549
+ -webkit-backdrop-filter: blur(var(--glass-overlay-blur, 28px)) saturate(170%);
550
+ backdrop-filter: blur(var(--glass-overlay-blur, 28px)) saturate(170%);
538
551
  }
539
552
 
540
553
  :root[data-lp-no-backdrop] .lp-glass-overlay {
541
554
  background-color: var(--color-surface-raised);
555
+ background-image: none;
542
556
  -webkit-backdrop-filter: none;
543
557
  backdrop-filter: none;
544
558
  }