@leavepulse/ui 0.56.0 → 0.58.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 (182) hide show
  1. package/dist/chunks/{ContextMenuItems-BtKU7074.js → ContextMenuItems-BY5sTVQF.js} +1 -1
  2. package/dist/chunks/{LayoutCanvas-BkaGXTjx.js → LayoutCanvas-BcGLJVBq.js} +1 -1
  3. package/dist/chunks/{LayoutNode-B3_9E1Xa.js → LayoutNode-BkBn6dvM.js} +1 -1
  4. package/dist/chunks/{LpActionBar-C3hkVsFz.js → LpActionBar-DCN-_pZP.js} +2 -2
  5. package/dist/chunks/{LpAddressInput-NUHmplCd.js → LpAddressInput-Dio6Sqed.js} +7 -7
  6. package/dist/chunks/{LpAlert-B6ilrM7T.js → LpAlert-CXMn1GYE.js} +1 -1
  7. package/dist/chunks/{LpAppShell-BEGjSqFl.js → LpAppShell-Be6T96td.js} +3 -3
  8. package/dist/chunks/{LpAutocomplete-C8IgSMsi.js → LpAutocomplete-hJ_6CHlh.js} +1 -1
  9. package/dist/chunks/{LpAvatar-LTvy0OkE.js → LpAvatar-BxNs23Xu.js} +2 -2
  10. package/dist/chunks/{LpBreadcrumbs-D4TsoFju.js → LpBreadcrumbs-BLzOEgAF.js} +1 -1
  11. package/dist/chunks/{LpButton-BdfZFNqi.js → LpButton-vhlwmpBy.js} +1 -1
  12. package/dist/chunks/{LpCalendar-DrA2McTU.js → LpCalendar-BLoE8Qm7.js} +1 -1
  13. package/dist/chunks/{LpCard-DbjO5yaF.js → LpCard-DhR7u0d8.js} +1 -1
  14. package/dist/chunks/{LpCheckbox-DvVYeOD-.js → LpCheckbox-B_C8sOoT.js} +1 -1
  15. package/dist/chunks/{LpCodeBlock-ca7ANFyW.js → LpCodeBlock-CXsqJwDy.js} +1 -1
  16. package/dist/chunks/{LpColorPicker-D1sQqtXS.js → LpColorPicker-DQ88niXq.js} +2 -2
  17. package/dist/chunks/{LpConfirmDialog-CrbUZgJE.js → LpConfirmDialog-DxlSdjmk.js} +3 -3
  18. package/dist/chunks/{LpConfirmHost-BgMIV8r9.js → LpConfirmHost-BcrmTNu_.js} +1 -1
  19. package/dist/chunks/{LpContextMenu-EF_90u3g.js → LpContextMenu-B1NGfpp1.js} +2 -2
  20. package/dist/chunks/{LpDatePicker-BrdFtSTy.js → LpDatePicker-B-VMHMgy.js} +3 -3
  21. package/dist/chunks/{LpDateRangePicker-Drd7EbIz.js → LpDateRangePicker-B68KPKPl.js} +6 -15
  22. package/dist/chunks/{LpDisclosure-Cc23-JOT.js → LpDisclosure-Ct-VBHxt.js} +1 -1
  23. package/dist/chunks/{LpDrawer-CBFVD624.js → LpDrawer-DSknigxH.js} +5 -7
  24. package/dist/chunks/{LpDropdownMenu-DrpJJBp0.js → LpDropdownMenu-C7oHVlKY.js} +2 -2
  25. package/dist/chunks/{LpEmptyState-BJbZXC8D.js → LpEmptyState-CBwwkkdA.js} +1 -1
  26. package/dist/chunks/{LpFieldNotice-wtieLUTj.js → LpFieldNotice-9hH4ITZa.js} +1 -1
  27. package/dist/chunks/{LpFileTree-3E4w4Rjr.js → LpFileTree-CMA0_rsB.js} +2 -2
  28. package/dist/chunks/{LpFileTreeNode-B4uB8Ks_.js → LpFileTreeNode-B4T258hF.js} +3 -3
  29. package/dist/chunks/{LpFormField-CtVYj6tO.js → LpFormField-BpQ6WJPe.js} +1 -1
  30. package/dist/chunks/LpHintLayer-B5-6FyEf.js +154 -0
  31. package/dist/chunks/LpHints-BqoZXHZk.js +109 -0
  32. package/dist/chunks/{LpIcon-DYtaid0K.js → LpIcon-B5guGKrW.js} +1 -1
  33. package/dist/chunks/{LpInput-D-YGQbEv.js → LpInput-WvyP02ID.js} +2 -2
  34. package/dist/chunks/{LpLightbox-EVXzAq62.js → LpLightbox-Dq-e36uT.js} +1 -1
  35. package/dist/chunks/{LpLogViewer-BfgtXVKm.js → LpLogViewer-DwiYE32z.js} +2 -2
  36. package/dist/chunks/{LpMapPicker-Cc84LaM3.js → LpMapPicker-BH5csaBt.js} +1 -1
  37. package/dist/chunks/{LpMediaGallery-BnlkN_1f.js → LpMediaGallery-B1kDypJ-.js} +2 -2
  38. package/dist/chunks/{LpModal-JNa3B_Lr.js → LpModal-BxYssnXg.js} +1 -1
  39. package/dist/chunks/{LpModalNav-DXBilXr0.js → LpModalNav-Bn2ws64b.js} +4 -4
  40. package/dist/chunks/{LpNotificationBell-DEL-RoHS.js → LpNotificationBell-DJfs4JjQ.js} +4 -4
  41. package/dist/chunks/{LpNumberField-B77O-z28.js → LpNumberField-CJPjmoDz.js} +1 -1
  42. package/dist/chunks/{LpPagination-BcDlo3r_.js → LpPagination-DehAaJLY.js} +1 -1
  43. package/dist/chunks/{LpPasswordInput-eKzQhbQP.js → LpPasswordInput-Bg5uTZbx.js} +2 -2
  44. package/dist/chunks/{LpPhoneInput-BCbrrhgE.js → LpPhoneInput-DStwcbnQ.js} +2 -2
  45. package/dist/chunks/{LpPie-B7ti5qCH.js → LpPie-DeTkbbOK.js} +3 -3
  46. package/dist/chunks/{LpRangeCalendar-DZuaRV0W.js → LpRangeCalendar-D5sDID9W.js} +1 -1
  47. package/dist/chunks/{LpSection-tKEH0jYf.js → LpSection-B19VPYmr.js} +1 -1
  48. package/dist/chunks/{LpSegmented-C09EEam2.js → LpSegmented-D3Eam8sa.js} +1 -1
  49. package/dist/chunks/{LpSelect-DNIL6MB9.js → LpSelect-DynjWK1A.js} +1 -1
  50. package/dist/chunks/{LpSidebar-C2oolgwy.js → LpSidebar-CshW0fqC.js} +3 -3
  51. package/dist/chunks/{LpSidebarNav-C9dHu-2b.js → LpSidebarNav-C9b3i9G2.js} +2 -2
  52. package/dist/chunks/{LpStat-B-7udBRP.js → LpStat-BS2TEXYK.js} +1 -1
  53. package/dist/chunks/{LpStepper-C7XiVKZj.js → LpStepper-BSglWF45.js} +1 -1
  54. package/dist/chunks/{LpTableFilter-CrW7_Rj4.js → LpTableFilter-CwHXVxJt.js} +2 -2
  55. package/dist/chunks/{LpTableOfContents-BUcAfGPQ.js → LpTableOfContents-DYuqkByy.js} +2 -8
  56. package/dist/chunks/{LpTabs-BMSBB78h.js → LpTabs-BBGRHY38.js} +1 -1
  57. package/dist/chunks/{LpTagsInput-D1SuqbhB.js → LpTagsInput-CHbQoAAA.js} +3 -3
  58. package/dist/chunks/{LpTextarea-uuScWo6-.js → LpTextarea-Bz2IWhTH.js} +1 -1
  59. package/dist/chunks/{LpThemeSwitcher-Fz85BCRS.js → LpThemeSwitcher-B_qhSl7q.js} +2 -2
  60. package/dist/chunks/{LpTimeField-Cqe7fXnh.js → LpTimeField-DvGXgdES.js} +1 -1
  61. package/dist/chunks/{LpToaster-DTtWuKvq.js → LpToaster-Bo9Q4QBP.js} +1 -1
  62. package/dist/chunks/{LpTooltip-CXVKcOXT.js → LpTooltip-suFCM34F.js} +2 -2
  63. package/dist/chunks/LpTour-D7m9ZxNk.js +334 -0
  64. package/dist/chunks/{LpUptimeBar-ugcv25xj.js → LpUptimeBar-B4ltCfno.js} +1 -1
  65. package/dist/chunks/{MenuTitle-J3ZDYjpV.js → MenuTitle-DQP6hc2N.js} +1 -1
  66. package/dist/chunks/TourCard-DCHOjr6e.js +100 -0
  67. package/dist/chunks/icons-QqBscG5I.js +43 -0
  68. package/dist/component-names.d.ts +1 -1
  69. package/dist/components/ContextMenuItems.vue.js +1 -1
  70. package/dist/components/LayoutCanvas.vue.js +1 -1
  71. package/dist/components/LayoutNode.vue.js +1 -1
  72. package/dist/components/LpActionBar.vue.js +1 -1
  73. package/dist/components/LpAddressInput.vue.js +1 -1
  74. package/dist/components/LpAlert.vue.js +1 -1
  75. package/dist/components/LpAppShell.vue.js +1 -1
  76. package/dist/components/LpAutocomplete.vue.js +1 -1
  77. package/dist/components/LpAvatar.vue.js +1 -1
  78. package/dist/components/LpBreadcrumbs.vue.js +1 -1
  79. package/dist/components/LpButton.vue.js +1 -1
  80. package/dist/components/LpCalendar.vue.js +1 -1
  81. package/dist/components/LpCard.vue.js +1 -1
  82. package/dist/components/LpChart.vue.js +2 -2
  83. package/dist/components/LpCheckbox.vue.js +1 -1
  84. package/dist/components/LpCodeBlock.vue.js +1 -1
  85. package/dist/components/LpColorPicker.vue.js +1 -1
  86. package/dist/components/LpCommandPalette.vue.js +2 -2
  87. package/dist/components/LpConfirmDialog.vue.js +1 -1
  88. package/dist/components/LpConfirmHost.vue.js +1 -1
  89. package/dist/components/LpContextMenu.vue.js +1 -1
  90. package/dist/components/LpDatePicker.vue.js +1 -1
  91. package/dist/components/LpDateRangePicker.vue.js +1 -1
  92. package/dist/components/LpDisclosure.vue.js +1 -1
  93. package/dist/components/LpDrawer.vue.js +1 -1
  94. package/dist/components/LpDropdownMenu.vue.js +1 -1
  95. package/dist/components/LpDropzone.vue.js +1 -1
  96. package/dist/components/LpEmptyState.vue.js +1 -1
  97. package/dist/components/LpFieldNotice.vue.js +1 -1
  98. package/dist/components/LpFileTree.vue.js +1 -1
  99. package/dist/components/LpFileTreeNode.vue.js +1 -1
  100. package/dist/components/LpFormField.vue.js +1 -1
  101. package/dist/components/LpHintLayer.vue.d.ts +33 -0
  102. package/dist/components/LpHintLayer.vue.js +4 -0
  103. package/dist/components/LpHints.vue.d.ts +20 -0
  104. package/dist/components/LpHints.vue.js +4 -0
  105. package/dist/components/LpIcon.vue.js +1 -1
  106. package/dist/components/LpImageEditor.vue.js +2 -2
  107. package/dist/components/LpInput.vue.js +1 -1
  108. package/dist/components/LpLightbox.vue.js +1 -1
  109. package/dist/components/LpLogViewer.vue.js +1 -1
  110. package/dist/components/LpMapPicker.vue.js +1 -1
  111. package/dist/components/LpMediaGallery.vue.js +1 -1
  112. package/dist/components/LpModal.vue.js +1 -1
  113. package/dist/components/LpModalNav.vue.js +1 -1
  114. package/dist/components/LpNotificationBell.vue.js +1 -1
  115. package/dist/components/LpNumberField.vue.js +1 -1
  116. package/dist/components/LpPagination.vue.js +1 -1
  117. package/dist/components/LpPaletteFacts.vue.js +1 -1
  118. package/dist/components/LpPaletteHeader.vue.js +2 -2
  119. package/dist/components/LpPasswordInput.vue.js +1 -1
  120. package/dist/components/LpPhoneInput.vue.js +1 -1
  121. package/dist/components/LpPie.vue.js +1 -1
  122. package/dist/components/LpRangeCalendar.vue.js +1 -1
  123. package/dist/components/LpSection.vue.js +1 -1
  124. package/dist/components/LpSegmented.vue.js +1 -1
  125. package/dist/components/LpSelect.vue.js +1 -1
  126. package/dist/components/LpShortcutsDialog.vue.js +1 -1
  127. package/dist/components/LpSidebar.vue.js +1 -1
  128. package/dist/components/LpSidebarNav.vue.js +1 -1
  129. package/dist/components/LpStat.vue.js +1 -1
  130. package/dist/components/LpStepper.vue.js +1 -1
  131. package/dist/components/LpTable.vue.js +6 -6
  132. package/dist/components/LpTableFilter.vue.js +1 -1
  133. package/dist/components/LpTableOfContents.vue.js +1 -1
  134. package/dist/components/LpTabs.vue.js +1 -1
  135. package/dist/components/LpTagsInput.vue.js +1 -1
  136. package/dist/components/LpTextarea.vue.js +1 -1
  137. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  138. package/dist/components/LpTimeField.vue.js +1 -1
  139. package/dist/components/LpToaster.vue.js +1 -1
  140. package/dist/components/LpTooltip.vue.js +1 -1
  141. package/dist/components/LpTour.vue.d.ts +98 -0
  142. package/dist/components/LpTour.vue.js +4 -0
  143. package/dist/components/LpUptimeBar.vue.js +1 -1
  144. package/dist/components/MenuTitle.vue.js +1 -1
  145. package/dist/components/TourCard.vue.d.ts +28 -0
  146. package/dist/components/TourCard.vue.js +4 -0
  147. package/dist/components/tour.d.ts +21 -0
  148. package/dist/components/tour.js +38 -0
  149. package/dist/composables/scroll.d.ts +17 -0
  150. package/dist/composables/scroll.js +29 -0
  151. package/dist/composables/useGamepadNav.js +2 -7
  152. package/dist/composables/useHintSystem.d.ts +195 -0
  153. package/dist/composables/useHintSystem.js +334 -0
  154. package/dist/composables/useHints.d.ts +18 -0
  155. package/dist/composables/useHints.js +120 -0
  156. package/dist/composables/useMediaQuery.d.ts +2 -0
  157. package/dist/composables/useMediaQuery.js +18 -0
  158. package/dist/composables/usePaintSelect.js +1 -7
  159. package/dist/icons-all.js +1 -1
  160. package/dist/index.d.ts +13 -0
  161. package/dist/index.js +89 -64
  162. package/package.json +1 -1
  163. package/src/component-names.ts +3 -0
  164. package/src/components/LpDateRangePicker.vue +3 -12
  165. package/src/components/LpDrawer.vue +4 -7
  166. package/src/components/LpHintLayer.vue +148 -0
  167. package/src/components/LpHints.vue +116 -0
  168. package/src/components/LpTableOfContents.vue +2 -14
  169. package/src/components/LpTooltip.vue +2 -4
  170. package/src/components/LpTour.vue +358 -0
  171. package/src/components/TourCard.vue +41 -0
  172. package/src/components/tour.ts +57 -0
  173. package/src/composables/scroll.ts +54 -0
  174. package/src/composables/useGamepadNav.ts +2 -7
  175. package/src/composables/useHintSystem.ts +512 -0
  176. package/src/composables/useHints.ts +151 -0
  177. package/src/composables/useMediaQuery.ts +20 -0
  178. package/src/composables/usePaintSelect.ts +1 -8
  179. package/src/index.ts +27 -0
  180. package/src/kit-icons.ts +1 -1
  181. package/src/tokens/tokens.css +1 -0
  182. package/dist/chunks/icons-CkA0njzm.js +0 -43
package/dist/index.js CHANGED
@@ -1,24 +1,24 @@
1
- import { _ } from "./chunks/LayoutCanvas-BkaGXTjx.js";
2
- import { _ as _2 } from "./chunks/LpAddressInput-NUHmplCd.js";
3
- import { _ as _3 } from "./chunks/LpMapPicker-Cc84LaM3.js";
1
+ import { _ } from "./chunks/LayoutCanvas-BcGLJVBq.js";
2
+ import { _ as _2 } from "./chunks/LpAddressInput-Dio6Sqed.js";
3
+ import { _ as _3 } from "./chunks/LpMapPicker-BH5csaBt.js";
4
4
  import { OSM_TILES, createNominatimProvider, emptyAddress, formatAddress, project, tileUrl, unproject } from "./components/address.js";
5
- import { _ as _4 } from "./chunks/LpActionBar-C3hkVsFz.js";
6
- import { _ as _5 } from "./chunks/LpAlert-B6ilrM7T.js";
7
- import { _ as _6 } from "./chunks/LpAppShell-BEGjSqFl.js";
8
- import { _ as _7 } from "./chunks/LpAutocomplete-C8IgSMsi.js";
9
- import { _ as _8 } from "./chunks/LpAvatar-LTvy0OkE.js";
5
+ import { _ as _4 } from "./chunks/LpActionBar-DCN-_pZP.js";
6
+ import { _ as _5 } from "./chunks/LpAlert-CXMn1GYE.js";
7
+ import { _ as _6 } from "./chunks/LpAppShell-Be6T96td.js";
8
+ import { _ as _7 } from "./chunks/LpAutocomplete-hJ_6CHlh.js";
9
+ import { _ as _8 } from "./chunks/LpAvatar-BxNs23Xu.js";
10
10
  import { _ as _9 } from "./chunks/LpBadge-Fe0D1bYx.js";
11
- import { _ as _10 } from "./chunks/LpBreadcrumbs-D4TsoFju.js";
12
- import { _ as _11 } from "./chunks/LpButton-BdfZFNqi.js";
13
- import { _ as _12 } from "./chunks/LpCalendar-DrA2McTU.js";
14
- import { _ as _13 } from "./chunks/LpCard-DbjO5yaF.js";
11
+ import { _ as _10 } from "./chunks/LpBreadcrumbs-BLzOEgAF.js";
12
+ import { _ as _11 } from "./chunks/LpButton-vhlwmpBy.js";
13
+ import { _ as _12 } from "./chunks/LpCalendar-BLoE8Qm7.js";
14
+ import { _ as _13 } from "./chunks/LpCard-DhR7u0d8.js";
15
15
  import { default as default2 } from "./components/LpChart.vue.js";
16
16
  import { backgroundOf, csvField, downloadFile, fileStem, inlineSvg, resolveCssColor, svgToPng, toCsv } from "./components/exportData.js";
17
17
  import { CHART_PALETTE, axisTicks, chartSummary, formatTick, markerIndex, niceScale, resolveSlices } from "./components/chart.js";
18
- import { _ as _14 } from "./chunks/LpCheckbox-DvVYeOD-.js";
19
- import { _ as _15 } from "./chunks/LpCodeBlock-ca7ANFyW.js";
18
+ import { _ as _14 } from "./chunks/LpCheckbox-B_C8sOoT.js";
19
+ import { _ as _15 } from "./chunks/LpCodeBlock-CXsqJwDy.js";
20
20
  import { tokenizeLines } from "./components/codeHighlight.js";
21
- import { _ as _16 } from "./chunks/LpColorPicker-D1sQqtXS.js";
21
+ import { _ as _16 } from "./chunks/LpColorPicker-DQ88niXq.js";
22
22
  import { DEFAULT_SWATCHES, clampHsv, formatColor, hsvToHsl, hsvToRgb, isLight, normalizeHue, opaqueCss, parseColor, parseHex, rgbToHex, rgbToHsv } from "./components/color.js";
23
23
  import { default as default3 } from "./components/LpCommandPalette.vue.js";
24
24
  import { applyQueryFilters, highlightParts, parseCommandQuery, parsePaletteQuery, rankCommandItems } from "./components/commandSearch.js";
@@ -30,46 +30,46 @@ import { default as default6 } from "./components/LpPaletteStats.vue.js";
30
30
  import { default as default7 } from "./components/LpPaletteSwatches.vue.js";
31
31
  import { default as default8 } from "./components/LpKeys.vue.js";
32
32
  import { comboKeys, comboSteps } from "./components/keys.js";
33
- import { _ as _18 } from "./chunks/LpConfirmDialog-CrbUZgJE.js";
34
- import { _ as _19 } from "./chunks/LpConfirmHost-BgMIV8r9.js";
33
+ import { _ as _18 } from "./chunks/LpConfirmDialog-DxlSdjmk.js";
34
+ import { _ as _19 } from "./chunks/LpConfirmHost-BcrmTNu_.js";
35
35
  import { confirm, useConfirm } from "./components/confirm.js";
36
- import { _ as _20 } from "./chunks/LpContextMenu-EF_90u3g.js";
37
- import { _ as _21 } from "./chunks/LpDatePicker-BrdFtSTy.js";
38
- import { _ as _22 } from "./chunks/LpDateRangePicker-Drd7EbIz.js";
39
- import { _ as _23 } from "./chunks/LpRangeCalendar-DZuaRV0W.js";
36
+ import { _ as _20 } from "./chunks/LpContextMenu-B1NGfpp1.js";
37
+ import { _ as _21 } from "./chunks/LpDatePicker-B-VMHMgy.js";
38
+ import { _ as _22 } from "./chunks/LpDateRangePicker-B68KPKPl.js";
39
+ import { _ as _23 } from "./chunks/LpRangeCalendar-D5sDID9W.js";
40
40
  import { clampRange, defaultRangePresets, formatRange, normalizeRange, sameRange } from "./components/dateRange.js";
41
- import { _ as _24 } from "./chunks/LpDisclosure-Cc23-JOT.js";
41
+ import { _ as _24 } from "./chunks/LpDisclosure-Ct-VBHxt.js";
42
42
  import { _ as _25 } from "./chunks/LpDivider-CUX46KR0.js";
43
- import { _ as _26 } from "./chunks/LpDrawer-CBFVD624.js";
44
- import { _ as _27 } from "./chunks/LpDropdownMenu-DrpJJBp0.js";
43
+ import { _ as _26 } from "./chunks/LpDrawer-DSknigxH.js";
44
+ import { _ as _27 } from "./chunks/LpDropdownMenu-C7oHVlKY.js";
45
45
  import { default as default9 } from "./components/LpDropzone.vue.js";
46
- import { _ as _28 } from "./chunks/LpEmptyState-BJbZXC8D.js";
47
- import { _ as _29 } from "./chunks/LpFileTree-3E4w4Rjr.js";
46
+ import { _ as _28 } from "./chunks/LpEmptyState-CBwwkkdA.js";
47
+ import { _ as _29 } from "./chunks/LpFileTree-CMA0_rsB.js";
48
48
  import { ancestorIds, checkStateOf, checkedCount, computeStats, fileIcon, formatModified, formatSize, sortNodes, subtreeIds, subtreeSize } from "./components/fileTree.js";
49
- import { _ as _30 } from "./chunks/LpFieldNotice-wtieLUTj.js";
50
- import { _ as _31 } from "./chunks/LpFormField-CtVYj6tO.js";
51
- import { _ as _32 } from "./chunks/LpIcon-DYtaid0K.js";
52
- import { i, b, a, c } from "./chunks/icons-CkA0njzm.js";
49
+ import { _ as _30 } from "./chunks/LpFieldNotice-9hH4ITZa.js";
50
+ import { _ as _31 } from "./chunks/LpFormField-BpQ6WJPe.js";
51
+ import { _ as _32 } from "./chunks/LpIcon-B5guGKrW.js";
52
+ import { i, b, a, c } from "./chunks/icons-QqBscG5I.js";
53
53
  import { default as default10 } from "./components/LpImageEditor.vue.js";
54
54
  import { _ as _33 } from "./chunks/LpInfraNode-Ds_8SZgN.js";
55
- import { _ as _34 } from "./chunks/LpInput-D-YGQbEv.js";
56
- import { _ as _35 } from "./chunks/LpLightbox-EVXzAq62.js";
55
+ import { _ as _34 } from "./chunks/LpInput-WvyP02ID.js";
56
+ import { _ as _35 } from "./chunks/LpLightbox-Dq-e36uT.js";
57
57
  import { fileNameOf } from "./components/lightbox.js";
58
58
  import { _ as _36 } from "./chunks/LpLink-Krkjhe_y.js";
59
- import { _ as _37 } from "./chunks/LpLogViewer-BfgtXVKm.js";
59
+ import { _ as _37 } from "./chunks/LpLogViewer-DwiYE32z.js";
60
60
  import { countLevels, matchPositions, matchesTerm, ringWindow, stepMatch } from "./components/logSearch.js";
61
- import { _ as _38 } from "./chunks/LpMediaGallery-BnlkN_1f.js";
62
- import { _ as _39 } from "./chunks/LpModal-JNa3B_Lr.js";
63
- import { _ as _40 } from "./chunks/LpModalNav-DXBilXr0.js";
64
- import { _ as _41 } from "./chunks/LpNotificationBell-DEL-RoHS.js";
65
- import { _ as _42 } from "./chunks/LpNumberField-B77O-z28.js";
66
- import { _ as _43 } from "./chunks/LpTimeField-Cqe7fXnh.js";
61
+ import { _ as _38 } from "./chunks/LpMediaGallery-B1kDypJ-.js";
62
+ import { _ as _39 } from "./chunks/LpModal-BxYssnXg.js";
63
+ import { _ as _40 } from "./chunks/LpModalNav-Bn2ws64b.js";
64
+ import { _ as _41 } from "./chunks/LpNotificationBell-DJfs4JjQ.js";
65
+ import { _ as _42 } from "./chunks/LpNumberField-CJPjmoDz.js";
66
+ import { _ as _43 } from "./chunks/LpTimeField-DvGXgdES.js";
67
67
  import { _ as _44 } from "./chunks/LpNumberFlow-z4WPZcZu.js";
68
68
  import { _ as _45 } from "./chunks/LpOtpInput-PbeNJ5In.js";
69
- import { _ as _46 } from "./chunks/LpPagination-BcDlo3r_.js";
70
- import { _ as _47 } from "./chunks/LpPasswordInput-eKzQhbQP.js";
71
- import { _ as _48 } from "./chunks/LpPie-B7ti5qCH.js";
72
- import { _ as _49 } from "./chunks/LpPhoneInput-BCbrrhgE.js";
69
+ import { _ as _46 } from "./chunks/LpPagination-DehAaJLY.js";
70
+ import { _ as _47 } from "./chunks/LpPasswordInput-Bg5uTZbx.js";
71
+ import { _ as _48 } from "./chunks/LpPie-DeTkbbOK.js";
72
+ import { _ as _49 } from "./chunks/LpPhoneInput-DStwcbnQ.js";
73
73
  import { filterCountries, flagEmoji, loadCountries, matchCountryByValue } from "./components/countries.js";
74
74
  import { _ as _50 } from "./chunks/LpPopover-DCKeqiVn.js";
75
75
  import { _ as _51 } from "./chunks/LpProgress-CBU1iI69.js";
@@ -77,36 +77,41 @@ import { _ as _52 } from "./chunks/LpRadio-CWA5Eb3M.js";
77
77
  import { _ as _53 } from "./chunks/LpRadioGroup-CCn-Q2_c.js";
78
78
  import { _ as _54 } from "./chunks/LpRollingText-DB7upf-5.js";
79
79
  import { _ as _55 } from "./chunks/LpScrollArea-Hz3-JWUG.js";
80
- import { _ as _56 } from "./chunks/LpSegmented-C09EEam2.js";
81
- import { _ as _57 } from "./chunks/LpSection-tKEH0jYf.js";
82
- import { _ as _58 } from "./chunks/LpSelect-DNIL6MB9.js";
80
+ import { _ as _56 } from "./chunks/LpSegmented-D3Eam8sa.js";
81
+ import { _ as _57 } from "./chunks/LpSection-B19VPYmr.js";
82
+ import { _ as _58 } from "./chunks/LpSelect-DynjWK1A.js";
83
83
  import { _ as _59 } from "./chunks/LpServiceNode-D8uJ8sGk.js";
84
84
  import { _ as _60 } from "./chunks/LpShift-DXNsauQZ.js";
85
85
  import { default as default11 } from "./components/LpShortcutsDialog.vue.js";
86
- import { _ as _61 } from "./chunks/LpSidebar-C2oolgwy.js";
86
+ import { _ as _61 } from "./chunks/LpSidebar-CshW0fqC.js";
87
87
  import { _ as _62 } from "./chunks/LpSkeleton-BB6XIV-w.js";
88
88
  import { _ as _63 } from "./chunks/LpSlider-Cuzmrd7p.js";
89
89
  import { _ as _64 } from "./chunks/LpRangeSlider-C1yF9-nP.js";
90
90
  import { _ as _65 } from "./chunks/LpVideoBackdrop-qWRYBOTQ.js";
91
91
  import { formatClipTime, isNativeLoop, planLoop, seamProgress, seamReached } from "./components/videoBackdrop.js";
92
92
  import { _ as _66 } from "./chunks/LpSparkline-CB80N8SJ.js";
93
- import { _ as _67 } from "./chunks/LpStat-B-7udBRP.js";
94
- import { _ as _68 } from "./chunks/LpStepper-C7XiVKZj.js";
93
+ import { _ as _67 } from "./chunks/LpStat-BS2TEXYK.js";
94
+ import { _ as _68 } from "./chunks/LpStepper-BSglWF45.js";
95
95
  import { _ as _69 } from "./chunks/LpSwitch-DG2AhXIL.js";
96
96
  import { default as default12 } from "./components/LpTable.vue.js";
97
97
  import { compareValues } from "./components/tableSort.js";
98
- import { _ as _70 } from "./chunks/LpTableOfContents-BUcAfGPQ.js";
99
- import { _ as _71 } from "./chunks/LpTabs-BMSBB78h.js";
100
- import { _ as _72 } from "./chunks/LpTagsInput-D1SuqbhB.js";
98
+ import { _ as _70 } from "./chunks/LpTableOfContents-DYuqkByy.js";
99
+ import { _ as _71 } from "./chunks/LpTabs-BBGRHY38.js";
100
+ import { _ as _72 } from "./chunks/LpTagsInput-CHbQoAAA.js";
101
101
  import { addTags, splitTags } from "./components/tags.js";
102
- import { _ as _73 } from "./chunks/LpTextarea-uuScWo6-.js";
103
- import { _ as _74 } from "./chunks/LpThemeSwitcher-Fz85BCRS.js";
102
+ import { _ as _73 } from "./chunks/LpTextarea-Bz2IWhTH.js";
103
+ import { _ as _74 } from "./chunks/LpThemeSwitcher-B_qhSl7q.js";
104
104
  import { _ as _75 } from "./chunks/LpTilt-B7KHX1zH.js";
105
- import { _ as _76 } from "./chunks/LpToaster-DTtWuKvq.js";
106
- import { _ as _77 } from "./chunks/LpTooltip-CXVKcOXT.js";
107
- import { _ as _78 } from "./chunks/LpTooltipProvider-CftjV7Xc.js";
108
- import { _ as _79 } from "./chunks/LpTopologyCanvas-6ozNrQKh.js";
109
- import { _ as _80 } from "./chunks/LpUptimeBar-ugcv25xj.js";
105
+ import { _ as _76 } from "./chunks/LpToaster-Bo9Q4QBP.js";
106
+ import { _ as _77 } from "./chunks/LpTooltip-suFCM34F.js";
107
+ import { _ as _78 } from "./chunks/LpTour-D7m9ZxNk.js";
108
+ import { holeOf, openContextMenu, panelsAround } from "./components/tour.js";
109
+ import { _ as _79 } from "./chunks/LpHintLayer-B5-6FyEf.js";
110
+ import { _ as _80 } from "./chunks/LpHints-BqoZXHZk.js";
111
+ import { createHintSystem, hintKey, hintSummary, localHintStorage, provideHintSystem, useHintSystem } from "./composables/useHintSystem.js";
112
+ import { _ as _81 } from "./chunks/LpTooltipProvider-CftjV7Xc.js";
113
+ import { _ as _82 } from "./chunks/LpTopologyCanvas-6ozNrQKh.js";
114
+ import { _ as _83 } from "./chunks/LpUptimeBar-B4ltCfno.js";
110
115
  import { deserializeLayout, serializeLayout } from "./layout/tree.js";
111
116
  import { addLeaf, addTab, countBlocks, countLeaves, makeLayout, moveLeaf, removeBlock, removeLeaf, reorderTab, resizeAt, setActiveTab } from "./layout/tree.js";
112
117
  import { blockTitle, defineBlocks } from "./layout/registry.js";
@@ -127,6 +132,9 @@ import { LP_FIELD, fieldMessageId, provideField, useField, useFieldControl } fro
127
132
  import { FIELD_NOTICE_MS, useFieldNotice } from "./composables/useFieldNotice.js";
128
133
  import { easeOut, easeTravel, easingToken, prefersReducedMotion } from "./composables/easing.js";
129
134
  import { useReveal } from "./composables/useReveal.js";
135
+ import { hints, vHint } from "./composables/useHints.js";
136
+ import { scrollParent, scrollToElement } from "./composables/scroll.js";
137
+ import { useMediaQuery } from "./composables/useMediaQuery.js";
130
138
  import { useAsyncTask } from "./composables/useAsyncTask.js";
131
139
  import { useRovingFocus } from "./composables/useRovingFocus.js";
132
140
  import { useFormDraft } from "./composables/useFormDraft.js";
@@ -137,7 +145,7 @@ import { useShift } from "./composables/useShift.js";
137
145
  import { useTilt } from "./composables/useTilt.js";
138
146
  import { useZoomPan } from "./composables/useZoomPan.js";
139
147
  import { useToast } from "./composables/useToast.js";
140
- import { _ as _81 } from "./chunks/LpThemeProvider-CQ2jC9TA.js";
148
+ import { _ as _84 } from "./chunks/LpThemeProvider-CQ2jC9TA.js";
141
149
  import { isInternalHref, resolveLink } from "./components/link.js";
142
150
  function defineTheme(base, overrides = {}) {
143
151
  const src = typeof base === "string" ? presets[base] : base;
@@ -209,6 +217,8 @@ export {
209
217
  _30 as LpFieldNotice,
210
218
  _29 as LpFileTree,
211
219
  _31 as LpFormField,
220
+ _79 as LpHintLayer,
221
+ _80 as LpHints,
212
222
  _32 as LpIcon,
213
223
  default10 as LpImageEditor,
214
224
  _33 as LpInfraNode,
@@ -260,15 +270,16 @@ export {
260
270
  _71 as LpTabs,
261
271
  _72 as LpTagsInput,
262
272
  _73 as LpTextarea,
263
- _81 as LpThemeProvider,
273
+ _84 as LpThemeProvider,
264
274
  _74 as LpThemeSwitcher,
265
275
  _75 as LpTilt,
266
276
  _43 as LpTimeField,
267
277
  _76 as LpToaster,
268
278
  _77 as LpTooltip,
269
- _78 as LpTooltipProvider,
270
- _79 as LpTopologyCanvas,
271
- _80 as LpUptimeBar,
279
+ _81 as LpTooltipProvider,
280
+ _82 as LpTopologyCanvas,
281
+ _78 as LpTour,
282
+ _83 as LpUptimeBar,
272
283
  _65 as LpVideoBackdrop,
273
284
  N as NO_BACKDROP_ATTR,
274
285
  OSM_TILES,
@@ -306,6 +317,7 @@ export {
306
317
  countBlocks,
307
318
  countLeaves,
308
319
  countLevels,
320
+ createHintSystem,
309
321
  createNominatimProvider,
310
322
  csvField,
311
323
  g as dark,
@@ -336,6 +348,10 @@ export {
336
348
  formatSize,
337
349
  formatTick,
338
350
  highlightParts,
351
+ hintKey,
352
+ hintSummary,
353
+ hints,
354
+ holeOf,
339
355
  hotkeyConflicts,
340
356
  hotkeySteps,
341
357
  hsvToHsl,
@@ -351,6 +367,7 @@ export {
351
367
  l as light,
352
368
  j as lime,
353
369
  loadCountries,
370
+ localHintStorage,
354
371
  lpConfig,
355
372
  makeLayout,
356
373
  markerIndex,
@@ -370,6 +387,8 @@ export {
370
387
  normalizeRange,
371
388
  k as normalizeTheme,
372
389
  opaqueCss,
390
+ openContextMenu,
391
+ panelsAround,
373
392
  parseColor,
374
393
  parseCommandQuery,
375
394
  parseConfig,
@@ -385,6 +404,7 @@ export {
385
404
  project,
386
405
  provideField,
387
406
  provideGlass,
407
+ provideHintSystem,
388
408
  provideLpConfig,
389
409
  pushRecent,
390
410
  rankCommandItems,
@@ -405,6 +425,8 @@ export {
405
425
  ringWindow,
406
426
  o as rose,
407
427
  sameRange,
428
+ scrollParent,
429
+ scrollToElement,
408
430
  seamProgress,
409
431
  seamReached,
410
432
  sequenceProgress,
@@ -441,6 +463,7 @@ export {
441
463
  useFormDraft,
442
464
  useGamepadNav,
443
465
  useGlass,
466
+ useHintSystem,
444
467
  useHotkeys,
445
468
  useInputFilter,
446
469
  useInputMask,
@@ -448,6 +471,7 @@ export {
448
471
  useLayout,
449
472
  useLinkComponent,
450
473
  useLpConfig,
474
+ useMediaQuery,
451
475
  useMergedAttrs,
452
476
  useMergedClass,
453
477
  useModalLayer,
@@ -460,5 +484,6 @@ export {
460
484
  useTilt,
461
485
  useToast,
462
486
  useZoomPan,
487
+ vHint,
463
488
  y as violet
464
489
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leavepulse/ui",
3
- "version": "0.56.0",
3
+ "version": "0.58.0",
4
4
  "type": "module",
5
5
  "description": "LeavePulse token-driven Vue component kit.",
6
6
  "repository": {
@@ -33,6 +33,8 @@ export const COMPONENT_NAMES = [
33
33
  "LpFieldNotice",
34
34
  "LpFileTree",
35
35
  "LpFormField",
36
+ "LpHintLayer",
37
+ "LpHints",
36
38
  "LpIcon",
37
39
  "LpImageEditor",
38
40
  "LpInfraNode",
@@ -92,6 +94,7 @@ export const COMPONENT_NAMES = [
92
94
  "LpTooltip",
93
95
  "LpTooltipProvider",
94
96
  "LpTopologyCanvas",
97
+ "LpTour",
95
98
  "LpUptimeBar",
96
99
  "LpVideoBackdrop"
97
100
  ] as const
@@ -10,11 +10,12 @@ export default { inheritAttrs: false }
10
10
  * Two months sit side by side where there is room, one on a phone.
11
11
  */
12
12
  import { PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from "reka-ui"
13
- import { computed, onBeforeUnmount, onMounted, ref, useAttrs } from "vue"
13
+ import { computed, ref, useAttrs } from "vue"
14
14
  import { useFieldControl } from "../composables/useField"
15
15
  import { useLocale } from "../composables/useLocale"
16
16
  import { useLabels } from "../composables/useLpConfig"
17
17
  import { mergeClass } from "../composables/useMergedClass"
18
+ import { useMediaQuery } from "../composables/useMediaQuery"
18
19
  import {
19
20
  clampRange,
20
21
  type DateRange,
@@ -113,17 +114,7 @@ function presetFits(preset: DateRangePreset): boolean {
113
114
  return !!clampRange(preset.range(), props.min, props.max)
114
115
  }
115
116
 
116
- const wide = ref(false)
117
- let query: MediaQueryList | undefined
118
- const onQuery = () => {
119
- wide.value = !!query?.matches
120
- }
121
- onMounted(() => {
122
- query = window.matchMedia("(min-width: 640px)")
123
- onQuery()
124
- query.addEventListener("change", onQuery)
125
- })
126
- onBeforeUnmount(() => query?.removeEventListener("change", onQuery))
117
+ const wide = useMediaQuery("(min-width: 640px)")
127
118
 
128
119
  const shellSize = {
129
120
  sm: "h-(--size-control-sm) text-xs",
@@ -34,6 +34,7 @@ import {
34
34
  } from "vaul-vue"
35
35
  import { computed, onBeforeUnmount, ref, useSlots, watch } from "vue"
36
36
  import { CLOSE_ICON } from "./dropdown"
37
+ import { prefersReducedMotion } from "../composables/easing"
37
38
  import { useGlass } from "../composables/useGlass"
38
39
  import { useLabels } from "../composables/useLpConfig"
39
40
  import LpIcon from "./LpIcon.vue"
@@ -328,10 +329,6 @@ function edgeEnabled(): boolean {
328
329
  let track: { id: number; x: number; y: number; t: number; lastX: number; lastT: number; speed: number } | null = null
329
330
  const owner = Symbol("edge-pull")
330
331
 
331
- function reducedMotion(): boolean {
332
- return typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches
333
- }
334
-
335
332
  // A horizontally scrollable ancestor that can still move the way the finger
336
333
  // pulls (a carousel, a wide table) keeps the gesture: the pull only claims
337
334
  // what nothing under the finger wants.
@@ -414,7 +411,7 @@ function settle() {
414
411
  peek.value = 0
415
412
  }, 60),
416
413
  )
417
- }, reducedMotion() ? 0 : 180),
414
+ }, prefersReducedMotion() ? 0 : 180),
418
415
  )
419
416
  } else {
420
417
  peek.value = 0
@@ -462,7 +459,7 @@ const previewStyle = computed(() => {
462
459
  const axis = dir.value === "right" ? off : -off
463
460
  return {
464
461
  transform: `translateX(${axis}%)`,
465
- transition: peeking.value || reducedMotion() ? "none" : "transform 0.18s cubic-bezier(0.32, 0.72, 0, 1)",
462
+ transition: peeking.value || prefersReducedMotion() ? "none" : "transform 0.18s cubic-bezier(0.32, 0.72, 0, 1)",
466
463
  pointerEvents: "none" as const,
467
464
  }
468
465
  })
@@ -477,7 +474,7 @@ const previewStyle = computed(() => {
477
474
  v-show="peeking || peek > 0"
478
475
  class="lp-scrim pointer-events-none fixed inset-0"
479
476
  :class="{ 'lp-scrim--light': frosted, 'lp-scrim--nested': layer.nested }"
480
- :style="{ zIndex: layer.scrim, opacity: peek, transition: peeking || reducedMotion() ? 'none' : 'opacity 0.18s ease-out' }"
477
+ :style="{ zIndex: layer.scrim, opacity: peek, transition: peeking || prefersReducedMotion() ? 'none' : 'opacity 0.18s ease-out' }"
481
478
  />
482
479
  <div
483
480
  v-show="peeking || peek > 0"
@@ -0,0 +1,148 @@
1
+ <script lang="ts">
2
+ export interface HintNote {
3
+ title: string
4
+ text?: string
5
+ }
6
+ </script>
7
+
8
+ <script setup lang="ts">
9
+ /*
10
+ * Quiet "new" markers over `v-hint` elements, with a short note when the user
11
+ * reaches for one. Nothing pops up by itself: a dot sits on the control, and
12
+ * the note opens only when the pointer or focus lands on it. Reading the note
13
+ * (or simply using the control) is the app's cue to stop marking it.
14
+ *
15
+ * The layer only draws. Which ids are new, and when a read counts, is decided
16
+ * by the app from the registry's seen/used events.
17
+ */
18
+ import { PopoverAnchor, PopoverContent, PopoverPortal, PopoverRoot } from "reka-ui"
19
+ import { computed, onBeforeUnmount, onMounted, ref, shallowRef, watch } from "vue"
20
+ import { hints } from "../composables/useHints"
21
+ import { useLabels } from "../composables/useLpConfig"
22
+ import { PANEL_SURFACE, POPOVER_ANIM } from "./dropdown"
23
+ import LpButton from "./LpButton.vue"
24
+
25
+ const props = withDefaults(
26
+ defineProps<{
27
+ /** Ids to mark, most important first. */
28
+ fresh: string[]
29
+ notes: Record<string, HintNote>
30
+ /** At most this many dots at once, so a page full of news stays calm. */
31
+ limit?: number
32
+ labels?: Partial<typeof DEFAULT_LABELS>
33
+ }>(),
34
+ { limit: 3 },
35
+ )
36
+
37
+ const emit = defineEmits<{ (e: "read", id: string): void }>()
38
+
39
+ const DEFAULT_LABELS = { gotIt: "Got it", badge: "New" }
40
+ const labels = useLabels("hintLayer", DEFAULT_LABELS, () => props.labels)
41
+
42
+ const OPEN_DELAY_MS = 350
43
+
44
+ const shown = computed(() => props.fresh.filter((id) => hints.present(id) && props.notes[id]).slice(0, props.limit))
45
+ const spots = ref<{ id: string; x: number; y: number }[]>([])
46
+ const openId = ref<string | null>(null)
47
+ const anchor = shallowRef<Element | null>(null)
48
+ let frame = 0
49
+ let timer: ReturnType<typeof setTimeout> | undefined
50
+
51
+ function place() {
52
+ spots.value = shown.value.flatMap((id) => {
53
+ const el = hints.element(id)
54
+ if (!el) return []
55
+ const r = el.getBoundingClientRect()
56
+ return r.width || r.height ? [{ id, x: r.right, y: r.top }] : []
57
+ })
58
+ frame = shown.value.length ? requestAnimationFrame(place) : 0
59
+ }
60
+
61
+ watch(
62
+ shown,
63
+ (ids) => {
64
+ if (ids.length && !frame) frame = requestAnimationFrame(place)
65
+ if (openId.value && !ids.includes(openId.value)) openId.value = null
66
+ },
67
+ { immediate: true },
68
+ )
69
+
70
+ function reveal(id: string) {
71
+ clearTimeout(timer)
72
+ timer = setTimeout(() => {
73
+ if (!shown.value.includes(id)) return
74
+ anchor.value = hints.element(id)
75
+ openId.value = anchor.value ? id : null
76
+ }, OPEN_DELAY_MS)
77
+ }
78
+
79
+ function read() {
80
+ const id = openId.value
81
+ openId.value = null
82
+ if (id) emit("read", id)
83
+ }
84
+
85
+ let off: (() => void)[] = []
86
+ onMounted(() => {
87
+ off = [
88
+ hints.on("hover", (id) => {
89
+ if (shown.value.includes(id)) reveal(id)
90
+ }),
91
+ hints.on("used", (id) => {
92
+ if (!shown.value.includes(id)) return
93
+ clearTimeout(timer)
94
+ if (openId.value === id) openId.value = null
95
+ emit("read", id)
96
+ }),
97
+ ]
98
+ })
99
+
100
+ onBeforeUnmount(() => {
101
+ cancelAnimationFrame(frame)
102
+ clearTimeout(timer)
103
+ for (const stop of off) stop()
104
+ })
105
+
106
+ const note = computed(() => (openId.value ? props.notes[openId.value] : undefined))
107
+ </script>
108
+
109
+ <template>
110
+ <Teleport to="body">
111
+ <button
112
+ v-for="spot in spots"
113
+ :key="spot.id"
114
+ type="button"
115
+ class="fixed z-(--z-popover) -translate-x-1/2 -translate-y-1/2 rounded-full p-1"
116
+ :style="{ left: `${spot.x}px`, top: `${spot.y}px` }"
117
+ :aria-label="`${labels.badge}: ${notes[spot.id]?.title ?? ''}`"
118
+ @pointerenter="reveal(spot.id)"
119
+ @focus="reveal(spot.id)"
120
+ @click="reveal(spot.id)"
121
+ >
122
+ <span class="relative flex size-2">
123
+ <span class="absolute inline-flex size-full animate-ping rounded-full bg-brand opacity-60 motion-reduce:animate-none" />
124
+ <span class="relative inline-flex size-2 rounded-full bg-brand ring-2 ring-surface" />
125
+ </span>
126
+ </button>
127
+ <PopoverRoot :open="!!openId" @update:open="(value) => !value && (openId = null)">
128
+ <PopoverAnchor v-if="anchor" :reference="anchor" />
129
+ <PopoverPortal>
130
+ <PopoverContent
131
+ side="bottom"
132
+ :side-offset="8"
133
+ :collision-padding="8"
134
+ :class="[PANEL_SURFACE, POPOVER_ANIM, 'z-(--z-popover) w-72 rounded-card p-3 text-sm text-ink outline-none']"
135
+ >
136
+ <p class="flex items-center gap-2 font-medium">
137
+ <span class="rounded-full bg-brand/15 px-1.5 text-[11px] text-brand">{{ labels.badge }}</span>
138
+ {{ note?.title }}
139
+ </p>
140
+ <p v-if="note?.text" class="mt-1.5 text-muted">{{ note.text }}</p>
141
+ <div class="mt-3 flex justify-end">
142
+ <LpButton size="sm" @click="read">{{ labels.gotIt }}</LpButton>
143
+ </div>
144
+ </PopoverContent>
145
+ </PopoverPortal>
146
+ </PopoverRoot>
147
+ </Teleport>
148
+ </template>
@@ -0,0 +1,116 @@
1
+ <script setup lang="ts">
2
+ /*
3
+ * Drop-in onboarding: put it once next to the app's root, hand it a hint
4
+ * system, and the tour offer, the "new" dots, "what changed" and nudges all
5
+ * work. Nothing to wire per page beyond `v-hint` on the elements.
6
+ *
7
+ * It must never cost the page itself. Any error thrown inside (a tour step's
8
+ * `enter`, a storage that rejects, a target that misbehaves) switches the layer
9
+ * off for the session and is logged once; the page under it keeps working.
10
+ */
11
+ import { computed, onBeforeUnmount, onErrorCaptured, onMounted, ref, unref, watch } from "vue"
12
+ import { provideHintSystem, type HintSystem } from "../composables/useHintSystem"
13
+ import { useLabels } from "../composables/useLpConfig"
14
+ import LpActionBar, { type ActionBarAction } from "./LpActionBar.vue"
15
+ import LpHintLayer from "./LpHintLayer.vue"
16
+ import LpTour from "./LpTour.vue"
17
+
18
+ const props = defineProps<{
19
+ system: HintSystem
20
+ labels?: Partial<typeof DEFAULT_LABELS>
21
+ }>()
22
+
23
+ const DEFAULT_LABELS = {
24
+ tourTitle: "New to {page}?",
25
+ tourText: "A one-minute look at what you can do here.",
26
+ assignedTitle: "You were asked to look at {page}",
27
+ assignedText: "A short tour of this page, about a minute.",
28
+ newsTitle: "What changed: {page}",
29
+ show: "Show me",
30
+ notNow: "Not now",
31
+ never: "Don't offer again",
32
+ gotIt: "Got it",
33
+ noThanks: "No thanks",
34
+ }
35
+ const labels = useLabels("hints", DEFAULT_LABELS, () => props.labels)
36
+
37
+ provideHintSystem(props.system)
38
+
39
+ const broken = ref(false)
40
+ onErrorCaptured((error) => {
41
+ if (!broken.value) console.warn("[LpHints] disabled after an error:", error)
42
+ broken.value = true
43
+ props.system.stop()
44
+ return false
45
+ })
46
+
47
+ onMounted(() => {
48
+ try {
49
+ props.system.start()
50
+ if (!props.system.state.loaded) void props.system.load()
51
+ } catch (error) {
52
+ broken.value = true
53
+ console.warn("[LpHints] disabled after an error:", error)
54
+ }
55
+ })
56
+ onBeforeUnmount(() => props.system.stop())
57
+
58
+ const state = props.system.state
59
+ const fresh = computed(() => unref(props.system.fresh))
60
+ const notes = computed(() => unref(props.system.notes))
61
+ watch(() => [state.tour.open, state.tour.step] as const, ([open, step]) => open && props.system.tourShown(step))
62
+
63
+ const offer = computed(() => state.offer)
64
+ const page = computed(() => (offer.value ? props.system.pageTitle(offer.value.page) : ""))
65
+ const fill = (text: string) => text.replace("{page}", page.value)
66
+
67
+ const title = computed(() => {
68
+ const o = offer.value
69
+ if (!o) return ""
70
+ if (o.kind === "nudge") return notes.value[o.nudge.feature]?.title ?? ""
71
+ if (o.kind === "news") return fill(labels.value.newsTitle)
72
+ return fill(o.kind === "assigned" ? labels.value.assignedTitle : labels.value.tourTitle)
73
+ })
74
+
75
+ const description = computed(() => {
76
+ const o = offer.value
77
+ if (!o) return ""
78
+ if (o.kind === "nudge") return o.nudge.text
79
+ if (o.kind === "news") return o.items.map((n) => n.text).join(" ")
80
+ return o.kind === "assigned" ? labels.value.assignedText : labels.value.tourText
81
+ })
82
+
83
+ const actions = computed<ActionBarAction[]>(() => {
84
+ const o = offer.value
85
+ if (!o) return []
86
+ const answer = (choice: "show" | "later" | "never") => () => props.system.answer(choice)
87
+ const show: ActionBarAction = { label: labels.value.show, icon: "lucide:sparkles", onClick: answer("show") }
88
+ if (o.kind === "nudge") return [{ label: labels.value.noThanks, variant: "ghost", onClick: answer("never") }, show]
89
+ if (o.kind === "news") return [{ label: labels.value.gotIt, variant: "ghost", onClick: answer("later") }, show]
90
+ return [
91
+ ...(o.kind === "tour" ? [{ label: labels.value.never, variant: "ghost" as const, onClick: answer("never") }] : []),
92
+ { label: labels.value.notNow, variant: "ghost", onClick: answer("later") },
93
+ show,
94
+ ]
95
+ })
96
+ </script>
97
+
98
+ <template>
99
+ <template v-if="!broken">
100
+ <LpTour
101
+ v-model:open="state.tour.open"
102
+ v-model:step="state.tour.step"
103
+ :steps="state.tour.steps"
104
+ @finish="system.tourEnded(true)"
105
+ @dismiss="system.tourEnded(false)"
106
+ />
107
+ <LpHintLayer :fresh="fresh" :notes="notes" @read="system.readFresh" />
108
+ <LpActionBar
109
+ :open="!!offer"
110
+ :title="title"
111
+ :description="description"
112
+ :icon="offer?.kind === 'news' ? 'lucide:gift' : 'lucide:compass'"
113
+ :actions="actions"
114
+ />
115
+ </template>
116
+ </template>