@leavepulse/ui 0.72.2 → 0.72.4

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 (146) hide show
  1. package/dist/chunks/{DropzoneLinkField-CvyzXHdc.js → DropzoneLinkField-C-9BCGJn.js} +2 -2
  2. package/dist/chunks/{FieldWrapper-DeOZzjBM.js → FieldWrapper-CORPzo9s.js} +1 -1
  3. package/dist/chunks/{LpActionBar-DZP0IpXS.js → LpActionBar-BP1ld7-S.js} +2 -2
  4. package/dist/chunks/{LpAddressInput-q8Vu3M8z.js → LpAddressInput-Al3HT5ey.js} +3 -3
  5. package/dist/chunks/{LpAlert-CGeYc3fe.js → LpAlert-n5XPAZfv.js} +2 -2
  6. package/dist/chunks/{LpAppShell-oB-QWu74.js → LpAppShell-D1-On_5L.js} +4 -4
  7. package/dist/chunks/{LpBadge-BVDOfrqI.js → LpBadge-wUYLN4U9.js} +1 -1
  8. package/dist/chunks/{LpBottomNav-w_5LKno0.js → LpBottomNav-CbNQQPuL.js} +1 -1
  9. package/dist/chunks/{LpBreadcrumbs-DJvcYVPM.js → LpBreadcrumbs-DPdbth-c.js} +1 -1
  10. package/dist/chunks/{LpButton-Cse4COe7.js → LpButton-CFb44IjC.js} +1 -1
  11. package/dist/chunks/{LpCard-BoB-UqzE.js → LpCard-r13WRqox.js} +1 -1
  12. package/dist/chunks/{LpCheckbox-B6lfW2GC.js → LpCheckbox-Cs4SHaHX.js} +1 -1
  13. package/dist/chunks/{LpCodeBlock-BjptdRIt.js → LpCodeBlock-DRC8VbXW.js} +1 -1
  14. package/dist/chunks/{LpCodeDisplay-BkVeLcLM.js → LpCodeDisplay-AQx-3nNb.js} +4 -4
  15. package/dist/chunks/{LpConfirmDialog-DqsrdJSK.js → LpConfirmDialog-DY2VjDji.js} +1 -1
  16. package/dist/chunks/{LpConfirmHost-BT8iDrWb.js → LpConfirmHost-DvSZTJ4N.js} +1 -1
  17. package/dist/chunks/{LpCopyButton-DhptBlXE.js → LpCopyButton-xF7wFVoU.js} +1 -1
  18. package/dist/chunks/{LpCountdown-9QnxUFXk.js → LpCountdown-BxE7stYk.js} +1 -1
  19. package/dist/chunks/{LpDataList-_D19wjXa.js → LpDataList-Jxyl9H5w.js} +1 -1
  20. package/dist/chunks/{LpEditable-BdjKjOMG.js → LpEditable-Cy6GLbFx.js} +2 -2
  21. package/dist/chunks/{LpFileTree-Cnw35H2X.js → LpFileTree-zIFEgo3I.js} +1 -1
  22. package/dist/chunks/{LpFileTreeNode-DR3YPYFb.js → LpFileTreeNode-cT6BIWvz.js} +3 -3
  23. package/dist/chunks/{LpFormField-CjnOXjFq.js → LpFormField-Ch69Jd-X.js} +1 -1
  24. package/dist/chunks/{LpHintLayer-DWWn7Xm2.js → LpHintLayer-CgObdUsM.js} +2 -2
  25. package/dist/chunks/{LpHints-Ctl4p_tS.js → LpHints-Bz-BlaSl.js} +3 -3
  26. package/dist/chunks/{LpInfraNode-CZrDvfnL.js → LpInfraNode-46XOCkfq.js} +2 -2
  27. package/dist/chunks/{LpInput-D9uwTIqP.js → LpInput-Tw-yxT4I.js} +1 -1
  28. package/dist/chunks/{LpLightbox-DlNPihiz.js → LpLightbox-B95yu22B.js} +2 -2
  29. package/dist/chunks/{LpLoader-DBVl6KDH.js → LpLoader-DVwkOmt9.js} +1 -1
  30. package/dist/chunks/{LpLogViewer-By_fmDpN.js → LpLogViewer-D25jM2_I.js} +3 -3
  31. package/dist/chunks/{LpMapPicker-CmuOTS7N.js → LpMapPicker-DRGgh89B.js} +1 -1
  32. package/dist/chunks/{LpMediaGallery-Dh5ONZt1.js → LpMediaGallery-Bkram-MO.js} +1 -1
  33. package/dist/chunks/{LpModalNav-CThPOPAF.js → LpModalNav-CE3iLnxB.js} +2 -2
  34. package/dist/chunks/{LpNotificationBell-674IgUph.js → LpNotificationBell-3244LUVc.js} +3 -3
  35. package/dist/chunks/{LpPaletteTheme-hsYXczIM.js → LpPaletteTheme-CdEEMMEF.js} +3 -3
  36. package/dist/chunks/{LpPasswordInput-DNCvyY6N.js → LpPasswordInput-BkHKQDhM.js} +2 -2
  37. package/dist/chunks/{LpPie-DCpGXbRS.js → LpPie-Mr3EtFEd.js} +2 -2
  38. package/dist/chunks/{LpPipeline-wh1SzEHF.js → LpPipeline-B3aOVOkV.js} +2 -2
  39. package/dist/chunks/{LpRepeater-BnOQXjWo.js → LpRepeater-D7ZmnIYd.js} +2 -2
  40. package/dist/chunks/{LpScramble-INTKwGsY.js → LpScramble-Bn9OoOtC.js} +1 -1
  41. package/dist/chunks/{LpSecretField-Ck0Mx6xx.js → LpSecretField-BbmqM9ye.js} +6 -6
  42. package/dist/chunks/{LpShift-DLU4_TFf.js → LpShift-CCq7dofl.js} +28 -5
  43. package/dist/chunks/{LpSidebar-DVPinvaL.js → LpSidebar-BciLMBgY.js} +2 -2
  44. package/dist/chunks/{LpSidebarNav-BBj2_6jV.js → LpSidebarNav-HYcG-ib8.js} +3 -2
  45. package/dist/chunks/{LpSidebarRow-C81reNEm.js → LpSidebarRow-Di2VYz-Z.js} +19 -6
  46. package/dist/chunks/{LpStat-tq-f6ZzL.js → LpStat-D6V6Fdfc.js} +1 -1
  47. package/dist/chunks/{LpStatus-Chbh_JJr.js → LpStatus-DglxttQ7.js} +2 -2
  48. package/dist/chunks/{LpStepMark-C064WOYG.js → LpStepMark-DpzKVyh8.js} +1 -1
  49. package/dist/chunks/{LpStepper-B15etK5F.js → LpStepper-DpsocBNJ.js} +1 -1
  50. package/dist/chunks/{LpSwap-BA5fh77B.js → LpSwap-C96c0LWh.js} +1 -1
  51. package/dist/chunks/{LpSwitch-ByaxQKEm.js → LpSwitch-BnXd1llc.js} +1 -1
  52. package/dist/chunks/{LpTabFace-Dcj2cO6x.js → LpTabFace-DPCwKkH2.js} +1 -1
  53. package/dist/chunks/{LpTableFilter-CT8Xml_m.js → LpTableFilter-CiVBpaq0.js} +3 -3
  54. package/dist/chunks/{LpTagsInput-CSv62X4B.js → LpTagsInput-C-mDG648.js} +2 -2
  55. package/dist/chunks/{LpTextarea-Blv8qdz0.js → LpTextarea-TE-yZv2U.js} +1 -1
  56. package/dist/chunks/{LpThemeStudio-DYQP_qbp.js → LpThemeStudio-BJDKf3_Z.js} +2 -2
  57. package/dist/chunks/{LpThemeSwitcher-BGIVrM1l.js → LpThemeSwitcher-BDrIJswA.js} +2 -2
  58. package/dist/chunks/{LpToaster-D5Pj_wc9.js → LpToaster-IwTXjbbU.js} +2 -2
  59. package/dist/chunks/{LpTopologyCanvas-Bq-k5tgI.js → LpTopologyCanvas-B1gIbS0n.js} +1 -1
  60. package/dist/chunks/{LpTour-BU4JX3RU.js → LpTour-BZq-mUMv.js} +1 -1
  61. package/dist/chunks/{LpTruncate-Cb7MDTD7.js → LpTruncate-CflAIwiJ.js} +1 -1
  62. package/dist/chunks/{LpUptimeBar-Cdq76qi_.js → LpUptimeBar-BtkZRwY0.js} +2 -2
  63. package/dist/chunks/{RevealToggle-DhL7mi-3.js → RevealToggle-BEit1n6Z.js} +1 -1
  64. package/dist/chunks/{TourCard-Dvo_vH_P.js → TourCard-DNclH3u_.js} +3 -3
  65. package/dist/components/DropzoneLinkField.vue.js +1 -1
  66. package/dist/components/FieldWrapper.vue.js +1 -1
  67. package/dist/components/LpActionBar.vue.js +1 -1
  68. package/dist/components/LpAddressInput.vue.js +1 -1
  69. package/dist/components/LpAlert.vue.js +1 -1
  70. package/dist/components/LpAppShell.vue.js +1 -1
  71. package/dist/components/LpBadge.vue.js +1 -1
  72. package/dist/components/LpBottomNav.vue.js +1 -1
  73. package/dist/components/LpBreadcrumbs.vue.js +1 -1
  74. package/dist/components/LpButton.vue.js +1 -1
  75. package/dist/components/LpCard.vue.js +1 -1
  76. package/dist/components/LpChart.vue.js +2 -2
  77. package/dist/components/LpCheckbox.vue.js +1 -1
  78. package/dist/components/LpCodeBlock.vue.js +1 -1
  79. package/dist/components/LpCodeDisplay.vue.js +1 -1
  80. package/dist/components/LpCommandPalette.vue.d.ts +1 -1
  81. package/dist/components/LpCommandPalette.vue.js +2 -2
  82. package/dist/components/LpConfirmDialog.vue.js +1 -1
  83. package/dist/components/LpConfirmHost.vue.js +1 -1
  84. package/dist/components/LpCopyButton.vue.js +1 -1
  85. package/dist/components/LpCountdown.vue.js +1 -1
  86. package/dist/components/LpDataList.vue.js +1 -1
  87. package/dist/components/LpDropzone.vue.js +1 -1
  88. package/dist/components/LpEditable.vue.js +1 -1
  89. package/dist/components/LpFileTree.vue.js +1 -1
  90. package/dist/components/LpFileTreeNode.vue.js +1 -1
  91. package/dist/components/LpFormField.vue.js +1 -1
  92. package/dist/components/LpHintLayer.vue.js +1 -1
  93. package/dist/components/LpHints.vue.js +1 -1
  94. package/dist/components/LpImageEditor.vue.js +1 -1
  95. package/dist/components/LpInfraNode.vue.js +1 -1
  96. package/dist/components/LpInput.vue.js +1 -1
  97. package/dist/components/LpLightbox.vue.js +1 -1
  98. package/dist/components/LpLoader.vue.js +1 -1
  99. package/dist/components/LpLogViewer.vue.js +1 -1
  100. package/dist/components/LpMapPicker.vue.js +1 -1
  101. package/dist/components/LpMediaGallery.vue.js +1 -1
  102. package/dist/components/LpModalNav.vue.js +1 -1
  103. package/dist/components/LpNotificationBell.vue.js +1 -1
  104. package/dist/components/LpPaletteTheme.vue.js +1 -1
  105. package/dist/components/LpPasswordInput.vue.js +1 -1
  106. package/dist/components/LpPie.vue.js +1 -1
  107. package/dist/components/LpPipeline.vue.js +1 -1
  108. package/dist/components/LpRepeater.vue.js +1 -1
  109. package/dist/components/LpScramble.vue.js +1 -1
  110. package/dist/components/LpSecretField.vue.js +1 -1
  111. package/dist/components/LpShift.vue.d.ts +4 -3
  112. package/dist/components/LpShift.vue.js +1 -1
  113. package/dist/components/LpSidebar.vue.js +1 -1
  114. package/dist/components/LpSidebarNav.vue.js +1 -1
  115. package/dist/components/LpSidebarRow.vue.d.ts +2 -0
  116. package/dist/components/LpSidebarRow.vue.js +1 -1
  117. package/dist/components/LpStat.vue.js +1 -1
  118. package/dist/components/LpStatus.vue.js +1 -1
  119. package/dist/components/LpStepMark.vue.js +1 -1
  120. package/dist/components/LpStepper.vue.js +1 -1
  121. package/dist/components/LpSwap.vue.js +1 -1
  122. package/dist/components/LpSwitch.vue.js +1 -1
  123. package/dist/components/LpTabFace.vue.js +1 -1
  124. package/dist/components/LpTable.vue.js +6 -6
  125. package/dist/components/LpTableFilter.vue.js +1 -1
  126. package/dist/components/LpTabs.vue.js +1 -1
  127. package/dist/components/LpTagsInput.vue.js +1 -1
  128. package/dist/components/LpTextarea.vue.js +1 -1
  129. package/dist/components/LpThemeStudio.vue.js +1 -1
  130. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  131. package/dist/components/LpToaster.vue.js +1 -1
  132. package/dist/components/LpTopologyCanvas.vue.js +1 -1
  133. package/dist/components/LpTour.vue.js +1 -1
  134. package/dist/components/LpTruncate.vue.js +1 -1
  135. package/dist/components/LpUptimeBar.vue.js +1 -1
  136. package/dist/components/RevealToggle.vue.js +1 -1
  137. package/dist/components/TourCard.vue.js +1 -1
  138. package/dist/composables/useToast.d.ts +2 -0
  139. package/dist/composables/useToast.js +12 -2
  140. package/dist/index.js +55 -55
  141. package/package.json +1 -1
  142. package/src/components/LpShift.vue +43 -7
  143. package/src/components/LpSidebarNav.vue +1 -0
  144. package/src/components/LpSidebarRow.vue +18 -1
  145. package/src/components/LpToaster.vue +1 -1
  146. package/src/composables/useToast.ts +21 -1
@@ -57,9 +57,10 @@ const props = withDefaults(
57
57
  * Fade the box's own children in and out as they come and go. Without it
58
58
  * the box glides but a control still pops into being as the edge uncovers
59
59
  * it, and one that leaves is gone in a frame while its space closes up.
60
- * Only direct children: what a child does inside itself is its own affair,
61
- * so a control that changes its label carries the label as its `key` and
62
- * is replaced, the old one fading as the new one arrives.
60
+ * Anything that takes room counts, at any depth, since controls often
61
+ * arrive by teleport into a wrapper inside the box; an absolutely placed
62
+ * overlay (a tab's marker) does not, it moves nothing and animates itself.
63
+ * Markup put back unchanged by a re-render is not animated either.
63
64
  */
64
65
  presence?: boolean
65
66
  }>(),
@@ -105,6 +106,7 @@ interface Seen {
105
106
  height: number
106
107
  x: number
107
108
  y: number
109
+ flow: boolean
108
110
  }
109
111
 
110
112
  const seen = new WeakMap<Element, Seen>()
@@ -129,15 +131,48 @@ function remember(box: HTMLElement) {
129
131
  height: r.height,
130
132
  x: r.left,
131
133
  y: r.top,
134
+ flow: inFlow(node),
132
135
  })
133
136
  }
134
137
  }
135
138
 
139
+ // Only what takes room in the box comes and goes visibly. An overlay inside a
140
+ // control (a tab's sliding marker, a badge pinned to a corner) moves nothing
141
+ // around it and is animated by its own component; fading it in on top would
142
+ // override that animation's transform, and the marker would pop instead of
143
+ // sliding.
144
+ function inFlow(node: Element): boolean {
145
+ const position = getComputedStyle(node).position
146
+ return position !== "absolute" && position !== "fixed"
147
+ }
148
+
149
+ // A re-render that puts back the same markup (a page redrawing its header
150
+ // actions, a list re-keyed after a reload) is not a change anyone sees, so the
151
+ // pair is left alone rather than flashing out and in.
152
+ function signature(node: Node): string | undefined {
153
+ return node instanceof Element ? node.outerHTML : undefined
154
+ }
155
+
156
+ function unchanged(records: MutationRecord[]): Set<Node> {
157
+ const removed = new Map<string, Node[]>()
158
+ for (const record of records) record.removedNodes.forEach((node) => {
159
+ const key = signature(node)
160
+ if (key) removed.set(key, [...(removed.get(key) ?? []), node])
161
+ })
162
+ const same = new Set<Node>()
163
+ for (const record of records) record.addedNodes.forEach((node) => {
164
+ const key = signature(node)
165
+ const twin = key ? removed.get(key)?.shift() : undefined
166
+ if (twin) same.add(twin).add(node)
167
+ })
168
+ return same
169
+ }
170
+
136
171
  const offset = () => (props.anchor === "end" ? -6 : 6)
137
172
 
138
173
  function ghost(box: HTMLElement, node: Element) {
139
174
  const at = seen.get(node)
140
- if (!at || !(node instanceof HTMLElement) || node.hasAttribute(GHOST) || at.width === 0) return
175
+ if (!at || !at.flow || !(node instanceof HTMLElement) || node.hasAttribute(GHOST) || at.width === 0) return
141
176
  const copy = node.cloneNode(true) as HTMLElement
142
177
  copy.setAttribute(GHOST, "")
143
178
  copy.setAttribute("aria-hidden", "true")
@@ -172,7 +207,7 @@ function ghost(box: HTMLElement, node: Element) {
172
207
  }
173
208
 
174
209
  function arrive(node: Element) {
175
- if (!(node instanceof HTMLElement) || node.hasAttribute(GHOST)) return
210
+ if (!(node instanceof HTMLElement) || node.hasAttribute(GHOST) || !inFlow(node)) return
176
211
  node.animate(
177
212
  [
178
213
  { opacity: 0, transform: `translateX(${offset()}px) scale(0.96)`, filter: "blur(2px)" },
@@ -208,11 +243,12 @@ watch(
208
243
  )
209
244
  if (real.length && !prefersReducedMotion()) {
210
245
  const added = new Set<Node>()
246
+ const same = unchanged(real)
211
247
  for (const record of real) {
212
- record.removedNodes.forEach((node) => node instanceof Element && ghost(box, node))
248
+ record.removedNodes.forEach((node) => node instanceof Element && !same.has(node) && ghost(box, node))
213
249
  record.addedNodes.forEach((node) => {
214
250
  added.add(node)
215
- if (node instanceof Element) arrive(node)
251
+ if (node instanceof Element && !same.has(node)) arrive(node)
216
252
  })
217
253
  }
218
254
  // Measured in the next frame, which still comes before the paint: by
@@ -281,6 +281,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
281
281
  :pill="false"
282
282
  :pill-id="pillId"
283
283
  compact
284
+ group
284
285
  :on-path="section.items.some((i) => itemActive(i) || onPath(i))"
285
286
  :preview-delay="previewDelay"
286
287
  :item-active="itemActive"
@@ -31,6 +31,8 @@ const props = withDefaults(
31
31
  pill: boolean
32
32
  pillId: string
33
33
  compact?: boolean
34
+ /** A folded group standing in for its pages: a click opens its card. */
35
+ group?: boolean
34
36
  /** A page under another: indented, a size smaller. */
35
37
  nested?: boolean
36
38
  menu?: ContextMenuItemDef[]
@@ -98,11 +100,25 @@ const initials = computed(() =>
98
100
  const hidden = computed(() => (props.compact || !props.expanded) && !!props.item.children?.some(waiting))
99
101
  const hasBadge = computed(() => props.item.badge != null || !!props.item.dot || hidden.value)
100
102
  const card = computed(() => !props.item.disabled && (props.compact || !!props.item.preview))
103
+
104
+ // A folded group with no page of its own is reached only through its card, so
105
+ // a click opens the card rather than waiting for the pointer to rest: a click
106
+ // that does nothing on a nav icon reads as broken.
107
+ const cardOpen = ref(false)
108
+ const opensCard = computed(() => props.compact && props.group && !!props.item.children?.length)
109
+ function press(): void {
110
+ if (opensCard.value) {
111
+ cardOpen.value = !cardOpen.value
112
+ return
113
+ }
114
+ emit("activate", props.item)
115
+ }
101
116
  const delay = computed(() => (props.compact && !props.item.preview ? 250 : props.previewDelay))
102
117
  </script>
103
118
 
104
119
  <template>
105
120
  <LpHoverCard
121
+ v-model:open="cardOpen"
106
122
  :disabled="!card"
107
123
  side="right"
108
124
  align="start"
@@ -136,7 +152,8 @@ const delay = computed(() => (props.compact && !props.item.preview ? 250 : props
136
152
  ? 'text-ink'
137
153
  : 'text-muted hover:text-ink',
138
154
  ]"
139
- @click="emit('activate', item)"
155
+ :aria-expanded="opensCard ? cardOpen : undefined"
156
+ @click="press"
140
157
  @pointerenter="emit('hover', item)"
141
158
  >
142
159
  <span
@@ -204,7 +204,7 @@ async function settle(t: ToastItem, run: () => void | Promise<void>) {
204
204
  */
205
205
  const copyable = (t: ToastItem) => t.variant === "error" || t.variant === "warning"
206
206
  // Read on click rather than bound, so a toast whose text is updated in place copies what it now says.
207
- const toastText = (t: ToastItem) => () => [t.title, t.message, t.details].filter(Boolean).join("\n")
207
+ const toastText = (t: ToastItem) => () => [t.title, t.message, t.details, t.context].filter(Boolean).join("\n")
208
208
 
209
209
  function selecting(): boolean {
210
210
  return Boolean(window.getSelection()?.toString())
@@ -25,6 +25,8 @@ export interface ToastItem {
25
25
  onClick?: () => void | Promise<void>
26
26
  /** Copied with the message, not shown: what a bug report needs (request, status, ids). */
27
27
  details?: string
28
+ /** Page and moment an error toast came up, copied last: "it broke" rarely says where or when. */
29
+ context?: string
28
30
  }
29
31
 
30
32
  export interface ToastOptions {
@@ -61,9 +63,26 @@ function detailsOf(source: unknown, depth = 0): string | undefined {
61
63
  return detailsOf(cause, depth + 1)
62
64
  }
63
65
 
66
+ // A code fault (TypeError and the like) has no request behind it, and its
67
+ // message alone ("x is undefined") says nothing about where it happened: the
68
+ // copy gets its type and stack instead. Firefox and Safari leave the message
69
+ // out of `stack`, Chromium starts with it; the head is written once either way.
70
+ function traceOf(source: unknown): string | undefined {
71
+ if (!(source instanceof Error)) return undefined
72
+ const head = `${source.name}: ${source.message}`
73
+ const frames = (source.stack ?? "").split("\n").filter((line) => line.trim() && line.trim() !== head)
74
+ const own = [head, ...frames].join("\n")
75
+ const inner = traceOf(source.cause)
76
+ return inner ? `${own}\nCaused by ${inner}` : own
77
+ }
78
+
64
79
  function split(message: ToastMessage, options: ToastOptions): [string, ToastOptions] {
65
80
  const text = typeof message === "string" ? message : message.message
66
- const details = options.details ?? (typeof message === "string" ? undefined : detailsOf(message)) ?? detailsOf(options.cause)
81
+ const details =
82
+ options.details ??
83
+ (typeof message === "string" ? undefined : detailsOf(message)) ??
84
+ detailsOf(options.cause) ??
85
+ traceOf(typeof message === "string" ? options.cause : message)
67
86
  return [text, { ...options, details }]
68
87
  }
69
88
 
@@ -101,6 +120,7 @@ function push(
101
120
  actions: options.actions,
102
121
  onClick: options.onClick,
103
122
  details: options.details,
123
+ context: variant === "error" ? [globalThis.location?.href, new Date().toISOString()].filter(Boolean).join(" · ") : undefined,
104
124
  }
105
125
 
106
126
  const same = options.key === undefined ? -1 : queue.findIndex((t) => t.key === options.key)