@leavepulse/ui 0.38.0 → 0.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/assets/{LpCommandPalette-C0yStVNy.css → LpCommandPalette-DdETX0-G.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 +7 -0
  13. package/dist/components/LpCommandPalette.vue.js +14 -7
  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 +19 -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-2a5d3558] {
2
+ .lp-cmd-excerpt[data-v-eecd2c28] {
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-2a5d3558] {
9
- animation: lp-cmd-in-2a5d3558 140ms ease-out both;
8
+ .lp-cmd-item[data-v-eecd2c28] {
9
+ animation: lp-cmd-in-eecd2c28 140ms ease-out both;
10
10
  }
11
- @keyframes lp-cmd-in-2a5d3558 {
11
+ @keyframes lp-cmd-in-eecd2c28 {
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-2a5d3558] {
18
+ .lp-cmd-item[data-v-eecd2c28] {
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
  };
@@ -151,6 +151,12 @@ type __VLS_Props = {
151
151
  * Hidden below the md breakpoint, where there is no room beside the list.
152
152
  */
153
153
  previewPane?: boolean;
154
+ /**
155
+ * Frosted glass instead of a solid panel: the page shows through,
156
+ * blurred, under a lighter scrim (.lp-glass-overlay, .lp-scrim--light).
157
+ * A look, not a behaviour, so it is the app's call and off by default.
158
+ */
159
+ glass?: boolean;
154
160
  };
155
161
  type __VLS_Slots = {
156
162
  /** Body of the preview pane for the highlighted command. */
@@ -170,6 +176,7 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {},
170
176
  placeholder: string;
171
177
  emptyText: string;
172
178
  minChars: number;
179
+ glass: boolean;
173
180
  debounce: number;
174
181
  recentLimit: number;
175
182
  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-C0yStVNy.css';const _hoisted_1 = { class: "flex shrink-0 items-center gap-2.5 border-b border-line px-4" };
9
+ import '../assets/LpCommandPalette-DdETX0-G.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 }) {
@@ -457,7 +458,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
457
458
  await nextTick();
458
459
  inputRef.value?.focus();
459
460
  }
460
- }
461
+ },
462
+ { immediate: true }
461
463
  );
462
464
  useHotkeys(
463
465
  () => props.hotkey ? [
@@ -478,9 +480,14 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
478
480
  default: withCtx(() => [
479
481
  createVNode(unref(DialogPortal), null, {
480
482
  default: withCtx(() => [
481
- createVNode(unref(DialogOverlay), { class: "lp-scrim fixed inset-0 z-(--z-overlay) data-[state=open]:animate-[fade-in_150ms_ease] data-[state=closed]:animate-[fade-out_130ms_ease]" }),
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"]),
482
486
  createVNode(unref(DialogContent), {
483
- class: normalizeClass(["fixed left-1/2 top-[12vh] z-(--z-modal) flex max-h-[76vh] w-[min(92vw,40rem)] -translate-x-1/2 flex-col overflow-hidden rounded-card border border-line lp-glass-overlay shadow-panel outline-none data-[state=open]:animate-[pop-in_160ms_var(--ease-emphasized)] data-[state=closed]:animate-[pop-out_130ms_ease]", __props.previewPane && "md:h-[min(76vh,36rem)] md:w-[min(92vw,64rem)]"]),
487
+ 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 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 && "md:h-[min(76vh,36rem)] md:w-[min(92vw,64rem)]"
490
+ ]]),
484
491
  onOpenAutoFocus: _cache[2] || (_cache[2] = withModifiers(() => {
485
492
  }, ["prevent"]))
486
493
  }, {
@@ -754,7 +761,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
754
761
  };
755
762
  }
756
763
  });
757
- const LpCommandPalette = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-2a5d3558"]]);
764
+ const LpCommandPalette = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-eecd2c28"]]);
758
765
  export {
759
766
  LpCommandPalette as default
760
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.0",
3
+ "version": "0.39.0",
4
4
  "type": "module",
5
5
  "description": "LeavePulse token-driven Vue component kit.",
6
6
  "repository": {
@@ -199,6 +199,12 @@ const props = withDefaults(
199
199
  * Hidden below the md breakpoint, where there is no room beside the list.
200
200
  */
201
201
  previewPane?: boolean
202
+ /**
203
+ * Frosted glass instead of a solid panel: the page shows through,
204
+ * blurred, under a lighter scrim (.lp-glass-overlay, .lp-scrim--light).
205
+ * A look, not a behaviour, so it is the app's call and off by default.
206
+ */
207
+ glass?: boolean
202
208
  }>(),
203
209
  {
204
210
  placeholder: "Type a command or search…",
@@ -213,6 +219,7 @@ const props = withDefaults(
213
219
  groupLimit: 0,
214
220
  layoutFallback: true,
215
221
  previewPane: false,
222
+ glass: false,
216
223
  },
217
224
  )
218
225
 
@@ -780,6 +787,11 @@ function onEscape(event: KeyboardEvent) {
780
787
  // into the mode someone left three screens deep is never what they meant by
781
788
  // ⌘K, and a pending request from last time is dropped rather than landing in
782
789
  // the new list.
790
+ //
791
+ // Immediate, because a palette mounted already open — lazily, on the first
792
+ // ⌘K — never sees `open` change, and reka's own auto-focus is turned off
793
+ // below: without this the first open left focus on the page and the first
794
+ // keystrokes went nowhere.
783
795
  watch(
784
796
  () => props.open,
785
797
  async (o) => {
@@ -795,6 +807,7 @@ watch(
795
807
  inputRef.value?.focus()
796
808
  }
797
809
  },
810
+ { immediate: true },
798
811
  )
799
812
 
800
813
  // Through useHotkeys rather than a listener of its own: the palette is the one
@@ -819,11 +832,15 @@ useHotkeys(() =>
819
832
  <DialogRoot :open="open" @update:open="(v) => emit('update:open', v)">
820
833
  <DialogPortal>
821
834
  <DialogOverlay
835
+ :class="{ 'lp-scrim--light': glass }"
822
836
  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]"
823
837
  />
824
838
  <DialogContent
825
- 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]"
826
- :class="previewPane && 'md:h-[min(76vh,36rem)] md:w-[min(92vw,64rem)]'"
839
+ 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 shadow-panel outline-none data-[state=open]:animate-[pop-in_160ms_var(--ease-emphasized)] data-[state=closed]:animate-[pop-out_130ms_ease]"
840
+ :class="[
841
+ glass ? 'lp-glass-overlay' : 'bg-surface-raised',
842
+ previewPane && 'md:h-[min(76vh,36rem)] md:w-[min(92vw,64rem)]',
843
+ ]"
827
844
  @open-auto-focus.prevent
828
845
  >
829
846
  <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
  }