@conduction/nextcloud-vue 2.61.0 → 2.63.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 (186) hide show
  1. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue.js +10 -0
  2. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue.js.map +1 -0
  3. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue2.js +189 -0
  4. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue2.js.map +1 -0
  5. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue3.js +72 -0
  6. package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue3.js.map +1 -0
  7. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +394 -27
  8. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
  9. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
  10. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +99 -9
  11. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  12. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +39 -18
  13. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  14. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +79 -2
  15. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
  16. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +38 -25
  17. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
  18. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue2.js +21 -1
  19. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue2.js.map +1 -1
  20. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue3.js +4 -1
  21. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue3.js.map +1 -1
  22. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
  23. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js +53 -37
  24. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
  25. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +30 -6
  26. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  27. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -2
  28. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  29. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue.js +10 -0
  30. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue.js.map +1 -0
  31. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js +293 -0
  32. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js.map +1 -0
  33. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js +189 -0
  34. package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js.map +1 -0
  35. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +256 -23
  36. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  37. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +119 -51
  38. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  39. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  40. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +2 -1
  41. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  42. package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue2.js +3 -1
  43. package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue2.js.map +1 -1
  44. package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue3.js +1 -1
  45. package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue3.js.map +1 -1
  46. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +234 -9
  47. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  48. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +96 -57
  49. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  50. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +51 -2
  51. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  52. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +5 -1
  53. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  54. package/dist/esm/components/CnIndexPage/selfModeActions.js +3 -3
  55. package/dist/esm/components/CnIndexPage/selfModeActions.js.map +1 -1
  56. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue2.js.map +1 -1
  57. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue3.js.map +1 -1
  58. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +36 -1
  59. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  60. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -1
  61. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  62. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +53 -0
  63. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  64. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +22 -5
  65. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  66. package/dist/esm/components/CnReportsPage/CnReportsPage.vue2.js.map +1 -1
  67. package/dist/esm/components/CnReportsPage/CnReportsPage.vue3.js.map +1 -1
  68. package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue2.js +121 -12
  69. package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue2.js.map +1 -1
  70. package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue3.js +3 -1
  71. package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue3.js.map +1 -1
  72. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +326 -1
  73. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  74. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +112 -26
  75. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  76. package/dist/esm/components/CnStorePage/CnStorePage.vue2.js.map +1 -1
  77. package/dist/esm/components/CnStorePage/CnStorePage.vue3.js.map +1 -1
  78. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js +10 -0
  79. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js.map +1 -0
  80. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +337 -0
  81. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -0
  82. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js +102 -0
  83. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -0
  84. package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js +34 -0
  85. package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js.map +1 -0
  86. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +131 -8
  87. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
  88. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js +40 -38
  89. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
  90. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +8 -6
  91. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
  92. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  93. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  94. package/dist/esm/components/CnWikiPage/CnWikiPage.vue2.js.map +1 -1
  95. package/dist/esm/components/CnWikiPage/CnWikiPage.vue3.js.map +1 -1
  96. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js +19 -0
  97. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js.map +1 -1
  98. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js +1 -1
  99. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js.map +1 -1
  100. package/dist/esm/composables/useDependencyCheck.js +65 -0
  101. package/dist/esm/composables/useDependencyCheck.js.map +1 -0
  102. package/dist/esm/composables/useWalkthrough.js +218 -6
  103. package/dist/esm/composables/useWalkthrough.js.map +1 -1
  104. package/dist/esm/index.js +2 -0
  105. package/dist/esm/index.js.map +1 -1
  106. package/dist/esm/l10n/en.json.js +48 -0
  107. package/dist/esm/l10n/en.json.js.map +1 -1
  108. package/dist/esm/l10n/nl.json.js +48 -0
  109. package/dist/esm/l10n/nl.json.js.map +1 -1
  110. package/dist/esm/nextcloud-vue.css +557 -9
  111. package/dist/esm/store/useObjectStore.js +8 -0
  112. package/dist/esm/store/useObjectStore.js.map +1 -1
  113. package/dist/esm/utils/columnFilters.js +311 -0
  114. package/dist/esm/utils/columnFilters.js.map +1 -0
  115. package/dist/esm/utils/groupAutocomplete.js +123 -0
  116. package/dist/esm/utils/groupAutocomplete.js.map +1 -0
  117. package/dist/esm/utils/libraryWidgetKeys.js +2 -0
  118. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  119. package/dist/esm/utils/pickerOptions.js +198 -0
  120. package/dist/esm/utils/pickerOptions.js.map +1 -0
  121. package/dist/esm/utils/schema.js +83 -7
  122. package/dist/esm/utils/schema.js.map +1 -1
  123. package/dist/esm/utils/timelineEvents.js +174 -0
  124. package/dist/esm/utils/timelineEvents.js.map +1 -0
  125. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  126. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  127. package/dist/esm/utils/walkthroughSignals.js +88 -0
  128. package/dist/esm/utils/walkthroughSignals.js.map +1 -0
  129. package/dist/nextcloud-vue.cjs.js +9779 -5236
  130. package/dist/nextcloud-vue.cjs.js.map +2 -2
  131. package/dist/nextcloud-vue.css +557 -9
  132. package/l10n/en.json +48 -0
  133. package/l10n/nl.json +48 -0
  134. package/package.json +3 -2
  135. package/scripts/manifest-schema-hash.js +41 -0
  136. package/scripts/record-manifest-schema-hash.js +52 -0
  137. package/src/components/CnAdminActionCard/CnAdminActionCard.vue +234 -0
  138. package/src/components/CnAdminActionCard/index.js +4 -0
  139. package/src/components/CnAppNav/CnAppNav.vue +394 -27
  140. package/src/components/CnAppRoot/CnAppRoot.vue +129 -15
  141. package/src/components/CnBannerWidget/CnBannerWidget.vue +113 -2
  142. package/src/components/CnBrandStripe/CnBrandStripe.md +16 -0
  143. package/src/components/CnBrandStripe/CnBrandStripe.vue +58 -5
  144. package/src/components/CnChoiceCards/CnChoiceCards.vue +56 -31
  145. package/src/components/CnDashboardPage/CnDashboardPage.vue +32 -8
  146. package/src/components/CnDataTable/CnColumnFilterPopover.vue +524 -0
  147. package/src/components/CnDataTable/CnDataTable.md +4 -1
  148. package/src/components/CnDataTable/CnDataTable.vue +302 -21
  149. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +2 -2
  150. package/src/components/CnFieldHelper/CnFieldHelper.vue +4 -2
  151. package/src/components/CnFormDialog/CnFormDialog.vue +290 -31
  152. package/src/components/CnIndexPage/CnIndexPage.vue +55 -2
  153. package/src/components/CnIndexPage/selfModeActions.js +3 -3
  154. package/src/components/CnLinkCardsPage/CnLinkCardsPage.vue +35 -2
  155. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +39 -1
  156. package/src/components/CnObjectSidebar/CnFilesTab.vue +65 -0
  157. package/src/components/CnReportsPage/CnReportsPage.vue +7 -0
  158. package/src/components/CnResourceSelect/CnResourceSelect.vue +123 -12
  159. package/src/components/CnSetupWizard/CnSetupWizard.vue +392 -3
  160. package/src/components/CnStorePage/CnStorePage.vue +7 -0
  161. package/src/components/CnTimelineWidget/CnTimelineWidget.vue +477 -0
  162. package/src/components/CnTimelineWidget/dashboardRegistration.js +32 -0
  163. package/src/components/CnTimelineWidget/index.js +4 -0
  164. package/src/components/CnWalkthrough/CnWalkthrough.vue +143 -10
  165. package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
  166. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  167. package/src/components/CnWikiPage/CnWikiPage.vue +7 -0
  168. package/src/components/CnWizardDialog/CnWizardDialog.vue +20 -1
  169. package/src/components/index.js +2 -0
  170. package/src/composables/useDependencyCheck.js +61 -0
  171. package/src/composables/useWalkthrough.js +217 -5
  172. package/src/css/dashboard.css +49 -0
  173. package/src/css/detail-page.css +19 -0
  174. package/src/css/table.css +82 -2
  175. package/src/index.js +2 -0
  176. package/src/public/index.js +5 -0
  177. package/src/schemas/app-manifest-v2.schema.json +23 -1
  178. package/src/store/useObjectStore.js +8 -0
  179. package/src/utils/columnFilters.js +324 -0
  180. package/src/utils/groupAutocomplete.js +119 -0
  181. package/src/utils/libraryWidgetKeys.js +2 -0
  182. package/src/utils/pickerOptions.js +194 -0
  183. package/src/utils/schema.js +83 -7
  184. package/src/utils/timelineEvents.js +171 -0
  185. package/src/utils/validateManifestV2.compiled.js +2 -2
  186. package/src/utils/walkthroughSignals.js +94 -0
@@ -1 +1 @@
1
- {"version":3,"file":"CnWalkthrough.vue3.js","sources":["../../../../src/components/CnWalkthrough/CnWalkthrough.vue?vue&type=template&id=5691d49a&lang.js"],"sourcesContent":["<!-- SPDX-License-Identifier: EUPL-1.2 -->\n<!-- SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl> -->\n<template>\n\t<div v-if=\"active\"\n\t\tclass=\"cn-walkthrough\"\n\t\t:style=\"{ zIndex }\"\n\t\trole=\"dialog\"\n\t\taria-modal=\"true\"\n\t\t:aria-label=\"dialogLabel\">\n\t\t<!-- aria-live announcement of the current step -->\n\t\t<div class=\"cn-walkthrough__live\" aria-live=\"polite\">\n\t\t\t{{ liveText }}\n\t\t</div>\n\n\t\t<!-- Dimmer: a centered step uses one full overlay; an anchored step uses\n\t\t four strips that frame the target rect, leaving a real interactive hole.\n\t\t An ANCHORED step that lost its anchor (target present but not laid\n\t\t out, e.g. inside a closed settings foldout) falls back to the full\n\t\t overlay too — but there it must let clicks THROUGH: the step's own\n\t\t task tells the user to click something in the app underneath, and an\n\t\t interactive backdrop would swallow exactly that click, leaving only\n\t\t Back/Finish usable. The trade-off is deliberate: a passthrough dim\n\t\t cannot be clicked to dismiss the tour, so on such a step the exits\n\t\t are ESC, the coachmark's close button and Back/Finish — a click on\n\t\t the greyed-out area reaches the app, which is the point. A centered\n\t\t step spotlights nothing, so its backdrop stays interactive and\n\t\t dismisses the tour. -->\n\t\t<template v-if=\"isCentered || !rect\">\n\t\t\t<div v-if=\"isCentered\"\n\t\t\t\tclass=\"cn-walkthrough__dim cn-walkthrough__dim--full\"\n\t\t\t\t@click.self=\"onBackdrop\" />\n\t\t\t<!-- No dismiss handler on a passthrough dim: `pointer-events: none`\n\t\t\t means a browser never delivers the click here anyway, and\n\t\t\t leaving the listener attached would make the SAME gesture\n\t\t\t dismiss the tour in any environment that ignores\n\t\t\t pointer-events (jsdom, a forced-colors mode) while reaching the\n\t\t\t app in a browser. One behaviour, not two. -->\n\t\t\t<div v-else\n\t\t\t\tclass=\"cn-walkthrough__dim cn-walkthrough__dim--full cn-walkthrough__dim--passthrough\"\n\t\t\t\taria-hidden=\"true\" />\n\t\t</template>\n\t\t<template v-else>\n\t\t\t<div class=\"cn-walkthrough__dim\" :style=\"strip.top\" @click.self=\"onBackdrop\" />\n\t\t\t<div class=\"cn-walkthrough__dim\" :style=\"strip.bottom\" @click.self=\"onBackdrop\" />\n\t\t\t<div class=\"cn-walkthrough__dim\" :style=\"strip.left\" @click.self=\"onBackdrop\" />\n\t\t\t<div class=\"cn-walkthrough__dim\" :style=\"strip.right\" @click.self=\"onBackdrop\" />\n\t\t\t<div class=\"cn-walkthrough__ring\" :style=\"ringStyle\" aria-hidden=\"true\" />\n\t\t</template>\n\n\t\t<!-- Coachmark card -->\n\t\t<div ref=\"card\"\n\t\t\tclass=\"cn-walkthrough__card\"\n\t\t\t:class=\"`cn-walkthrough__card--${cardPlacement}`\"\n\t\t\t:style=\"cardStyle\">\n\t\t\t<NcButton class=\"cn-walkthrough__close\"\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t:aria-label=\"closeLabel\"\n\t\t\t\t:title=\"closeLabel\"\n\t\t\t\t@click=\"close\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Close :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t</NcButton>\n\t\t\t<!-- @slot coachmark Override the whole coachmark body. Scope: { step, index, total, next, back, skip, skipLabel }.\n\t\t\t The default body has no Skip control (ADR-062 gives that job to\n\t\t\t the corner close button), so `skipLabel` reaches the only place\n\t\t\t a Skip control can live: a host-supplied coachmark. -->\n\t\t\t<slot name=\"coachmark\"\n\t\t\t\t:step=\"step\"\n\t\t\t\t:index=\"index\"\n\t\t\t\t:total=\"total\"\n\t\t\t\t:next=\"advance\"\n\t\t\t\t:back=\"back\"\n\t\t\t\t:skip=\"skip\"\n\t\t\t\t:skipLabel=\"skipLabel\">\n\t\t\t\t<div class=\"cn-walkthrough__counter\">\n\t\t\t\t\t{{ index + 1 }} / {{ total }}\n\t\t\t\t</div>\n\t\t\t\t<h3 v-if=\"stepTitle\" class=\"cn-walkthrough__title\">\n\t\t\t\t\t{{ stepTitle }}\n\t\t\t\t</h3>\n\t\t\t\t<p v-if=\"stepBody\" class=\"cn-walkthrough__body\">\n\t\t\t\t\t{{ stepBody }}\n\t\t\t\t</p>\n\t\t\t\t<p v-if=\"stepTask\" class=\"cn-walkthrough__task\">\n\t\t\t\t\t<span class=\"cn-walkthrough__task-icon\" aria-hidden=\"true\">👉</span> {{ stepTask }}\n\t\t\t\t</p>\n\t\t\t\t<div class=\"cn-walkthrough__actions\">\n\t\t\t\t\t<NcButton v-if=\"!isFirst\" variant=\"secondary\" @click=\"back\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ChevronLeft :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ backLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<span class=\"cn-walkthrough__spacer\" />\n\t\t\t\t\t<NcButton v-if=\"isHandoff\"\n\t\t\t\t\t\tref=\"firstBtn\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\talignment=\"center-reverse\"\n\t\t\t\t\t\t@click=\"doHandoff\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ChevronRight :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ handoffLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton v-else-if=\"showNext\"\n\t\t\t\t\t\tref=\"firstBtn\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\talignment=\"center-reverse\"\n\t\t\t\t\t\t@click=\"advance\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ChevronRight :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ isLast ? finishLabel : nextLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</slot>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton } from '@nextcloud/vue'\nimport ChevronLeft from 'vue-material-design-icons/ChevronLeft.vue'\nimport ChevronRight from 'vue-material-design-icons/ChevronRight.vue'\nimport Close from 'vue-material-design-icons/Close.vue'\nimport { useWalkthrough } from '../../composables/useWalkthrough.js'\n\n/**\n * CnWalkthrough — abstract, manifest-driven product walkthrough (ADR-043).\n *\n * Renders a `manifest.walkthrough` tour as a gray dimmer with a spotlight cutout\n * around one real, interactive element, plus an auto-positioned coachmark card.\n * It never promotes the target's `z-index` — the dimmer is four strips framing\n * the target rect, so the element stays clickable in place regardless of its\n * stacking context. When a target is hidden inside a collapsed `NcAppNavigation`\n * group (children rendered but `display:none`, so unmeasurable), the engine\n * best-effort expands the group to reveal the item before spotlighting it, rather\n * than falling back to a centered coachmark. Step advancement is declarative: the component sources\n * signals (route change via `$router`, a `cn-walkthrough:object-created` window\n * event, an appearing element via `MutationObserver`, a click on the target, or a\n * delay) and feeds them to {@link useWalkthrough}, which captures route params /\n * object ids into a context bag interpolated into later steps.\n *\n * Mount it gated by `CnAppRoot` over the live shell, or standalone with an\n * explicit `tour-id`.\n *\n * ```vue\n * <CnWalkthrough :app-id=\"'pipelinq'\" :manifest=\"manifest\" :seen-version=\"seen\"\n * @complete=\"onComplete\" />\n * ```\n */\nexport default {\n\tname: 'CnWalkthrough',\n\n\tcomponents: { NcButton, Close, ChevronLeft, ChevronRight },\n\n\tprops: {\n\t\t/** The Nextcloud app id (walkthrough machine cache key). */\n\t\tappId: { type: String, required: true },\n\t\t/** The app manifest; `manifest.walkthrough` + `manifest.version` are read. */\n\t\tmanifest: { type: Object, default: null },\n\t\t/** The user's last-seen app version (drives \"what's new\" composition). */\n\t\tseenVersion: { type: String, default: '' },\n\t\t/** Force a specific tour id; when empty the auto-start tour is used. */\n\t\ttourId: { type: String, default: '' },\n\t\t/** Optional resume token `{ tourId, stepId }` (refresh / cross-app hand-off). */\n\t\tresume: { type: Object, default: null },\n\t\t/** Stacking order of the overlay. */\n\t\tzIndex: { type: Number, default: 10000 },\n\t\t/** Next button label. */\n\t\tnextLabel: { type: String, default: () => t('nextcloud-vue', 'Next') },\n\t\t/** Back button label. */\n\t\tbackLabel: { type: String, default: () => t('nextcloud-vue', 'Back') },\n\t\t/** Skip button label. */\n\t\tskipLabel: { type: String, default: () => t('nextcloud-vue', 'Skip') },\n\t\t/** Final-step button label. */\n\t\tfinishLabel: { type: String, default: () => t('nextcloud-vue', 'Finish') },\n\t\t/** Accessible label for the corner close button that ends the tour. */\n\t\tcloseLabel: { type: String, default: () => t('nextcloud-vue', 'Close tour') },\n\t\t/** Optional translate function applied to step title/body/task i18n keys. */\n\t\ttranslate: { type: Function, default: null },\n\t},\n\n\temits: ['complete', 'dismiss', 'step-change', 'advance', 'handoff'],\n\n\tsetup(props) {\n\t\tconst wt = useWalkthrough(props.appId, props.manifest, {\n\t\t\tseenVersion: props.seenVersion,\n\t\t\tresume: props.resume,\n\t\t\tonComplete: undefined,\n\t\t})\n\t\treturn { wt }\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trect: null, // target bounding rect (viewport coords)\n\t\t\tcardPos: { top: 0, left: 0 },\n\t\t\tcardPlacement: 'bottom',\n\t\t\ttargetEl: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tactive() {\n\t\t\treturn this.wt.running.value && !!this.step\n\t\t},\n\n\t\tstep() {\n\t\t\treturn this.wt.currentStep.value\n\t\t},\n\n\t\tindex() {\n\t\t\treturn this.wt.activeTour.value ? this.wt.activeTour.value.steps.findIndex((s) => s.id === (this.step && this.step.id)) : 0\n\t\t},\n\n\t\ttotal() {\n\t\t\treturn this.wt.totalSteps.value\n\t\t},\n\n\t\tisFirst() {\n\t\t\treturn this.wt.isFirst.value\n\t\t},\n\n\t\tisLast() {\n\t\t\treturn this.wt.isLast.value\n\t\t},\n\n\t\tisCentered() {\n\t\t\treturn this.step && this.step.placement === 'center'\n\t\t},\n\n\t\t/**\n\t\t * Whether the Next control is shown. Hidden on an enforced action step\n\t\t * (a task + a non-manual advanceOn) unless `allowManualNext` is set.\n\t\t *\n\t\t * @return {boolean} True when Next should render.\n\t\t */\n\t\tshowNext() {\n\t\t\tif (!this.step) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst enforced = !!this.step.task && this.step.advanceOn && this.step.advanceOn.type !== 'manual'\n\t\t\treturn !enforced || this.step.allowManualNext === true\n\t\t},\n\n\t\t/**\n\t\t * Whether the active step is a cross-app hand-off (declares `handoff.url`).\n\t\t *\n\t\t * @return {boolean} True when the coachmark offers \"Continue in {app}\".\n\t\t */\n\t\tisHandoff() {\n\t\t\treturn !!(this.step && this.step.handoff && this.step.handoff.url)\n\t\t},\n\n\t\thandoffLabel() {\n\t\t\tconst app = this.step && this.step.handoff && this.step.handoff.app\n\t\t\treturn app ? t('nextcloud-vue', 'Continue in {app}', { app }) : t('nextcloud-vue', 'Continue')\n\t\t},\n\n\t\tstepTitle() {\n\t\t\treturn this.tr(this.step && this.step.title)\n\t\t},\n\n\t\tstepBody() {\n\t\t\treturn this.tr(this.step && this.step.body)\n\t\t},\n\n\t\t/**\n\t\t * The task line, through the same `translate` prop as title/body — a\n\t\t * raw `step.task` shipped English even in fully translated locales.\n\t\t *\n\t\t * @return {string} The translated task, or `''` when the step has none.\n\t\t */\n\t\tstepTask() {\n\t\t\treturn this.tr(this.step && this.step.task)\n\t\t},\n\n\t\tdialogLabel() {\n\t\t\treturn this.stepTitle || t('nextcloud-vue', 'Walkthrough')\n\t\t},\n\n\t\tliveText() {\n\t\t\tif (!this.step) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Step {n} of {total}', { n: this.index + 1, total: this.total }) + ': ' + (this.stepTitle || '')\n\t\t},\n\n\t\tringStyle() {\n\t\t\tif (!this.rect) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tconst pad = 6\n\t\t\treturn {\n\t\t\t\ttop: (this.rect.top - pad) + 'px',\n\t\t\t\tleft: (this.rect.left - pad) + 'px',\n\t\t\t\twidth: (this.rect.width + pad * 2) + 'px',\n\t\t\t\theight: (this.rect.height + pad * 2) + 'px',\n\t\t\t}\n\t\t},\n\n\t\tstrip() {\n\t\t\tconst r = this.rect\n\t\t\tif (!r) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tconst pad = 6\n\t\t\tconst top = Math.max(0, r.top - pad)\n\t\t\tconst bottom = r.top + r.height + pad\n\t\t\tconst left = Math.max(0, r.left - pad)\n\t\t\tconst right = r.left + r.width + pad\n\t\t\treturn {\n\t\t\t\ttop: { top: 0, left: 0, width: '100%', height: top + 'px' },\n\t\t\t\tbottom: { top: bottom + 'px', left: 0, width: '100%', bottom: 0 },\n\t\t\t\tleft: { top: top + 'px', left: 0, width: left + 'px', height: (bottom - top) + 'px' },\n\t\t\t\tright: { top: top + 'px', left: right + 'px', right: 0, height: (bottom - top) + 'px' },\n\t\t\t}\n\t\t},\n\n\t\tcardStyle() {\n\t\t\tif (this.isCentered || !this.rect) {\n\t\t\t\treturn { top: '50%', left: '50%', transform: 'translate(-50%, -50%)' }\n\t\t\t}\n\t\t\treturn { top: this.cardPos.top + 'px', left: this.cardPos.left + 'px' }\n\t\t},\n\t},\n\n\twatch: {\n\t\tstep(newStep, oldStep) {\n\t\t\tif (!newStep) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!oldStep || newStep.id !== oldStep.id) {\n\t\t\t\t/**\n\t\t\t\t * @event step-change Emitted when the active step changes.\n\t\t\t\t * @type {{ stepId: string, index: number }}\n\t\t\t\t */\n\t\t\t\tthis.$emit('step-change', { stepId: newStep.id, index: this.index })\n\t\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t\t}\n\t\t},\n\n\t\tactive(now) {\n\t\t\tif (now) {\n\t\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t\t} else {\n\t\t\t\tthis.teardownStep()\n\t\t\t}\n\t\t},\n\t},\n\n\t/**\n\t * Initialise non-reactive instance state (event handler refs, DOM observer\n\t * handles). These use the `_` prefix convention and are set here rather than\n\t * in `data()` to avoid Vue's reserved-key warning for `_`-prefixed fields.\n\t *\n\t * @spec openspec/changes/cn-walkthrough-engine/specs/cn-walkthrough/spec.md\n\t */\n\tcreated() {\n\t\t// Non-reactive instance state: event handlers and DOM watchers.\n\t\t// Not in data() to avoid triggering Vue's reserved-key warning (_prefix).\n\t\tthis._revealAttempted = false\n\t\tthis._observer = null\n\t\tthis._resizeObs = null\n\t\tthis._delayTimer = null\n\t\tthis._onScroll = null\n\t\tthis._onKey = null\n\t\tthis._onObjectCreated = null\n\t\tthis._routeUnhook = null\n\t\t// Layout watchers for the ACTIVE step's target (see trackTargetLayout).\n\t\tthis._layoutObs = null\n\t\tthis._bodyResizeObs = null\n\t\tthis._remeasureRaf = null\n\t\t// A qualifying auto-start tour whose first-step page is NOT the current\n\t\t// route — held here until the user navigates to that page (see hookRouter).\n\t\tthis._pendingAutoTour = null\n\t},\n\n\tmounted() {\n\t\t// Auto-start a qualifying tour unless one is already running.\n\t\tthis.maybeAutoStart()\n\t\tthis._onScroll = () => this.computeRect()\n\t\twindow.addEventListener('scroll', this._onScroll, true)\n\t\twindow.addEventListener('resize', this._onScroll)\n\t\tthis._onKey = (e) => {\n\t\t\tif (e.key === 'Escape') {\n\t\t\t\tthis.onBackdrop()\n\t\t\t}\n\t\t}\n\t\twindow.addEventListener('keydown', this._onKey)\n\t\tthis._onObjectCreated = (e) => this.wt.notify({ kind: 'object-created', object: (e && e.detail) || {} })\n\t\twindow.addEventListener('cn-walkthrough:object-created', this._onObjectCreated)\n\t\tthis.hookRouter()\n\t\tif (this.active) {\n\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\twindow.removeEventListener('scroll', this._onScroll, true)\n\t\twindow.removeEventListener('resize', this._onScroll)\n\t\twindow.removeEventListener('keydown', this._onKey)\n\t\twindow.removeEventListener('cn-walkthrough:object-created', this._onObjectCreated)\n\t\tif (typeof this._routeUnhook === 'function') {\n\t\t\tthis._routeUnhook()\n\t\t}\n\t\tthis.teardownStep()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Translate an i18n key/text via the `translate` prop when given.\n\t\t *\n\t\t * @param {string} value The key or literal.\n\t\t * @return {string} The resolved string.\n\t\t */\n\t\ttr(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn typeof this.translate === 'function' ? this.translate(value) : value\n\t\t},\n\n\t\t/**\n\t\t * Hook `$router.afterEach` (when a router is present) to feed route signals.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\thookRouter() {\n\t\t\tconst router = this.$router\n\t\t\tif (!router || typeof router.afterEach !== 'function') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis._routeUnhook = router.afterEach((to) => {\n\t\t\t\tthis.wt.notify({ kind: 'route', route: to.name, params: to.params || {} })\n\t\t\t\t// A first-visit tour deferred because the user deep-linked onto a\n\t\t\t\t// different page starts once they reach its first-step page.\n\t\t\t\tif (this._pendingAutoTour && !this.wt.running.value\n\t\t\t\t\t&& this.routeMatchesTour(this._pendingAutoTour, to.name)) {\n\t\t\t\t\tconst tour = this._pendingAutoTour\n\t\t\t\t\tthis._pendingAutoTour = null\n\t\t\t\t\tthis.wt.start(tour.id)\n\t\t\t\t}\n\t\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Start a qualifying auto-start tour — but a `first-visit` tour whose\n\t\t * first step SPOTLIGHTS something on a specific page only opens when the\n\t\t * user is ON that page. A centered welcome step spotlights nothing and\n\t\t * therefore starts anywhere. Deep-linking onto an unrelated route (e.g. a shared detail-page\n\t\t * URL) defers the tour via `_pendingAutoTour` instead of popping it over\n\t\t * the wrong screen (ADR-062). A forced `tourId` always starts; a tour\n\t\t * whose first step is not page-anchored keeps the prior any-route\n\t\t * behaviour; without a router there is nothing to match, so it starts.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tmaybeAutoStart() {\n\t\t\tif (this.wt.running.value) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.tourId) {\n\t\t\t\tthis.wt.start(this.tourId)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst auto = this.wt.autoStartTour.value\n\t\t\tif (!auto) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!this.$router) {\n\t\t\t\tthis.wt.start(auto.id)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst routeName = this.$route && this.$route.name\n\t\t\tif (this.routeMatchesTour(auto, routeName)) {\n\t\t\t\tthis.wt.start(auto.id)\n\t\t\t} else {\n\t\t\t\tthis._pendingAutoTour = auto\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The route name a tour's FIRST step is anchored to, or null when the\n\t\t * first step is not page-anchored (a centered welcome step, or a\n\t\t * non-page target). Used to gate auto-start to the right screen.\n\t\t *\n\t\t * A `placement: \"center\"` first step returns NULL however it is\n\t\t * anchored. This is the whole point: a centered step spotlights\n\t\t * nothing — it is a welcome card floating in the middle of the screen —\n\t\t * so there is no element for the user to be \"on the right page\" for,\n\t\t * and gating its auto-open on a route only decides whether the tour\n\t\t * opens at all.\n\t\t *\n\t\t * That is what it did. This docblock already CLAIMED a centered welcome\n\t\t * step returned null; the code never checked `placement`, so any app\n\t\t * whose welcome step happened to carry a page/nav anchor other than its\n\t\t * landing route parked the tour in `_pendingAutoTour` and never opened\n\t\t * it — no error, no log, just no walkthrough. Measured on one instance\n\t\t * with an empty seen-state: opencatalogi (welcome anchored to\n\t\t * `Catalogs`) and pipelinq (anchored to `Products`) both showed nothing\n\t\t * at `#/` and the full tour at the anchored route, while dossiq\n\t\t * (anchored to its landing `Dashboard`) worked — the control that shows\n\t\t * the anchor, not the tour, was the variable. 19 of 20 fleet manifests\n\t\t * declare a walkthrough, so this is a class, not two instances.\n\t\t *\n\t\t * A step that genuinely spotlights an element keeps the ADR-062\n\t\t * behaviour, which is what it was built for: it still waits for its\n\t\t * page rather than popping over a deep-linked detail screen.\n\t\t *\n\t\t * @param {object} tour The tour definition.\n\t\t * @return {string|null} The first-step page/nav route name, or null.\n\t\t */\n\t\tfirstStepPage(tour) {\n\t\t\tconst steps = (tour && tour.steps) || []\n\t\t\tconst first = steps[0]\n\t\t\t// A CENTERED first step is not page-anchored, whatever it targets.\n\t\t\t//\n\t\t\t// The doc comment above has always said so; the code did not check.\n\t\t\t// `placement: 'center'` renders a card in the middle of the screen\n\t\t\t// and spotlights NOTHING — `locateTarget()` returns early on\n\t\t\t// `isCentered` and never resolves the target at all. So gating\n\t\t\t// auto-start on being on that step's route asks the user to be\n\t\t\t// somewhere the step will not point at.\n\t\t\t//\n\t\t\t// Consequence measured 2026-08-27 on one running instance, same\n\t\t\t// user, empty seen-state:\n\t\t\t//\n\t\t\t// opencatalogi welcome anchored to `Catalogs` -> `/` shows nothing,\n\t\t\t// `#/catalogi` shows \"1 / 8 Welcome to OpenCatalogi\"\n\t\t\t// pipelinq welcome anchored to `Products` -> `/` shows nothing,\n\t\t\t// `#/products` shows \"1 / 11 Welcome to Pipelinq\"\n\t\t\t// dossiq welcome anchored to `Dashboard` -> works (control)\n\t\t\t//\n\t\t\t// The tour was parked in `_pendingAutoTour` and opened only if the\n\t\t\t// user happened to navigate to that page — so the getting-started\n\t\t\t// tour never appeared on the landing page, silently, with nothing\n\t\t\t// logged. 19 of 20 fleet manifests declare a walkthrough, so this\n\t\t\t// is not two apps; it is however many anchored their welcome step\n\t\t\t// somewhere other than their landing route.\n\t\t\t//\n\t\t\t// ADR-062's intent is preserved exactly: a first step that really\n\t\t\t// does spotlight an element still gates on that element's route, so\n\t\t\t// a tour never pops over a deep-linked detail page.\n\t\t\tif (!first) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tif (first.placement === 'center') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst tgt = first.target\n\t\t\tif (tgt && (tgt.kind === 'page' || tgt.kind === 'nav-item') && tgt.ref) {\n\t\t\t\treturn String(tgt.ref)\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\t/**\n\t\t * Whether a tour may auto-open on the given route. True when the tour's\n\t\t * first step is not page-anchored — which includes every `center`-placed\n\t\t * welcome step, since it spotlights nothing (see firstStepPage) — so the\n\t\t * tour starts wherever the user lands. Otherwise the route name must\n\t\t * equal the first-step page.\n\t\t *\n\t\t * This is the ONLY consumer of firstStepPage, and both auto-start paths\n\t\t * (maybeAutoStart on mount, and the router afterEach that resumes a\n\t\t * deferred tour) go through here — so a centered step cannot be\n\t\t * swallowed by either.\n\t\t *\n\t\t * @param {object} tour The tour definition.\n\t\t * @param {string} [routeName] The current route name.\n\t\t * @return {boolean} True when the tour may open on this route.\n\t\t */\n\t\trouteMatchesTour(tour, routeName) {\n\t\t\tconst page = this.firstStepPage(tour)\n\t\t\tif (!page) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn !!routeName && String(routeName) === page\n\t\t},\n\n\t\t/**\n\t\t * Resolve, scroll to, and measure the active step's target, then install\n\t\t * the step's advance watcher.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tlocateTarget() {\n\t\t\tthis.teardownStep()\n\t\t\tif (!this.step || this.isCentered) {\n\t\t\t\tthis.rect = null\n\t\t\t\tthis.focusCard()\n\t\t\t\tthis.armDelay()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst el = this.resolveTarget(this.step)\n\t\t\tthis.targetEl = el\n\t\t\tif (!el) {\n\t\t\t\t// Optional step whose target is absent → skip; else wait for it.\n\t\t\t\tif (this.step.optional) {\n\t\t\t\t\tthis.wt.skip()\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t// Genuinely not in the DOM (nothing to reveal) — wait for it to\n\t\t\t\t// appear. A target hidden by a collapsed nav group is handled by\n\t\t\t\t// computeRect()'s zero-size check below instead, since `el` is\n\t\t\t\t// resolvable there and revealTarget() can walk up from it.\n\t\t\t\tthis.observeForTarget()\n\t\t\t\tthis.rect = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tel.scrollIntoView({ block: 'center', inline: 'center' })\n\t\t\t} catch { /* jsdom */ }\n\t\t\tthis.computeRect()\n\t\t\tthis.armStep(el)\n\t\t\t// The ResizeObserver only fires when the target itself resizes. A nav\n\t\t\t// item can MOVE (siblings render/settle after mount) without resizing,\n\t\t\t// leaving a stale cutout one row off. Re-measure across the next few\n\t\t\t// frames so the spotlight tracks the settled position, and keep\n\t\t\t// tracking it afterwards for content that arrives later still.\n\t\t\tthis.scheduleSettleRemeasure()\n\t\t\tthis.trackTargetLayout()\n\t\t},\n\n\t\t/**\n\t\t * Resolve a step's `target` to a DOM element, preferring stable manifest\n\t\t * identities, then `data-walkthrough-id`/`data-testid`, then raw CSS.\n\t\t *\n\t\t * @param {object} step The active step.\n\t\t * @return {Element|null} The resolved element.\n\t\t */\n\t\tresolveTarget(step) {\n\t\t\tconst tgt = step.target || {}\n\t\t\tconst ref = tgt.ref\n\t\t\tconst q = (sel) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn document.querySelector(sel)\n\t\t\t\t} catch {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst esc = (v) => (window.CSS && CSS.escape ? CSS.escape(v) : String(v).replace(/\"/g, '\\\\\"'))\n\t\t\tif (tgt.kind === 'selector' && tgt.selector) {\n\t\t\t\treturn q(tgt.selector)\n\t\t\t}\n\t\t\tif (!ref) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst byId = q(`[data-walkthrough-id=\"${esc(ref)}\"]`) || q(`[data-testid=\"${esc(ref)}\"]`)\n\t\t\tif (byId) {\n\t\t\t\treturn byId\n\t\t\t}\n\t\t\tif (tgt.kind === 'nav-item' || tgt.kind === 'page') {\n\t\t\t\treturn q(`[data-cn-route=\"${esc(ref)}\"]`) || q(`a[href$=\"#/${esc(ref)}\"]`) || q(`[data-route=\"${esc(ref)}\"]`)\n\t\t\t}\n\t\t\tif (tgt.kind === 'widget') {\n\t\t\t\treturn q(`[data-widget-key=\"${esc(ref)}\"]`) || q(`[data-widget-id=\"${esc(ref)}\"]`)\n\t\t\t}\n\t\t\tif (tgt.kind === 'action') {\n\t\t\t\treturn q(`[data-action-id=\"${esc(ref)}\"]`)\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\t/**\n\t\t * Expand only the collapsed nav groups that are actual ancestors of\n\t\t * `this.targetEl` (already resolved, just `display:none`'d by a\n\t\t * collapsed parent), instead of scanning the whole navigation.\n\t\t * Attempted at most once per step (`_revealAttempted`, reset in\n\t\t * teardownStep).\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\trevealTarget() {\n\t\t\tif (this._revealAttempted || !this.targetEl) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis._revealAttempted = true\n\t\t\tconst click = (btn) => {\n\t\t\t\tif (btn && typeof btn.click === 'function') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tbtn.click()\n\t\t\t\t\t} catch { /* jsdom / detached */ }\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Walk every ancestor (not just .closest() jumps) since a nav group\n\t\t\t// and the Settings foldout use different container markup and can\n\t\t\t// nest either way.\n\t\t\tlet node = this.targetEl.parentElement\n\t\t\twhile (node) {\n\t\t\t\tif (node.classList.contains('app-navigation-entry--collapsible')) {\n\t\t\t\t\tclick(node.querySelector('button.icon-collapse, .app-navigation-entry__children-toggle, .app-navigation-entry__collapse'))\n\t\t\t\t} else if (node.matches?.('[data-testid=\"cn-nav-settings\"]')) {\n\t\t\t\t\t// NcAppNavigationSettings: CSS-module classes are hashed at\n\t\t\t\t\t// build time, so aria-expanded is the only stable handle on\n\t\t\t\t\t// its own toggle button.\n\t\t\t\t\tclick(node.querySelector('button[aria-expanded]'))\n\t\t\t\t}\n\t\t\t\tnode = node.parentElement\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Re-measure the target a few times after arming, to catch post-mount\n\t\t * layout settling (sibling nav items rendering, fonts, etc.) that moves\n\t\t * the target without resizing it. Cleared in teardownStep.\n\t\t *\n\t\t * Fast path only; later movement is caught by `trackTargetLayout()`.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tscheduleSettleRemeasure() {\n\t\t\tconst again = () => {\n\t\t\t\tif (this.targetEl && !this.isCentered) {\n\t\t\t\t\tthis.computeRect()\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis._settleTimers = (this._settleTimers || [])\n\t\t\tthis._settleTimers.push(setTimeout(again, 100), setTimeout(again, 300), setTimeout(again, 600))\n\t\t},\n\n\t\t/**\n\t\t * Re-measure on any layout change for as long as the step is active.\n\t\t *\n\t\t * The settle timers above stop at 600ms; a target on a page whose content\n\t\t * arrives from the network keeps moving after that, leaving the cutout on\n\t\t * empty space. The MutationObserver catches inserted content, the\n\t\t * ResizeObserver catches reflows that insert no nodes.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\ttrackTargetLayout() {\n\t\t\tconst remeasure = () => {\n\t\t\t\tif (this._remeasureRaf) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst raf = typeof window.requestAnimationFrame === 'function'\n\t\t\t\t\t? window.requestAnimationFrame\n\t\t\t\t\t: (fn) => setTimeout(fn, 16)\n\t\t\t\tthis._remeasureRaf = raf(() => {\n\t\t\t\t\tthis._remeasureRaf = null\n\t\t\t\t\tif (this.targetEl && !this.isCentered) {\n\t\t\t\t\t\tthis.computeRect()\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t\tif (window.MutationObserver) {\n\t\t\t\tthis._layoutObs = new MutationObserver(remeasure)\n\t\t\t\tthis._layoutObs.observe(document.body, { childList: true, subtree: true })\n\t\t\t}\n\t\t\tif (window.ResizeObserver) {\n\t\t\t\tthis._bodyResizeObs = new ResizeObserver(remeasure)\n\t\t\t\tthis._bodyResizeObs.observe(document.body)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Measure the target into a viewport rect.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tcomputeRect() {\n\t\t\tif (!this.targetEl) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst r = this.targetEl.getBoundingClientRect()\n\t\t\t// Target present but not laid out (e.g. a nav item inside a collapsed\n\t\t\t// group, or a hidden element): fall back to an anchorless centered\n\t\t\t// coachmark — no point-sized cutout — until it becomes visible. The\n\t\t\t// ResizeObserver armed in armStep() recomputes when it gains size.\n\t\t\tif (r.width === 0 || r.height === 0) {\n\t\t\t\t// Resolved but not laid out — most often a nav item inside a\n\t\t\t\t// collapsed group. Expand the group so the ResizeObserver armed in\n\t\t\t\t// armStep() recomputes once it gains size; until then, anchorless.\n\t\t\t\tthis.revealTarget()\n\t\t\t\tthis.rect = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// The overlay (.cn-walkthrough) is position:fixed, but a transformed\n\t\t\t// ancestor (NC content area) can become its containing block, so its\n\t\t\t// top-left is not the viewport origin. Convert the viewport rect into\n\t\t\t// the overlay's coordinate space so the dim/ring/card line up with the\n\t\t\t// real element instead of sitting a header's height too low.\n\t\t\tconst host = this.$el && this.$el.getBoundingClientRect ? this.$el.getBoundingClientRect() : { top: 0, left: 0 }\n\t\t\t// Remember the overlay's own viewport offset so placeCard can clamp\n\t\t\t// the coachmark against the REAL viewport even when a transformed\n\t\t\t// ancestor makes the position:fixed origin non-zero.\n\t\t\tthis._hostOffset = { top: host.top, left: host.left }\n\t\t\tconst next = { top: r.top - host.top, left: r.left - host.left, width: r.width, height: r.height }\n\t\t\t// Bail when nothing moved: trackTargetLayout() calls this on every\n\t\t\t// layout change, and placeCard()'s focusCard() would otherwise steal\n\t\t\t// focus back from whatever the user is typing in.\n\t\t\tconst p = this.rect\n\t\t\tif (p && p.top === next.top && p.left === next.left && p.width === next.width && p.height === next.height) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.rect = next\n\t\t\tthis.$nextTick(() => this.placeCard())\n\t\t},\n\n\t\t/**\n\t\t * Position the coachmark near the target with a simple flip so it stays\n\t\t * on-screen.\n\t\t *\n\t\t * @spec openspec/changes/cn-walkthrough-engine/specs/cn-walkthrough/spec.md\n\t\t * @return {void}\n\t\t */\n\t\tplaceCard() {\n\t\t\tconst card = this.$refs.card\n\t\t\tif (!card || !this.rect) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst cw = card.offsetWidth || 320\n\t\t\tconst ch = card.offsetHeight || 160\n\t\t\tconst gap = 12\n\t\t\tconst vw = window.innerWidth\n\t\t\tconst vh = window.innerHeight\n\t\t\tconst r = this.rect\n\t\t\tlet placement = this.step.placement && this.step.placement !== 'auto' ? this.step.placement : 'bottom'\n\t\t\tlet top\n\t\t\tlet left\n\t\t\tif (placement === 'bottom' && r.top + r.height + gap + ch > vh) {\n\t\t\t\tplacement = 'top'\n\t\t\t}\n\t\t\tif (placement === 'top' && r.top - gap - ch < 0) {\n\t\t\t\tplacement = 'bottom'\n\t\t\t}\n\t\t\tif (placement === 'bottom') {\n\t\t\t\ttop = r.top + r.height + gap\n\t\t\t\tleft = r.left\n\t\t\t} else if (placement === 'top') {\n\t\t\t\ttop = r.top - gap - ch\n\t\t\t\tleft = r.left\n\t\t\t} else if (placement === 'left') {\n\t\t\t\ttop = r.top\n\t\t\t\tleft = r.left - gap - cw\n\t\t\t} else {\n\t\t\t\ttop = r.top\n\t\t\t\tleft = r.left + r.width + gap\n\t\t\t}\n\t\t\t// Clamp to the viewport. `left`/`top` live in overlay-relative space\n\t\t\t// (this.rect was host-subtracted), so the viewport bounds must be\n\t\t\t// host-subtracted too — otherwise a scrolled/transformed host would\n\t\t\t// let the card render off-screen (ADR-062: the coachmark is always\n\t\t\t// fully on-screen).\n\t\t\tconst ho = this._hostOffset || { top: 0, left: 0 }\n\t\t\tleft = Math.max(gap - ho.left, Math.min(left, vw - cw - gap - ho.left))\n\t\t\ttop = Math.max(gap - ho.top, Math.min(top, vh - ch - gap - ho.top))\n\t\t\tthis.cardPlacement = placement\n\t\t\tthis.cardPos = { top, left }\n\t\t\tthis.focusCard()\n\t\t},\n\n\t\t/**\n\t\t * Move focus to the coachmark's first control (focus management / a11y).\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tfocusCard() {\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tconst btn = this.$refs.firstBtn && this.$refs.firstBtn.$el\n\t\t\t\tif (btn && typeof btn.focus === 'function') {\n\t\t\t\t\tbtn.focus()\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Install the advance watcher for the active step (click / element / delay;\n\t\t * route + object-created are global listeners installed at mount).\n\t\t *\n\t\t * @param {Element} el The resolved target.\n\t\t * @return {void}\n\t\t */\n\t\tarmStep(el) {\n\t\t\tconst a = this.step.advanceOn || {}\n\t\t\tif (a.type === 'click-target' && el) {\n\t\t\t\tthis._clickHandler = () => this.wt.notify({ kind: 'click' })\n\t\t\t\tel.addEventListener('click', this._clickHandler, { once: true })\n\t\t\t}\n\t\t\t// Recompute the cutout when the target's size changes — covers a target\n\t\t\t// that starts hidden/zero-size (e.g. inside a collapsed nav group) and\n\t\t\t// becomes visible while the step is active.\n\t\t\tif (el && window.ResizeObserver) {\n\t\t\t\tthis._resizeObs = new ResizeObserver(() => this.computeRect())\n\t\t\t\tthis._resizeObs.observe(el)\n\t\t\t}\n\t\t\tthis.armDelay()\n\t\t},\n\n\t\t/**\n\t\t * Arm the delay timer for a `delay` advance.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tarmDelay() {\n\t\t\tconst a = (this.step && this.step.advanceOn) || {}\n\t\t\tif (a.type === 'delay') {\n\t\t\t\tthis._delayTimer = setTimeout(() => this.wt.notify({ kind: 'delay' }), a.ms || 1500)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Watch the DOM until an `element-appears` (or a not-yet-mounted) target\n\t\t * resolves, then re-locate.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tobserveForTarget() {\n\t\t\tthis._observer = new MutationObserver(() => {\n\t\t\t\tconst el = this.resolveTarget(this.step)\n\t\t\t\tif (el) {\n\t\t\t\t\tthis._observer.disconnect()\n\t\t\t\t\tthis._observer = null\n\t\t\t\t\tif (this.step.advanceOn && this.step.advanceOn.type === 'element-appears') {\n\t\t\t\t\t\tthis.wt.notify({ kind: 'element' })\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.locateTarget()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t\tthis._observer.observe(document.body, { childList: true, subtree: true })\n\t\t},\n\n\t\t/**\n\t\t * Tear down the current step's watchers/timers/listeners.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tteardownStep() {\n\t\t\tthis._revealAttempted = false\n\t\t\t// computeRect()'s bail-when-unchanged optimisation must not survive\n\t\t\t// into the next step, or a same-geometry target never re-runs\n\t\t\t// placeCard()/focusCard() on step change.\n\t\t\tthis.rect = null\n\t\t\tif (this._observer) {\n\t\t\t\tthis._observer.disconnect()\n\t\t\t\tthis._observer = null\n\t\t\t}\n\t\t\tif (this._resizeObs) {\n\t\t\t\tthis._resizeObs.disconnect()\n\t\t\t\tthis._resizeObs = null\n\t\t\t}\n\t\t\tif (this._layoutObs) {\n\t\t\t\tthis._layoutObs.disconnect()\n\t\t\t\tthis._layoutObs = null\n\t\t\t}\n\t\t\tif (this._bodyResizeObs) {\n\t\t\t\tthis._bodyResizeObs.disconnect()\n\t\t\t\tthis._bodyResizeObs = null\n\t\t\t}\n\t\t\tif (this._remeasureRaf) {\n\t\t\t\tconst cancel = typeof window.cancelAnimationFrame === 'function' ? window.cancelAnimationFrame : clearTimeout\n\t\t\t\tcancel(this._remeasureRaf)\n\t\t\t\tthis._remeasureRaf = null\n\t\t\t}\n\t\t\tif (this._delayTimer) {\n\t\t\t\tclearTimeout(this._delayTimer)\n\t\t\t\tthis._delayTimer = null\n\t\t\t}\n\t\t\tif (this._settleTimers) {\n\t\t\t\tthis._settleTimers.forEach(clearTimeout)\n\t\t\t\tthis._settleTimers = null\n\t\t\t}\n\t\t\tif (this.targetEl && this._clickHandler) {\n\t\t\t\tthis.targetEl.removeEventListener('click', this._clickHandler)\n\t\t\t\tthis._clickHandler = null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Advance the tour one step, completing on the last step.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tadvance() {\n\t\t\t/**\n\t\t\t * @event advance Emitted when the user advances the tour.\n\t\t\t * @type {{ stepId: string }}\n\t\t\t */\n\t\t\tthis.$emit('advance', { stepId: this.step && this.step.id })\n\t\t\tconst wasLast = this.isLast\n\t\t\tthis.wt.next()\n\t\t\tif (wasLast) {\n\t\t\t\t/**\n\t\t\t\t * @event complete Emitted when the last step is passed.\n\t\t\t\t */\n\t\t\t\tthis.$emit('complete')\n\t\t\t}\n\t\t},\n\n\t\tback() {\n\t\t\tthis.wt.back()\n\t\t},\n\n\t\t/**\n\t\t * The coachmark's \"Skip\" control — ENDS the tour (it does not advance a\n\t\t * step). Marks the tour complete so the seen-version is persisted and it\n\t\t * won't auto-reopen, then emits `dismiss` + `complete` (ADR-062: Skip\n\t\t * dismisses the whole tour; Next is the only control that advances).\n\t\t * The internal per-step skip (an optional step whose target is absent)\n\t\t * still uses `wt.skip()` directly in `locateTarget`.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tskip() {\n\t\t\tthis.wt.complete()\n\t\t\t/**\n\t\t\t * @event dismiss Emitted when the user skips (ends) the tour.\n\t\t\t */\n\t\t\tthis.$emit('dismiss')\n\t\t\tthis.$emit('complete')\n\t\t},\n\n\t\t/**\n\t\t * Execute a cross-app hand-off: complete this tour locally, then navigate\n\t\t * the browser to the destination URL carrying a `cn_resume_tour` /\n\t\t * `cn_resume_step` token so the destination app's useWalkthrough resumes.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tdoHandoff() {\n\t\t\tconst h = (this.step && this.step.handoff) || {}\n\t\t\tif (!h.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst tour = h.tour || (this.wt.activeTour.value && this.wt.activeTour.value.id) || ''\n\t\t\tlet url = h.url + (h.url.indexOf('?') === -1 ? '?' : '&') + 'cn_resume_tour=' + encodeURIComponent(tour)\n\t\t\tif (h.step) {\n\t\t\t\turl += '&cn_resume_step=' + encodeURIComponent(h.step)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event handoff Emitted just before navigating to a cross-app destination.\n\t\t\t * @type {{ app: string, url: string }}\n\t\t\t */\n\t\t\tthis.$emit('handoff', { app: h.app, url })\n\t\t\tthis.$emit('complete')\n\t\t\tthis.wt.complete()\n\t\t\ttry {\n\t\t\t\twindow.location.href = url\n\t\t\t} catch { /* jsdom */ }\n\t\t},\n\n\t\t/**\n\t\t * End the tour for good from the corner close button: mark it complete so\n\t\t * the seen-version is persisted and it does not auto-show again.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tclose() {\n\t\t\tthis.wt.complete()\n\t\t\tthis.$emit('complete')\n\t\t},\n\n\t\t/**\n\t\t * Dismiss the tour from a backdrop click or ESC.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonBackdrop() {\n\t\t\tthis.wt.dismiss()\n\t\t\t/**\n\t\t\t * @event dismiss Emitted when the user dismisses the tour (backdrop / ESC).\n\t\t\t */\n\t\t\tthis.$emit('dismiss')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-walkthrough {\n\tposition: fixed;\n\tinset: 0;\n\tpointer-events: none;\n}\n\n.cn-walkthrough__live {\n\tposition: absolute;\n\twidth: 1px;\n\theight: 1px;\n\toverflow: hidden;\n\tclip-path: inset(50%);\n}\n\n.cn-walkthrough__dim {\n\tposition: fixed;\n\tbackground: var(--color-modal-background, rgba(0, 0, 0, 0.5));\n\tpointer-events: auto;\n}\n\n.cn-walkthrough__dim--full {\n\tinset: 0;\n}\n\n/* Anchorless fallback for an ANCHORED step: dim everything, but do not\n intercept pointer events — the step's task requires clicking the app\n underneath (see the template comment). */\n/* Lets the step's own task through — and therefore cannot be clicked to\n dismiss; ESC, the close button and Back/Finish remain (see the template). */\n.cn-walkthrough__dim--passthrough {\n\tpointer-events: none;\n}\n\n.cn-walkthrough__ring {\n\tposition: fixed;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbox-shadow: 0 0 0 2px var(--color-primary-element, #0082c9);\n\tpointer-events: none;\n}\n\n.cn-walkthrough__card {\n\tposition: fixed;\n\twidth: 320px;\n\tmax-width: calc(100vw - 24px);\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tborder-radius: var(--border-radius-large, 8px);\n\tbox-shadow: 0 2px 12px var(--color-box-shadow, rgba(0, 0, 0, 0.3));\n\tpadding: 16px;\n\tpointer-events: auto;\n}\n\n.cn-walkthrough__close {\n\t/* !important beats NcButton's own `position: relative`, which would otherwise\n\t leave the close button in-flow at the card's top-left. */\n\tposition: absolute !important;\n\ttop: 8px;\n\tinset-inline-end: 8px;\n\tinset-inline-start: auto;\n\tz-index: 1;\n}\n\n.cn-walkthrough__counter {\n\tfont-size: 0.8125rem;\n\tcolor: var(--color-text-maxcontrast);\n\tmargin-bottom: 4px;\n\t/* Keep the counter/title clear of the absolutely-positioned close button. */\n\tpadding-inline-end: 32px;\n}\n\n.cn-walkthrough__title {\n\tmargin: 0 0 6px;\n\tfont-size: 1.1rem;\n}\n\n.cn-walkthrough__body {\n\tmargin: 0 0 8px;\n}\n\n.cn-walkthrough__task {\n\tmargin: 0 0 8px;\n\tpadding: 8px;\n\tbackground: var(--color-primary-element-light, var(--color-background-hover));\n\tborder-radius: var(--border-radius, 4px);\n\tfont-weight: 600;\n}\n\n.cn-walkthrough__task-icon {\n\tmargin-inline-end: 4px;\n}\n\n.cn-walkthrough__actions {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tmargin-top: 8px;\n}\n\n.cn-walkthrough__spacer {\n\tflex: 1 1 auto;\n}\n</style>\n"],"names":["_createElementBlock","_createElementVNode","_toDisplayString","_Fragment","_openBlock","_normalizeStyle","_normalizeClass","_createVNode","_createTextVNode","_createBlock"],"mappings":"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"}
1
+ {"version":3,"file":"CnWalkthrough.vue3.js","sources":["../../../../src/components/CnWalkthrough/CnWalkthrough.vue?vue&type=template&id=5691d49a&lang.js"],"sourcesContent":["<!-- SPDX-License-Identifier: EUPL-1.2 -->\n<!-- SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl> -->\n<template>\n\t<div v-if=\"active\"\n\t\tclass=\"cn-walkthrough\"\n\t\t:style=\"{ zIndex }\"\n\t\trole=\"dialog\"\n\t\taria-modal=\"true\"\n\t\t:aria-label=\"dialogLabel\">\n\t\t<!-- aria-live announcement of the current step -->\n\t\t<div class=\"cn-walkthrough__live\" aria-live=\"polite\">\n\t\t\t{{ liveText }}\n\t\t</div>\n\n\t\t<!-- Dimmer: a centered step uses one full overlay; an anchored step uses\n\t\t four strips that frame the target rect, leaving a real interactive hole.\n\t\t An ANCHORED step that lost its anchor (target present but not laid\n\t\t out, e.g. inside a closed settings foldout) falls back to the full\n\t\t overlay too — but there it must let clicks THROUGH: the step's own\n\t\t task tells the user to click something in the app underneath, and an\n\t\t interactive backdrop would swallow exactly that click, leaving only\n\t\t Back/Finish usable. The trade-off is deliberate: a passthrough dim\n\t\t cannot be clicked to dismiss the tour, so on such a step the exits\n\t\t are ESC, the coachmark's close button and Back/Finish — a click on\n\t\t the greyed-out area reaches the app, which is the point. A centered\n\t\t step spotlights nothing, so its backdrop stays interactive and\n\t\t dismisses the tour. -->\n\t\t<!-- Quiet: an app dialog is open, or the step's target belongs to\n\t\t another page. No dim and no cutout, so the overlay never sits on\n\t\t top of the dialog and never invites a click on the wrong page.\n\t\t Only the docked coachmark stays. -->\n\t\t<template v-if=\"quiet\" />\n\t\t<template v-else-if=\"isCentered || !rect\">\n\t\t\t<div v-if=\"isCentered\"\n\t\t\t\tclass=\"cn-walkthrough__dim cn-walkthrough__dim--full\"\n\t\t\t\t@click.self=\"onBackdrop\" />\n\t\t\t<!-- No dismiss handler on a passthrough dim: `pointer-events: none`\n\t\t\t means a browser never delivers the click here anyway, and\n\t\t\t leaving the listener attached would make the SAME gesture\n\t\t\t dismiss the tour in any environment that ignores\n\t\t\t pointer-events (jsdom, a forced-colors mode) while reaching the\n\t\t\t app in a browser. One behaviour, not two. -->\n\t\t\t<div v-else\n\t\t\t\tclass=\"cn-walkthrough__dim cn-walkthrough__dim--full cn-walkthrough__dim--passthrough\"\n\t\t\t\taria-hidden=\"true\" />\n\t\t</template>\n\t\t<template v-else>\n\t\t\t<div class=\"cn-walkthrough__dim\" :style=\"strip.top\" @click.self=\"onBackdrop\" />\n\t\t\t<div class=\"cn-walkthrough__dim\" :style=\"strip.bottom\" @click.self=\"onBackdrop\" />\n\t\t\t<div class=\"cn-walkthrough__dim\" :style=\"strip.left\" @click.self=\"onBackdrop\" />\n\t\t\t<div class=\"cn-walkthrough__dim\" :style=\"strip.right\" @click.self=\"onBackdrop\" />\n\t\t\t<div class=\"cn-walkthrough__ring\" :style=\"ringStyle\" aria-hidden=\"true\" />\n\t\t</template>\n\n\t\t<!-- Coachmark card -->\n\t\t<div ref=\"card\"\n\t\t\tclass=\"cn-walkthrough__card\"\n\t\t\t:class=\"[`cn-walkthrough__card--${cardPlacement}`, { 'cn-walkthrough__card--docked': quiet }]\"\n\t\t\t:style=\"cardStyle\">\n\t\t\t<NcButton class=\"cn-walkthrough__close\"\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t:aria-label=\"closeLabel\"\n\t\t\t\t:title=\"closeLabel\"\n\t\t\t\t@click=\"close\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Close :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t</NcButton>\n\t\t\t<!-- @slot coachmark Override the whole coachmark body. Scope: { step, index, total, next, back, skip, skipLabel }.\n\t\t\t The default body has no Skip control (ADR-062 gives that job to\n\t\t\t the corner close button), so `skipLabel` reaches the only place\n\t\t\t a Skip control can live: a host-supplied coachmark. -->\n\t\t\t<slot name=\"coachmark\"\n\t\t\t\t:step=\"step\"\n\t\t\t\t:index=\"index\"\n\t\t\t\t:total=\"total\"\n\t\t\t\t:next=\"advance\"\n\t\t\t\t:back=\"back\"\n\t\t\t\t:skip=\"skip\"\n\t\t\t\t:skipLabel=\"skipLabel\">\n\t\t\t\t<div class=\"cn-walkthrough__counter\">\n\t\t\t\t\t{{ index + 1 }} / {{ total }}\n\t\t\t\t</div>\n\t\t\t\t<h3 v-if=\"stepTitle\" class=\"cn-walkthrough__title\">\n\t\t\t\t\t{{ stepTitle }}\n\t\t\t\t</h3>\n\t\t\t\t<p v-if=\"stepBody\" class=\"cn-walkthrough__body\">\n\t\t\t\t\t{{ stepBody }}\n\t\t\t\t</p>\n\t\t\t\t<p v-if=\"stepTask\" class=\"cn-walkthrough__task\">\n\t\t\t\t\t<span class=\"cn-walkthrough__task-icon\" aria-hidden=\"true\">👉</span> {{ stepTask }}\n\t\t\t\t</p>\n\t\t\t\t<div class=\"cn-walkthrough__actions\">\n\t\t\t\t\t<NcButton v-if=\"!isFirst\" variant=\"secondary\" @click=\"back\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ChevronLeft :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ backLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<span class=\"cn-walkthrough__spacer\" />\n\t\t\t\t\t<NcButton v-if=\"isHandoff\"\n\t\t\t\t\t\tref=\"firstBtn\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\talignment=\"center-reverse\"\n\t\t\t\t\t\t@click=\"doHandoff\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ChevronRight :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ handoffLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton v-else-if=\"showNext\"\n\t\t\t\t\t\tref=\"firstBtn\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\talignment=\"center-reverse\"\n\t\t\t\t\t\t@click=\"advance\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<ChevronRight :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ isLast ? finishLabel : nextLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</slot>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton } from '@nextcloud/vue'\nimport ChevronLeft from 'vue-material-design-icons/ChevronLeft.vue'\nimport ChevronRight from 'vue-material-design-icons/ChevronRight.vue'\nimport Close from 'vue-material-design-icons/Close.vue'\nimport { useWalkthrough } from '../../composables/useWalkthrough.js'\n\n/**\n * CnWalkthrough — abstract, manifest-driven product walkthrough (ADR-043).\n *\n * Renders a `manifest.walkthrough` tour as a gray dimmer with a spotlight cutout\n * around one real, interactive element, plus an auto-positioned coachmark card.\n * It never promotes the target's `z-index` — the dimmer is four strips framing\n * the target rect, so the element stays clickable in place regardless of its\n * stacking context. When a target is hidden inside a collapsed `NcAppNavigation`\n * group (children rendered but `display:none`, so unmeasurable), the engine\n * best-effort expands the group to reveal the item before spotlighting it, rather\n * than falling back to a centered coachmark. Step advancement is declarative: the component sources\n * signals (route change via `$router`, a `cn-walkthrough:object-created` window\n * event, an appearing element via `MutationObserver`, a click on the target, or a\n * delay) and feeds them to {@link useWalkthrough}, which captures route params /\n * object ids into a context bag interpolated into later steps.\n *\n * Mount it gated by `CnAppRoot` over the live shell, or standalone with an\n * explicit `tour-id`.\n *\n * ```vue\n * <CnWalkthrough :app-id=\"'pipelinq'\" :manifest=\"manifest\" :seen-version=\"seen\"\n * @complete=\"onComplete\" />\n * ```\n */\nexport default {\n\tname: 'CnWalkthrough',\n\n\tcomponents: { NcButton, Close, ChevronLeft, ChevronRight },\n\n\tprops: {\n\t\t/** The Nextcloud app id (walkthrough machine cache key). */\n\t\tappId: { type: String, required: true },\n\t\t/** The app manifest; `manifest.walkthrough` + `manifest.version` are read. */\n\t\tmanifest: { type: Object, default: null },\n\t\t/** The user's last-seen app version (drives \"what's new\" composition). */\n\t\tseenVersion: { type: String, default: '' },\n\t\t/** Force a specific tour id; when empty the auto-start tour is used. */\n\t\ttourId: { type: String, default: '' },\n\t\t/** Optional resume token `{ tourId, stepId }` (refresh / cross-app hand-off). */\n\t\tresume: { type: Object, default: null },\n\t\t/** Stacking order of the overlay. */\n\t\tzIndex: { type: Number, default: 10000 },\n\t\t/** Next button label. */\n\t\tnextLabel: { type: String, default: () => t('nextcloud-vue', 'Next') },\n\t\t/** Back button label. */\n\t\tbackLabel: { type: String, default: () => t('nextcloud-vue', 'Back') },\n\t\t/** Skip button label. */\n\t\tskipLabel: { type: String, default: () => t('nextcloud-vue', 'Skip') },\n\t\t/** Final-step button label. */\n\t\tfinishLabel: { type: String, default: () => t('nextcloud-vue', 'Finish') },\n\t\t/** Accessible label for the corner close button that ends the tour. */\n\t\tcloseLabel: { type: String, default: () => t('nextcloud-vue', 'Close tour') },\n\t\t/** Optional translate function applied to step title/body/task i18n keys. */\n\t\ttranslate: { type: Function, default: null },\n\t},\n\n\temits: ['complete', 'dismiss', 'pause', 'progress', 'step-change', 'advance', 'handoff'],\n\n\tsetup(props) {\n\t\tconst wt = useWalkthrough(props.appId, props.manifest, {\n\t\t\tseenVersion: props.seenVersion,\n\t\t\tresume: props.resume,\n\t\t\tonComplete: undefined,\n\t\t})\n\t\treturn { wt }\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trect: null, // target bounding rect (viewport coords)\n\t\t\tcardPos: { top: 0, left: 0 },\n\t\t\tcardPlacement: 'bottom',\n\t\t\ttargetEl: null,\n\t\t\t// An app dialog (NcModal / NcDialog) is open over the page.\n\t\t\tappModalOpen: false,\n\t\t\t// The step's target was found on another page than the current one.\n\t\t\toffPage: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tactive() {\n\t\t\treturn this.wt.running.value && !!this.step\n\t\t},\n\n\t\tstep() {\n\t\t\treturn this.wt.currentStep.value\n\t\t},\n\n\t\tindex() {\n\t\t\treturn this.wt.activeTour.value ? this.wt.activeTour.value.steps.findIndex((s) => s.id === (this.step && this.step.id)) : 0\n\t\t},\n\n\t\ttotal() {\n\t\t\treturn this.wt.totalSteps.value\n\t\t},\n\n\t\tisFirst() {\n\t\t\treturn this.wt.isFirst.value\n\t\t},\n\n\t\tisLast() {\n\t\t\treturn this.wt.isLast.value\n\t\t},\n\n\t\t/**\n\t\t * Whether the overlay steps back: no dim, no cutout, a docked card.\n\t\t * True while an app dialog is open or the step's target is on\n\t\t * another page.\n\t\t *\n\t\t * @spec openspec/changes/walkthrough-advance-pause-resume/specs/cn-walkthrough/spec.md\n\t\t * @return {boolean}\n\t\t */\n\t\tquiet() {\n\t\t\treturn this.appModalOpen || this.offPage\n\t\t},\n\n\t\tisCentered() {\n\t\t\treturn this.step && this.step.placement === 'center'\n\t\t},\n\n\t\t/**\n\t\t * Whether the Next control is shown. Hidden on an enforced action step\n\t\t * (a task + a non-manual advanceOn) unless `allowManualNext` is set.\n\t\t *\n\t\t * @return {boolean} True when Next should render.\n\t\t */\n\t\tshowNext() {\n\t\t\tif (!this.step) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst enforced = !!this.step.task && this.step.advanceOn && this.step.advanceOn.type !== 'manual'\n\t\t\treturn !enforced || this.step.allowManualNext === true\n\t\t},\n\n\t\t/**\n\t\t * Whether the active step is a cross-app hand-off (declares `handoff.url`).\n\t\t *\n\t\t * @return {boolean} True when the coachmark offers \"Continue in {app}\".\n\t\t */\n\t\tisHandoff() {\n\t\t\treturn !!(this.step && this.step.handoff && this.step.handoff.url)\n\t\t},\n\n\t\thandoffLabel() {\n\t\t\tconst app = this.step && this.step.handoff && this.step.handoff.app\n\t\t\treturn app ? t('nextcloud-vue', 'Continue in {app}', { app }) : t('nextcloud-vue', 'Continue')\n\t\t},\n\n\t\tstepTitle() {\n\t\t\treturn this.tr(this.step && this.step.title)\n\t\t},\n\n\t\tstepBody() {\n\t\t\treturn this.tr(this.step && this.step.body)\n\t\t},\n\n\t\t/**\n\t\t * The task line, through the same `translate` prop as title/body — a\n\t\t * raw `step.task` shipped English even in fully translated locales.\n\t\t *\n\t\t * @return {string} The translated task, or `''` when the step has none.\n\t\t */\n\t\tstepTask() {\n\t\t\treturn this.tr(this.step && this.step.task)\n\t\t},\n\n\t\tdialogLabel() {\n\t\t\treturn this.stepTitle || t('nextcloud-vue', 'Walkthrough')\n\t\t},\n\n\t\tliveText() {\n\t\t\tif (!this.step) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Step {n} of {total}', { n: this.index + 1, total: this.total }) + ': ' + (this.stepTitle || '')\n\t\t},\n\n\t\tringStyle() {\n\t\t\tif (!this.rect) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tconst pad = 6\n\t\t\treturn {\n\t\t\t\ttop: (this.rect.top - pad) + 'px',\n\t\t\t\tleft: (this.rect.left - pad) + 'px',\n\t\t\t\twidth: (this.rect.width + pad * 2) + 'px',\n\t\t\t\theight: (this.rect.height + pad * 2) + 'px',\n\t\t\t}\n\t\t},\n\n\t\tstrip() {\n\t\t\tconst r = this.rect\n\t\t\tif (!r) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tconst pad = 6\n\t\t\tconst top = Math.max(0, r.top - pad)\n\t\t\tconst bottom = r.top + r.height + pad\n\t\t\tconst left = Math.max(0, r.left - pad)\n\t\t\tconst right = r.left + r.width + pad\n\t\t\treturn {\n\t\t\t\ttop: { top: 0, left: 0, width: '100%', height: top + 'px' },\n\t\t\t\tbottom: { top: bottom + 'px', left: 0, width: '100%', bottom: 0 },\n\t\t\t\tleft: { top: top + 'px', left: 0, width: left + 'px', height: (bottom - top) + 'px' },\n\t\t\t\tright: { top: top + 'px', left: right + 'px', right: 0, height: (bottom - top) + 'px' },\n\t\t\t}\n\t\t},\n\n\t\tcardStyle() {\n\t\t\tif (this.quiet) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\tif (this.isCentered || !this.rect) {\n\t\t\t\treturn { top: '50%', left: '50%', transform: 'translate(-50%, -50%)' }\n\t\t\t}\n\t\t\treturn { top: this.cardPos.top + 'px', left: this.cardPos.left + 'px' }\n\t\t},\n\t},\n\n\twatch: {\n\t\tstep(newStep, oldStep) {\n\t\t\tif (!newStep) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!oldStep || newStep.id !== oldStep.id) {\n\t\t\t\t/**\n\t\t\t\t * @event step-change Emitted when the active step changes.\n\t\t\t\t * @type {{ stepId: string, index: number }}\n\t\t\t\t */\n\t\t\t\tthis.$emit('step-change', { stepId: newStep.id, index: this.index })\n\t\t\t\t/**\n\t\t\t\t * @event progress Emitted on every step change so the host can remember where the user is.\n\t\t\t\t * @type {{ tourId: string, stepId: string, index: number }}\n\t\t\t\t */\n\t\t\t\tthis.$emit('progress', {\n\t\t\t\t\ttourId: (this.wt.activeTour.value && this.wt.activeTour.value.id) || '',\n\t\t\t\t\tstepId: newStep.id,\n\t\t\t\t\tindex: this.index,\n\t\t\t\t})\n\t\t\t\t// A new step finds its own page again.\n\t\t\t\tthis._homeRoute = null\n\t\t\t\tthis.offPage = false\n\t\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t\t}\n\t\t},\n\n\t\tactive(now) {\n\t\t\tif (now) {\n\t\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t\t} else {\n\t\t\t\tthis.teardownStep()\n\t\t\t}\n\t\t},\n\t},\n\n\t/**\n\t * Initialise non-reactive instance state (event handler refs, DOM observer\n\t * handles). These use the `_` prefix convention and are set here rather than\n\t * in `data()` to avoid Vue's reserved-key warning for `_`-prefixed fields.\n\t *\n\t * @spec openspec/changes/cn-walkthrough-engine/specs/cn-walkthrough/spec.md\n\t */\n\tcreated() {\n\t\t// Non-reactive instance state: event handlers and DOM watchers.\n\t\t// Not in data() to avoid triggering Vue's reserved-key warning (_prefix).\n\t\tthis._revealAttempted = false\n\t\tthis._observer = null\n\t\tthis._resizeObs = null\n\t\tthis._delayTimer = null\n\t\tthis._onScroll = null\n\t\tthis._onKey = null\n\t\tthis._onObjectCreated = null\n\t\tthis._routeUnhook = null\n\t\t// Layout watchers for the ACTIVE step's target (see trackTargetLayout).\n\t\tthis._layoutObs = null\n\t\tthis._bodyResizeObs = null\n\t\tthis._remeasureRaf = null\n\t\t// A qualifying auto-start tour whose first-step page is NOT the current\n\t\t// route — held here until the user navigates to that page (see hookRouter).\n\t\tthis._pendingAutoTour = null\n\t\t// The route the active step's target was first found on (see locateTarget).\n\t\tthis._homeRoute = null\n\t\tthis._modalObs = null\n\t},\n\n\tmounted() {\n\t\t// Auto-start a qualifying tour unless one is already running.\n\t\tthis.maybeAutoStart()\n\t\tthis._onScroll = () => this.computeRect()\n\t\twindow.addEventListener('scroll', this._onScroll, true)\n\t\twindow.addEventListener('resize', this._onScroll)\n\t\tthis._onKey = (e) => {\n\t\t\tif (e.key !== 'Escape' || !this.active) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// ESC while an app dialog is open belongs to that dialog. It used\n\t\t\t// to dismiss the tour too, so closing a create form ended it.\n\t\t\tif (this.isAppModalOpen()) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.onBackdrop()\n\t\t}\n\t\twindow.addEventListener('keydown', this._onKey)\n\t\tthis._onObjectCreated = (e) => {\n\t\t\tconst d = (e && e.detail) || {}\n\t\t\tthis.wt.notify({\n\t\t\t\tkind: 'object-created',\n\t\t\t\tregister: d.register,\n\t\t\t\tschema: d.schema,\n\t\t\t\tobject: d.object && typeof d.object === 'object' ? d.object : d,\n\t\t\t})\n\t\t}\n\t\tthis.watchAppModals()\n\t\twindow.addEventListener('cn-walkthrough:object-created', this._onObjectCreated)\n\t\tthis.hookRouter()\n\t\tif (this.active) {\n\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\twindow.removeEventListener('scroll', this._onScroll, true)\n\t\twindow.removeEventListener('resize', this._onScroll)\n\t\twindow.removeEventListener('keydown', this._onKey)\n\t\twindow.removeEventListener('cn-walkthrough:object-created', this._onObjectCreated)\n\t\tif (this._modalObs) {\n\t\t\tthis._modalObs.disconnect()\n\t\t\tthis._modalObs = null\n\t\t}\n\t\tif (typeof this._routeUnhook === 'function') {\n\t\t\tthis._routeUnhook()\n\t\t}\n\t\tthis.teardownStep()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether an app dialog (NcModal / NcDialog, or any other modal\n\t\t * dialog that is not this overlay) is open.\n\t\t *\n\t\t * @spec openspec/changes/walkthrough-advance-pause-resume/specs/cn-walkthrough/spec.md\n\t\t * @return {boolean}\n\t\t */\n\t\tisAppModalOpen() {\n\t\t\tif (typeof document === 'undefined') {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst own = this.$el\n\t\t\tconst nodes = document.querySelectorAll('.modal-mask, [role=\"dialog\"][aria-modal=\"true\"]')\n\t\t\tfor (const node of nodes) {\n\t\t\t\tif (own && typeof own.contains === 'function' && (own === node || own.contains(node))) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\n\t\t/**\n\t\t * Follow app dialogs opening and closing, so the overlay steps back\n\t\t * while one is open and comes back after.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\twatchAppModals() {\n\t\t\tconst update = () => {\n\t\t\t\tconst open = this.isAppModalOpen()\n\t\t\t\tif (open !== this.appModalOpen) {\n\t\t\t\t\tthis.appModalOpen = open\n\t\t\t\t\tif (!open && this.active) {\n\t\t\t\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tupdate()\n\t\t\tif (typeof window !== 'undefined' && window.MutationObserver) {\n\t\t\t\tthis._modalObs = new MutationObserver(update)\n\t\t\t\tthis._modalObs.observe(document.body, { childList: true, subtree: true })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Translate an i18n key/text via the `translate` prop when given.\n\t\t *\n\t\t * @param {string} value The key or literal.\n\t\t * @return {string} The resolved string.\n\t\t */\n\t\ttr(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn typeof this.translate === 'function' ? this.translate(value) : value\n\t\t},\n\n\t\t/**\n\t\t * Hook `$router.afterEach` (when a router is present) to feed route signals.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\thookRouter() {\n\t\t\tconst router = this.$router\n\t\t\tif (!router || typeof router.afterEach !== 'function') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis._routeUnhook = router.afterEach((to) => {\n\t\t\t\tthis.wt.notify({ kind: 'route', route: to.name, params: to.params || {} })\n\t\t\t\t// A first-visit tour deferred because the user deep-linked onto a\n\t\t\t\t// different page starts once they reach its first-step page.\n\t\t\t\tif (this._pendingAutoTour && !this.wt.running.value\n\t\t\t\t\t&& this.routeMatchesTour(this._pendingAutoTour, to.name)) {\n\t\t\t\t\tconst tour = this._pendingAutoTour\n\t\t\t\t\tthis._pendingAutoTour = null\n\t\t\t\t\tthis.wt.start(tour.id)\n\t\t\t\t}\n\t\t\t\tthis.$nextTick(() => this.locateTarget())\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Start a qualifying auto-start tour — but a `first-visit` tour whose\n\t\t * first step SPOTLIGHTS something on a specific page only opens when the\n\t\t * user is ON that page. A centered welcome step spotlights nothing and\n\t\t * therefore starts anywhere. Deep-linking onto an unrelated route (e.g. a shared detail-page\n\t\t * URL) defers the tour via `_pendingAutoTour` instead of popping it over\n\t\t * the wrong screen (ADR-062). A forced `tourId` always starts; a tour\n\t\t * whose first step is not page-anchored keeps the prior any-route\n\t\t * behaviour; without a router there is nothing to match, so it starts.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tmaybeAutoStart() {\n\t\t\tif (this.wt.running.value) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.tourId) {\n\t\t\t\tthis.wt.start(this.tourId)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst auto = this.wt.autoStartTour.value\n\t\t\tif (!auto) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!this.$router) {\n\t\t\t\tthis.wt.start(auto.id)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst routeName = this.$route && this.$route.name\n\t\t\tif (this.routeMatchesTour(auto, routeName)) {\n\t\t\t\tthis.wt.start(auto.id)\n\t\t\t} else {\n\t\t\t\tthis._pendingAutoTour = auto\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The route name a tour's FIRST step is anchored to, or null when the\n\t\t * first step is not page-anchored (a centered welcome step, or a\n\t\t * non-page target). Used to gate auto-start to the right screen.\n\t\t *\n\t\t * A `placement: \"center\"` first step returns NULL however it is\n\t\t * anchored. This is the whole point: a centered step spotlights\n\t\t * nothing — it is a welcome card floating in the middle of the screen —\n\t\t * so there is no element for the user to be \"on the right page\" for,\n\t\t * and gating its auto-open on a route only decides whether the tour\n\t\t * opens at all.\n\t\t *\n\t\t * That is what it did. This docblock already CLAIMED a centered welcome\n\t\t * step returned null; the code never checked `placement`, so any app\n\t\t * whose welcome step happened to carry a page/nav anchor other than its\n\t\t * landing route parked the tour in `_pendingAutoTour` and never opened\n\t\t * it — no error, no log, just no walkthrough. Measured on one instance\n\t\t * with an empty seen-state: opencatalogi (welcome anchored to\n\t\t * `Catalogs`) and pipelinq (anchored to `Products`) both showed nothing\n\t\t * at `#/` and the full tour at the anchored route, while dossiq\n\t\t * (anchored to its landing `Dashboard`) worked — the control that shows\n\t\t * the anchor, not the tour, was the variable. 19 of 20 fleet manifests\n\t\t * declare a walkthrough, so this is a class, not two instances.\n\t\t *\n\t\t * A step that genuinely spotlights an element keeps the ADR-062\n\t\t * behaviour, which is what it was built for: it still waits for its\n\t\t * page rather than popping over a deep-linked detail screen.\n\t\t *\n\t\t * @param {object} tour The tour definition.\n\t\t * @return {string|null} The first-step page/nav route name, or null.\n\t\t */\n\t\tfirstStepPage(tour) {\n\t\t\tconst steps = (tour && tour.steps) || []\n\t\t\tconst first = steps[0]\n\t\t\t// A CENTERED first step is not page-anchored, whatever it targets.\n\t\t\t//\n\t\t\t// The doc comment above has always said so; the code did not check.\n\t\t\t// `placement: 'center'` renders a card in the middle of the screen\n\t\t\t// and spotlights NOTHING — `locateTarget()` returns early on\n\t\t\t// `isCentered` and never resolves the target at all. So gating\n\t\t\t// auto-start on being on that step's route asks the user to be\n\t\t\t// somewhere the step will not point at.\n\t\t\t//\n\t\t\t// Consequence measured 2026-08-27 on one running instance, same\n\t\t\t// user, empty seen-state:\n\t\t\t//\n\t\t\t// opencatalogi welcome anchored to `Catalogs` -> `/` shows nothing,\n\t\t\t// `#/catalogi` shows \"1 / 8 Welcome to OpenCatalogi\"\n\t\t\t// pipelinq welcome anchored to `Products` -> `/` shows nothing,\n\t\t\t// `#/products` shows \"1 / 11 Welcome to Pipelinq\"\n\t\t\t// dossiq welcome anchored to `Dashboard` -> works (control)\n\t\t\t//\n\t\t\t// The tour was parked in `_pendingAutoTour` and opened only if the\n\t\t\t// user happened to navigate to that page — so the getting-started\n\t\t\t// tour never appeared on the landing page, silently, with nothing\n\t\t\t// logged. 19 of 20 fleet manifests declare a walkthrough, so this\n\t\t\t// is not two apps; it is however many anchored their welcome step\n\t\t\t// somewhere other than their landing route.\n\t\t\t//\n\t\t\t// ADR-062's intent is preserved exactly: a first step that really\n\t\t\t// does spotlight an element still gates on that element's route, so\n\t\t\t// a tour never pops over a deep-linked detail page.\n\t\t\tif (!first) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tif (first.placement === 'center') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst tgt = first.target\n\t\t\tif (tgt && (tgt.kind === 'page' || tgt.kind === 'nav-item') && tgt.ref) {\n\t\t\t\treturn String(tgt.ref)\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\t/**\n\t\t * Whether a tour may auto-open on the given route. True when the tour's\n\t\t * first step is not page-anchored — which includes every `center`-placed\n\t\t * welcome step, since it spotlights nothing (see firstStepPage) — so the\n\t\t * tour starts wherever the user lands. Otherwise the route name must\n\t\t * equal the first-step page.\n\t\t *\n\t\t * This is the ONLY consumer of firstStepPage, and both auto-start paths\n\t\t * (maybeAutoStart on mount, and the router afterEach that resumes a\n\t\t * deferred tour) go through here — so a centered step cannot be\n\t\t * swallowed by either.\n\t\t *\n\t\t * @param {object} tour The tour definition.\n\t\t * @param {string} [routeName] The current route name.\n\t\t * @return {boolean} True when the tour may open on this route.\n\t\t */\n\t\trouteMatchesTour(tour, routeName) {\n\t\t\tconst page = this.firstStepPage(tour)\n\t\t\tif (!page) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn !!routeName && String(routeName) === page\n\t\t},\n\n\t\t/**\n\t\t * Resolve, scroll to, and measure the active step's target, then install\n\t\t * the step's advance watcher.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tlocateTarget() {\n\t\t\tthis.teardownStep()\n\t\t\tif (!this.step || this.isCentered) {\n\t\t\t\tthis.rect = null\n\t\t\t\tthis.focusCard()\n\t\t\t\tthis.armDelay()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst el = this.resolveTarget(this.step)\n\t\t\t// A step's target belongs to the page it was first found on. The\n\t\t\t// same `index-add` button exists on every list page, and a step\n\t\t\t// left half-done on Products used to spotlight the Clients page's\n\t\t\t// button too, inviting a click that belonged to another step.\n\t\t\tconst kind = (this.step.target && this.step.target.kind) || ''\n\t\t\tconst routeName = this.$route ? this.$route.name : null\n\t\t\tif (el && kind !== 'page' && kind !== 'nav-item' && routeName) {\n\t\t\t\tif (this._homeRoute === null) {\n\t\t\t\t\tthis._homeRoute = routeName\n\t\t\t\t} else if (this._homeRoute !== routeName) {\n\t\t\t\t\tthis.offPage = true\n\t\t\t\t\tthis.targetEl = null\n\t\t\t\t\tthis.rect = null\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.offPage = false\n\t\t\tthis.targetEl = el\n\t\t\tif (!el) {\n\t\t\t\t// Optional step whose target is absent → skip; else wait for it.\n\t\t\t\tif (this.step.optional) {\n\t\t\t\t\tthis.wt.skip()\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t// Genuinely not in the DOM (nothing to reveal) — wait for it to\n\t\t\t\t// appear. A target hidden by a collapsed nav group is handled by\n\t\t\t\t// computeRect()'s zero-size check below instead, since `el` is\n\t\t\t\t// resolvable there and revealTarget() can walk up from it.\n\t\t\t\tthis.observeForTarget()\n\t\t\t\tthis.rect = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tel.scrollIntoView({ block: 'center', inline: 'center' })\n\t\t\t} catch { /* jsdom */ }\n\t\t\tthis.computeRect()\n\t\t\tthis.armStep(el)\n\t\t\t// The ResizeObserver only fires when the target itself resizes. A nav\n\t\t\t// item can MOVE (siblings render/settle after mount) without resizing,\n\t\t\t// leaving a stale cutout one row off. Re-measure across the next few\n\t\t\t// frames so the spotlight tracks the settled position, and keep\n\t\t\t// tracking it afterwards for content that arrives later still.\n\t\t\tthis.scheduleSettleRemeasure()\n\t\t\tthis.trackTargetLayout()\n\t\t},\n\n\t\t/**\n\t\t * Resolve a step's `target` to a DOM element, preferring stable manifest\n\t\t * identities, then `data-walkthrough-id`/`data-testid`, then raw CSS.\n\t\t *\n\t\t * @param {object} step The active step.\n\t\t * @return {Element|null} The resolved element.\n\t\t */\n\t\tresolveTarget(step) {\n\t\t\tconst tgt = step.target || {}\n\t\t\tconst ref = tgt.ref\n\t\t\tconst q = (sel) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn document.querySelector(sel)\n\t\t\t\t} catch {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst esc = (v) => (window.CSS && CSS.escape ? CSS.escape(v) : String(v).replace(/\"/g, '\\\\\"'))\n\t\t\tif (tgt.kind === 'selector' && tgt.selector) {\n\t\t\t\treturn q(tgt.selector)\n\t\t\t}\n\t\t\tif (!ref) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst byId = q(`[data-walkthrough-id=\"${esc(ref)}\"]`) || q(`[data-testid=\"${esc(ref)}\"]`)\n\t\t\tif (byId) {\n\t\t\t\treturn byId\n\t\t\t}\n\t\t\tif (tgt.kind === 'nav-item' || tgt.kind === 'page') {\n\t\t\t\treturn q(`[data-cn-route=\"${esc(ref)}\"]`) || q(`a[href$=\"#/${esc(ref)}\"]`) || q(`[data-route=\"${esc(ref)}\"]`)\n\t\t\t}\n\t\t\tif (tgt.kind === 'widget') {\n\t\t\t\treturn q(`[data-widget-key=\"${esc(ref)}\"]`) || q(`[data-widget-id=\"${esc(ref)}\"]`)\n\t\t\t}\n\t\t\tif (tgt.kind === 'action') {\n\t\t\t\treturn q(`[data-action-id=\"${esc(ref)}\"]`)\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\t/**\n\t\t * Expand only the collapsed nav groups that are actual ancestors of\n\t\t * `this.targetEl` (already resolved, just `display:none`'d by a\n\t\t * collapsed parent), instead of scanning the whole navigation.\n\t\t * Attempted at most once per step (`_revealAttempted`, reset in\n\t\t * teardownStep).\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\trevealTarget() {\n\t\t\tif (this._revealAttempted || !this.targetEl) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis._revealAttempted = true\n\t\t\tconst click = (btn) => {\n\t\t\t\tif (btn && typeof btn.click === 'function') {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tbtn.click()\n\t\t\t\t\t} catch { /* jsdom / detached */ }\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Walk every ancestor (not just .closest() jumps) since a nav group\n\t\t\t// and the Settings foldout use different container markup and can\n\t\t\t// nest either way.\n\t\t\tlet node = this.targetEl.parentElement\n\t\t\twhile (node) {\n\t\t\t\tif (node.classList.contains('app-navigation-entry--collapsible')) {\n\t\t\t\t\tclick(node.querySelector('button.icon-collapse, .app-navigation-entry__children-toggle, .app-navigation-entry__collapse'))\n\t\t\t\t} else if (node.matches?.('[data-testid=\"cn-nav-settings\"]')) {\n\t\t\t\t\t// NcAppNavigationSettings: CSS-module classes are hashed at\n\t\t\t\t\t// build time, so aria-expanded is the only stable handle on\n\t\t\t\t\t// its own toggle button.\n\t\t\t\t\tclick(node.querySelector('button[aria-expanded]'))\n\t\t\t\t}\n\t\t\t\tnode = node.parentElement\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Re-measure the target a few times after arming, to catch post-mount\n\t\t * layout settling (sibling nav items rendering, fonts, etc.) that moves\n\t\t * the target without resizing it. Cleared in teardownStep.\n\t\t *\n\t\t * Fast path only; later movement is caught by `trackTargetLayout()`.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tscheduleSettleRemeasure() {\n\t\t\tconst again = () => {\n\t\t\t\tif (this.targetEl && !this.isCentered) {\n\t\t\t\t\tthis.computeRect()\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis._settleTimers = (this._settleTimers || [])\n\t\t\tthis._settleTimers.push(setTimeout(again, 100), setTimeout(again, 300), setTimeout(again, 600))\n\t\t},\n\n\t\t/**\n\t\t * Re-measure on any layout change for as long as the step is active.\n\t\t *\n\t\t * The settle timers above stop at 600ms; a target on a page whose content\n\t\t * arrives from the network keeps moving after that, leaving the cutout on\n\t\t * empty space. The MutationObserver catches inserted content, the\n\t\t * ResizeObserver catches reflows that insert no nodes.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\ttrackTargetLayout() {\n\t\t\tconst remeasure = () => {\n\t\t\t\tif (this._remeasureRaf) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst raf = typeof window.requestAnimationFrame === 'function'\n\t\t\t\t\t? window.requestAnimationFrame\n\t\t\t\t\t: (fn) => setTimeout(fn, 16)\n\t\t\t\tthis._remeasureRaf = raf(() => {\n\t\t\t\t\tthis._remeasureRaf = null\n\t\t\t\t\tif (this.targetEl && !this.isCentered) {\n\t\t\t\t\t\tthis.computeRect()\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t\tif (window.MutationObserver) {\n\t\t\t\tthis._layoutObs = new MutationObserver(remeasure)\n\t\t\t\tthis._layoutObs.observe(document.body, { childList: true, subtree: true })\n\t\t\t}\n\t\t\tif (window.ResizeObserver) {\n\t\t\t\tthis._bodyResizeObs = new ResizeObserver(remeasure)\n\t\t\t\tthis._bodyResizeObs.observe(document.body)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Measure the target into a viewport rect.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tcomputeRect() {\n\t\t\tif (!this.targetEl) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst r = this.targetEl.getBoundingClientRect()\n\t\t\t// Target present but not laid out (e.g. a nav item inside a collapsed\n\t\t\t// group, or a hidden element): fall back to an anchorless centered\n\t\t\t// coachmark — no point-sized cutout — until it becomes visible. The\n\t\t\t// ResizeObserver armed in armStep() recomputes when it gains size.\n\t\t\tif (r.width === 0 || r.height === 0) {\n\t\t\t\t// Resolved but not laid out — most often a nav item inside a\n\t\t\t\t// collapsed group. Expand the group so the ResizeObserver armed in\n\t\t\t\t// armStep() recomputes once it gains size; until then, anchorless.\n\t\t\t\tthis.revealTarget()\n\t\t\t\tthis.rect = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// The overlay (.cn-walkthrough) is position:fixed, but a transformed\n\t\t\t// ancestor (NC content area) can become its containing block, so its\n\t\t\t// top-left is not the viewport origin. Convert the viewport rect into\n\t\t\t// the overlay's coordinate space so the dim/ring/card line up with the\n\t\t\t// real element instead of sitting a header's height too low.\n\t\t\tconst host = this.$el && this.$el.getBoundingClientRect ? this.$el.getBoundingClientRect() : { top: 0, left: 0 }\n\t\t\t// Remember the overlay's own viewport offset so placeCard can clamp\n\t\t\t// the coachmark against the REAL viewport even when a transformed\n\t\t\t// ancestor makes the position:fixed origin non-zero.\n\t\t\tthis._hostOffset = { top: host.top, left: host.left }\n\t\t\tconst next = { top: r.top - host.top, left: r.left - host.left, width: r.width, height: r.height }\n\t\t\t// Bail when nothing moved: trackTargetLayout() calls this on every\n\t\t\t// layout change, and placeCard()'s focusCard() would otherwise steal\n\t\t\t// focus back from whatever the user is typing in.\n\t\t\tconst p = this.rect\n\t\t\tif (p && p.top === next.top && p.left === next.left && p.width === next.width && p.height === next.height) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.rect = next\n\t\t\tthis.$nextTick(() => this.placeCard())\n\t\t},\n\n\t\t/**\n\t\t * Position the coachmark near the target with a simple flip so it stays\n\t\t * on-screen.\n\t\t *\n\t\t * @spec openspec/changes/cn-walkthrough-engine/specs/cn-walkthrough/spec.md\n\t\t * @return {void}\n\t\t */\n\t\tplaceCard() {\n\t\t\tconst card = this.$refs.card\n\t\t\tif (!card || !this.rect) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst cw = card.offsetWidth || 320\n\t\t\tconst ch = card.offsetHeight || 160\n\t\t\tconst gap = 12\n\t\t\tconst vw = window.innerWidth\n\t\t\tconst vh = window.innerHeight\n\t\t\tconst r = this.rect\n\t\t\tlet placement = this.step.placement && this.step.placement !== 'auto' ? this.step.placement : 'bottom'\n\t\t\tlet top\n\t\t\tlet left\n\t\t\tif (placement === 'bottom' && r.top + r.height + gap + ch > vh) {\n\t\t\t\tplacement = 'top'\n\t\t\t}\n\t\t\tif (placement === 'top' && r.top - gap - ch < 0) {\n\t\t\t\tplacement = 'bottom'\n\t\t\t}\n\t\t\tif (placement === 'bottom') {\n\t\t\t\ttop = r.top + r.height + gap\n\t\t\t\tleft = r.left\n\t\t\t} else if (placement === 'top') {\n\t\t\t\ttop = r.top - gap - ch\n\t\t\t\tleft = r.left\n\t\t\t} else if (placement === 'left') {\n\t\t\t\ttop = r.top\n\t\t\t\tleft = r.left - gap - cw\n\t\t\t} else {\n\t\t\t\ttop = r.top\n\t\t\t\tleft = r.left + r.width + gap\n\t\t\t}\n\t\t\t// Clamp to the viewport. `left`/`top` live in overlay-relative space\n\t\t\t// (this.rect was host-subtracted), so the viewport bounds must be\n\t\t\t// host-subtracted too — otherwise a scrolled/transformed host would\n\t\t\t// let the card render off-screen (ADR-062: the coachmark is always\n\t\t\t// fully on-screen).\n\t\t\tconst ho = this._hostOffset || { top: 0, left: 0 }\n\t\t\tleft = Math.max(gap - ho.left, Math.min(left, vw - cw - gap - ho.left))\n\t\t\ttop = Math.max(gap - ho.top, Math.min(top, vh - ch - gap - ho.top))\n\t\t\tthis.cardPlacement = placement\n\t\t\tthis.cardPos = { top, left }\n\t\t\tthis.focusCard()\n\t\t},\n\n\t\t/**\n\t\t * Move focus to the coachmark's first control (focus management / a11y).\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tfocusCard() {\n\t\t\t// Never take focus from an open app dialog.\n\t\t\tif (this.appModalOpen) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tconst btn = this.$refs.firstBtn && this.$refs.firstBtn.$el\n\t\t\t\tif (btn && typeof btn.focus === 'function') {\n\t\t\t\t\tbtn.focus()\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Install the advance watcher for the active step (click / element / delay;\n\t\t * route + object-created are global listeners installed at mount).\n\t\t *\n\t\t * @param {Element} el The resolved target.\n\t\t * @return {void}\n\t\t */\n\t\tarmStep(el) {\n\t\t\tconst a = this.step.advanceOn || {}\n\t\t\tif (a.type === 'click-target' && el) {\n\t\t\t\tthis._clickHandler = () => this.wt.notify({ kind: 'click' })\n\t\t\t\tel.addEventListener('click', this._clickHandler, { once: true })\n\t\t\t}\n\t\t\t// Recompute the cutout when the target's size changes — covers a target\n\t\t\t// that starts hidden/zero-size (e.g. inside a collapsed nav group) and\n\t\t\t// becomes visible while the step is active.\n\t\t\tif (el && window.ResizeObserver) {\n\t\t\t\tthis._resizeObs = new ResizeObserver(() => this.computeRect())\n\t\t\t\tthis._resizeObs.observe(el)\n\t\t\t}\n\t\t\tthis.armDelay()\n\t\t},\n\n\t\t/**\n\t\t * Arm the delay timer for a `delay` advance.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tarmDelay() {\n\t\t\tconst a = (this.step && this.step.advanceOn) || {}\n\t\t\tif (a.type === 'delay') {\n\t\t\t\tthis._delayTimer = setTimeout(() => this.wt.notify({ kind: 'delay' }), a.ms || 1500)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Watch the DOM until an `element-appears` (or a not-yet-mounted) target\n\t\t * resolves, then re-locate.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tobserveForTarget() {\n\t\t\tthis._observer = new MutationObserver(() => {\n\t\t\t\tconst el = this.resolveTarget(this.step)\n\t\t\t\tif (el) {\n\t\t\t\t\tthis._observer.disconnect()\n\t\t\t\t\tthis._observer = null\n\t\t\t\t\tif (this.step.advanceOn && this.step.advanceOn.type === 'element-appears') {\n\t\t\t\t\t\tthis.wt.notify({ kind: 'element' })\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.locateTarget()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t\tthis._observer.observe(document.body, { childList: true, subtree: true })\n\t\t},\n\n\t\t/**\n\t\t * Tear down the current step's watchers/timers/listeners.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tteardownStep() {\n\t\t\tthis._revealAttempted = false\n\t\t\t// computeRect()'s bail-when-unchanged optimisation must not survive\n\t\t\t// into the next step, or a same-geometry target never re-runs\n\t\t\t// placeCard()/focusCard() on step change.\n\t\t\tthis.rect = null\n\t\t\tif (this._observer) {\n\t\t\t\tthis._observer.disconnect()\n\t\t\t\tthis._observer = null\n\t\t\t}\n\t\t\tif (this._resizeObs) {\n\t\t\t\tthis._resizeObs.disconnect()\n\t\t\t\tthis._resizeObs = null\n\t\t\t}\n\t\t\tif (this._layoutObs) {\n\t\t\t\tthis._layoutObs.disconnect()\n\t\t\t\tthis._layoutObs = null\n\t\t\t}\n\t\t\tif (this._bodyResizeObs) {\n\t\t\t\tthis._bodyResizeObs.disconnect()\n\t\t\t\tthis._bodyResizeObs = null\n\t\t\t}\n\t\t\tif (this._remeasureRaf) {\n\t\t\t\tconst cancel = typeof window.cancelAnimationFrame === 'function' ? window.cancelAnimationFrame : clearTimeout\n\t\t\t\tcancel(this._remeasureRaf)\n\t\t\t\tthis._remeasureRaf = null\n\t\t\t}\n\t\t\tif (this._delayTimer) {\n\t\t\t\tclearTimeout(this._delayTimer)\n\t\t\t\tthis._delayTimer = null\n\t\t\t}\n\t\t\tif (this._settleTimers) {\n\t\t\t\tthis._settleTimers.forEach(clearTimeout)\n\t\t\t\tthis._settleTimers = null\n\t\t\t}\n\t\t\tif (this.targetEl && this._clickHandler) {\n\t\t\t\tthis.targetEl.removeEventListener('click', this._clickHandler)\n\t\t\t\tthis._clickHandler = null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Advance the tour one step, completing on the last step.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tadvance() {\n\t\t\t/**\n\t\t\t * @event advance Emitted when the user advances the tour.\n\t\t\t * @type {{ stepId: string }}\n\t\t\t */\n\t\t\tthis.$emit('advance', { stepId: this.step && this.step.id })\n\t\t\tconst wasLast = this.isLast\n\t\t\tthis.wt.next()\n\t\t\tif (wasLast) {\n\t\t\t\t/**\n\t\t\t\t * @event complete Emitted when the last step is passed.\n\t\t\t\t */\n\t\t\t\tthis.$emit('complete')\n\t\t\t}\n\t\t},\n\n\t\tback() {\n\t\t\tthis.wt.back()\n\t\t},\n\n\t\t/**\n\t\t * The coachmark's \"Skip\" control — ENDS the tour (it does not advance a\n\t\t * step). Marks the tour complete so the seen-version is persisted and it\n\t\t * won't auto-reopen, then emits `dismiss` + `complete` (ADR-062: Skip\n\t\t * dismisses the whole tour; Next is the only control that advances).\n\t\t * The internal per-step skip (an optional step whose target is absent)\n\t\t * still uses `wt.skip()` directly in `locateTarget`.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tskip() {\n\t\t\tthis.wt.complete()\n\t\t\t/**\n\t\t\t * @event dismiss Emitted when the user skips (ends) the tour.\n\t\t\t */\n\t\t\tthis.$emit('dismiss')\n\t\t\tthis.$emit('complete')\n\t\t},\n\n\t\t/**\n\t\t * Execute a cross-app hand-off: complete this tour locally, then navigate\n\t\t * the browser to the destination URL carrying a `cn_resume_tour` /\n\t\t * `cn_resume_step` token so the destination app's useWalkthrough resumes.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tdoHandoff() {\n\t\t\tconst h = (this.step && this.step.handoff) || {}\n\t\t\tif (!h.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst tour = h.tour || (this.wt.activeTour.value && this.wt.activeTour.value.id) || ''\n\t\t\tlet url = h.url + (h.url.indexOf('?') === -1 ? '?' : '&') + 'cn_resume_tour=' + encodeURIComponent(tour)\n\t\t\tif (h.step) {\n\t\t\t\turl += '&cn_resume_step=' + encodeURIComponent(h.step)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event handoff Emitted just before navigating to a cross-app destination.\n\t\t\t * @type {{ app: string, url: string }}\n\t\t\t */\n\t\t\tthis.$emit('handoff', { app: h.app, url })\n\t\t\tthis.$emit('complete')\n\t\t\tthis.wt.complete()\n\t\t\ttry {\n\t\t\t\twindow.location.href = url\n\t\t\t} catch { /* jsdom */ }\n\t\t},\n\n\t\t/**\n\t\t * End the tour for good from the corner close button: mark it complete so\n\t\t * the seen-version is persisted and it does not auto-show again.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tclose() {\n\t\t\tthis.wt.complete()\n\t\t\tthis.$emit('complete')\n\t\t},\n\n\t\t/**\n\t\t * Pause the tour from a backdrop click or ESC. The tour hides and\n\t\t * keeps its step; only Skip (the close button) and Finish end it.\n\t\t * Pressing ESC used to dismiss it, and the host recorded that as\n\t\t * \"seen\", so the tour never came back.\n\t\t *\n\t\t * @spec openspec/changes/walkthrough-advance-pause-resume/specs/cn-walkthrough/spec.md\n\t\t * @return {void}\n\t\t */\n\t\tonBackdrop() {\n\t\t\tthis.wt.pause()\n\t\t\t/**\n\t\t\t * @event pause Emitted when the user hides the tour (backdrop / ESC). The tour can continue later.\n\t\t\t */\n\t\t\tthis.$emit('pause')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-walkthrough {\n\tposition: fixed;\n\tinset: 0;\n\tpointer-events: none;\n}\n\n.cn-walkthrough__live {\n\tposition: absolute;\n\twidth: 1px;\n\theight: 1px;\n\toverflow: hidden;\n\tclip-path: inset(50%);\n}\n\n.cn-walkthrough__dim {\n\tposition: fixed;\n\tbackground: var(--color-modal-background, rgba(0, 0, 0, 0.5));\n\tpointer-events: auto;\n}\n\n.cn-walkthrough__dim--full {\n\tinset: 0;\n}\n\n/* Anchorless fallback for an ANCHORED step: dim everything, but do not\n intercept pointer events — the step's task requires clicking the app\n underneath (see the template comment). */\n/* Lets the step's own task through — and therefore cannot be clicked to\n dismiss; ESC, the close button and Back/Finish remain (see the template). */\n.cn-walkthrough__dim--passthrough {\n\tpointer-events: none;\n}\n\n.cn-walkthrough__ring {\n\tposition: fixed;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbox-shadow: 0 0 0 2px var(--color-primary-element, #0082c9);\n\tpointer-events: none;\n}\n\n.cn-walkthrough__card {\n\tposition: fixed;\n\twidth: 320px;\n\tmax-width: calc(100vw - 24px);\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tborder-radius: var(--border-radius-large, 8px);\n\tbox-shadow: 0 2px 12px var(--color-box-shadow, rgba(0, 0, 0, 0.3));\n\tpadding: 16px;\n\tpointer-events: auto;\n}\n\n.cn-walkthrough__card--docked {\n\tinset-block-end: 16px;\n\tinset-inline-start: 16px;\n}\n\n.cn-walkthrough__close {\n\t/* !important beats NcButton's own `position: relative`, which would otherwise\n\t leave the close button in-flow at the card's top-left. */\n\tposition: absolute !important;\n\ttop: 8px;\n\tinset-inline-end: 8px;\n\tinset-inline-start: auto;\n\tz-index: 1;\n}\n\n.cn-walkthrough__counter {\n\tfont-size: 0.8125rem;\n\tcolor: var(--color-text-maxcontrast);\n\tmargin-bottom: 4px;\n\t/* Keep the counter/title clear of the absolutely-positioned close button. */\n\tpadding-inline-end: 32px;\n}\n\n.cn-walkthrough__title {\n\tmargin: 0 0 6px;\n\tfont-size: 1.1rem;\n}\n\n.cn-walkthrough__body {\n\tmargin: 0 0 8px;\n}\n\n.cn-walkthrough__task {\n\tmargin: 0 0 8px;\n\tpadding: 8px;\n\tbackground: var(--color-primary-element-light, var(--color-background-hover));\n\tborder-radius: var(--border-radius, 4px);\n\tfont-weight: 600;\n}\n\n.cn-walkthrough__task-icon {\n\tmargin-inline-end: 4px;\n}\n\n.cn-walkthrough__actions {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tmargin-top: 8px;\n}\n\n.cn-walkthrough__spacer {\n\tflex: 1 1 auto;\n}\n</style>\n"],"names":["_createElementBlock","_createElementVNode","_toDisplayString","_Fragment","_openBlock","_normalizeStyle","_normalizeClass","_createVNode","_createTextVNode","_createBlock"],"mappings":"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"}
@@ -1,14 +1,15 @@
1
1
  import __sfc_main$b from '../CnAuditTrailWidget/CnAuditTrailWidget.vue.js';
2
2
  import __sfc_main$a from '../CnBannerWidget/CnBannerWidget.vue.js';
3
- import __sfc_main$e from '../CnDividerWidget/CnDividerWidget.vue.js';
4
- import __sfc_main$c from '../CnHeaderWidget/CnHeaderWidget.vue.js';
3
+ import __sfc_main$f from '../CnDividerWidget/CnDividerWidget.vue.js';
4
+ import __sfc_main$d from '../CnHeaderWidget/CnHeaderWidget.vue.js';
5
5
  import __sfc_main$9 from '../CnIntegrationWidget/CnIntegrationWidget.vue.js';
6
6
  import __sfc_main$5 from '../CnNavCardGrid/CnNavCardGrid.vue.js';
7
7
  import __sfc_main$6 from '../CnObjectDataWidget/CnObjectDataWidget.vue.js';
8
8
  import __sfc_main$3 from '../CnObjectGeoWidget/CnObjectGeoWidget.vue.js';
9
9
  import __sfc_main$7 from '../CnObjectMetadataWidget/CnObjectMetadataWidget.vue.js';
10
10
  import __sfc_main$8 from '../CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue.js';
11
- import __sfc_main$d from '../CnTextWidget/CnTextWidget.vue.js';
11
+ import __sfc_main$e from '../CnTextWidget/CnTextWidget.vue.js';
12
+ import __sfc_main$c from '../CnTimelineWidget/CnTimelineWidget.vue.js';
12
13
  import __sfc_main$4 from '../CnWidgetCardGrid/CnWidgetCardGrid.vue.js';
13
14
  import __sfc_main$1 from '../CnWidgetFormRenderer/CnWidgetFormRenderer.vue.js';
14
15
  import __sfc_main$2 from '../CnWidgetMapViewer/CnWidgetMapViewer.vue.js';
@@ -85,9 +86,10 @@ const BUILT_IN_WIDGETS = {
85
86
  integration: __sfc_main$9,
86
87
  banner: __sfc_main$a,
87
88
  'audit-trail': __sfc_main$b,
88
- header: __sfc_main$c,
89
- text: __sfc_main$d,
90
- divider: __sfc_main$e,
89
+ timeline: __sfc_main$c,
90
+ header: __sfc_main$d,
91
+ text: __sfc_main$e,
92
+ divider: __sfc_main$f,
91
93
  };
92
94
 
93
95
  export { BUILT_IN_WIDGETS };
@@ -1 +1 @@
1
- {"version":3,"file":"builtInWidgets.js","sources":["../../../../src/components/CnWidgetGrid/builtInWidgets.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2024 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * builtInWidgets — internal registry of the library's built-in v2 widget components.\n *\n * Keys are the `widgetKey` values used in v2 manifests.\n * Values are the Vue component definitions.\n *\n * CnWidgetGrid resolves widget keys first against this registry, then against\n * the consumer-supplied `cnRegistry` inject. A consumer can override a built-in\n * by registering a custom component with the same key in `cnRegistry`.\n *\n * Spec: REQ-MVR-005 (manifest-v2-renderer) — widget key resolution\n */\n\nimport CnAuditTrailWidget from '../CnAuditTrailWidget/CnAuditTrailWidget.vue'\nimport CnBannerWidget from '../CnBannerWidget/CnBannerWidget.vue'\nimport CnDividerWidget from '../CnDividerWidget/CnDividerWidget.vue'\nimport CnHeaderWidget from '../CnHeaderWidget/CnHeaderWidget.vue'\nimport CnIntegrationWidget from '../CnIntegrationWidget/CnIntegrationWidget.vue'\nimport CnNavCardGrid from '../CnNavCardGrid/CnNavCardGrid.vue'\nimport CnObjectDataWidget from '../CnObjectDataWidget/CnObjectDataWidget.vue'\nimport CnObjectGeoWidget from '../CnObjectGeoWidget/CnObjectGeoWidget.vue'\nimport CnObjectMetadataWidget from '../CnObjectMetadataWidget/CnObjectMetadataWidget.vue'\nimport CnRelatedObjectsWidget from '../CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue'\nimport CnTextWidget from '../CnTextWidget/CnTextWidget.vue'\nimport CnWidgetCardGrid from '../CnWidgetCardGrid/CnWidgetCardGrid.vue'\nimport CnWidgetFormRenderer from '../CnWidgetFormRenderer/CnWidgetFormRenderer.vue'\nimport CnWidgetMapViewer from '../CnWidgetMapViewer/CnWidgetMapViewer.vue'\nimport CnWidgetObjectTable from '../CnWidgetObjectTable/CnWidgetObjectTable.vue'\n\n/**\n * Built-in widget registry.\n *\n * The `data` and `metadata` keys are the canonical object-detail widgets:\n * a manifest `type:\"detail\"` page that places `widgetKey:\"data\"` /\n * `widgetKey:\"metadata\"` gets the object's editable property grid /\n * read-only `@self` metadata respectively. Both need the loaded object —\n * `CnPageRenderer` loads it for the detail page and `CnWidgetGrid` merges\n * `{ objectData, schema, objectType, objectId, register, store }` into each\n * widget's props (see CnWidgetGrid.resolvedWidgets), so the manifest entry\n * needs no per-widget `props`.\n *\n * The `integration` key places an OpenRegister integration leaf\n * (`CnIntegrationWidget`) on the page — e.g. Integriq's `sync-contract`\n * (\"Synced from\"). Pick the leaf with `props.only: \"<integrationId>\"`; the\n * object's `register` / `objectId` arrive via the detail-context merge, and\n * the manifest should set `props.schema` to the schema slug (the merged\n * context `schema` is the schema *object*, so an explicit slug prop wins).\n *\n * The `banner` key places a declarative notice banner (CnBannerWidget):\n * `props { variant, text, visibleWhen?, route? }` — see the component for\n * the visibleWhen predicate shape.\n *\n * The `audit-trail` key places the object change-log card\n * (CnAuditTrailWidget) — a detail-page widget whose register / schema /\n * objectId arrive via the detail-context merge (or explicit `props`).\n *\n * The `header` / `text` / `divider` keys reuse the dashboard catalog's\n * content-only presentation widgets on v2 pages: each takes a single\n * `props.content` object with the same shape their dashboard `content`\n * blob uses (see the components' docblocks).\n *\n * The `nav-card-grid` key (ADR-044 §4 cards-collapse) places CnNavCardGrid:\n * a grid of arbitrary navigation-link cards from `props.entries` (the\n * navCardEntry shape). Unlike `card-grid` (CnWidgetCardGrid, which renders\n * OpenRegister objects as CnObjectCard), `nav-card-grid` renders links, not\n * data — the intended remedy for a deep menu group collapsed into one\n * top-level entry via `menu-layout.json` relocations.\n *\n * @type {Record<string, import('vue').Component>}\n */\nexport const BUILT_IN_WIDGETS = {\n\t'object-table': CnWidgetObjectTable,\n\t'form-renderer': CnWidgetFormRenderer,\n\t'map-viewer': CnWidgetMapViewer,\n\t'object-geo': CnObjectGeoWidget,\n\t'card-grid': CnWidgetCardGrid,\n\t'nav-card-grid': CnNavCardGrid,\n\tdata: CnObjectDataWidget,\n\tmetadata: CnObjectMetadataWidget,\n\trelated: CnRelatedObjectsWidget,\n\tintegration: CnIntegrationWidget,\n\tbanner: CnBannerWidget,\n\t'audit-trail': CnAuditTrailWidget,\n\theader: CnHeaderWidget,\n\ttext: CnTextWidget,\n\tdivider: CnDividerWidget,\n}\n"],"names":["CnWidgetObjectTable","CnWidgetFormRenderer","CnWidgetMapViewer","CnObjectGeoWidget","CnWidgetCardGrid","CnNavCardGrid","CnObjectDataWidget","CnObjectMetadataWidget","CnRelatedObjectsWidget","CnIntegrationWidget","CnBannerWidget","CnAuditTrailWidget","CnHeaderWidget","CnTextWidget","CnDividerWidget"],"mappings":";;;;;;;;;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAgBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,gBAAgB,GAAG;AAChC,CAAC,cAAc,EAAEA,UAAmB;AACpC,CAAC,eAAe,EAAEC,YAAoB;AACtC,CAAC,YAAY,EAAEC,YAAiB;AAChC,CAAC,YAAY,EAAEC,YAAiB;AAChC,CAAC,WAAW,EAAEC,YAAgB;AAC9B,CAAC,eAAe,EAAEC,YAAa;AAC/B,CAAC,IAAI,EAAEC,YAAkB;AACzB,CAAC,QAAQ,EAAEC,YAAsB;AACjC,CAAC,OAAO,EAAEC,YAAsB;AAChC,CAAC,WAAW,EAAEC,YAAmB;AACjC,CAAC,MAAM,EAAEC,YAAc;AACvB,CAAC,aAAa,EAAEC,YAAkB;AAClC,CAAC,MAAM,EAAEC,YAAc;AACvB,CAAC,IAAI,EAAEC,YAAY;AACnB,CAAC,OAAO,EAAEC,YAAe;AACzB;;;;"}
1
+ {"version":3,"file":"builtInWidgets.js","sources":["../../../../src/components/CnWidgetGrid/builtInWidgets.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2024 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * builtInWidgets — internal registry of the library's built-in v2 widget components.\n *\n * Keys are the `widgetKey` values used in v2 manifests.\n * Values are the Vue component definitions.\n *\n * CnWidgetGrid resolves widget keys first against this registry, then against\n * the consumer-supplied `cnRegistry` inject. A consumer can override a built-in\n * by registering a custom component with the same key in `cnRegistry`.\n *\n * Spec: REQ-MVR-005 (manifest-v2-renderer) — widget key resolution\n */\n\nimport CnAuditTrailWidget from '../CnAuditTrailWidget/CnAuditTrailWidget.vue'\nimport CnBannerWidget from '../CnBannerWidget/CnBannerWidget.vue'\nimport CnDividerWidget from '../CnDividerWidget/CnDividerWidget.vue'\nimport CnHeaderWidget from '../CnHeaderWidget/CnHeaderWidget.vue'\nimport CnIntegrationWidget from '../CnIntegrationWidget/CnIntegrationWidget.vue'\nimport CnNavCardGrid from '../CnNavCardGrid/CnNavCardGrid.vue'\nimport CnObjectDataWidget from '../CnObjectDataWidget/CnObjectDataWidget.vue'\nimport CnObjectGeoWidget from '../CnObjectGeoWidget/CnObjectGeoWidget.vue'\nimport CnObjectMetadataWidget from '../CnObjectMetadataWidget/CnObjectMetadataWidget.vue'\nimport CnRelatedObjectsWidget from '../CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue'\nimport CnTextWidget from '../CnTextWidget/CnTextWidget.vue'\nimport CnTimelineWidget from '../CnTimelineWidget/CnTimelineWidget.vue'\nimport CnWidgetCardGrid from '../CnWidgetCardGrid/CnWidgetCardGrid.vue'\nimport CnWidgetFormRenderer from '../CnWidgetFormRenderer/CnWidgetFormRenderer.vue'\nimport CnWidgetMapViewer from '../CnWidgetMapViewer/CnWidgetMapViewer.vue'\nimport CnWidgetObjectTable from '../CnWidgetObjectTable/CnWidgetObjectTable.vue'\n\n/**\n * Built-in widget registry.\n *\n * The `data` and `metadata` keys are the canonical object-detail widgets:\n * a manifest `type:\"detail\"` page that places `widgetKey:\"data\"` /\n * `widgetKey:\"metadata\"` gets the object's editable property grid /\n * read-only `@self` metadata respectively. Both need the loaded object —\n * `CnPageRenderer` loads it for the detail page and `CnWidgetGrid` merges\n * `{ objectData, schema, objectType, objectId, register, store }` into each\n * widget's props (see CnWidgetGrid.resolvedWidgets), so the manifest entry\n * needs no per-widget `props`.\n *\n * The `integration` key places an OpenRegister integration leaf\n * (`CnIntegrationWidget`) on the page — e.g. Integriq's `sync-contract`\n * (\"Synced from\"). Pick the leaf with `props.only: \"<integrationId>\"`; the\n * object's `register` / `objectId` arrive via the detail-context merge, and\n * the manifest should set `props.schema` to the schema slug (the merged\n * context `schema` is the schema *object*, so an explicit slug prop wins).\n *\n * The `banner` key places a declarative notice banner (CnBannerWidget):\n * `props { variant, text, visibleWhen?, route? }` — see the component for\n * the visibleWhen predicate shape.\n *\n * The `audit-trail` key places the object change-log card\n * (CnAuditTrailWidget) — a detail-page widget whose register / schema /\n * objectId arrive via the detail-context merge (or explicit `props`).\n *\n * The `header` / `text` / `divider` keys reuse the dashboard catalog's\n * content-only presentation widgets on v2 pages: each takes a single\n * `props.content` object with the same shape their dashboard `content`\n * blob uses (see the components' docblocks).\n *\n * The `nav-card-grid` key (ADR-044 §4 cards-collapse) places CnNavCardGrid:\n * a grid of arbitrary navigation-link cards from `props.entries` (the\n * navCardEntry shape). Unlike `card-grid` (CnWidgetCardGrid, which renders\n * OpenRegister objects as CnObjectCard), `nav-card-grid` renders links, not\n * data — the intended remedy for a deep menu group collapsed into one\n * top-level entry via `menu-layout.json` relocations.\n *\n * @type {Record<string, import('vue').Component>}\n */\nexport const BUILT_IN_WIDGETS = {\n\t'object-table': CnWidgetObjectTable,\n\t'form-renderer': CnWidgetFormRenderer,\n\t'map-viewer': CnWidgetMapViewer,\n\t'object-geo': CnObjectGeoWidget,\n\t'card-grid': CnWidgetCardGrid,\n\t'nav-card-grid': CnNavCardGrid,\n\tdata: CnObjectDataWidget,\n\tmetadata: CnObjectMetadataWidget,\n\trelated: CnRelatedObjectsWidget,\n\tintegration: CnIntegrationWidget,\n\tbanner: CnBannerWidget,\n\t'audit-trail': CnAuditTrailWidget,\n\ttimeline: CnTimelineWidget,\n\theader: CnHeaderWidget,\n\ttext: CnTextWidget,\n\tdivider: CnDividerWidget,\n}\n"],"names":["CnWidgetObjectTable","CnWidgetFormRenderer","CnWidgetMapViewer","CnObjectGeoWidget","CnWidgetCardGrid","CnNavCardGrid","CnObjectDataWidget","CnObjectMetadataWidget","CnRelatedObjectsWidget","CnIntegrationWidget","CnBannerWidget","CnAuditTrailWidget","CnTimelineWidget","CnHeaderWidget","CnTextWidget","CnDividerWidget"],"mappings":";;;;;;;;;;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAiBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,gBAAgB,GAAG;AAChC,CAAC,cAAc,EAAEA,UAAmB;AACpC,CAAC,eAAe,EAAEC,YAAoB;AACtC,CAAC,YAAY,EAAEC,YAAiB;AAChC,CAAC,YAAY,EAAEC,YAAiB;AAChC,CAAC,WAAW,EAAEC,YAAgB;AAC9B,CAAC,eAAe,EAAEC,YAAa;AAC/B,CAAC,IAAI,EAAEC,YAAkB;AACzB,CAAC,QAAQ,EAAEC,YAAsB;AACjC,CAAC,OAAO,EAAEC,YAAsB;AAChC,CAAC,WAAW,EAAEC,YAAmB;AACjC,CAAC,MAAM,EAAEC,YAAc;AACvB,CAAC,aAAa,EAAEC,YAAkB;AAClC,CAAC,QAAQ,EAAEC,YAAgB;AAC3B,CAAC,MAAM,EAAEC,YAAc;AACvB,CAAC,IAAI,EAAEC,YAAY;AACnB,CAAC,OAAO,EAAEC,YAAe;AACzB;;;;"}
@@ -48,6 +48,7 @@ import '../CnObjectDataWidget/dashboardRegistration.js';
48
48
  import '../CnObjectGeoWidget/dashboardRegistration.js';
49
49
  import '../CnWidgetObjectTable/dashboardRegistration.js';
50
50
  import '../CnAuditTrailWidget/dashboardRegistration.js';
51
+ import '../CnTimelineWidget/dashboardRegistration.js';
51
52
  import '../CnKbSearchWidget/index.js';
52
53
  import '../CnInteractionFormWidget/index.js';
53
54
  import '../CnBannerWidget/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"registerDashboardWidgets.js","sources":["../../../../src/components/CnWidgetGrid/registerDashboardWidgets.js"],"sourcesContent":["/**\n * Side-effect aggregator for the cn-widget-library dashboard widget catalog.\n *\n * Importing this module imports every migrated widget's `index.js`, each of\n * which self-registers its `{ renderer, form, defaultContent, displayName,\n * icon }` into `dashboardWidgetRegistry` at load time. The library barrel\n * imports this module so the catalog is populated whenever a consuming app\n * imports `@conduction/nextcloud-vue`; `CnWidgetGrid` then resolves widgets by\n * type key and `CnAddWidgetModal` lists the addable types.\n *\n * The individual widget components are intentionally NOT public exports — they\n * are resolved by their registry type key (the whole point of the registry),\n * so consumers reference them via the manifest `widgetKey`, not by import.\n *\n * @module components/CnWidgetGrid/registerDashboardWidgets\n */\n\nimport CnChartWidget from '../CnChartWidget/CnChartWidget.vue'\nimport CnChartWidgetForm from '../CnChartWidgetForm/CnChartWidgetForm.vue'\nimport CnConversationWidget from '../CnConversationThread/CnConversationWidget.vue'\nimport CnCountdownWidget from '../CnCountdownWidget/CnCountdownWidget.vue'\nimport CnCountdownWidgetForm from '../CnCountdownWidgetForm/CnCountdownWidgetForm.vue'\nimport CnDocumentReviewWidget from '../CnDocumentReviewList/CnDocumentReviewWidget.vue'\nimport CnMapWidget from '../CnMapWidget/CnMapWidget.vue'\nimport CnMapWidgetForm from '../CnMapWidgetForm/CnMapWidgetForm.vue'\nimport CnNextStepWidget from '../CnNextStepCard/CnNextStepWidget.vue'\nimport CnObjectListWidget2 from '../CnObjectListWidget/CnObjectListWidget.vue'\nimport CnObjectListWidgetForm2 from '../CnObjectListWidgetForm/CnObjectListWidgetForm.vue'\nimport CnRelatedObjectsWidget from '../CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue'\nimport CnRelatedObjectsWidgetForm from '../CnRelatedObjectsWidgetForm/CnRelatedObjectsWidgetForm.vue'\nimport CnStackedBarWidget from '../CnStackedBarWidget/CnStackedBarWidget.vue'\nimport CnStackedBarWidgetForm from '../CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue'\nimport CnStagesWidget from '../CnStagesWidget/CnStagesWidget.vue'\nimport CnStagesWidgetForm from '../CnStagesWidgetForm/CnStagesWidgetForm.vue'\nimport CnStatsBlockWidget from '../CnStatsBlockWidget/CnStatsBlockWidget.vue'\nimport CnStatsBlockWidgetForm from '../CnStatsBlockWidgetForm/CnStatsBlockWidgetForm.vue'\nimport CnTabsWidget from '../CnTabsWidget/CnTabsWidget.vue'\nimport CnTabsWidgetForm from '../CnTabsWidgetForm/CnTabsWidgetForm.vue'\nimport CnWeekStripWidget from '../CnWeekStripWidget/CnWeekStripWidget.vue'\nimport CnWeekStripWidgetForm from '../CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue'\n// Typed widgets registered with an explicit renderer + config FORM. chart /\n// stats-block render through CnDashboardPage's own isChart()/isStatsBlock()\n// branches and `related` through CnDetailPage's isRelatedWidget() branch — the\n// registry entry exists so the cog editor + Add-widget picker get a form (and,\n// for `related`, the detail-page surface). `table` aliases the object-list\n// renderer/form for legacy manifests using type:'table'.\nimport { registerDashboardWidget } from './dashboardWidgetRegistry.js'\n\nimport '../CnLabelWidget/index.js'\nimport '../CnTextWidget/index.js'\nimport '../CnImageWidget/index.js'\nimport '../CnLinkButtonWidget/index.js'\nimport '../CnDividerWidget/index.js'\nimport '../CnHeaderWidget/index.js'\nimport '../CnQuicklinksWidget/index.js'\nimport '../CnVideoWidget/index.js'\nimport '../CnDashTileWidget/index.js'\nimport '../CnLinksWidget/index.js'\nimport '../CnMenuWidget/index.js'\nimport '../CnContainerWidget/index.js'\nimport '../CnFilesWidget/index.js'\nimport '../CnPeopleWidget/index.js'\nimport '../CnCalendarWidget/index.js'\nimport '../CnNcWidgetWidget/index.js'\nimport '../CnSpendAnalyticsWidget/index.js'\nimport '../CnStatWidget/index.js'\nimport '../CnDeltaWidget/index.js'\nimport '../CnGaugeWidget/index.js'\nimport '../CnFlowRunsWidget/index.js'\nimport '../CnTasksWidget/index.js'\nimport '../CnObjectDataWidget/dashboardRegistration.js'\nimport '../CnObjectGeoWidget/dashboardRegistration.js'\nimport '../CnWidgetObjectTable/dashboardRegistration.js'\nimport '../CnAuditTrailWidget/dashboardRegistration.js'\nimport '../CnKbSearchWidget/index.js'\nimport '../CnInteractionFormWidget/index.js'\nimport '../CnBannerWidget/index.js'\nimport '../CnWorkspaceFilterWidget/index.js'\n\nregisterDashboardWidget('chart', {\n\trenderer: CnChartWidget,\n\tform: CnChartWidgetForm,\n\tdefaultContent: {\n\t\tchartKind: 'area',\n\t\tdataSource: { register: '', schema: '', filter: {}, bucket: { field: '', interval: 'month', metric: 'count', metricField: '' } },\n\t},\n\tdisplayName: 'Chart',\n\ticon: 'ChartLine',\n})\n\nregisterDashboardWidget('stats-block', {\n\trenderer: CnStatsBlockWidget,\n\tform: CnStatsBlockWidgetForm,\n\tdefaultContent: {\n\t\ttitle: '',\n\t\tprops: { countLabel: '', variant: 'default', iconClass: '' },\n\t\tdataSource: { register: '', schema: '', metric: 'count', field: '', filter: {} },\n\t},\n\tdisplayName: 'Statistic card',\n\ticon: 'ChartBar',\n\t// Card family (docs/architecture/cards-and-widgets.md), like `stat` and\n\t// `delta`: the tile headlines itself with its own title, so the wrapper\n\t// renders no header of its own and would otherwise show the title twice.\n\tcard: true,\n})\n\n// `table` and `object-list` (below) are legacy aliases of `object-table` — same\n// config form (CnObjectListWidgetForm), same job. They stay registered so\n// existing placements keep rendering, but are hidden from every Add-widget\n// picker via a sentinel surface no picker queries (see widgetTypeAllowsSurface):\n// `object-table` is the one to add now. Rendering resolves through\n// getWidgetTypeEntry, which ignores `surfaces`, so hidden ≠ broken.\nregisterDashboardWidget('table', {\n\trenderer: CnObjectListWidget2,\n\tform: CnObjectListWidgetForm2,\n\tdefaultContent: {\n\t\tregister: '',\n\t\tschema: '',\n\t\tfilter: {},\n\t\tsort: { field: '', dir: 'asc' },\n\t\tlimit: 10,\n\t\tcolumns: [],\n\t},\n\tdisplayName: 'Table',\n\ticon: 'ClipboardList',\n\tsurfaces: ['legacy'],\n})\n\n// `object-list` is registered HERE and only here. Registering it from\n// CnObjectListWidget/index.js instead is not enough: package.json declares\n// `sideEffects: [\"**/*.css\"]` (ADR-061 tree-shaking), which lets\n// rollup/webpack legally DROP bare imports of side-effect-free JS modules —\n// the dist bundle shipped without the object-list registration and every\n// manifest `type:\"object-list\"` widget rendered blank. Inline calls in this\n// module survive because consumers import live bindings from the registry.\n//\n// That index.js ALSO self-registered, with a divergent entry (limit 5, no\n// `surfaces`), so both ran in a consumer's build — components/index.js\n// re-exports it, so it was never tree-shaken — and last-registration-wins\n// decided whether this legacy alias stayed hidden from the Add-widget picker.\n// The self-registration is gone; this entry is the one that already won.\nregisterDashboardWidget('object-list', {\n\trenderer: CnObjectListWidget2,\n\tform: CnObjectListWidgetForm2,\n\tdefaultContent: {\n\t\tregister: '',\n\t\tschema: '',\n\t\tfilter: {},\n\t\tsort: { field: '', dir: 'asc' },\n\t\tlimit: 25,\n\t\tcolumns: [],\n\t},\n\tdisplayName: 'Object list',\n\ticon: 'ClipboardList',\n\tsurfaces: ['legacy'],\n})\n\nregisterDashboardWidget('related', {\n\trenderer: CnRelatedObjectsWidget,\n\tform: CnRelatedObjectsWidgetForm,\n\tdefaultContent: { title: '', groups: [], hideSingleTabTitle: true, showTotalCount: true },\n\tdisplayName: 'Object relations',\n\ticon: 'FileTreeOutline',\n\tsurfaces: ['detail-page'],\n\townsTitle: true,\n})\n\n// `map` is registered here for the same tree-shaking reason as `object-list`\n// above — a bare `import '../CnMapWidget/index.js'` is a side-effect-free JS module\n// as far as the bundler is concerned, so it may legally be dropped and the widget\n// would silently never appear in Add Widget. CnMapWidget/index.js carried a\n// byte-identical copy of this entry, which only produced a duplicate-registration\n// warning on every consumer's boot; it no longer registers.\nregisterDashboardWidget('map', {\n\trenderer: CnMapWidget,\n\tform: CnMapWidgetForm,\n\tdefaultContent: {\n\t\tregister: '',\n\t\tschema: '',\n\t\t// The Netherlands, roughly — only visible until autoFit frames the objects.\n\t\tcenter: [52.13, 5.29],\n\t\tzoom: 7,\n\t\theight: '400px',\n\t\tpopupField: '',\n\t\tclustering: false,\n\t\tautoFit: true,\n\t\t// A map with no tile layer renders as a grey box. `basemaps` is opt-in and\n\t\t// empty by default (so consumers declaring a `tile` entry in `layers` are\n\t\t// unaffected), which meant a freshly-placed Map widget had NO background at\n\t\t// all. Ship OpenStreetMap — the Nextcloud CSP already allows *.tile.openstreetmap.org.\n\t\tbasemaps: [\n\t\t\t{\n\t\t\t\tname: 'OpenStreetMap',\n\t\t\t\turl: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',\n\t\t\t\tattribution: '© OpenStreetMap contributors',\n\t\t\t\toptions: { maxZoom: 19 },\n\t\t\t},\n\t\t],\n\t\tmarkers: {\n\t\t\tdataSource: { register: '', schema: '' },\n\t\t\tpopupField: '',\n\t\t\tclustering: false,\n\t\t},\n\t},\n\tdisplayName: 'Map',\n\ticon: 'Map',\n})\n\nregisterDashboardWidget('countdown', {\n\trenderer: CnCountdownWidget,\n\tform: CnCountdownWidgetForm,\n\tdefaultContent: {\n\t\tlabel: '',\n\t\tfield: '',\n\t\ticon: 'ClockOutline',\n\t\tthresholds: {},\n\t\tshowDate: true,\n\t},\n\tdisplayName: 'Countdown',\n\ticon: 'ClockOutline',\n\t// Card family: the tile headlines itself, so the wrapper draws no header.\n\tcard: true,\n\t// It reads a date off the BOUND RECORD, which only a detail page has.\n\tsurfaces: ['detail-page'],\n})\n\n// The case surfaces (detail-action-model-and-case-surfaces). Each reads the\n// BOUND RECORD, which only a detail page has, and none has a config form yet,\n// so `form: null` keeps them out of the Add-widget picker: they are placed\n// from the manifest. Registered inline for the same tree-shaking reason as\n// `map` above.\nregisterDashboardWidget('next-step', {\n\trenderer: CnNextStepWidget,\n\tform: null,\n\tdefaultContent: { field: 'status', stages: {} },\n\tdisplayName: 'Next step',\n\ticon: 'CheckCircleOutline',\n\tsurfaces: ['detail-page'],\n\townsTitle: true,\n})\n\nregisterDashboardWidget('document-review', {\n\trenderer: CnDocumentReviewWidget,\n\tform: null,\n\tdefaultContent: { field: 'documents', statusField: 'reviewStatus', statuses: [] },\n\tdisplayName: 'Document review',\n\ticon: 'FileDocumentCheckOutline',\n\tsurfaces: ['detail-page'],\n\townsTitle: true,\n})\n\nregisterDashboardWidget('conversation', {\n\trenderer: CnConversationWidget,\n\tform: null,\n\tdefaultContent: { field: 'messages', endpoint: '' },\n\tdisplayName: 'Conversation',\n\ticon: 'ForumOutline',\n\tsurfaces: ['detail-page'],\n\townsTitle: true,\n})\n\n// The record's stages, where clicking a stage moves the record there.\n// Registered inline for the same tree-shaking reason as `map` above.\nregisterDashboardWidget('stages', {\n\trenderer: CnStagesWidget,\n\tform: CnStagesWidgetForm,\n\tdefaultContent: {\n\t\tcurrentField: 'status',\n\t\torientation: 'horizontal',\n\t\tsize: 'medium',\n\t\tstagesEndpoint: { url: '', path: '' },\n\t\t// The LIFECYCLE, not a record write. OpenRegister decides what is\n\t\t// reachable and re-validates the move; a field write has neither.\n\t\ttransition: { kind: 'lifecycle' },\n\t},\n\tdisplayName: 'Stages',\n\ticon: 'Timeline',\n\t// Card family on the detail grid: a titled wrapper around a self-contained\n\t// strip, like the KPI tiles it usually sits beside.\n\tcard: true,\n\t// It reads and moves the BOUND RECORD, which only a detail page has.\n\tsurfaces: ['detail-page'],\n})\n\nregisterDashboardWidget('tabs', {\n\trenderer: CnTabsWidget,\n\tform: CnTabsWidgetForm,\n\tdefaultContent: {\n\t\ttabs: [],\n\t\tariaLabel: '',\n\t},\n\tdisplayName: 'Tabs',\n\ticon: 'TabPlus',\n\t// Renders OTHER widgets, so CnDetailWidgetHost hands it the surface's\n\t// sibling definitions and the context its children will need.\n\tcontainer: true,\n\t// The strip's labels ARE the titles, so a card header above them would name\n\t// the group twice.\n\townsTitle: true,\n\tsurfaces: ['detail-page'],\n})\n\n// The current week as day columns, with the dated items under each day.\n// Registered inline for the same tree-shaking reason as `map` above.\nregisterDashboardWidget('week-strip', {\n\trenderer: CnWeekStripWidget,\n\tform: CnWeekStripWidgetForm,\n\tdefaultContent: {\n\t\tsource: { register: '', schema: '', filter: {} },\n\t\tdateField: '',\n\t\ttitleField: '',\n\t\tmetaFields: [],\n\t\tdays: 5,\n\t\titemRoute: '',\n\t\temptyText: '',\n\t},\n\tdisplayName: 'Week strip',\n\ticon: 'Calendar',\n})\n\n// One segmented bar with a legend that carries the numbers.\nregisterDashboardWidget('stacked-bar', {\n\trenderer: CnStackedBarWidget,\n\tform: CnStackedBarWidgetForm,\n\tdefaultContent: {\n\t\tsource: { register: '', schema: '', groupBy: '', filter: {} },\n\t\torder: [],\n\t\tlabels: {},\n\t\temptyText: '',\n\t},\n\tdisplayName: 'Stacked bar',\n\ticon: 'ChartBar',\n})\n\n/**\n * Explicit no-op that guarantees this module (and therefore every widget's\n * self-registration side effect) is evaluated. Call it once at app bootstrap if\n * a bundler tree-shakes bare side-effect imports.\n *\n * @return {void}\n */\nexport function registerBuiltinDashboardWidgets() {}\n"],"names":["CnChartWidget","CnChartWidgetForm","CnStatsBlockWidget","CnStatsBlockWidgetForm","CnObjectListWidget2","CnObjectListWidgetForm2","CnRelatedObjectsWidget","CnRelatedObjectsWidgetForm","CnMapWidget","CnMapWidgetForm","CnCountdownWidget","CnCountdownWidgetForm","CnNextStepWidget","CnDocumentReviewWidget","CnConversationWidget","CnStagesWidget","CnStagesWidgetForm","CnTabsWidget","CnTabsWidgetForm","CnWeekStripWidget","CnWeekStripWidgetForm","CnStackedBarWidget","CnStackedBarWidgetForm"],"mappings":"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"}
1
+ {"version":3,"file":"registerDashboardWidgets.js","sources":["../../../../src/components/CnWidgetGrid/registerDashboardWidgets.js"],"sourcesContent":["/**\n * Side-effect aggregator for the cn-widget-library dashboard widget catalog.\n *\n * Importing this module imports every migrated widget's `index.js`, each of\n * which self-registers its `{ renderer, form, defaultContent, displayName,\n * icon }` into `dashboardWidgetRegistry` at load time. The library barrel\n * imports this module so the catalog is populated whenever a consuming app\n * imports `@conduction/nextcloud-vue`; `CnWidgetGrid` then resolves widgets by\n * type key and `CnAddWidgetModal` lists the addable types.\n *\n * The individual widget components are intentionally NOT public exports — they\n * are resolved by their registry type key (the whole point of the registry),\n * so consumers reference them via the manifest `widgetKey`, not by import.\n *\n * @module components/CnWidgetGrid/registerDashboardWidgets\n */\n\nimport CnChartWidget from '../CnChartWidget/CnChartWidget.vue'\nimport CnChartWidgetForm from '../CnChartWidgetForm/CnChartWidgetForm.vue'\nimport CnConversationWidget from '../CnConversationThread/CnConversationWidget.vue'\nimport CnCountdownWidget from '../CnCountdownWidget/CnCountdownWidget.vue'\nimport CnCountdownWidgetForm from '../CnCountdownWidgetForm/CnCountdownWidgetForm.vue'\nimport CnDocumentReviewWidget from '../CnDocumentReviewList/CnDocumentReviewWidget.vue'\nimport CnMapWidget from '../CnMapWidget/CnMapWidget.vue'\nimport CnMapWidgetForm from '../CnMapWidgetForm/CnMapWidgetForm.vue'\nimport CnNextStepWidget from '../CnNextStepCard/CnNextStepWidget.vue'\nimport CnObjectListWidget2 from '../CnObjectListWidget/CnObjectListWidget.vue'\nimport CnObjectListWidgetForm2 from '../CnObjectListWidgetForm/CnObjectListWidgetForm.vue'\nimport CnRelatedObjectsWidget from '../CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue'\nimport CnRelatedObjectsWidgetForm from '../CnRelatedObjectsWidgetForm/CnRelatedObjectsWidgetForm.vue'\nimport CnStackedBarWidget from '../CnStackedBarWidget/CnStackedBarWidget.vue'\nimport CnStackedBarWidgetForm from '../CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue'\nimport CnStagesWidget from '../CnStagesWidget/CnStagesWidget.vue'\nimport CnStagesWidgetForm from '../CnStagesWidgetForm/CnStagesWidgetForm.vue'\nimport CnStatsBlockWidget from '../CnStatsBlockWidget/CnStatsBlockWidget.vue'\nimport CnStatsBlockWidgetForm from '../CnStatsBlockWidgetForm/CnStatsBlockWidgetForm.vue'\nimport CnTabsWidget from '../CnTabsWidget/CnTabsWidget.vue'\nimport CnTabsWidgetForm from '../CnTabsWidgetForm/CnTabsWidgetForm.vue'\nimport CnWeekStripWidget from '../CnWeekStripWidget/CnWeekStripWidget.vue'\nimport CnWeekStripWidgetForm from '../CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue'\n// Typed widgets registered with an explicit renderer + config FORM. chart /\n// stats-block render through CnDashboardPage's own isChart()/isStatsBlock()\n// branches and `related` through CnDetailPage's isRelatedWidget() branch — the\n// registry entry exists so the cog editor + Add-widget picker get a form (and,\n// for `related`, the detail-page surface). `table` aliases the object-list\n// renderer/form for legacy manifests using type:'table'.\nimport { registerDashboardWidget } from './dashboardWidgetRegistry.js'\n\nimport '../CnLabelWidget/index.js'\nimport '../CnTextWidget/index.js'\nimport '../CnImageWidget/index.js'\nimport '../CnLinkButtonWidget/index.js'\nimport '../CnDividerWidget/index.js'\nimport '../CnHeaderWidget/index.js'\nimport '../CnQuicklinksWidget/index.js'\nimport '../CnVideoWidget/index.js'\nimport '../CnDashTileWidget/index.js'\nimport '../CnLinksWidget/index.js'\nimport '../CnMenuWidget/index.js'\nimport '../CnContainerWidget/index.js'\nimport '../CnFilesWidget/index.js'\nimport '../CnPeopleWidget/index.js'\nimport '../CnCalendarWidget/index.js'\nimport '../CnNcWidgetWidget/index.js'\nimport '../CnSpendAnalyticsWidget/index.js'\nimport '../CnStatWidget/index.js'\nimport '../CnDeltaWidget/index.js'\nimport '../CnGaugeWidget/index.js'\nimport '../CnFlowRunsWidget/index.js'\nimport '../CnTasksWidget/index.js'\nimport '../CnObjectDataWidget/dashboardRegistration.js'\nimport '../CnObjectGeoWidget/dashboardRegistration.js'\nimport '../CnWidgetObjectTable/dashboardRegistration.js'\nimport '../CnAuditTrailWidget/dashboardRegistration.js'\nimport '../CnTimelineWidget/dashboardRegistration.js'\nimport '../CnKbSearchWidget/index.js'\nimport '../CnInteractionFormWidget/index.js'\nimport '../CnBannerWidget/index.js'\nimport '../CnWorkspaceFilterWidget/index.js'\n\nregisterDashboardWidget('chart', {\n\trenderer: CnChartWidget,\n\tform: CnChartWidgetForm,\n\tdefaultContent: {\n\t\tchartKind: 'area',\n\t\tdataSource: { register: '', schema: '', filter: {}, bucket: { field: '', interval: 'month', metric: 'count', metricField: '' } },\n\t},\n\tdisplayName: 'Chart',\n\ticon: 'ChartLine',\n})\n\nregisterDashboardWidget('stats-block', {\n\trenderer: CnStatsBlockWidget,\n\tform: CnStatsBlockWidgetForm,\n\tdefaultContent: {\n\t\ttitle: '',\n\t\tprops: { countLabel: '', variant: 'default', iconClass: '' },\n\t\tdataSource: { register: '', schema: '', metric: 'count', field: '', filter: {} },\n\t},\n\tdisplayName: 'Statistic card',\n\ticon: 'ChartBar',\n\t// Card family (docs/architecture/cards-and-widgets.md), like `stat` and\n\t// `delta`: the tile headlines itself with its own title, so the wrapper\n\t// renders no header of its own and would otherwise show the title twice.\n\tcard: true,\n})\n\n// `table` and `object-list` (below) are legacy aliases of `object-table` — same\n// config form (CnObjectListWidgetForm), same job. They stay registered so\n// existing placements keep rendering, but are hidden from every Add-widget\n// picker via a sentinel surface no picker queries (see widgetTypeAllowsSurface):\n// `object-table` is the one to add now. Rendering resolves through\n// getWidgetTypeEntry, which ignores `surfaces`, so hidden ≠ broken.\nregisterDashboardWidget('table', {\n\trenderer: CnObjectListWidget2,\n\tform: CnObjectListWidgetForm2,\n\tdefaultContent: {\n\t\tregister: '',\n\t\tschema: '',\n\t\tfilter: {},\n\t\tsort: { field: '', dir: 'asc' },\n\t\tlimit: 10,\n\t\tcolumns: [],\n\t},\n\tdisplayName: 'Table',\n\ticon: 'ClipboardList',\n\tsurfaces: ['legacy'],\n})\n\n// `object-list` is registered HERE and only here. Registering it from\n// CnObjectListWidget/index.js instead is not enough: package.json declares\n// `sideEffects: [\"**/*.css\"]` (ADR-061 tree-shaking), which lets\n// rollup/webpack legally DROP bare imports of side-effect-free JS modules —\n// the dist bundle shipped without the object-list registration and every\n// manifest `type:\"object-list\"` widget rendered blank. Inline calls in this\n// module survive because consumers import live bindings from the registry.\n//\n// That index.js ALSO self-registered, with a divergent entry (limit 5, no\n// `surfaces`), so both ran in a consumer's build — components/index.js\n// re-exports it, so it was never tree-shaken — and last-registration-wins\n// decided whether this legacy alias stayed hidden from the Add-widget picker.\n// The self-registration is gone; this entry is the one that already won.\nregisterDashboardWidget('object-list', {\n\trenderer: CnObjectListWidget2,\n\tform: CnObjectListWidgetForm2,\n\tdefaultContent: {\n\t\tregister: '',\n\t\tschema: '',\n\t\tfilter: {},\n\t\tsort: { field: '', dir: 'asc' },\n\t\tlimit: 25,\n\t\tcolumns: [],\n\t},\n\tdisplayName: 'Object list',\n\ticon: 'ClipboardList',\n\tsurfaces: ['legacy'],\n})\n\nregisterDashboardWidget('related', {\n\trenderer: CnRelatedObjectsWidget,\n\tform: CnRelatedObjectsWidgetForm,\n\tdefaultContent: { title: '', groups: [], hideSingleTabTitle: true, showTotalCount: true },\n\tdisplayName: 'Object relations',\n\ticon: 'FileTreeOutline',\n\tsurfaces: ['detail-page'],\n\townsTitle: true,\n})\n\n// `map` is registered here for the same tree-shaking reason as `object-list`\n// above — a bare `import '../CnMapWidget/index.js'` is a side-effect-free JS module\n// as far as the bundler is concerned, so it may legally be dropped and the widget\n// would silently never appear in Add Widget. CnMapWidget/index.js carried a\n// byte-identical copy of this entry, which only produced a duplicate-registration\n// warning on every consumer's boot; it no longer registers.\nregisterDashboardWidget('map', {\n\trenderer: CnMapWidget,\n\tform: CnMapWidgetForm,\n\tdefaultContent: {\n\t\tregister: '',\n\t\tschema: '',\n\t\t// The Netherlands, roughly — only visible until autoFit frames the objects.\n\t\tcenter: [52.13, 5.29],\n\t\tzoom: 7,\n\t\theight: '400px',\n\t\tpopupField: '',\n\t\tclustering: false,\n\t\tautoFit: true,\n\t\t// A map with no tile layer renders as a grey box. `basemaps` is opt-in and\n\t\t// empty by default (so consumers declaring a `tile` entry in `layers` are\n\t\t// unaffected), which meant a freshly-placed Map widget had NO background at\n\t\t// all. Ship OpenStreetMap — the Nextcloud CSP already allows *.tile.openstreetmap.org.\n\t\tbasemaps: [\n\t\t\t{\n\t\t\t\tname: 'OpenStreetMap',\n\t\t\t\turl: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',\n\t\t\t\tattribution: '© OpenStreetMap contributors',\n\t\t\t\toptions: { maxZoom: 19 },\n\t\t\t},\n\t\t],\n\t\tmarkers: {\n\t\t\tdataSource: { register: '', schema: '' },\n\t\t\tpopupField: '',\n\t\t\tclustering: false,\n\t\t},\n\t},\n\tdisplayName: 'Map',\n\ticon: 'Map',\n})\n\nregisterDashboardWidget('countdown', {\n\trenderer: CnCountdownWidget,\n\tform: CnCountdownWidgetForm,\n\tdefaultContent: {\n\t\tlabel: '',\n\t\tfield: '',\n\t\ticon: 'ClockOutline',\n\t\tthresholds: {},\n\t\tshowDate: true,\n\t},\n\tdisplayName: 'Countdown',\n\ticon: 'ClockOutline',\n\t// Card family: the tile headlines itself, so the wrapper draws no header.\n\tcard: true,\n\t// It reads a date off the BOUND RECORD, which only a detail page has.\n\tsurfaces: ['detail-page'],\n})\n\n// The case surfaces (detail-action-model-and-case-surfaces). Each reads the\n// BOUND RECORD, which only a detail page has, and none has a config form yet,\n// so `form: null` keeps them out of the Add-widget picker: they are placed\n// from the manifest. Registered inline for the same tree-shaking reason as\n// `map` above.\nregisterDashboardWidget('next-step', {\n\trenderer: CnNextStepWidget,\n\tform: null,\n\tdefaultContent: { field: 'status', stages: {} },\n\tdisplayName: 'Next step',\n\ticon: 'CheckCircleOutline',\n\tsurfaces: ['detail-page'],\n\townsTitle: true,\n})\n\nregisterDashboardWidget('document-review', {\n\trenderer: CnDocumentReviewWidget,\n\tform: null,\n\tdefaultContent: { field: 'documents', statusField: 'reviewStatus', statuses: [] },\n\tdisplayName: 'Document review',\n\ticon: 'FileDocumentCheckOutline',\n\tsurfaces: ['detail-page'],\n\townsTitle: true,\n})\n\nregisterDashboardWidget('conversation', {\n\trenderer: CnConversationWidget,\n\tform: null,\n\tdefaultContent: { field: 'messages', endpoint: '' },\n\tdisplayName: 'Conversation',\n\ticon: 'ForumOutline',\n\tsurfaces: ['detail-page'],\n\townsTitle: true,\n})\n\n// The record's stages, where clicking a stage moves the record there.\n// Registered inline for the same tree-shaking reason as `map` above.\nregisterDashboardWidget('stages', {\n\trenderer: CnStagesWidget,\n\tform: CnStagesWidgetForm,\n\tdefaultContent: {\n\t\tcurrentField: 'status',\n\t\torientation: 'horizontal',\n\t\tsize: 'medium',\n\t\tstagesEndpoint: { url: '', path: '' },\n\t\t// The LIFECYCLE, not a record write. OpenRegister decides what is\n\t\t// reachable and re-validates the move; a field write has neither.\n\t\ttransition: { kind: 'lifecycle' },\n\t},\n\tdisplayName: 'Stages',\n\ticon: 'Timeline',\n\t// Card family on the detail grid: a titled wrapper around a self-contained\n\t// strip, like the KPI tiles it usually sits beside.\n\tcard: true,\n\t// It reads and moves the BOUND RECORD, which only a detail page has.\n\tsurfaces: ['detail-page'],\n})\n\nregisterDashboardWidget('tabs', {\n\trenderer: CnTabsWidget,\n\tform: CnTabsWidgetForm,\n\tdefaultContent: {\n\t\ttabs: [],\n\t\tariaLabel: '',\n\t},\n\tdisplayName: 'Tabs',\n\ticon: 'TabPlus',\n\t// Renders OTHER widgets, so CnDetailWidgetHost hands it the surface's\n\t// sibling definitions and the context its children will need.\n\tcontainer: true,\n\t// The strip's labels ARE the titles, so a card header above them would name\n\t// the group twice.\n\townsTitle: true,\n\tsurfaces: ['detail-page'],\n})\n\n// The current week as day columns, with the dated items under each day.\n// Registered inline for the same tree-shaking reason as `map` above.\nregisterDashboardWidget('week-strip', {\n\trenderer: CnWeekStripWidget,\n\tform: CnWeekStripWidgetForm,\n\tdefaultContent: {\n\t\tsource: { register: '', schema: '', filter: {} },\n\t\tdateField: '',\n\t\ttitleField: '',\n\t\tmetaFields: [],\n\t\tdays: 5,\n\t\titemRoute: '',\n\t\temptyText: '',\n\t},\n\tdisplayName: 'Week strip',\n\ticon: 'Calendar',\n})\n\n// One segmented bar with a legend that carries the numbers.\nregisterDashboardWidget('stacked-bar', {\n\trenderer: CnStackedBarWidget,\n\tform: CnStackedBarWidgetForm,\n\tdefaultContent: {\n\t\tsource: { register: '', schema: '', groupBy: '', filter: {} },\n\t\torder: [],\n\t\tlabels: {},\n\t\temptyText: '',\n\t},\n\tdisplayName: 'Stacked bar',\n\ticon: 'ChartBar',\n})\n\n/**\n * Explicit no-op that guarantees this module (and therefore every widget's\n * self-registration side effect) is evaluated. Call it once at app bootstrap if\n * a bundler tree-shakes bare side-effect imports.\n *\n * @return {void}\n */\nexport function registerBuiltinDashboardWidgets() {}\n"],"names":["CnChartWidget","CnChartWidgetForm","CnStatsBlockWidget","CnStatsBlockWidgetForm","CnObjectListWidget2","CnObjectListWidgetForm2","CnRelatedObjectsWidget","CnRelatedObjectsWidgetForm","CnMapWidget","CnMapWidgetForm","CnCountdownWidget","CnCountdownWidgetForm","CnNextStepWidget","CnDocumentReviewWidget","CnConversationWidget","CnStagesWidget","CnStagesWidgetForm","CnTabsWidget","CnTabsWidgetForm","CnWeekStripWidget","CnWeekStripWidgetForm","CnStackedBarWidget","CnStackedBarWidgetForm"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CnWikiPage.vue2.js","sources":["../../../../src/components/CnWikiPage/CnWikiPage.vue"],"sourcesContent":["<!--\n CnWikiPage — Manifest-driven markdown article surface.\n\n Renders one wiki / docs / knowledge-base article declared in\n `pages[].config` for `type: \"wiki\"` pages. Closes the gap that\n forces every consumer's docs / help / article-detail route onto\n `type: \"custom\"`.\n\n The page reads its content from props rather than fetching itself.\n Following the precedent set by CnFilesPage and CnFormPage, the\n consumer (or a wiki-aware wrapper) populates `article` (object)\n and `tree` (array) — typically from a `useDetailView()` /\n list-call against the manifest-declared register/schema. This\n keeps the lib zero-dep on a particular OpenRegister fetcher\n while still making the manifest config the source of truth for\n field names.\n\n Markdown rendering is delegated to the `cnRenderMarkdown` composable\n which configures `marked` with `{ gfm: true, breaks: false }` —\n GitHub-flavoured markdown, no auto-`<br>` on newlines.\n\n Slots:\n - `#header` — overrides the default header. Scope `{ title, article }`.\n - `#sidebar` — overrides the default tree sidebar. Scope `{ tree, onClick }`.\n - `#body` — overrides the default markdown body. Scope `{ article, html }`.\n - `#empty` — replaces the empty-state when `article` is null.\n\n Events:\n - `@tree-click` — `node` whenever a sidebar tree node is clicked.\n - `@error` — error object if markdown parsing throws.\n\n Spec: REQ-MWPT-* (manifest-wiki-page-type).\n-->\n<template>\n\t<div class=\"cn-wiki-page\" data-testid=\"cn-wiki-page\" :class=\"{ 'cn-wiki-page--has-sidebar': hasSidebar }\">\n\t\t<!--\n\t\t\t@slot sidebar\n\t\t\t@description Replaces the default sidebar tree. Scoped props `{ tree, onClick }` —\n\t\t\t`onClick(node)` re-emits `@tree-click` with the node.\n\t\t-->\n\t\t<slot\n\t\t\tv-if=\"hasSidebar\"\n\t\t\tname=\"sidebar\"\n\t\t\t:tree=\"tree\"\n\t\t\t:onClick=\"onTreeClick\">\n\t\t\t<nav class=\"cn-wiki-page__sidebar\">\n\t\t\t\t<ul class=\"cn-wiki-page__tree\">\n\t\t\t\t\t<CnWikiTreeNode\n\t\t\t\t\t\tv-for=\"node in tree\"\n\t\t\t\t\t\t:key=\"treeKey(node)\"\n\t\t\t\t\t\t:node=\"node\"\n\t\t\t\t\t\t:titleField=\"effectiveSidebarTitleField\"\n\t\t\t\t\t\t:treeField=\"treeField\"\n\t\t\t\t\t\t@click=\"onTreeClick\" />\n\t\t\t\t</ul>\n\t\t\t</nav>\n\t\t</slot>\n\n\t\t<div class=\"cn-wiki-page__main\">\n\t\t\t<!--\n\t\t\t\t@slot header\n\t\t\t\t@description Replaces the default page header. Scoped props `{ title, article }`.\n\t\t\t-->\n\t\t\t<slot\n\t\t\t\tname=\"header\"\n\t\t\t\t:title=\"resolvedTitle\"\n\t\t\t\t:article=\"article\">\n\t\t\t\t<header v-if=\"resolvedTitle\" class=\"cn-wiki-page__header\">\n\t\t\t\t\t<h1 class=\"cn-wiki-page__title\">\n\t\t\t\t\t\t{{ resolvedTitle }}\n\t\t\t\t\t</h1>\n\t\t\t\t</header>\n\t\t\t</slot>\n\n\t\t\t<!--\n\t\t\t\t@slot empty\n\t\t\t\t@description Replaces the empty-state shown when `article` is null/undefined.\n\t\t\t-->\n\t\t\t<slot v-if=\"!article\" name=\"empty\">\n\t\t\t\t<NcEmptyContent\n\t\t\t\t\t:name=\"emptyText\"\n\t\t\t\t\t:description=\"emptyDescription\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<FileDocumentOutline :size=\"64\" />\n\t\t\t\t\t</template>\n\t\t\t\t</NcEmptyContent>\n\t\t\t</slot>\n\n\t\t\t<!--\n\t\t\t\t@slot body\n\t\t\t\t@description Replaces the default markdown body. Scoped props `{ article, html }` —\n\t\t\t\t`html` is the pre-parsed markdown so a custom renderer can wrap it.\n\t\t\t-->\n\t\t\t<slot\n\t\t\t\tv-else\n\t\t\t\tname=\"body\"\n\t\t\t\t:article=\"article\"\n\t\t\t\t:html=\"renderedBody\">\n\t\t\t\t<div\n\t\t\t\t\tv-if=\"renderedBody\"\n\t\t\t\t\tclass=\"cn-wiki-page__body\"\n\t\t\t\t\tv-html=\"renderedBody\" /><!-- eslint-disable-line vue/no-v-html -->\n\t\t\t\t<NcEmptyContent\n\t\t\t\t\tv-else\n\t\t\t\t\t:name=\"emptyBodyText\"\n\t\t\t\t\t:description=\"emptyBodyDescription\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<FileDocumentOutline :size=\"64\" />\n\t\t\t\t\t</template>\n\t\t\t\t</NcEmptyContent>\n\t\t\t</slot>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcEmptyContent } from '@nextcloud/vue'\nimport FileDocumentOutline from 'vue-material-design-icons/FileDocumentOutline.vue'\nimport CnWikiTreeNode from './CnWikiTreeNode.vue'\nimport { cnRenderMarkdown } from '../../composables/cnRenderMarkdown.js'\n\n/**\n * CnWikiPage — Manifest-driven markdown article surface.\n *\n * Reads `article` (object) and `tree` (array) as props rather than\n * fetching itself; consumers populate these from their own data\n * store or a wrapper composable. The manifest config still drives\n * which fields/registers are involved (via `contentField`,\n * `titleField`, `sidebarSchema`, …) so the validator stays\n * meaningful even though the lib does not own the fetch.\n *\n * Basic usage:\n *\n * <CnWikiPage\n * :article=\"article\"\n * :register=\"'pipelinq'\"\n * :schema=\"'article'\"\n * :content-field=\"'body'\" />\n *\n * With sidebar tree:\n *\n * <CnWikiPage\n * :article=\"article\"\n * :tree=\"categoryTree\"\n * :sidebar-schema=\"'category'\"\n *\n * @tree-click=\"onCategoryClick\" />\n */\nexport default {\n\tname: 'CnWikiPage',\n\n\tcomponents: {\n\t\tNcEmptyContent,\n\t\tFileDocumentOutline,\n\t\tCnWikiTreeNode,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The article record to render. When null/undefined the\n\t\t * component shows its empty-state. The shape is consumer-defined;\n\t\t * the component reads only `article[contentField]` and\n\t\t * `article[titleField]`.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tarticle: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The (optional) sidebar tree. Each node:\n\t\t * `{ id, [titleField], children?: [...] }`. Empty array hides\n\t\t * the sidebar entirely.\n\t\t *\n\t\t * @type {Array<object>}\n\t\t */\n\t\ttree: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * OpenRegister register slug. Informational at this layer\n\t\t * (the consumer does the fetch); declared as a prop so the\n\t\t * manifest-driven prop forwarder in CnPageRenderer surfaces it\n\t\t * for child components / debugging.\n\t\t */\n\t\tregister: { // eslint-disable-line vue/no-unused-properties -- informational at this layer; CnPageRenderer forwards it from the manifest for child components.\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * OpenRegister schema slug. Informational at this layer —\n\t\t * see `register` above.\n\t\t */\n\t\tschema: { // eslint-disable-line vue/no-unused-properties -- informational at this layer; CnPageRenderer forwards it from the manifest for child components.\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Property on `article` holding the markdown body. Defaults\n\t\t * to `'body'`.\n\t\t */\n\t\tcontentField: {\n\t\t\ttype: String,\n\t\t\tdefault: 'body',\n\t\t},\n\n\t\t/**\n\t\t * Property on `article` holding the page title. Defaults\n\t\t * to `'title'`.\n\t\t */\n\t\ttitleField: {\n\t\t\ttype: String,\n\t\t\tdefault: 'title',\n\t\t},\n\n\t\t/**\n\t\t * `$route.params` key holding the article id. Informational at\n\t\t * this layer; declared so the consumer's data store can wire\n\t\t * the same default the validator and renderer both reference.\n\t\t */\n\t\tidParam: { // eslint-disable-line vue/no-unused-properties -- informational at this layer; CnPageRenderer forwards it from the manifest for child components.\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\n\t\t/**\n\t\t * Schema slug for the sidebar tree. When set AND `tree` is\n\t\t * non-empty the sidebar renders. Unset suppresses the sidebar\n\t\t * regardless of `tree` content.\n\t\t */\n\t\tsidebarSchema: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Register slug for the sidebar tree. Defaults to `register`\n\t\t * when unset (the common case).\n\t\t */\n\t\tsidebarRegister: { // eslint-disable-line vue/no-unused-properties -- informational at this layer; CnPageRenderer forwards it from the manifest for child components.\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Property on each sidebar node holding its child array.\n\t\t * Defaults to `'children'`.\n\t\t */\n\t\ttreeField: {\n\t\t\ttype: String,\n\t\t\tdefault: 'children',\n\t\t},\n\n\t\t/**\n\t\t * Property on each sidebar node holding its label. Defaults\n\t\t * to `titleField` (so `'title'` unless overridden).\n\t\t */\n\t\tsidebarTitleField: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Empty-state heading when `article` is null. */\n\t\temptyText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Article not found'),\n\t\t},\n\n\t\t/** Empty-state description when `article` is null. */\n\t\temptyDescription: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'The requested article could not be found.'),\n\t\t},\n\n\t\t/** Empty-body heading when the article has no `contentField`. */\n\t\temptyBodyText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No content'),\n\t\t},\n\n\t\t/** Empty-body description when the article has no `contentField`. */\n\t\temptyBodyDescription: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'This article has no content yet.'),\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted when a sidebar tree node is clicked. The payload is\n\t\t * the clicked node object (the same shape the consumer placed\n\t\t * in `tree`).\n\t\t */\n\t\t'tree-click',\n\t\t/**\n\t\t * Emitted when markdown parsing throws. Rare — `marked` and\n\t\t * the helper are defensive — but the contract is explicit so\n\t\t * consumers can log / surface a banner.\n\t\t */\n\t\t'error',\n\t],\n\n\tcomputed: {\n\t\t/**\n\t\t * Effective sidebar-title field — falls back to the article\n\t\t * title field when unset so consumers can leave it off in\n\t\t * the simple case.\n\t\t */\n\t\teffectiveSidebarTitleField() {\n\t\t\treturn this.sidebarTitleField || this.titleField\n\t\t},\n\n\t\t/**\n\t\t * Whether the sidebar should render. Both a non-empty tree\n\t\t * AND an explicit `sidebarSchema` are required — the schema\n\t\t * acts as the manifest-author's \"yes, I want a sidebar\" signal,\n\t\t * matching the spec's REQ-MWPT-5.\n\t\t */\n\t\thasSidebar() {\n\t\t\treturn Boolean(this.sidebarSchema) && Array.isArray(this.tree) && this.tree.length > 0\n\t\t},\n\n\t\t/** Article title resolved through `titleField`. */\n\t\tresolvedTitle() {\n\t\t\tif (!this.article) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst value = this.article[this.titleField]\n\t\t\treturn typeof value === 'string' ? value : ''\n\t\t},\n\n\t\t/**\n\t\t * Article body parsed to HTML through `cnRenderMarkdown`.\n\t\t * Empty string when the article is null or has no body.\n\t\t */\n\t\trenderedBody() {\n\t\t\tif (!this.article) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst source = this.article[this.contentField]\n\t\t\ttry {\n\t\t\t\treturn cnRenderMarkdown(source)\n\t\t\t} catch (error) {\n\t\t\t\tthis.$emit('error', error)\n\t\t\t\treturn ''\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Re-emit `@tree-click` with the clicked node.\n\t\t *\n\t\t * @param {object} node The tree node the user clicked.\n\t\t */\n\t\tonTreeClick(node) {\n\t\t\tthis.$emit('tree-click', node)\n\t\t},\n\n\t\t/**\n\t\t * Stable v-for key for tree nodes — prefer `id`, fall back\n\t\t * to the title field, finally to JSON-stringified shape.\n\t\t *\n\t\t * @param {object} node Tree node.\n\t\t * @return {string}\n\t\t */\n\t\ttreeKey(node) {\n\t\t\tif (!node) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (typeof node.id === 'string' || typeof node.id === 'number') {\n\t\t\t\treturn String(node.id)\n\t\t\t}\n\t\t\tconst title = node[this.effectiveSidebarTitleField]\n\t\t\tif (typeof title === 'string') {\n\t\t\t\treturn title\n\t\t\t}\n\t\t\treturn JSON.stringify(node)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-wiki-page {\n\tdisplay: flex;\n\tgap: 24px;\n\tpadding: 20px;\n\tmax-width: 1200px;\n\tmargin: 0 auto;\n}\n\n.cn-wiki-page__sidebar {\n\tflex: 0 0 240px;\n\tborder-right: 1px solid var(--color-border);\n\tpadding-right: 16px;\n}\n\n.cn-wiki-page__tree {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-wiki-page__main {\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n.cn-wiki-page__header {\n\tmargin-bottom: 16px;\n}\n\n.cn-wiki-page__title {\n\tfont-size: 1.6em;\n\tmargin: 0;\n\tcolor: var(--color-main-text);\n}\n\n.cn-wiki-page__body {\n\tline-height: 1.7;\n\tcolor: var(--color-main-text);\n}\n\n.cn-wiki-page__body :deep(h1),\n.cn-wiki-page__body :deep(h2),\n.cn-wiki-page__body :deep(h3) {\n\tcolor: var(--color-main-text);\n\tmargin-top: 1.4em;\n\tmargin-bottom: 0.5em;\n}\n\n.cn-wiki-page__body :deep(p) {\n\tmargin: 0 0 1em;\n}\n\n.cn-wiki-page__body :deep(code) {\n\tbackground: var(--color-background-dark);\n\tpadding: 2px 6px;\n\tborder-radius: var(--border-radius);\n\tfont-family: var(--font-face-monospace, monospace);\n\tfont-size: 0.9em;\n}\n\n.cn-wiki-page__body :deep(pre) {\n\tbackground: var(--color-background-dark);\n\tpadding: 12px;\n\tborder-radius: var(--border-radius);\n\toverflow-x: auto;\n}\n\n.cn-wiki-page__body :deep(pre code) {\n\tbackground: transparent;\n\tpadding: 0;\n}\n\n.cn-wiki-page__body :deep(blockquote) {\n\tborder-left: 3px solid var(--color-border);\n\tpadding-left: 12px;\n\tcolor: var(--color-text-lighter);\n\tmargin: 0 0 1em;\n}\n\n.cn-wiki-page__body :deep(table) {\n\tborder-collapse: collapse;\n\tmargin: 0 0 1em;\n}\n\n.cn-wiki-page__body :deep(th),\n.cn-wiki-page__body :deep(td) {\n\tborder: 1px solid var(--color-border);\n\tpadding: 6px 10px;\n}\n\n.cn-wiki-page--has-sidebar .cn-wiki-page__main {\n\tpadding-left: 8px;\n}\n</style>\n"],"names":["CnWikiTreeNode","t"],"mappings":"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"}
1
+ {"version":3,"file":"CnWikiPage.vue2.js","sources":["../../../../src/components/CnWikiPage/CnWikiPage.vue"],"sourcesContent":["<!--\n CnWikiPage — Manifest-driven markdown article surface.\n\n Renders one wiki / docs / knowledge-base article declared in\n `pages[].config` for `type: \"wiki\"` pages. Closes the gap that\n forces every consumer's docs / help / article-detail route onto\n `type: \"custom\"`.\n\n The page reads its content from props rather than fetching itself.\n Following the precedent set by CnFilesPage and CnFormPage, the\n consumer (or a wiki-aware wrapper) populates `article` (object)\n and `tree` (array) — typically from a `useDetailView()` /\n list-call against the manifest-declared register/schema. This\n keeps the lib zero-dep on a particular OpenRegister fetcher\n while still making the manifest config the source of truth for\n field names.\n\n Markdown rendering is delegated to the `cnRenderMarkdown` composable\n which configures `marked` with `{ gfm: true, breaks: false }` —\n GitHub-flavoured markdown, no auto-`<br>` on newlines.\n\n Slots:\n - `#header` — overrides the default header. Scope `{ title, article }`.\n - `#sidebar` — overrides the default tree sidebar. Scope `{ tree, onClick }`.\n - `#body` — overrides the default markdown body. Scope `{ article, html }`.\n - `#empty` — replaces the empty-state when `article` is null.\n\n Events:\n - `@tree-click` — `node` whenever a sidebar tree node is clicked.\n - `@error` — error object if markdown parsing throws.\n\n Spec: REQ-MWPT-* (manifest-wiki-page-type).\n-->\n<template>\n\t<div class=\"cn-wiki-page\" data-testid=\"cn-wiki-page\" :class=\"{ 'cn-wiki-page--has-sidebar': hasSidebar }\">\n\t\t<!--\n\t\t\t@slot sidebar\n\t\t\t@description Replaces the default sidebar tree. Scoped props `{ tree, onClick }` —\n\t\t\t`onClick(node)` re-emits `@tree-click` with the node.\n\t\t-->\n\t\t<slot\n\t\t\tv-if=\"hasSidebar\"\n\t\t\tname=\"sidebar\"\n\t\t\t:tree=\"tree\"\n\t\t\t:onClick=\"onTreeClick\">\n\t\t\t<nav class=\"cn-wiki-page__sidebar\">\n\t\t\t\t<ul class=\"cn-wiki-page__tree\">\n\t\t\t\t\t<CnWikiTreeNode\n\t\t\t\t\t\tv-for=\"node in tree\"\n\t\t\t\t\t\t:key=\"treeKey(node)\"\n\t\t\t\t\t\t:node=\"node\"\n\t\t\t\t\t\t:titleField=\"effectiveSidebarTitleField\"\n\t\t\t\t\t\t:treeField=\"treeField\"\n\t\t\t\t\t\t@click=\"onTreeClick\" />\n\t\t\t\t</ul>\n\t\t\t</nav>\n\t\t</slot>\n\n\t\t<div class=\"cn-wiki-page__main\">\n\t\t\t<!--\n\t\t\t\t@slot header\n\t\t\t\t@description Replaces the default page header. Scoped props `{ title, article }`.\n\t\t\t-->\n\t\t\t<slot\n\t\t\t\tname=\"header\"\n\t\t\t\t:title=\"resolvedTitle\"\n\t\t\t\t:article=\"article\">\n\t\t\t\t<header v-if=\"resolvedTitle\" class=\"cn-wiki-page__header\">\n\t\t\t\t\t<h1 class=\"cn-wiki-page__title\">\n\t\t\t\t\t\t{{ resolvedTitle }}\n\t\t\t\t\t</h1>\n\t\t\t\t</header>\n\t\t\t</slot>\n\n\t\t\t<!--\n\t\t\t\t@slot empty\n\t\t\t\t@description Replaces the empty-state shown when `article` is null/undefined.\n\t\t\t-->\n\t\t\t<slot v-if=\"!article\" name=\"empty\">\n\t\t\t\t<NcEmptyContent\n\t\t\t\t\t:name=\"emptyText\"\n\t\t\t\t\t:description=\"emptyDescription\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<FileDocumentOutline :size=\"64\" />\n\t\t\t\t\t</template>\n\t\t\t\t</NcEmptyContent>\n\t\t\t</slot>\n\n\t\t\t<!--\n\t\t\t\t@slot body\n\t\t\t\t@description Replaces the default markdown body. Scoped props `{ article, html }` —\n\t\t\t\t`html` is the pre-parsed markdown so a custom renderer can wrap it.\n\t\t\t-->\n\t\t\t<slot\n\t\t\t\tv-else\n\t\t\t\tname=\"body\"\n\t\t\t\t:article=\"article\"\n\t\t\t\t:html=\"renderedBody\">\n\t\t\t\t<div\n\t\t\t\t\tv-if=\"renderedBody\"\n\t\t\t\t\tclass=\"cn-wiki-page__body\"\n\t\t\t\t\tv-html=\"renderedBody\" /><!-- eslint-disable-line vue/no-v-html -->\n\t\t\t\t<NcEmptyContent\n\t\t\t\t\tv-else\n\t\t\t\t\t:name=\"emptyBodyText\"\n\t\t\t\t\t:description=\"emptyBodyDescription\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<FileDocumentOutline :size=\"64\" />\n\t\t\t\t\t</template>\n\t\t\t\t</NcEmptyContent>\n\t\t\t</slot>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcEmptyContent } from '@nextcloud/vue'\nimport FileDocumentOutline from 'vue-material-design-icons/FileDocumentOutline.vue'\nimport CnWikiTreeNode from './CnWikiTreeNode.vue'\nimport { cnRenderMarkdown } from '../../composables/cnRenderMarkdown.js'\n\n/**\n * CnWikiPage — Manifest-driven markdown article surface.\n *\n * Reads `article` (object) and `tree` (array) as props rather than\n * fetching itself; consumers populate these from their own data\n * store or a wrapper composable. The manifest config still drives\n * which fields/registers are involved (via `contentField`,\n * `titleField`, `sidebarSchema`, …) so the validator stays\n * meaningful even though the lib does not own the fetch.\n *\n * Basic usage:\n *\n * <CnWikiPage\n * :article=\"article\"\n * :register=\"'pipelinq'\"\n * :schema=\"'article'\"\n * :content-field=\"'body'\" />\n *\n * With sidebar tree:\n *\n * <CnWikiPage\n * :article=\"article\"\n * :tree=\"categoryTree\"\n * :sidebar-schema=\"'category'\"\n *\n * @tree-click=\"onCategoryClick\" />\n */\nexport default {\n\tname: 'CnWikiPage',\n\n\tcomponents: {\n\t\tNcEmptyContent,\n\t\tFileDocumentOutline,\n\t\tCnWikiTreeNode,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The article record to render. When null/undefined the\n\t\t * component shows its empty-state. The shape is consumer-defined;\n\t\t * the component reads only `article[contentField]` and\n\t\t * `article[titleField]`.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tarticle: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The (optional) sidebar tree. Each node:\n\t\t * `{ id, [titleField], children?: [...] }`. Empty array hides\n\t\t * the sidebar entirely.\n\t\t *\n\t\t * @type {Array<object>}\n\t\t */\n\t\ttree: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * OpenRegister register slug. Informational at this layer\n\t\t * (the consumer does the fetch); declared as a prop so the\n\t\t * manifest-driven prop forwarder in CnPageRenderer surfaces it\n\t\t * for child components / debugging.\n\t\t */\n\t\tregister: { // eslint-disable-line vue/no-unused-properties -- informational at this layer; CnPageRenderer forwards it from the manifest for child components.\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * OpenRegister schema slug. Informational at this layer —\n\t\t * see `register` above.\n\t\t */\n\t\tschema: { // eslint-disable-line vue/no-unused-properties -- informational at this layer; CnPageRenderer forwards it from the manifest for child components.\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Property on `article` holding the markdown body. Defaults\n\t\t * to `'body'`.\n\t\t */\n\t\tcontentField: {\n\t\t\ttype: String,\n\t\t\tdefault: 'body',\n\t\t},\n\n\t\t/**\n\t\t * Property on `article` holding the page title. Defaults\n\t\t * to `'title'`.\n\t\t */\n\t\ttitleField: {\n\t\t\ttype: String,\n\t\t\tdefault: 'title',\n\t\t},\n\n\t\t/**\n\t\t * `$route.params` key holding the article id. Informational at\n\t\t * this layer; declared so the consumer's data store can wire\n\t\t * the same default the validator and renderer both reference.\n\t\t */\n\t\tidParam: { // eslint-disable-line vue/no-unused-properties -- informational at this layer; CnPageRenderer forwards it from the manifest for child components.\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\n\t\t/**\n\t\t * Schema slug for the sidebar tree. When set AND `tree` is\n\t\t * non-empty the sidebar renders. Unset suppresses the sidebar\n\t\t * regardless of `tree` content.\n\t\t */\n\t\tsidebarSchema: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Register slug for the sidebar tree. Defaults to `register`\n\t\t * when unset (the common case).\n\t\t */\n\t\tsidebarRegister: { // eslint-disable-line vue/no-unused-properties -- informational at this layer; CnPageRenderer forwards it from the manifest for child components.\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Property on each sidebar node holding its child array.\n\t\t * Defaults to `'children'`.\n\t\t */\n\t\ttreeField: {\n\t\t\ttype: String,\n\t\t\tdefault: 'children',\n\t\t},\n\n\t\t/**\n\t\t * Property on each sidebar node holding its label. Defaults\n\t\t * to `titleField` (so `'title'` unless overridden).\n\t\t */\n\t\tsidebarTitleField: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Empty-state heading when `article` is null. */\n\t\temptyText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Article not found'),\n\t\t},\n\n\t\t/** Empty-state description when `article` is null. */\n\t\temptyDescription: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'The requested article could not be found.'),\n\t\t},\n\n\t\t/** Empty-body heading when the article has no `contentField`. */\n\t\temptyBodyText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No content'),\n\t\t},\n\n\t\t/** Empty-body description when the article has no `contentField`. */\n\t\temptyBodyDescription: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'This article has no content yet.'),\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted when a sidebar tree node is clicked. The payload is\n\t\t * the clicked node object (the same shape the consumer placed\n\t\t * in `tree`).\n\t\t */\n\t\t'tree-click',\n\t\t/**\n\t\t * Emitted when markdown parsing throws. Rare — `marked` and\n\t\t * the helper are defensive — but the contract is explicit so\n\t\t * consumers can log / surface a banner.\n\t\t */\n\t\t'error',\n\t],\n\n\tcomputed: {\n\t\t/**\n\t\t * Effective sidebar-title field — falls back to the article\n\t\t * title field when unset so consumers can leave it off in\n\t\t * the simple case.\n\t\t */\n\t\teffectiveSidebarTitleField() {\n\t\t\treturn this.sidebarTitleField || this.titleField\n\t\t},\n\n\t\t/**\n\t\t * Whether the sidebar should render. Both a non-empty tree\n\t\t * AND an explicit `sidebarSchema` are required — the schema\n\t\t * acts as the manifest-author's \"yes, I want a sidebar\" signal,\n\t\t * matching the spec's REQ-MWPT-5.\n\t\t */\n\t\thasSidebar() {\n\t\t\treturn Boolean(this.sidebarSchema) && Array.isArray(this.tree) && this.tree.length > 0\n\t\t},\n\n\t\t/** Article title resolved through `titleField`. */\n\t\tresolvedTitle() {\n\t\t\tif (!this.article) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst value = this.article[this.titleField]\n\t\t\treturn typeof value === 'string' ? value : ''\n\t\t},\n\n\t\t/**\n\t\t * Article body parsed to HTML through `cnRenderMarkdown`.\n\t\t * Empty string when the article is null or has no body.\n\t\t */\n\t\trenderedBody() {\n\t\t\tif (!this.article) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst source = this.article[this.contentField]\n\t\t\ttry {\n\t\t\t\treturn cnRenderMarkdown(source)\n\t\t\t} catch (error) {\n\t\t\t\tthis.$emit('error', error)\n\t\t\t\treturn ''\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Re-emit `@tree-click` with the clicked node.\n\t\t *\n\t\t * @param {object} node The tree node the user clicked.\n\t\t */\n\t\tonTreeClick(node) {\n\t\t\tthis.$emit('tree-click', node)\n\t\t},\n\n\t\t/**\n\t\t * Stable v-for key for tree nodes — prefer `id`, fall back\n\t\t * to the title field, finally to JSON-stringified shape.\n\t\t *\n\t\t * @param {object} node Tree node.\n\t\t * @return {string}\n\t\t */\n\t\ttreeKey(node) {\n\t\t\tif (!node) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (typeof node.id === 'string' || typeof node.id === 'number') {\n\t\t\t\treturn String(node.id)\n\t\t\t}\n\t\t\tconst title = node[this.effectiveSidebarTitleField]\n\t\t\tif (typeof title === 'string') {\n\t\t\t\treturn title\n\t\t\t}\n\t\t\treturn JSON.stringify(node)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-wiki-page {\n\tdisplay: flex;\n\tgap: 24px;\n\tpadding: 20px;\n\tmax-width: 1200px;\n\tmargin: 0 auto;\n}\n\n.cn-wiki-page__sidebar {\n\tflex: 0 0 240px;\n\tborder-right: 1px solid var(--color-border);\n\tpadding-right: 16px;\n}\n\n.cn-wiki-page__tree {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-wiki-page__main {\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n.cn-wiki-page__header {\n\tmargin-bottom: 16px;\n}\n\n/* Without a sidebar the title starts at the left edge of the content, under\n the Nextcloud navigation toggle button (44px wide). Clear it as CnPageHeader\n does. With a sidebar the tree sits there instead and the title is clear. */\n.cn-wiki-page:not(.cn-wiki-page--has-sidebar) .cn-wiki-page__header {\n\tpadding-inline-start: 56px;\n}\n\n.cn-wiki-page__title {\n\tfont-size: 1.6em;\n\tmargin: 0;\n\tcolor: var(--color-main-text);\n}\n\n.cn-wiki-page__body {\n\tline-height: 1.7;\n\tcolor: var(--color-main-text);\n}\n\n.cn-wiki-page__body :deep(h1),\n.cn-wiki-page__body :deep(h2),\n.cn-wiki-page__body :deep(h3) {\n\tcolor: var(--color-main-text);\n\tmargin-top: 1.4em;\n\tmargin-bottom: 0.5em;\n}\n\n.cn-wiki-page__body :deep(p) {\n\tmargin: 0 0 1em;\n}\n\n.cn-wiki-page__body :deep(code) {\n\tbackground: var(--color-background-dark);\n\tpadding: 2px 6px;\n\tborder-radius: var(--border-radius);\n\tfont-family: var(--font-face-monospace, monospace);\n\tfont-size: 0.9em;\n}\n\n.cn-wiki-page__body :deep(pre) {\n\tbackground: var(--color-background-dark);\n\tpadding: 12px;\n\tborder-radius: var(--border-radius);\n\toverflow-x: auto;\n}\n\n.cn-wiki-page__body :deep(pre code) {\n\tbackground: transparent;\n\tpadding: 0;\n}\n\n.cn-wiki-page__body :deep(blockquote) {\n\tborder-left: 3px solid var(--color-border);\n\tpadding-left: 12px;\n\tcolor: var(--color-text-lighter);\n\tmargin: 0 0 1em;\n}\n\n.cn-wiki-page__body :deep(table) {\n\tborder-collapse: collapse;\n\tmargin: 0 0 1em;\n}\n\n.cn-wiki-page__body :deep(th),\n.cn-wiki-page__body :deep(td) {\n\tborder: 1px solid var(--color-border);\n\tpadding: 6px 10px;\n}\n\n.cn-wiki-page--has-sidebar .cn-wiki-page__main {\n\tpadding-left: 8px;\n}\n</style>\n"],"names":["CnWikiTreeNode","t"],"mappings":"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"}