@conduction/nextcloud-vue 2.56.0-beta.30 → 2.56.0-beta.31

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 (173) hide show
  1. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  2. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +3 -3
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  4. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +80 -4
  5. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  6. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +95 -91
  7. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  8. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +85 -6
  9. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
  10. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +1 -1
  11. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
  12. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +4 -1
  13. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  14. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  15. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +1 -1
  16. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -1
  17. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -1
  18. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +2 -2
  19. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
  20. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
  21. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +40 -3
  22. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  23. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  24. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +19 -11
  25. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  26. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  27. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +43 -0
  28. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  29. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +1 -1
  30. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  31. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js +1 -1
  32. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js.map +1 -1
  33. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js.map +1 -1
  34. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js +7 -7
  35. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js.map +1 -1
  36. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue3.js.map +1 -1
  37. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +125 -0
  38. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  39. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  40. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +3 -2
  41. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
  42. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
  43. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +302 -4
  44. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  45. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +417 -244
  46. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  47. package/dist/esm/components/CnFilesBrowser/filesBrowser.js +101 -1
  48. package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
  49. package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js +141 -1
  50. package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js.map +1 -1
  51. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +60 -0
  52. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
  53. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js +74 -49
  54. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
  55. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +29 -1
  56. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  57. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +48 -4
  58. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  59. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +7 -7
  60. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
  61. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
  62. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js +1 -1
  63. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js.map +1 -1
  64. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +1 -1
  65. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  66. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  67. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +63 -1
  68. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  69. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +8 -1
  70. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  71. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +14 -0
  72. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
  73. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js +5 -1
  74. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js.map +1 -1
  75. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +13 -6
  76. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  77. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  78. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +84 -1
  79. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
  80. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +85 -41
  81. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
  82. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +7 -2
  83. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  84. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  85. package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
  86. package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js.map +1 -1
  87. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +197 -6
  88. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
  89. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +94 -50
  90. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
  91. package/dist/esm/composables/useListView.js +4 -4
  92. package/dist/esm/composables/useListView.js.map +1 -1
  93. package/dist/esm/index.js +10 -1
  94. package/dist/esm/index.js.map +1 -1
  95. package/dist/esm/l10n/en.json.js +8 -1
  96. package/dist/esm/l10n/en.json.js.map +1 -1
  97. package/dist/esm/l10n/nl.json.js +54 -1
  98. package/dist/esm/l10n/nl.json.js.map +1 -1
  99. package/dist/esm/nextcloud-vue.css +198 -8
  100. package/dist/esm/utils/facets.js +75 -3
  101. package/dist/esm/utils/facets.js.map +1 -1
  102. package/dist/esm/utils/fillTemplate.js +7 -1
  103. package/dist/esm/utils/fillTemplate.js.map +1 -1
  104. package/dist/esm/utils/pageControls.js +81 -0
  105. package/dist/esm/utils/pageControls.js.map +1 -0
  106. package/dist/esm/utils/personalLenses.js +40 -3
  107. package/dist/esm/utils/personalLenses.js.map +1 -1
  108. package/dist/esm/utils/resolveFilterTokens.js +1 -1
  109. package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
  110. package/dist/esm/utils/resolveManifestTokens.js +2 -0
  111. package/dist/esm/utils/resolveManifestTokens.js.map +1 -1
  112. package/dist/esm/utils/sentinelTokens.js +19 -1
  113. package/dist/esm/utils/sentinelTokens.js.map +1 -1
  114. package/dist/esm/utils/userGroupsPeek.js +91 -0
  115. package/dist/esm/utils/userGroupsPeek.js.map +1 -0
  116. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  117. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  118. package/dist/esm/utils/visibleWhen.js +57 -5
  119. package/dist/esm/utils/visibleWhen.js.map +1 -1
  120. package/dist/esm/utils/widgetVisibility.js +4 -57
  121. package/dist/esm/utils/widgetVisibility.js.map +1 -1
  122. package/dist/nextcloud-vue.cjs.js +4309 -2233
  123. package/dist/nextcloud-vue.cjs.js.map +2 -2
  124. package/dist/nextcloud-vue.css +198 -8
  125. package/l10n/en.json +8 -1
  126. package/l10n/nl.json +54 -1
  127. package/package.json +2 -1
  128. package/scripts/check-docs-mdx.mjs +108 -0
  129. package/src/components/CnActionsBar/CnActionsBar.vue +6 -1
  130. package/src/components/CnAppRoot/CnAppRoot.md +4 -2
  131. package/src/components/CnAppRoot/CnAppRoot.vue +151 -59
  132. package/src/components/CnBannerWidget/CnBannerWidget.vue +112 -7
  133. package/src/components/CnCellRenderer/CnCellRenderer.vue +4 -1
  134. package/src/components/CnConversationThread/CnConversationWidget.vue +1 -1
  135. package/src/components/CnDashboardGrid/CnDashboardGrid.vue +2 -2
  136. package/src/components/CnDashboardPage/CnDashboardPage.vue +40 -3
  137. package/src/components/CnDataTable/CnDataTable.vue +19 -11
  138. package/src/components/CnDetailPage/CnDetailPage.vue +44 -1
  139. package/src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue +1 -1
  140. package/src/components/CnFacetSidebar/CnFacetSidebar.vue +7 -7
  141. package/src/components/CnFactsWidget/CnFactsWidget.vue +125 -0
  142. package/src/components/CnFieldListWidget/CnFieldListWidget.vue +3 -2
  143. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +588 -117
  144. package/src/components/CnFilesBrowser/filesBrowser.js +100 -0
  145. package/src/components/CnFilesBrowser/filesBrowserColumns.js +140 -0
  146. package/src/components/CnFormPage/CnFormPage.vue +81 -0
  147. package/src/components/CnIndexPage/CnIndexPage.vue +56 -3
  148. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +7 -7
  149. package/src/components/CnNextStepCard/CnNextStepWidget.vue +1 -1
  150. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +1 -1
  151. package/src/components/CnObjectSidebar/CnFilesTab.vue +70 -1
  152. package/src/components/CnObjectSidebar/CnNotesTab.vue +16 -0
  153. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +13 -6
  154. package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +180 -3
  155. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +7 -2
  156. package/src/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
  157. package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +281 -10
  158. package/src/components/index.js +8 -0
  159. package/src/composables/useListView.js +4 -4
  160. package/src/css/look-board-index.css +16 -8
  161. package/src/index.js +8 -0
  162. package/src/schemas/app-manifest-v2.schema.json +93 -9
  163. package/src/utils/facets.js +74 -2
  164. package/src/utils/fillTemplate.js +7 -1
  165. package/src/utils/pageControls.js +78 -0
  166. package/src/utils/personalLenses.js +39 -2
  167. package/src/utils/resolveFilterTokens.js +3 -1
  168. package/src/utils/resolveManifestTokens.js +2 -0
  169. package/src/utils/sentinelTokens.js +19 -1
  170. package/src/utils/userGroupsPeek.js +87 -0
  171. package/src/utils/validateManifestV2.compiled.js +3 -3
  172. package/src/utils/visibleWhen.js +56 -4
  173. package/src/utils/widgetVisibility.js +7 -56
@@ -1,7 +1,9 @@
1
1
  import { translate } from '@nextcloud/l10n';
2
2
  import { NcNoteCard } from '@nextcloud/vue';
3
3
  import { normalizeLook } from '../../composables/useLook.js';
4
+ import { fillTemplate } from '../../utils/fillTemplate.js';
4
5
  import { resolveHref, followLinkClick } from '../../utils/linkNavigation.js';
6
+ import { readPath } from '../../utils/readPath.js';
5
7
  import { safeHref } from '../../utils/safeHref.js';
6
8
  import { nextUid } from '../../utils/uid.js';
7
9
  import { readVisibleWhenValue, compareVisibleWhen } from '../../utils/visibleWhen.js';
@@ -202,6 +204,18 @@ var script = {
202
204
  default: null,
203
205
  },
204
206
 
207
+ /**
208
+ * Attention card: stack the actions under the text instead of beside it,
209
+ * the "Begin hier" card of PqMijnWerk (16px title, the action under the
210
+ * reason). Falls back to the `content` blob.
211
+ *
212
+ * @spec openspec/changes/screens-start-here-banner/specs/start-here-banner/spec.md#requirement-a-card-can-stack-its-action-under-the-text
213
+ */
214
+ stacked: {
215
+ type: Boolean,
216
+ default: false,
217
+ },
218
+
205
219
  /**
206
220
  * Stored content blob (CnDashboardPage registry branch) carrying the
207
221
  * same keys as the flat props: `{ variant, text, visibleWhen, route,
@@ -262,6 +276,12 @@ var script = {
262
276
  * the text wherever it says `{value}`.
263
277
  */
264
278
  conditionValue: null,
279
+ /**
280
+ * The first row of the `visibleWhen` source query (its `order`
281
+ * applied), the card's subject: `{subject.<field>}` in the texts
282
+ * and `@subject.<field>` in an action route read it.
283
+ */
284
+ conditionRow: null,
265
285
  /** Whether the visibleWhen request itself failed (not: answered "no"). */
266
286
  conditionFailed: false,
267
287
  /** Why it failed, as the request reported it. */
@@ -319,6 +339,15 @@ var script = {
319
339
  return (this.layout || (this.content && this.content.layout) || 'banner') === 'attention'
320
340
  },
321
341
 
342
+ /**
343
+ * Whether the actions sit under the text.
344
+ *
345
+ * @return {boolean}
346
+ */
347
+ isStacked() {
348
+ return this.stacked || Boolean(this.content && this.content.stacked)
349
+ },
350
+
322
351
  /**
323
352
  * The attention card's kicker (prop, else content).
324
353
  *
@@ -327,7 +356,7 @@ var script = {
327
356
  */
328
357
  resolvedKicker() {
329
358
  const kicker = this.kicker || (this.content && this.content.kicker) || '';
330
- return kicker ? this.tl(kicker) : ''
359
+ return kicker ? this.fillValue(this.tl(kicker)) : ''
331
360
  },
332
361
 
333
362
  /**
@@ -395,7 +424,7 @@ var script = {
395
424
  let target = null;
396
425
  let href = '';
397
426
  if (action.route) {
398
- target = typeof action.route === 'string' ? { name: action.route } : action.route;
427
+ target = typeof action.route === 'string' ? { name: action.route } : this.resolveSubject(action.route);
399
428
  href = resolveHref(target, this.$router);
400
429
  } else if (typeof action.href === 'string' && action.href !== '') {
401
430
  const safe = safeHref(action.href);
@@ -454,7 +483,7 @@ var script = {
454
483
  */
455
484
  uncheckedText() {
456
485
  const title = this.title || (this.content && this.content.title) || this.resolvedText;
457
- if (!title || title.includes('{value}')) {
486
+ if (!title || title.includes('{value}') || title.includes('{subject.')) {
458
487
  return translate('nextcloud-vue', 'Could not check')
459
488
  }
460
489
  // `escape: false`: the result is rendered through `{{ }}`, which escapes.
@@ -526,10 +555,55 @@ var script = {
526
555
  * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card
527
556
  */
528
557
  fillValue(text) {
529
- if (this.conditionValue === null || !text.includes('{value}')) {
558
+ let out = text;
559
+ if (this.conditionValue !== null && out.includes('{value}')) {
560
+ out = out.replaceAll('{value}', String(this.conditionValue));
561
+ }
562
+ return this.fillSubject(out)
563
+ },
564
+
565
+ /**
566
+ * Fill `{subject.<field>}` (and `{subject.<field>|date}`, `|days`) from
567
+ * the card's subject row. A token with no value empties the whole text,
568
+ * as in a record list. Text without a subject token is untouched.
569
+ *
570
+ * @param {string} text The text.
571
+ * @return {string} The filled text.
572
+ * @spec openspec/changes/screens-start-here-banner/specs/start-here-banner/spec.md#requirement-a-banner-can-name-the-first-row-of-a-query
573
+ */
574
+ fillSubject(text) {
575
+ if (typeof text !== 'string' || !text.includes('{subject.')) {
530
576
  return text
531
577
  }
532
- return text.replaceAll('{value}', String(this.conditionValue))
578
+ // `{value}` is not a subject token: keep it out of the template fill.
579
+ const guard = '\u0000value\u0000';
580
+ const filled = fillTemplate(text.replaceAll('{value}', guard), { subject: this.conditionRow || {} });
581
+ return filled.replaceAll(guard, '{value}')
582
+ },
583
+
584
+ /**
585
+ * Replace `@subject.<field>` strings anywhere in an action route
586
+ * (params, query) by the subject's value.
587
+ *
588
+ * @param {unknown} node A route name, location or any nested part of one.
589
+ * @return {unknown} The same shape with the tokens resolved.
590
+ * @spec openspec/changes/screens-start-here-banner/specs/start-here-banner/spec.md#requirement-a-banner-can-name-the-first-row-of-a-query
591
+ */
592
+ resolveSubject(node) {
593
+ if (typeof node === 'string') {
594
+ if (!node.startsWith('@subject.')) {
595
+ return node
596
+ }
597
+ const value = this.conditionRow ? readPath(this.conditionRow, node.slice('@subject.'.length)) : undefined;
598
+ return value === undefined || value === null ? '' : String(value)
599
+ }
600
+ if (Array.isArray(node)) {
601
+ return node.map((item) => this.resolveSubject(item))
602
+ }
603
+ if (node && typeof node === 'object') {
604
+ return Object.fromEntries(Object.entries(node).map(([key, value]) => [key, this.resolveSubject(value)]))
605
+ }
606
+ return node
533
607
  },
534
608
 
535
609
  /**
@@ -572,6 +646,7 @@ var script = {
572
646
  if (outcome) {
573
647
  this.conditionMet = outcome.met === true;
574
648
  this.conditionValue = outcome.value !== undefined ? outcome.value : null;
649
+ this.conditionRow = outcome.row && typeof outcome.row === 'object' ? outcome.row : null;
575
650
  this.conditionFailed = outcome.failed === true;
576
651
  this.conditionFailureReason = outcome.failed === true && typeof outcome.reason === 'string' ? outcome.reason : '';
577
652
  return
@@ -580,12 +655,15 @@ var script = {
580
655
  if (!cond) {
581
656
  this.conditionMet = null;
582
657
  this.conditionValue = null;
658
+ this.conditionRow = null;
583
659
  this.conditionFailed = false;
584
660
  this.conditionFailureReason = '';
585
661
  return
586
662
  }
587
663
  try {
588
- const value = await readVisibleWhenValue(cond);
664
+ const sink = {};
665
+ const value = await readVisibleWhenValue(cond, undefined, sink);
666
+ this.conditionRow = sink.row || null;
589
667
  this.conditionValue = value;
590
668
  this.conditionMet = compareVisibleWhen(value, cond.op || 'eq', cond.value);
591
669
  this.conditionFailed = false;
@@ -598,6 +676,7 @@ var script = {
598
676
  console.warn('[CnBannerWidget] visibleWhen could not be evaluated, banner hidden:', error?.message || error);
599
677
  this.conditionMet = false;
600
678
  this.conditionValue = null;
679
+ this.conditionRow = null;
601
680
  this.conditionFailed = true;
602
681
  this.conditionFailureReason = typeof error?.message === 'string' ? error.message : String(error ?? '');
603
682
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CnBannerWidget.vue2.js","sources":["../../../../src/components/CnBannerWidget/CnBannerWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<section\n\t\tv-if=\"visible && isAttention\"\n\t\tclass=\"cn-banner-widget cn-banner-widget--attention\"\n\t\t:class=\"'cn-banner-widget--' + resolvedVariant\"\n\t\t:aria-labelledby=\"headingId\"\n\t\tdata-testid=\"cn-banner-widget-attention\">\n\t\t<div class=\"cn-banner-widget__body\">\n\t\t\t<p v-if=\"resolvedKicker\" class=\"cn-banner-widget__kicker\">\n\t\t\t\t{{ resolvedKicker }}\n\t\t\t</p>\n\t\t\t<h3 :id=\"headingId\" class=\"cn-banner-widget__title\">\n\t\t\t\t{{ displayTitle }}\n\t\t\t</h3>\n\t\t\t<p v-if=\"displayReason\" class=\"cn-banner-widget__reason\">\n\t\t\t\t{{ displayReason }}\n\t\t\t</p>\n\t\t</div>\n\t\t<div v-if=\"resolvedActions.length > 0\" class=\"cn-banner-widget__actions\">\n\t\t\t<component\n\t\t\t\t:is=\"action.href ? 'a' : 'button'\"\n\t\t\t\tv-for=\"action in resolvedActions\"\n\t\t\t\t:key=\"action.key\"\n\t\t\t\tclass=\"cn-banner-widget__action\"\n\t\t\t\t:class=\"action.primary ? 'cn-banner-widget__action--primary' : 'cn-banner-widget__action--secondary'\"\n\t\t\t\t:href=\"action.href || null\"\n\t\t\t\t:type=\"action.href ? null : 'button'\"\n\t\t\t\tdata-testid=\"cn-banner-widget-action\"\n\t\t\t\t@click=\"onActionClick($event, action)\">\n\t\t\t\t{{ action.label }}\n\t\t\t</component>\n\t\t</div>\n\t</section>\n\t<!-- The count could not be read. One quiet line, not the card and not an\n\t alarm: hidden would read as \"nothing to report\", which is not known. -->\n\t<p\n\t\tv-else-if=\"checkFailed\"\n\t\tclass=\"cn-banner-widget cn-banner-widget--unchecked\"\n\t\t:title=\"failureTooltip\"\n\t\tdata-testid=\"cn-banner-widget-unchecked\">\n\t\t<span class=\"cn-banner-widget__unchecked-text\">{{ uncheckedText }}</span>\n\t\t<span class=\"cn-banner-widget__unchecked-reason\">{{ failureTooltip }}</span>\n\t</p>\n\t<div v-else-if=\"visible\" class=\"cn-banner-widget\">\n\t\t<NcNoteCard :type=\"resolvedVariant\" class=\"cn-banner-widget__card\">\n\t\t\t<component\n\t\t\t\t:is=\"clickable ? 'a' : 'span'\"\n\t\t\t\tclass=\"cn-banner-widget__text\"\n\t\t\t\t:class=\"{ 'cn-banner-widget__text--clickable': clickable }\"\n\t\t\t\t:href=\"clickable ? routeHref : null\"\n\t\t\t\tdata-testid=\"cn-banner-widget-text\"\n\t\t\t\t@click=\"onClick\">\n\t\t\t\t{{ displayText }}\n\t\t\t</component>\n\t\t</NcNoteCard>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcNoteCard } from '@nextcloud/vue'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport { followLinkClick, resolveHref } from '../../utils/linkNavigation.js'\nimport { safeHref } from '../../utils/safeHref.js'\nimport { nextUid } from '../../utils/uid.js'\nimport { compareVisibleWhen, readVisibleWhenValue } from '../../utils/visibleWhen.js'\n\n/** Variants understood by NcNoteCard. */\nconst VARIANTS = ['info', 'warning', 'error', 'success']\n\n/** Layouts: the note card (default) or the attention card. */\nconst LAYOUTS = ['banner', 'attention']\n\n/** An attention card offers a primary and a secondary action, no more. */\nconst MAX_ACTIONS = 2\n\n/**\n * Under the board look the card takes what the boards draw: DqMijnWerk's\n * \"Vandaag eerst\" has three secondary actions and the primary one.\n */\nconst MAX_ACTIONS_BOARD = 4\n\n/**\n * CnBannerWidget — declarative notice banner for dashboards and v2 pages\n * (`banner` widget type, Wave 1 of nextcloud-vue#91).\n *\n * Renders a themed NcNoteCard with a variant (`info | warning | error`),\n * a text, an optional click-through route, and an optional `visibleWhen`\n * condition: a simple `{ field, op, value }` predicate evaluated against\n * an endpoint response or an OpenRegister source (the doriath\n * migration-banner case — \"show while `pending > 0`\").\n *\n * Config arrives either as flat props (v2 grid spreads `props`) or as a\n * stored `content` blob (CnDashboardPage's registry branch) — explicit\n * flat props win on collision.\n *\n * ```json\n * {\n * \"widgetKey\": \"banner\",\n * \"props\": {\n * \"variant\": \"warning\",\n * \"text\": \"Migrations pending — open the migration overview.\",\n * \"visibleWhen\": { \"endpoint\": \"/apps/doriath/api/migrations/status\", \"field\": \"pending\", \"op\": \"gt\", \"value\": 0 },\n * \"route\": \"migrations\"\n * }\n * }\n * ```\n *\n * Fail-safe: with a `visibleWhen`, the banner stays HIDDEN until the\n * condition evaluates true — a failed fetch never breaks (or spams) a\n * dashboard.\n *\n * An attention card (`layout: \"attention\"`) whose `visibleWhen` request\n * FAILS renders one quiet line instead, \"Could not check\", with the reason as\n * a tooltip. A hidden card reads as \"nothing needs attention\", and a failed\n * count does not know that. A request that succeeds with a value that does\n * not meet the condition still renders nothing.\n *\n * `layout: \"attention\"` turns the banner into an attention card: a card with\n * a coloured edge by severity, a `kicker` label, a `title`, a `reason` line\n * and up to two `actions` (the first is the primary one unless an action says\n * `primary` itself). `visibleWhen` works the same way. Without `layout` the\n * note card above renders, as it always did.\n *\n * ```json\n * {\n * \"widgetKey\": \"banner\",\n * \"props\": {\n * \"layout\": \"attention\",\n * \"variant\": \"error\",\n * \"kicker\": \"First today\",\n * \"title\": \"Parking permits city centre\",\n * \"reason\": \"The deadline ends today.\",\n * \"actions\": [\n * { \"label\": \"Open case\", \"route\": { \"name\": \"CaseDetail\", \"params\": { \"id\": \"2026-0061\" } } },\n * { \"label\": \"Suspend deadline\", \"route\": \"CaseSuspend\" }\n * ]\n * }\n * }\n * ```\n */\nexport default {\n\tname: 'CnBannerWidget',\n\n\tcomponents: { NcNoteCard },\n\n\tinject: {\n\t\t/**\n\t\t * The host's label lookup (CnAppRoot provides it); identity without one, so a\n\t\t * label that is not a key renders as written.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The app's look, provided by CnAppRoot or CnPageRenderer (`nextcloud` or `board`). */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Banner severity variant: `info | warning | error` (plus `success`\n\t\t * for completeness — NcNoteCard's set). Empty falls back to the\n\t\t * `content` blob, then `info`.\n\t\t */\n\t\tvariant: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t\tvalidator: (v) => v === '' || VARIANTS.includes(v),\n\t\t},\n\n\t\t/** Pre-translated banner text. Empty falls back to the `content` blob. */\n\t\ttext: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Optional visibility condition. Shape:\n\t\t * `{ endpoint?, source?, field?, op?, value }` — exactly one of\n\t\t * `endpoint` (a same-origin URL returning JSON) or `source`\n\t\t * (`{ register, schema, filter? }`, an OpenRegister object query whose\n\t\t * `filter` supports the shared @-token grammar). `field` is a\n\t\t * dot-path into the response (for a `source`, into the first result;\n\t\t * omit it — or use `@total` — to compare the collection total).\n\t\t * `op` is `eq | neq | gt | gte | lt | lte` (default `eq`); `value` is\n\t\t * the literal right-hand side. `null` (the default) shows the banner\n\t\t * unconditionally.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tvisibleWhen: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Optional click-through route: a vue-router route NAME (string) or\n\t\t * a full location object. When set, the banner text is a link to that\n\t\t * route. `null` renders static text.\n\t\t *\n\t\t * @type {string|object|null}\n\t\t */\n\t\troute: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * How the banner renders: `banner` (the note card, default) or\n\t\t * `attention` (a card with a severity edge, kicker, title, reason and\n\t\t * up to two actions). Empty falls back to the `content` blob, then\n\t\t * `banner`.\n\t\t *\n\t\t * @type {''|'banner'|'attention'}\n\t\t */\n\t\tlayout: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t\tvalidator: (v) => v === '' || LAYOUTS.includes(v),\n\t\t},\n\n\t\t/** Attention card: the small label above the title (\"First today\"). */\n\t\tkicker: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Attention card: the title. Falls back to `text` when empty. */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Attention card: the line under the title that says why this needs attention. `{value}` is replaced like in `text`. */\n\t\treason: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Attention card: its actions, two at most (four under the board look,\n\t\t * or `maxActions`). Each is `{ label, route?,\n\t\t * href?, primary?, id? }`: `route` is a route name or location,\n\t\t * `href` an URL. An action with neither renders a button that emits\n\t\t * `action`. The first action is the primary one unless one sets\n\t\t * `primary: true`.\n\t\t *\n\t\t * @type {Array<{label: string, route?: (string|object), href?: string, primary?: boolean, id?: string}>|null}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Attention card: how many actions it shows at most. Null takes the\n\t\t * default: two, four under the board look.\n\t\t *\n\t\t * @spec openspec/changes/screens-banner-actions-parity/specs/banner-actions/spec.md#requirement-the-board-attention-card-shows-the-boards-actions\n\t\t */\n\t\tmaxActions: {\n\t\t\ttype: Number,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Stored content blob (CnDashboardPage registry branch) carrying the\n\t\t * same keys as the flat props: `{ variant, text, visibleWhen, route,\n\t\t * layout, kicker, title, reason, actions }`. Explicit flat props win\n\t\t * on collision.\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Pre-evaluated `visibleWhen` outcome `{ met, value }`, injected by a\n\t\t * host that already ran the predicate (CnDashboardPage evaluates it\n\t\t * to know whether to collapse the banner's grid cell). When present,\n\t\t * the banner renders from this verdict instead of fetching again —\n\t\t * no duplicate request and no hidden-until-self-evaluated flash —\n\t\t * and `value` feeds the `{value}` text placeholder. `null` (the\n\t\t * default) keeps the banner self-evaluating. A host whose request\n\t\t * failed passes `{ met: false, value: null, failed: true, reason }`,\n\t\t * and an attention card then says it could not check.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tconditionOutcome: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted when an attention-card action without a `route` or `href`\n\t\t * is clicked, so the host can run it.\n\t\t *\n\t\t * @event action\n\t\t * @type {{id: (string|undefined), label: string, index: number}}\n\t\t */\n\t\t'action',\n\t],\n\n\t/**\n\t * The banner's own state: the evaluated condition, the value it read,\n\t * and whether the request for it failed.\n\t *\n\t * @return {object} The state.\n\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t */\n\tdata() {\n\t\treturn {\n\t\t\t/** Id that ties the attention card's region to its title. */\n\t\t\theadingId: `cn-banner-title-${nextUid()}`,\n\t\t\t/** Evaluated visibleWhen outcome (null = not yet evaluated). */\n\t\t\tconditionMet: null,\n\t\t\t/**\n\t\t\t * The raw field value the predicate read (from the injected\n\t\t\t * outcome or the banner's own evaluation) — interpolated into\n\t\t\t * the text wherever it says `{value}`.\n\t\t\t */\n\t\t\tconditionValue: null,\n\t\t\t/** Whether the visibleWhen request itself failed (not: answered \"no\"). */\n\t\t\tconditionFailed: false,\n\t\t\t/** Why it failed, as the request reported it. */\n\t\t\tconditionFailureReason: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** The effective variant (prop → content → 'info'). */\n\t\tresolvedVariant() {\n\t\t\tconst v = this.variant || (this.content && this.content.variant) || 'info'\n\t\t\treturn VARIANTS.includes(v) ? v : 'info'\n\t\t},\n\n\t\t/** The effective banner text (prop → content → ''). */\n\t\tresolvedText() {\n\t\t\treturn this.text || (this.content && this.content.text) || ''\n\t\t},\n\n\t\t/** The effective visibleWhen condition (prop → content → null). */\n\t\tresolvedVisibleWhen() {\n\t\t\treturn this.visibleWhen || (this.content && this.content.visibleWhen) || null\n\t\t},\n\n\t\t/** The effective injected outcome (prop → content → null). */\n\t\tresolvedConditionOutcome() {\n\t\t\treturn this.conditionOutcome || (this.content && this.content.conditionOutcome) || null\n\t\t},\n\n\t\t/**\n\t\t * The rendered text: `resolvedText` with `{value}` replaced by the\n\t\t * predicate's field value once one is known. Without a known value\n\t\t * the text renders as written — a `{value}` placeholder only makes\n\t\t * sense on a conditional banner, which stays hidden until its\n\t\t * predicate (and therefore its value) resolves.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tdisplayText() {\n\t\t\treturn this.fillValue(this.resolvedText)\n\t\t},\n\n\t\t/** The effective click-through route (prop → content → null). */\n\t\tresolvedRoute() {\n\t\t\treturn this.route || (this.content && this.content.route) || null\n\t\t},\n\n\t\t/**\n\t\t * Whether the attention card renders instead of the note card.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tisAttention() {\n\t\t\treturn (this.layout || (this.content && this.content.layout) || 'banner') === 'attention'\n\t\t},\n\n\t\t/**\n\t\t * The attention card's kicker (prop, else content).\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tresolvedKicker() {\n\t\t\tconst kicker = this.kicker || (this.content && this.content.kicker) || ''\n\t\t\treturn kicker ? this.tl(kicker) : ''\n\t\t},\n\n\t\t/**\n\t\t * The attention card's title: `title`, else `text`, with `{value}` filled in.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tdisplayTitle() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\treturn title ? this.fillValue(this.tl(title)) : this.displayText\n\t\t},\n\n\t\t/**\n\t\t * The attention card's reason line. When the title already used\n\t\t * `text`, the text is not repeated here.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tdisplayReason() {\n\t\t\tconst reason = this.reason || (this.content && this.content.reason) || ''\n\t\t\tif (reason) {\n\t\t\t\treturn this.fillValue(this.tl(reason))\n\t\t\t}\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\t// A card that sets `text` to the same words as its `title` says\n\t\t\t// them once.\n\t\t\treturn title && this.displayText !== this.displayTitle ? this.displayText : ''\n\t\t},\n\n\t\t/**\n\t\t * The attention card's actions: the first `actionCap` that have a label, each\n\t\t * with its href and whether it is the primary one.\n\t\t *\n\t\t * @return {Array<{key: string, id: (string|undefined), label: string, href: string, target: (object|null), primary: boolean, index: number}>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\t/**\n\t\t * How many actions the attention card shows: `maxActions` (prop, then\n\t\t * content), else four under the board look and two without it.\n\t\t *\n\t\t * @return {number} The cap.\n\t\t * @spec openspec/changes/screens-banner-actions-parity/specs/banner-actions/spec.md#requirement-the-board-attention-card-shows-the-boards-actions\n\t\t */\n\t\tactionCap() {\n\t\t\tconst explicit = this.maxActions ?? (this.content ? this.content.maxActions : undefined)\n\t\t\tif (Number.isInteger(explicit) && explicit > 0) {\n\t\t\t\treturn explicit\n\t\t\t}\n\t\t\treturn normalizeLook(this.cnLook) === 'board' ? MAX_ACTIONS_BOARD : MAX_ACTIONS\n\t\t},\n\n\t\tresolvedActions() {\n\t\t\tconst raw = this.actions || (this.content && this.content.actions) || []\n\t\t\tif (!Array.isArray(raw)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst usable = raw\n\t\t\t\t.filter((action) => action && typeof action === 'object' && typeof action.label === 'string' && action.label !== '')\n\t\t\t\t.slice(0, this.actionCap)\n\t\t\tconst declaredPrimary = usable.findIndex((action) => action.primary === true)\n\t\t\tconst primaryIndex = declaredPrimary === -1 ? 0 : declaredPrimary\n\t\t\treturn usable.map((action, index) => {\n\t\t\t\tlet target = null\n\t\t\t\tlet href = ''\n\t\t\t\tif (action.route) {\n\t\t\t\t\ttarget = typeof action.route === 'string' ? { name: action.route } : action.route\n\t\t\t\t\thref = resolveHref(target, this.$router)\n\t\t\t\t} else if (typeof action.href === 'string' && action.href !== '') {\n\t\t\t\t\tconst safe = safeHref(action.href)\n\t\t\t\t\thref = safe === '#' ? '' : safe\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tkey: `${index}-${action.id || action.label}`,\n\t\t\t\t\tid: action.id,\n\t\t\t\t\tlabel: this.tl(action.label),\n\t\t\t\t\thref,\n\t\t\t\t\ttarget,\n\t\t\t\t\tprimary: index === primaryIndex,\n\t\t\t\t\tindex,\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Whether the banner renders: no condition = always; else the evaluated outcome.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tvisible() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\tif (this.resolvedText === '' && !(this.isAttention && title !== '')) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (!this.resolvedVisibleWhen) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn this.conditionMet === true\n\t\t},\n\n\t\t/**\n\t\t * Whether to say the check failed: an attention card, with words to\n\t\t * show, whose `visibleWhen` request failed. A plain banner stays\n\t\t * hidden on failure, as it always did.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tcheckFailed() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\treturn this.isAttention\n\t\t\t\t&& !!this.resolvedVisibleWhen\n\t\t\t\t&& this.conditionFailed\n\t\t\t\t&& (title !== '' || this.resolvedText !== '')\n\t\t},\n\n\t\t/**\n\t\t * The one line shown when the check failed. It names the card by its\n\t\t * title, unless the title needs the value that could not be read.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tuncheckedText() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || this.resolvedText\n\t\t\tif (!title || title.includes('{value}')) {\n\t\t\t\treturn t('nextcloud-vue', 'Could not check')\n\t\t\t}\n\t\t\t// `escape: false`: the result is rendered through `{{ }}`, which escapes.\n\t\t\treturn t('nextcloud-vue', 'Could not check: {subject}', { subject: title }, undefined, { escape: false })\n\t\t},\n\n\t\t/**\n\t\t * The reason the check failed, for the tooltip and for assistive\n\t\t * technology.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tfailureTooltip() {\n\t\t\tif (!this.conditionFailureReason) {\n\t\t\t\treturn t('nextcloud-vue', 'The check failed.')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'The check failed: {reason}', { reason: this.conditionFailureReason }, undefined, { escape: false })\n\t\t},\n\n\t\t/** Whether the banner navigates on click (route set + router present). */\n\t\tclickable() {\n\t\t\treturn !!this.resolvedRoute && !!this.$router\n\t\t},\n\n\t\t/** The click-through route as a router location (a string is a route name). */\n\t\trouteLocation() {\n\t\t\tconst route = this.resolvedRoute\n\t\t\treturn typeof route === 'string' ? { name: route } : route\n\t\t},\n\n\t\t/** The href of the click-through link. */\n\t\trouteHref() {\n\t\t\treturn resolveHref(this.routeLocation, this.$router)\n\t\t},\n\t},\n\n\twatch: {\n\t\tresolvedVisibleWhen: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tthis.evaluateCondition()\n\t\t\t},\n\t\t},\n\n\t\tresolvedConditionOutcome() {\n\t\t\tthis.evaluateCondition()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * A manifest string through the host's label lookup (`cnTranslate`), so a key\n\t\t * such as \"Mine\" reads in the user's language.\n\t\t *\n\t\t * @param {string} text The text as written in the manifest.\n\t\t * @return {string} The translated text.\n\t\t */\n\t\ttl(text) {\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/**\n\t\t * Replace `{value}` in a text by the value the predicate read, once\n\t\t * one is known.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The text with the value filled in.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tfillValue(text) {\n\t\t\tif (this.conditionValue === null || !text.includes('{value}')) {\n\t\t\t\treturn text\n\t\t\t}\n\t\t\treturn text.replaceAll('{value}', String(this.conditionValue))\n\t\t},\n\n\t\t/**\n\t\t * Run an attention-card action: route an in-app link, leave an\n\t\t * external link to the browser, and emit `action` for a button.\n\t\t *\n\t\t * @param {MouseEvent} event The click.\n\t\t * @param {object} action The resolved action.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tonActionClick(event, action) {\n\t\t\tif (action.target) {\n\t\t\t\tfollowLinkClick(event, action.target, this.$router)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!action.href) {\n\t\t\t\tthis.$emit('action', { id: action.id, label: action.label, index: action.index })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve the `visibleWhen` verdict. A host-injected\n\t\t * `conditionOutcome` wins outright — the host already ran the\n\t\t * predicate, so re-fetching would double the request and flash the\n\t\t * banner hidden until its own copy resolved. Otherwise evaluate\n\t\t * through the shared util primitives (endpoint / OpenRegister-source\n\t\t * modes — the Wave-1 banner shape is the canonical one, extracted to\n\t\t * `utils/visibleWhen.js` in Wave 3 so manifest actions reuse it),\n\t\t * keeping the read VALUE for the `{value}` text placeholder.\n\t\t * Fail-safe: any fetch/shape error leaves the banner hidden. The\n\t\t * failure is kept (`conditionFailed`, with its reason) so an\n\t\t * attention card can say it could not check.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tasync evaluateCondition() {\n\t\t\tconst outcome = this.resolvedConditionOutcome\n\t\t\tif (outcome) {\n\t\t\t\tthis.conditionMet = outcome.met === true\n\t\t\t\tthis.conditionValue = outcome.value !== undefined ? outcome.value : null\n\t\t\t\tthis.conditionFailed = outcome.failed === true\n\t\t\t\tthis.conditionFailureReason = outcome.failed === true && typeof outcome.reason === 'string' ? outcome.reason : ''\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst cond = this.resolvedVisibleWhen\n\t\t\tif (!cond) {\n\t\t\t\tthis.conditionMet = null\n\t\t\t\tthis.conditionValue = null\n\t\t\t\tthis.conditionFailed = false\n\t\t\t\tthis.conditionFailureReason = ''\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst value = await readVisibleWhenValue(cond)\n\t\t\t\tthis.conditionValue = value\n\t\t\t\tthis.conditionMet = compareVisibleWhen(value, cond.op || 'eq', cond.value)\n\t\t\t\tthis.conditionFailed = false\n\t\t\t\tthis.conditionFailureReason = ''\n\t\t\t} catch (error) {\n\t\t\t\t// Still hidden: a banner that cannot tell whether it applies\n\t\t\t\t// should not claim attention. But not silently, because a\n\t\t\t\t// failed count and \"nothing to report\" look the same on screen.\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnBannerWidget] visibleWhen could not be evaluated, banner hidden:', error?.message || error)\n\t\t\t\tthis.conditionMet = false\n\t\t\t\tthis.conditionValue = null\n\t\t\t\tthis.conditionFailed = true\n\t\t\t\tthis.conditionFailureReason = typeof error?.message === 'string' ? error.message : String(error ?? '')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Route a plain click on the link; a modified click opens the href\n\t\t * in the browser.\n\t\t *\n\t\t * @param {MouseEvent} event The click event.\n\t\t * @return {void}\n\t\t */\n\t\tonClick(event) {\n\t\t\tif (!this.clickable) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfollowLinkClick(event, this.routeLocation, this.$router)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-banner-widget {\n\twidth: 100%;\n}\n\n.cn-banner-widget__card {\n\tmargin: 0;\n}\n\n.cn-banner-widget__text--clickable {\n\tcursor: pointer;\n\ttext-decoration: underline;\n\tcolor: inherit;\n}\n\n/* The check failed. Muted text, no fill, no severity colour: it is a note\n that something is not known, not a warning. */\n.cn-banner-widget--unchecked {\n\tbox-sizing: border-box;\n\tmargin: 0;\n\tpadding: 8px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tcursor: help;\n}\n\n/* The reason, for assistive technology; sighted readers get the tooltip. */\n.cn-banner-widget__unchecked-reason {\n\tposition: absolute;\n\twidth: 1px;\n\theight: 1px;\n\tmargin: -1px;\n\tpadding: 0;\n\toverflow: hidden;\n\tclip-path: inset(50%);\n\twhite-space: nowrap;\n\tborder: 0;\n}\n\n/* Attention card. The severity edge is an inset shadow, so it takes no width. */\n.cn-banner-widget--attention {\n\t--cn-banner-accent: var(--color-primary-element);\n\t--cn-banner-accent-text: var(--color-primary-element);\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 16px 24px;\n\tbox-sizing: border-box;\n\tpadding: 20px 24px 20px 28px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-container, var(--border-radius-large, 12px));\n\tbackground: var(--color-main-background);\n\tbox-shadow: inset 4px 0 0 0 var(--cn-banner-accent);\n}\n\n.cn-banner-widget--attention.cn-banner-widget--warning {\n\t--cn-banner-accent: var(--color-element-warning, var(--color-warning));\n\t--cn-banner-accent-text: var(--color-text-warning, var(--color-warning-text, var(--color-main-text)));\n}\n\n.cn-banner-widget--attention.cn-banner-widget--error {\n\t--cn-banner-accent: var(--color-element-error, var(--color-error));\n\t--cn-banner-accent-text: var(--color-text-error, var(--color-error-text));\n}\n\n.cn-banner-widget--attention.cn-banner-widget--success {\n\t--cn-banner-accent: var(--color-element-success, var(--color-success));\n\t--cn-banner-accent-text: var(--color-text-success, var(--color-success-text));\n}\n\n.cn-banner-widget__body {\n\tdisplay: flex;\n\tflex: 1 1 320px;\n\tflex-direction: column;\n\tgap: 6px;\n\tmin-width: 0;\n}\n\n.cn-banner-widget__kicker {\n\tmargin: 0;\n\tfont-size: 0.85em;\n\tfont-weight: 700;\n\tletter-spacing: 0.06em;\n\ttext-transform: uppercase;\n\tcolor: var(--cn-banner-accent-text);\n}\n\n.cn-banner-widget__title {\n\tmargin: 0;\n\tfont-size: 1.3em;\n\tfont-weight: 700;\n\tline-height: 1.3;\n\tcolor: var(--color-main-text);\n}\n\n.cn-banner-widget__reason {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-banner-widget__actions {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 10px;\n}\n\n.cn-banner-widget__action {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\tbox-sizing: border-box;\n\tmin-height: var(--default-clickable-area, 40px);\n\tmargin: 0;\n\tpadding: 0 16px;\n\tborder: 1px solid var(--color-border-maxcontrast);\n\tborder-radius: var(--border-radius-element, var(--border-radius-large, 8px));\n\tfont: inherit;\n\tfont-weight: 600;\n\ttext-decoration: none;\n\tcursor: pointer;\n}\n\n.cn-banner-widget__action--secondary {\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n}\n\n.cn-banner-widget__action--secondary:hover {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-banner-widget__action--primary {\n\tborder-color: var(--color-primary-element);\n\tbackground: var(--color-primary-element);\n\tcolor: var(--color-primary-element-text);\n}\n\n.cn-banner-widget__action--primary:hover {\n\tborder-color: var(--color-primary-element-hover);\n\tbackground: var(--color-primary-element-hover);\n}\n\n.cn-banner-widget__action:focus-visible {\n\toutline: 2px solid var(--color-main-text);\n\toutline-offset: 2px;\n}\n</style>\n"],"names":["t"],"mappings":"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"}
1
+ {"version":3,"file":"CnBannerWidget.vue2.js","sources":["../../../../src/components/CnBannerWidget/CnBannerWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<section\n\t\tv-if=\"visible && isAttention\"\n\t\tclass=\"cn-banner-widget cn-banner-widget--attention\"\n\t\t:class=\"['cn-banner-widget--' + resolvedVariant, { 'cn-banner-widget--stacked': isStacked }]\"\n\t\t:aria-labelledby=\"headingId\"\n\t\tdata-testid=\"cn-banner-widget-attention\">\n\t\t<div class=\"cn-banner-widget__body\">\n\t\t\t<p v-if=\"resolvedKicker\" class=\"cn-banner-widget__kicker\">\n\t\t\t\t{{ resolvedKicker }}\n\t\t\t</p>\n\t\t\t<h3 :id=\"headingId\" class=\"cn-banner-widget__title\">\n\t\t\t\t{{ displayTitle }}\n\t\t\t</h3>\n\t\t\t<p v-if=\"displayReason\" class=\"cn-banner-widget__reason\">\n\t\t\t\t{{ displayReason }}\n\t\t\t</p>\n\t\t</div>\n\t\t<div v-if=\"resolvedActions.length > 0\" class=\"cn-banner-widget__actions\">\n\t\t\t<component\n\t\t\t\t:is=\"action.href ? 'a' : 'button'\"\n\t\t\t\tv-for=\"action in resolvedActions\"\n\t\t\t\t:key=\"action.key\"\n\t\t\t\tclass=\"cn-banner-widget__action\"\n\t\t\t\t:class=\"action.primary ? 'cn-banner-widget__action--primary' : 'cn-banner-widget__action--secondary'\"\n\t\t\t\t:href=\"action.href || null\"\n\t\t\t\t:type=\"action.href ? null : 'button'\"\n\t\t\t\tdata-testid=\"cn-banner-widget-action\"\n\t\t\t\t@click=\"onActionClick($event, action)\">\n\t\t\t\t{{ action.label }}\n\t\t\t</component>\n\t\t</div>\n\t</section>\n\t<!-- The count could not be read. One quiet line, not the card and not an\n\t alarm: hidden would read as \"nothing to report\", which is not known. -->\n\t<p\n\t\tv-else-if=\"checkFailed\"\n\t\tclass=\"cn-banner-widget cn-banner-widget--unchecked\"\n\t\t:title=\"failureTooltip\"\n\t\tdata-testid=\"cn-banner-widget-unchecked\">\n\t\t<span class=\"cn-banner-widget__unchecked-text\">{{ uncheckedText }}</span>\n\t\t<span class=\"cn-banner-widget__unchecked-reason\">{{ failureTooltip }}</span>\n\t</p>\n\t<div v-else-if=\"visible\" class=\"cn-banner-widget\">\n\t\t<NcNoteCard :type=\"resolvedVariant\" class=\"cn-banner-widget__card\">\n\t\t\t<component\n\t\t\t\t:is=\"clickable ? 'a' : 'span'\"\n\t\t\t\tclass=\"cn-banner-widget__text\"\n\t\t\t\t:class=\"{ 'cn-banner-widget__text--clickable': clickable }\"\n\t\t\t\t:href=\"clickable ? routeHref : null\"\n\t\t\t\tdata-testid=\"cn-banner-widget-text\"\n\t\t\t\t@click=\"onClick\">\n\t\t\t\t{{ displayText }}\n\t\t\t</component>\n\t\t</NcNoteCard>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcNoteCard } from '@nextcloud/vue'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport { fillTemplate } from '../../utils/fillTemplate.js'\nimport { followLinkClick, resolveHref } from '../../utils/linkNavigation.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { safeHref } from '../../utils/safeHref.js'\nimport { nextUid } from '../../utils/uid.js'\nimport { compareVisibleWhen, readVisibleWhenValue } from '../../utils/visibleWhen.js'\n\n/** Variants understood by NcNoteCard. */\nconst VARIANTS = ['info', 'warning', 'error', 'success']\n\n/** Layouts: the note card (default) or the attention card. */\nconst LAYOUTS = ['banner', 'attention']\n\n/** An attention card offers a primary and a secondary action, no more. */\nconst MAX_ACTIONS = 2\n\n/**\n * Under the board look the card takes what the boards draw: DqMijnWerk's\n * \"Vandaag eerst\" has three secondary actions and the primary one.\n */\nconst MAX_ACTIONS_BOARD = 4\n\n/**\n * CnBannerWidget — declarative notice banner for dashboards and v2 pages\n * (`banner` widget type, Wave 1 of nextcloud-vue#91).\n *\n * Renders a themed NcNoteCard with a variant (`info | warning | error`),\n * a text, an optional click-through route, and an optional `visibleWhen`\n * condition: a simple `{ field, op, value }` predicate evaluated against\n * an endpoint response or an OpenRegister source (the doriath\n * migration-banner case — \"show while `pending > 0`\").\n *\n * Config arrives either as flat props (v2 grid spreads `props`) or as a\n * stored `content` blob (CnDashboardPage's registry branch) — explicit\n * flat props win on collision.\n *\n * ```json\n * {\n * \"widgetKey\": \"banner\",\n * \"props\": {\n * \"variant\": \"warning\",\n * \"text\": \"Migrations pending — open the migration overview.\",\n * \"visibleWhen\": { \"endpoint\": \"/apps/doriath/api/migrations/status\", \"field\": \"pending\", \"op\": \"gt\", \"value\": 0 },\n * \"route\": \"migrations\"\n * }\n * }\n * ```\n *\n * Fail-safe: with a `visibleWhen`, the banner stays HIDDEN until the\n * condition evaluates true — a failed fetch never breaks (or spams) a\n * dashboard.\n *\n * An attention card (`layout: \"attention\"`) whose `visibleWhen` request\n * FAILS renders one quiet line instead, \"Could not check\", with the reason as\n * a tooltip. A hidden card reads as \"nothing needs attention\", and a failed\n * count does not know that. A request that succeeds with a value that does\n * not meet the condition still renders nothing.\n *\n * `layout: \"attention\"` turns the banner into an attention card: a card with\n * a coloured edge by severity, a `kicker` label, a `title`, a `reason` line\n * and up to two `actions` (the first is the primary one unless an action says\n * `primary` itself). `visibleWhen` works the same way. Without `layout` the\n * note card above renders, as it always did.\n *\n * ```json\n * {\n * \"widgetKey\": \"banner\",\n * \"props\": {\n * \"layout\": \"attention\",\n * \"variant\": \"error\",\n * \"kicker\": \"First today\",\n * \"title\": \"Parking permits city centre\",\n * \"reason\": \"The deadline ends today.\",\n * \"actions\": [\n * { \"label\": \"Open case\", \"route\": { \"name\": \"CaseDetail\", \"params\": { \"id\": \"2026-0061\" } } },\n * { \"label\": \"Suspend deadline\", \"route\": \"CaseSuspend\" }\n * ]\n * }\n * }\n * ```\n */\nexport default {\n\tname: 'CnBannerWidget',\n\n\tcomponents: { NcNoteCard },\n\n\tinject: {\n\t\t/**\n\t\t * The host's label lookup (CnAppRoot provides it); identity without one, so a\n\t\t * label that is not a key renders as written.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The app's look, provided by CnAppRoot or CnPageRenderer (`nextcloud` or `board`). */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Banner severity variant: `info | warning | error` (plus `success`\n\t\t * for completeness — NcNoteCard's set). Empty falls back to the\n\t\t * `content` blob, then `info`.\n\t\t */\n\t\tvariant: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t\tvalidator: (v) => v === '' || VARIANTS.includes(v),\n\t\t},\n\n\t\t/** Pre-translated banner text. Empty falls back to the `content` blob. */\n\t\ttext: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Optional visibility condition. Shape:\n\t\t * `{ endpoint?, source?, field?, op?, value }` — exactly one of\n\t\t * `endpoint` (a same-origin URL returning JSON) or `source`\n\t\t * (`{ register, schema, filter? }`, an OpenRegister object query whose\n\t\t * `filter` supports the shared @-token grammar). `field` is a\n\t\t * dot-path into the response (for a `source`, into the first result;\n\t\t * omit it — or use `@total` — to compare the collection total).\n\t\t * `op` is `eq | neq | gt | gte | lt | lte` (default `eq`); `value` is\n\t\t * the literal right-hand side. `null` (the default) shows the banner\n\t\t * unconditionally.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tvisibleWhen: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Optional click-through route: a vue-router route NAME (string) or\n\t\t * a full location object. When set, the banner text is a link to that\n\t\t * route. `null` renders static text.\n\t\t *\n\t\t * @type {string|object|null}\n\t\t */\n\t\troute: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * How the banner renders: `banner` (the note card, default) or\n\t\t * `attention` (a card with a severity edge, kicker, title, reason and\n\t\t * up to two actions). Empty falls back to the `content` blob, then\n\t\t * `banner`.\n\t\t *\n\t\t * @type {''|'banner'|'attention'}\n\t\t */\n\t\tlayout: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t\tvalidator: (v) => v === '' || LAYOUTS.includes(v),\n\t\t},\n\n\t\t/** Attention card: the small label above the title (\"First today\"). */\n\t\tkicker: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Attention card: the title. Falls back to `text` when empty. */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Attention card: the line under the title that says why this needs attention. `{value}` is replaced like in `text`. */\n\t\treason: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Attention card: its actions, two at most (four under the board look,\n\t\t * or `maxActions`). Each is `{ label, route?,\n\t\t * href?, primary?, id? }`: `route` is a route name or location,\n\t\t * `href` an URL. An action with neither renders a button that emits\n\t\t * `action`. The first action is the primary one unless one sets\n\t\t * `primary: true`.\n\t\t *\n\t\t * @type {Array<{label: string, route?: (string|object), href?: string, primary?: boolean, id?: string}>|null}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Attention card: how many actions it shows at most. Null takes the\n\t\t * default: two, four under the board look.\n\t\t *\n\t\t * @spec openspec/changes/screens-banner-actions-parity/specs/banner-actions/spec.md#requirement-the-board-attention-card-shows-the-boards-actions\n\t\t */\n\t\tmaxActions: {\n\t\t\ttype: Number,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Attention card: stack the actions under the text instead of beside it,\n\t\t * the \"Begin hier\" card of PqMijnWerk (16px title, the action under the\n\t\t * reason). Falls back to the `content` blob.\n\t\t *\n\t\t * @spec openspec/changes/screens-start-here-banner/specs/start-here-banner/spec.md#requirement-a-card-can-stack-its-action-under-the-text\n\t\t */\n\t\tstacked: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Stored content blob (CnDashboardPage registry branch) carrying the\n\t\t * same keys as the flat props: `{ variant, text, visibleWhen, route,\n\t\t * layout, kicker, title, reason, actions }`. Explicit flat props win\n\t\t * on collision.\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Pre-evaluated `visibleWhen` outcome `{ met, value }`, injected by a\n\t\t * host that already ran the predicate (CnDashboardPage evaluates it\n\t\t * to know whether to collapse the banner's grid cell). When present,\n\t\t * the banner renders from this verdict instead of fetching again —\n\t\t * no duplicate request and no hidden-until-self-evaluated flash —\n\t\t * and `value` feeds the `{value}` text placeholder. `null` (the\n\t\t * default) keeps the banner self-evaluating. A host whose request\n\t\t * failed passes `{ met: false, value: null, failed: true, reason }`,\n\t\t * and an attention card then says it could not check.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tconditionOutcome: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted when an attention-card action without a `route` or `href`\n\t\t * is clicked, so the host can run it.\n\t\t *\n\t\t * @event action\n\t\t * @type {{id: (string|undefined), label: string, index: number}}\n\t\t */\n\t\t'action',\n\t],\n\n\t/**\n\t * The banner's own state: the evaluated condition, the value it read,\n\t * and whether the request for it failed.\n\t *\n\t * @return {object} The state.\n\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t */\n\tdata() {\n\t\treturn {\n\t\t\t/** Id that ties the attention card's region to its title. */\n\t\t\theadingId: `cn-banner-title-${nextUid()}`,\n\t\t\t/** Evaluated visibleWhen outcome (null = not yet evaluated). */\n\t\t\tconditionMet: null,\n\t\t\t/**\n\t\t\t * The raw field value the predicate read (from the injected\n\t\t\t * outcome or the banner's own evaluation) — interpolated into\n\t\t\t * the text wherever it says `{value}`.\n\t\t\t */\n\t\t\tconditionValue: null,\n\t\t\t/**\n\t\t\t * The first row of the `visibleWhen` source query (its `order`\n\t\t\t * applied), the card's subject: `{subject.<field>}` in the texts\n\t\t\t * and `@subject.<field>` in an action route read it.\n\t\t\t */\n\t\t\tconditionRow: null,\n\t\t\t/** Whether the visibleWhen request itself failed (not: answered \"no\"). */\n\t\t\tconditionFailed: false,\n\t\t\t/** Why it failed, as the request reported it. */\n\t\t\tconditionFailureReason: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** The effective variant (prop → content → 'info'). */\n\t\tresolvedVariant() {\n\t\t\tconst v = this.variant || (this.content && this.content.variant) || 'info'\n\t\t\treturn VARIANTS.includes(v) ? v : 'info'\n\t\t},\n\n\t\t/** The effective banner text (prop → content → ''). */\n\t\tresolvedText() {\n\t\t\treturn this.text || (this.content && this.content.text) || ''\n\t\t},\n\n\t\t/** The effective visibleWhen condition (prop → content → null). */\n\t\tresolvedVisibleWhen() {\n\t\t\treturn this.visibleWhen || (this.content && this.content.visibleWhen) || null\n\t\t},\n\n\t\t/** The effective injected outcome (prop → content → null). */\n\t\tresolvedConditionOutcome() {\n\t\t\treturn this.conditionOutcome || (this.content && this.content.conditionOutcome) || null\n\t\t},\n\n\t\t/**\n\t\t * The rendered text: `resolvedText` with `{value}` replaced by the\n\t\t * predicate's field value once one is known. Without a known value\n\t\t * the text renders as written — a `{value}` placeholder only makes\n\t\t * sense on a conditional banner, which stays hidden until its\n\t\t * predicate (and therefore its value) resolves.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tdisplayText() {\n\t\t\treturn this.fillValue(this.resolvedText)\n\t\t},\n\n\t\t/** The effective click-through route (prop → content → null). */\n\t\tresolvedRoute() {\n\t\t\treturn this.route || (this.content && this.content.route) || null\n\t\t},\n\n\t\t/**\n\t\t * Whether the attention card renders instead of the note card.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tisAttention() {\n\t\t\treturn (this.layout || (this.content && this.content.layout) || 'banner') === 'attention'\n\t\t},\n\n\t\t/**\n\t\t * Whether the actions sit under the text.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisStacked() {\n\t\t\treturn this.stacked || Boolean(this.content && this.content.stacked)\n\t\t},\n\n\t\t/**\n\t\t * The attention card's kicker (prop, else content).\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tresolvedKicker() {\n\t\t\tconst kicker = this.kicker || (this.content && this.content.kicker) || ''\n\t\t\treturn kicker ? this.fillValue(this.tl(kicker)) : ''\n\t\t},\n\n\t\t/**\n\t\t * The attention card's title: `title`, else `text`, with `{value}` filled in.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tdisplayTitle() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\treturn title ? this.fillValue(this.tl(title)) : this.displayText\n\t\t},\n\n\t\t/**\n\t\t * The attention card's reason line. When the title already used\n\t\t * `text`, the text is not repeated here.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tdisplayReason() {\n\t\t\tconst reason = this.reason || (this.content && this.content.reason) || ''\n\t\t\tif (reason) {\n\t\t\t\treturn this.fillValue(this.tl(reason))\n\t\t\t}\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\t// A card that sets `text` to the same words as its `title` says\n\t\t\t// them once.\n\t\t\treturn title && this.displayText !== this.displayTitle ? this.displayText : ''\n\t\t},\n\n\t\t/**\n\t\t * The attention card's actions: the first `actionCap` that have a label, each\n\t\t * with its href and whether it is the primary one.\n\t\t *\n\t\t * @return {Array<{key: string, id: (string|undefined), label: string, href: string, target: (object|null), primary: boolean, index: number}>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\t/**\n\t\t * How many actions the attention card shows: `maxActions` (prop, then\n\t\t * content), else four under the board look and two without it.\n\t\t *\n\t\t * @return {number} The cap.\n\t\t * @spec openspec/changes/screens-banner-actions-parity/specs/banner-actions/spec.md#requirement-the-board-attention-card-shows-the-boards-actions\n\t\t */\n\t\tactionCap() {\n\t\t\tconst explicit = this.maxActions ?? (this.content ? this.content.maxActions : undefined)\n\t\t\tif (Number.isInteger(explicit) && explicit > 0) {\n\t\t\t\treturn explicit\n\t\t\t}\n\t\t\treturn normalizeLook(this.cnLook) === 'board' ? MAX_ACTIONS_BOARD : MAX_ACTIONS\n\t\t},\n\n\t\tresolvedActions() {\n\t\t\tconst raw = this.actions || (this.content && this.content.actions) || []\n\t\t\tif (!Array.isArray(raw)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst usable = raw\n\t\t\t\t.filter((action) => action && typeof action === 'object' && typeof action.label === 'string' && action.label !== '')\n\t\t\t\t.slice(0, this.actionCap)\n\t\t\tconst declaredPrimary = usable.findIndex((action) => action.primary === true)\n\t\t\tconst primaryIndex = declaredPrimary === -1 ? 0 : declaredPrimary\n\t\t\treturn usable.map((action, index) => {\n\t\t\t\tlet target = null\n\t\t\t\tlet href = ''\n\t\t\t\tif (action.route) {\n\t\t\t\t\ttarget = typeof action.route === 'string' ? { name: action.route } : this.resolveSubject(action.route)\n\t\t\t\t\thref = resolveHref(target, this.$router)\n\t\t\t\t} else if (typeof action.href === 'string' && action.href !== '') {\n\t\t\t\t\tconst safe = safeHref(action.href)\n\t\t\t\t\thref = safe === '#' ? '' : safe\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tkey: `${index}-${action.id || action.label}`,\n\t\t\t\t\tid: action.id,\n\t\t\t\t\tlabel: this.tl(action.label),\n\t\t\t\t\thref,\n\t\t\t\t\ttarget,\n\t\t\t\t\tprimary: index === primaryIndex,\n\t\t\t\t\tindex,\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Whether the banner renders: no condition = always; else the evaluated outcome.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tvisible() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\tif (this.resolvedText === '' && !(this.isAttention && title !== '')) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (!this.resolvedVisibleWhen) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn this.conditionMet === true\n\t\t},\n\n\t\t/**\n\t\t * Whether to say the check failed: an attention card, with words to\n\t\t * show, whose `visibleWhen` request failed. A plain banner stays\n\t\t * hidden on failure, as it always did.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tcheckFailed() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\treturn this.isAttention\n\t\t\t\t&& !!this.resolvedVisibleWhen\n\t\t\t\t&& this.conditionFailed\n\t\t\t\t&& (title !== '' || this.resolvedText !== '')\n\t\t},\n\n\t\t/**\n\t\t * The one line shown when the check failed. It names the card by its\n\t\t * title, unless the title needs the value that could not be read.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tuncheckedText() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || this.resolvedText\n\t\t\tif (!title || title.includes('{value}') || title.includes('{subject.')) {\n\t\t\t\treturn t('nextcloud-vue', 'Could not check')\n\t\t\t}\n\t\t\t// `escape: false`: the result is rendered through `{{ }}`, which escapes.\n\t\t\treturn t('nextcloud-vue', 'Could not check: {subject}', { subject: title }, undefined, { escape: false })\n\t\t},\n\n\t\t/**\n\t\t * The reason the check failed, for the tooltip and for assistive\n\t\t * technology.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tfailureTooltip() {\n\t\t\tif (!this.conditionFailureReason) {\n\t\t\t\treturn t('nextcloud-vue', 'The check failed.')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'The check failed: {reason}', { reason: this.conditionFailureReason }, undefined, { escape: false })\n\t\t},\n\n\t\t/** Whether the banner navigates on click (route set + router present). */\n\t\tclickable() {\n\t\t\treturn !!this.resolvedRoute && !!this.$router\n\t\t},\n\n\t\t/** The click-through route as a router location (a string is a route name). */\n\t\trouteLocation() {\n\t\t\tconst route = this.resolvedRoute\n\t\t\treturn typeof route === 'string' ? { name: route } : route\n\t\t},\n\n\t\t/** The href of the click-through link. */\n\t\trouteHref() {\n\t\t\treturn resolveHref(this.routeLocation, this.$router)\n\t\t},\n\t},\n\n\twatch: {\n\t\tresolvedVisibleWhen: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tthis.evaluateCondition()\n\t\t\t},\n\t\t},\n\n\t\tresolvedConditionOutcome() {\n\t\t\tthis.evaluateCondition()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * A manifest string through the host's label lookup (`cnTranslate`), so a key\n\t\t * such as \"Mine\" reads in the user's language.\n\t\t *\n\t\t * @param {string} text The text as written in the manifest.\n\t\t * @return {string} The translated text.\n\t\t */\n\t\ttl(text) {\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/**\n\t\t * Replace `{value}` in a text by the value the predicate read, once\n\t\t * one is known.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The text with the value filled in.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tfillValue(text) {\n\t\t\tlet out = text\n\t\t\tif (this.conditionValue !== null && out.includes('{value}')) {\n\t\t\t\tout = out.replaceAll('{value}', String(this.conditionValue))\n\t\t\t}\n\t\t\treturn this.fillSubject(out)\n\t\t},\n\n\t\t/**\n\t\t * Fill `{subject.<field>}` (and `{subject.<field>|date}`, `|days`) from\n\t\t * the card's subject row. A token with no value empties the whole text,\n\t\t * as in a record list. Text without a subject token is untouched.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The filled text.\n\t\t * @spec openspec/changes/screens-start-here-banner/specs/start-here-banner/spec.md#requirement-a-banner-can-name-the-first-row-of-a-query\n\t\t */\n\t\tfillSubject(text) {\n\t\t\tif (typeof text !== 'string' || !text.includes('{subject.')) {\n\t\t\t\treturn text\n\t\t\t}\n\t\t\t// `{value}` is not a subject token: keep it out of the template fill.\n\t\t\tconst guard = '\\u0000value\\u0000'\n\t\t\tconst filled = fillTemplate(text.replaceAll('{value}', guard), { subject: this.conditionRow || {} })\n\t\t\treturn filled.replaceAll(guard, '{value}')\n\t\t},\n\n\t\t/**\n\t\t * Replace `@subject.<field>` strings anywhere in an action route\n\t\t * (params, query) by the subject's value.\n\t\t *\n\t\t * @param {unknown} node A route name, location or any nested part of one.\n\t\t * @return {unknown} The same shape with the tokens resolved.\n\t\t * @spec openspec/changes/screens-start-here-banner/specs/start-here-banner/spec.md#requirement-a-banner-can-name-the-first-row-of-a-query\n\t\t */\n\t\tresolveSubject(node) {\n\t\t\tif (typeof node === 'string') {\n\t\t\t\tif (!node.startsWith('@subject.')) {\n\t\t\t\t\treturn node\n\t\t\t\t}\n\t\t\t\tconst value = this.conditionRow ? readPath(this.conditionRow, node.slice('@subject.'.length)) : undefined\n\t\t\t\treturn value === undefined || value === null ? '' : String(value)\n\t\t\t}\n\t\t\tif (Array.isArray(node)) {\n\t\t\t\treturn node.map((item) => this.resolveSubject(item))\n\t\t\t}\n\t\t\tif (node && typeof node === 'object') {\n\t\t\t\treturn Object.fromEntries(Object.entries(node).map(([key, value]) => [key, this.resolveSubject(value)]))\n\t\t\t}\n\t\t\treturn node\n\t\t},\n\n\t\t/**\n\t\t * Run an attention-card action: route an in-app link, leave an\n\t\t * external link to the browser, and emit `action` for a button.\n\t\t *\n\t\t * @param {MouseEvent} event The click.\n\t\t * @param {object} action The resolved action.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tonActionClick(event, action) {\n\t\t\tif (action.target) {\n\t\t\t\tfollowLinkClick(event, action.target, this.$router)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!action.href) {\n\t\t\t\tthis.$emit('action', { id: action.id, label: action.label, index: action.index })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve the `visibleWhen` verdict. A host-injected\n\t\t * `conditionOutcome` wins outright — the host already ran the\n\t\t * predicate, so re-fetching would double the request and flash the\n\t\t * banner hidden until its own copy resolved. Otherwise evaluate\n\t\t * through the shared util primitives (endpoint / OpenRegister-source\n\t\t * modes — the Wave-1 banner shape is the canonical one, extracted to\n\t\t * `utils/visibleWhen.js` in Wave 3 so manifest actions reuse it),\n\t\t * keeping the read VALUE for the `{value}` text placeholder.\n\t\t * Fail-safe: any fetch/shape error leaves the banner hidden. The\n\t\t * failure is kept (`conditionFailed`, with its reason) so an\n\t\t * attention card can say it could not check.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tasync evaluateCondition() {\n\t\t\tconst outcome = this.resolvedConditionOutcome\n\t\t\tif (outcome) {\n\t\t\t\tthis.conditionMet = outcome.met === true\n\t\t\t\tthis.conditionValue = outcome.value !== undefined ? outcome.value : null\n\t\t\t\tthis.conditionRow = outcome.row && typeof outcome.row === 'object' ? outcome.row : null\n\t\t\t\tthis.conditionFailed = outcome.failed === true\n\t\t\t\tthis.conditionFailureReason = outcome.failed === true && typeof outcome.reason === 'string' ? outcome.reason : ''\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst cond = this.resolvedVisibleWhen\n\t\t\tif (!cond) {\n\t\t\t\tthis.conditionMet = null\n\t\t\t\tthis.conditionValue = null\n\t\t\t\tthis.conditionRow = null\n\t\t\t\tthis.conditionFailed = false\n\t\t\t\tthis.conditionFailureReason = ''\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst sink = {}\n\t\t\t\tconst value = await readVisibleWhenValue(cond, undefined, sink)\n\t\t\t\tthis.conditionRow = sink.row || null\n\t\t\t\tthis.conditionValue = value\n\t\t\t\tthis.conditionMet = compareVisibleWhen(value, cond.op || 'eq', cond.value)\n\t\t\t\tthis.conditionFailed = false\n\t\t\t\tthis.conditionFailureReason = ''\n\t\t\t} catch (error) {\n\t\t\t\t// Still hidden: a banner that cannot tell whether it applies\n\t\t\t\t// should not claim attention. But not silently, because a\n\t\t\t\t// failed count and \"nothing to report\" look the same on screen.\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnBannerWidget] visibleWhen could not be evaluated, banner hidden:', error?.message || error)\n\t\t\t\tthis.conditionMet = false\n\t\t\t\tthis.conditionValue = null\n\t\t\t\tthis.conditionRow = null\n\t\t\t\tthis.conditionFailed = true\n\t\t\t\tthis.conditionFailureReason = typeof error?.message === 'string' ? error.message : String(error ?? '')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Route a plain click on the link; a modified click opens the href\n\t\t * in the browser.\n\t\t *\n\t\t * @param {MouseEvent} event The click event.\n\t\t * @return {void}\n\t\t */\n\t\tonClick(event) {\n\t\t\tif (!this.clickable) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfollowLinkClick(event, this.routeLocation, this.$router)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-banner-widget {\n\twidth: 100%;\n}\n\n.cn-banner-widget__card {\n\tmargin: 0;\n}\n\n.cn-banner-widget__text--clickable {\n\tcursor: pointer;\n\ttext-decoration: underline;\n\tcolor: inherit;\n}\n\n/* The check failed. Muted text, no fill, no severity colour: it is a note\n that something is not known, not a warning. */\n.cn-banner-widget--unchecked {\n\tbox-sizing: border-box;\n\tmargin: 0;\n\tpadding: 8px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tcursor: help;\n}\n\n/* The reason, for assistive technology; sighted readers get the tooltip. */\n.cn-banner-widget__unchecked-reason {\n\tposition: absolute;\n\twidth: 1px;\n\theight: 1px;\n\tmargin: -1px;\n\tpadding: 0;\n\toverflow: hidden;\n\tclip-path: inset(50%);\n\twhite-space: nowrap;\n\tborder: 0;\n}\n\n/* Attention card. The severity edge is an inset shadow, so it takes no width. */\n.cn-banner-widget--attention {\n\t--cn-banner-accent: var(--color-primary-element);\n\t--cn-banner-accent-text: var(--color-primary-element);\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 16px 24px;\n\tbox-sizing: border-box;\n\tpadding: 20px 24px 20px 28px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-container, var(--border-radius-large, 12px));\n\tbackground: var(--color-main-background);\n\tbox-shadow: inset 4px 0 0 0 var(--cn-banner-accent);\n}\n\n.cn-banner-widget--attention.cn-banner-widget--warning {\n\t--cn-banner-accent: var(--color-element-warning, var(--color-warning));\n\t--cn-banner-accent-text: var(--color-text-warning, var(--color-warning-text, var(--color-main-text)));\n}\n\n.cn-banner-widget--attention.cn-banner-widget--error {\n\t--cn-banner-accent: var(--color-element-error, var(--color-error));\n\t--cn-banner-accent-text: var(--color-text-error, var(--color-error-text));\n}\n\n.cn-banner-widget--attention.cn-banner-widget--success {\n\t--cn-banner-accent: var(--color-element-success, var(--color-success));\n\t--cn-banner-accent-text: var(--color-text-success, var(--color-success-text));\n}\n\n.cn-banner-widget__body {\n\tdisplay: flex;\n\tflex: 1 1 320px;\n\tflex-direction: column;\n\tgap: 6px;\n\tmin-width: 0;\n}\n\n.cn-banner-widget__kicker {\n\tmargin: 0;\n\tfont-size: 0.85em;\n\tfont-weight: 700;\n\tletter-spacing: 0.06em;\n\ttext-transform: uppercase;\n\tcolor: var(--cn-banner-accent-text);\n}\n\n.cn-banner-widget__title {\n\tmargin: 0;\n\tfont-size: 1.3em;\n\tfont-weight: 700;\n\tline-height: 1.3;\n\tcolor: var(--color-main-text);\n}\n\n.cn-banner-widget__reason {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* Stacked (PqMijnWerk \"Begin hier\"): the text, then the action under it. */\n.cn-banner-widget--stacked {\n\tflex-direction: column;\n\talign-items: flex-start;\n\tgap: 10px;\n}\n\n.cn-banner-widget--stacked .cn-banner-widget__body {\n\tflex: 0 0 auto;\n\tgap: 10px;\n}\n\n.cn-banner-widget--stacked .cn-banner-widget__title {\n\tfont-size: 16px;\n\tline-height: 1.4;\n}\n\n.cn-banner-widget--stacked .cn-banner-widget__reason {\n\tfont-size: 14px;\n}\n\n.cn-banner-widget--stacked .cn-banner-widget__action {\n\tmin-height: var(--cn-board-control-height, 40px);\n\tfont-size: 14px;\n}\n\n.cn-banner-widget__actions {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 10px;\n}\n\n.cn-banner-widget__action {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\tbox-sizing: border-box;\n\tmin-height: var(--default-clickable-area, 40px);\n\tmargin: 0;\n\tpadding: 0 16px;\n\tborder: 1px solid var(--color-border-maxcontrast);\n\tborder-radius: var(--border-radius-element, var(--border-radius-large, 8px));\n\tfont: inherit;\n\tfont-weight: 600;\n\ttext-decoration: none;\n\tcursor: pointer;\n}\n\n.cn-banner-widget__action--secondary {\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n}\n\n.cn-banner-widget__action--secondary:hover {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-banner-widget__action--primary {\n\tborder-color: var(--color-primary-element);\n\tbackground: var(--color-primary-element);\n\tcolor: var(--color-primary-element-text);\n}\n\n.cn-banner-widget__action--primary:hover {\n\tborder-color: var(--color-primary-element-hover);\n\tbackground: var(--color-primary-element-hover);\n}\n\n.cn-banner-widget__action:focus-visible {\n\toutline: 2px solid var(--color-main-text);\n\toutline-offset: 2px;\n}\n</style>\n"],"names":["t"],"mappings":"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"}
@@ -29,7 +29,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
29
29
  return ($options.visible && $options.isAttention)
30
30
  ? (openBlock(), createElementBlock("section", {
31
31
  key: 0,
32
- class: normalizeClass(["cn-banner-widget cn-banner-widget--attention", 'cn-banner-widget--' + $options.resolvedVariant]),
32
+ class: normalizeClass(["cn-banner-widget cn-banner-widget--attention", ['cn-banner-widget--' + $options.resolvedVariant, { 'cn-banner-widget--stacked': $options.isStacked }]]),
33
33
  "aria-labelledby": $data.headingId,
34
34
  "data-testid": "cn-banner-widget-attention"
35
35
  }, [
@@ -1 +1 @@
1
- {"version":3,"file":"CnBannerWidget.vue3.js","sources":["../../../../src/components/CnBannerWidget/CnBannerWidget.vue?vue&type=template&id=3531692f&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<section\n\t\tv-if=\"visible && isAttention\"\n\t\tclass=\"cn-banner-widget cn-banner-widget--attention\"\n\t\t:class=\"'cn-banner-widget--' + resolvedVariant\"\n\t\t:aria-labelledby=\"headingId\"\n\t\tdata-testid=\"cn-banner-widget-attention\">\n\t\t<div class=\"cn-banner-widget__body\">\n\t\t\t<p v-if=\"resolvedKicker\" class=\"cn-banner-widget__kicker\">\n\t\t\t\t{{ resolvedKicker }}\n\t\t\t</p>\n\t\t\t<h3 :id=\"headingId\" class=\"cn-banner-widget__title\">\n\t\t\t\t{{ displayTitle }}\n\t\t\t</h3>\n\t\t\t<p v-if=\"displayReason\" class=\"cn-banner-widget__reason\">\n\t\t\t\t{{ displayReason }}\n\t\t\t</p>\n\t\t</div>\n\t\t<div v-if=\"resolvedActions.length > 0\" class=\"cn-banner-widget__actions\">\n\t\t\t<component\n\t\t\t\t:is=\"action.href ? 'a' : 'button'\"\n\t\t\t\tv-for=\"action in resolvedActions\"\n\t\t\t\t:key=\"action.key\"\n\t\t\t\tclass=\"cn-banner-widget__action\"\n\t\t\t\t:class=\"action.primary ? 'cn-banner-widget__action--primary' : 'cn-banner-widget__action--secondary'\"\n\t\t\t\t:href=\"action.href || null\"\n\t\t\t\t:type=\"action.href ? null : 'button'\"\n\t\t\t\tdata-testid=\"cn-banner-widget-action\"\n\t\t\t\t@click=\"onActionClick($event, action)\">\n\t\t\t\t{{ action.label }}\n\t\t\t</component>\n\t\t</div>\n\t</section>\n\t<!-- The count could not be read. One quiet line, not the card and not an\n\t alarm: hidden would read as \"nothing to report\", which is not known. -->\n\t<p\n\t\tv-else-if=\"checkFailed\"\n\t\tclass=\"cn-banner-widget cn-banner-widget--unchecked\"\n\t\t:title=\"failureTooltip\"\n\t\tdata-testid=\"cn-banner-widget-unchecked\">\n\t\t<span class=\"cn-banner-widget__unchecked-text\">{{ uncheckedText }}</span>\n\t\t<span class=\"cn-banner-widget__unchecked-reason\">{{ failureTooltip }}</span>\n\t</p>\n\t<div v-else-if=\"visible\" class=\"cn-banner-widget\">\n\t\t<NcNoteCard :type=\"resolvedVariant\" class=\"cn-banner-widget__card\">\n\t\t\t<component\n\t\t\t\t:is=\"clickable ? 'a' : 'span'\"\n\t\t\t\tclass=\"cn-banner-widget__text\"\n\t\t\t\t:class=\"{ 'cn-banner-widget__text--clickable': clickable }\"\n\t\t\t\t:href=\"clickable ? routeHref : null\"\n\t\t\t\tdata-testid=\"cn-banner-widget-text\"\n\t\t\t\t@click=\"onClick\">\n\t\t\t\t{{ displayText }}\n\t\t\t</component>\n\t\t</NcNoteCard>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcNoteCard } from '@nextcloud/vue'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport { followLinkClick, resolveHref } from '../../utils/linkNavigation.js'\nimport { safeHref } from '../../utils/safeHref.js'\nimport { nextUid } from '../../utils/uid.js'\nimport { compareVisibleWhen, readVisibleWhenValue } from '../../utils/visibleWhen.js'\n\n/** Variants understood by NcNoteCard. */\nconst VARIANTS = ['info', 'warning', 'error', 'success']\n\n/** Layouts: the note card (default) or the attention card. */\nconst LAYOUTS = ['banner', 'attention']\n\n/** An attention card offers a primary and a secondary action, no more. */\nconst MAX_ACTIONS = 2\n\n/**\n * Under the board look the card takes what the boards draw: DqMijnWerk's\n * \"Vandaag eerst\" has three secondary actions and the primary one.\n */\nconst MAX_ACTIONS_BOARD = 4\n\n/**\n * CnBannerWidget — declarative notice banner for dashboards and v2 pages\n * (`banner` widget type, Wave 1 of nextcloud-vue#91).\n *\n * Renders a themed NcNoteCard with a variant (`info | warning | error`),\n * a text, an optional click-through route, and an optional `visibleWhen`\n * condition: a simple `{ field, op, value }` predicate evaluated against\n * an endpoint response or an OpenRegister source (the doriath\n * migration-banner case — \"show while `pending > 0`\").\n *\n * Config arrives either as flat props (v2 grid spreads `props`) or as a\n * stored `content` blob (CnDashboardPage's registry branch) — explicit\n * flat props win on collision.\n *\n * ```json\n * {\n * \"widgetKey\": \"banner\",\n * \"props\": {\n * \"variant\": \"warning\",\n * \"text\": \"Migrations pending — open the migration overview.\",\n * \"visibleWhen\": { \"endpoint\": \"/apps/doriath/api/migrations/status\", \"field\": \"pending\", \"op\": \"gt\", \"value\": 0 },\n * \"route\": \"migrations\"\n * }\n * }\n * ```\n *\n * Fail-safe: with a `visibleWhen`, the banner stays HIDDEN until the\n * condition evaluates true — a failed fetch never breaks (or spams) a\n * dashboard.\n *\n * An attention card (`layout: \"attention\"`) whose `visibleWhen` request\n * FAILS renders one quiet line instead, \"Could not check\", with the reason as\n * a tooltip. A hidden card reads as \"nothing needs attention\", and a failed\n * count does not know that. A request that succeeds with a value that does\n * not meet the condition still renders nothing.\n *\n * `layout: \"attention\"` turns the banner into an attention card: a card with\n * a coloured edge by severity, a `kicker` label, a `title`, a `reason` line\n * and up to two `actions` (the first is the primary one unless an action says\n * `primary` itself). `visibleWhen` works the same way. Without `layout` the\n * note card above renders, as it always did.\n *\n * ```json\n * {\n * \"widgetKey\": \"banner\",\n * \"props\": {\n * \"layout\": \"attention\",\n * \"variant\": \"error\",\n * \"kicker\": \"First today\",\n * \"title\": \"Parking permits city centre\",\n * \"reason\": \"The deadline ends today.\",\n * \"actions\": [\n * { \"label\": \"Open case\", \"route\": { \"name\": \"CaseDetail\", \"params\": { \"id\": \"2026-0061\" } } },\n * { \"label\": \"Suspend deadline\", \"route\": \"CaseSuspend\" }\n * ]\n * }\n * }\n * ```\n */\nexport default {\n\tname: 'CnBannerWidget',\n\n\tcomponents: { NcNoteCard },\n\n\tinject: {\n\t\t/**\n\t\t * The host's label lookup (CnAppRoot provides it); identity without one, so a\n\t\t * label that is not a key renders as written.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The app's look, provided by CnAppRoot or CnPageRenderer (`nextcloud` or `board`). */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Banner severity variant: `info | warning | error` (plus `success`\n\t\t * for completeness — NcNoteCard's set). Empty falls back to the\n\t\t * `content` blob, then `info`.\n\t\t */\n\t\tvariant: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t\tvalidator: (v) => v === '' || VARIANTS.includes(v),\n\t\t},\n\n\t\t/** Pre-translated banner text. Empty falls back to the `content` blob. */\n\t\ttext: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Optional visibility condition. Shape:\n\t\t * `{ endpoint?, source?, field?, op?, value }` — exactly one of\n\t\t * `endpoint` (a same-origin URL returning JSON) or `source`\n\t\t * (`{ register, schema, filter? }`, an OpenRegister object query whose\n\t\t * `filter` supports the shared @-token grammar). `field` is a\n\t\t * dot-path into the response (for a `source`, into the first result;\n\t\t * omit it — or use `@total` — to compare the collection total).\n\t\t * `op` is `eq | neq | gt | gte | lt | lte` (default `eq`); `value` is\n\t\t * the literal right-hand side. `null` (the default) shows the banner\n\t\t * unconditionally.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tvisibleWhen: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Optional click-through route: a vue-router route NAME (string) or\n\t\t * a full location object. When set, the banner text is a link to that\n\t\t * route. `null` renders static text.\n\t\t *\n\t\t * @type {string|object|null}\n\t\t */\n\t\troute: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * How the banner renders: `banner` (the note card, default) or\n\t\t * `attention` (a card with a severity edge, kicker, title, reason and\n\t\t * up to two actions). Empty falls back to the `content` blob, then\n\t\t * `banner`.\n\t\t *\n\t\t * @type {''|'banner'|'attention'}\n\t\t */\n\t\tlayout: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t\tvalidator: (v) => v === '' || LAYOUTS.includes(v),\n\t\t},\n\n\t\t/** Attention card: the small label above the title (\"First today\"). */\n\t\tkicker: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Attention card: the title. Falls back to `text` when empty. */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Attention card: the line under the title that says why this needs attention. `{value}` is replaced like in `text`. */\n\t\treason: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Attention card: its actions, two at most (four under the board look,\n\t\t * or `maxActions`). Each is `{ label, route?,\n\t\t * href?, primary?, id? }`: `route` is a route name or location,\n\t\t * `href` an URL. An action with neither renders a button that emits\n\t\t * `action`. The first action is the primary one unless one sets\n\t\t * `primary: true`.\n\t\t *\n\t\t * @type {Array<{label: string, route?: (string|object), href?: string, primary?: boolean, id?: string}>|null}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Attention card: how many actions it shows at most. Null takes the\n\t\t * default: two, four under the board look.\n\t\t *\n\t\t * @spec openspec/changes/screens-banner-actions-parity/specs/banner-actions/spec.md#requirement-the-board-attention-card-shows-the-boards-actions\n\t\t */\n\t\tmaxActions: {\n\t\t\ttype: Number,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Stored content blob (CnDashboardPage registry branch) carrying the\n\t\t * same keys as the flat props: `{ variant, text, visibleWhen, route,\n\t\t * layout, kicker, title, reason, actions }`. Explicit flat props win\n\t\t * on collision.\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Pre-evaluated `visibleWhen` outcome `{ met, value }`, injected by a\n\t\t * host that already ran the predicate (CnDashboardPage evaluates it\n\t\t * to know whether to collapse the banner's grid cell). When present,\n\t\t * the banner renders from this verdict instead of fetching again —\n\t\t * no duplicate request and no hidden-until-self-evaluated flash —\n\t\t * and `value` feeds the `{value}` text placeholder. `null` (the\n\t\t * default) keeps the banner self-evaluating. A host whose request\n\t\t * failed passes `{ met: false, value: null, failed: true, reason }`,\n\t\t * and an attention card then says it could not check.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tconditionOutcome: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted when an attention-card action without a `route` or `href`\n\t\t * is clicked, so the host can run it.\n\t\t *\n\t\t * @event action\n\t\t * @type {{id: (string|undefined), label: string, index: number}}\n\t\t */\n\t\t'action',\n\t],\n\n\t/**\n\t * The banner's own state: the evaluated condition, the value it read,\n\t * and whether the request for it failed.\n\t *\n\t * @return {object} The state.\n\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t */\n\tdata() {\n\t\treturn {\n\t\t\t/** Id that ties the attention card's region to its title. */\n\t\t\theadingId: `cn-banner-title-${nextUid()}`,\n\t\t\t/** Evaluated visibleWhen outcome (null = not yet evaluated). */\n\t\t\tconditionMet: null,\n\t\t\t/**\n\t\t\t * The raw field value the predicate read (from the injected\n\t\t\t * outcome or the banner's own evaluation) — interpolated into\n\t\t\t * the text wherever it says `{value}`.\n\t\t\t */\n\t\t\tconditionValue: null,\n\t\t\t/** Whether the visibleWhen request itself failed (not: answered \"no\"). */\n\t\t\tconditionFailed: false,\n\t\t\t/** Why it failed, as the request reported it. */\n\t\t\tconditionFailureReason: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** The effective variant (prop → content → 'info'). */\n\t\tresolvedVariant() {\n\t\t\tconst v = this.variant || (this.content && this.content.variant) || 'info'\n\t\t\treturn VARIANTS.includes(v) ? v : 'info'\n\t\t},\n\n\t\t/** The effective banner text (prop → content → ''). */\n\t\tresolvedText() {\n\t\t\treturn this.text || (this.content && this.content.text) || ''\n\t\t},\n\n\t\t/** The effective visibleWhen condition (prop → content → null). */\n\t\tresolvedVisibleWhen() {\n\t\t\treturn this.visibleWhen || (this.content && this.content.visibleWhen) || null\n\t\t},\n\n\t\t/** The effective injected outcome (prop → content → null). */\n\t\tresolvedConditionOutcome() {\n\t\t\treturn this.conditionOutcome || (this.content && this.content.conditionOutcome) || null\n\t\t},\n\n\t\t/**\n\t\t * The rendered text: `resolvedText` with `{value}` replaced by the\n\t\t * predicate's field value once one is known. Without a known value\n\t\t * the text renders as written — a `{value}` placeholder only makes\n\t\t * sense on a conditional banner, which stays hidden until its\n\t\t * predicate (and therefore its value) resolves.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tdisplayText() {\n\t\t\treturn this.fillValue(this.resolvedText)\n\t\t},\n\n\t\t/** The effective click-through route (prop → content → null). */\n\t\tresolvedRoute() {\n\t\t\treturn this.route || (this.content && this.content.route) || null\n\t\t},\n\n\t\t/**\n\t\t * Whether the attention card renders instead of the note card.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tisAttention() {\n\t\t\treturn (this.layout || (this.content && this.content.layout) || 'banner') === 'attention'\n\t\t},\n\n\t\t/**\n\t\t * The attention card's kicker (prop, else content).\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tresolvedKicker() {\n\t\t\tconst kicker = this.kicker || (this.content && this.content.kicker) || ''\n\t\t\treturn kicker ? this.tl(kicker) : ''\n\t\t},\n\n\t\t/**\n\t\t * The attention card's title: `title`, else `text`, with `{value}` filled in.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tdisplayTitle() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\treturn title ? this.fillValue(this.tl(title)) : this.displayText\n\t\t},\n\n\t\t/**\n\t\t * The attention card's reason line. When the title already used\n\t\t * `text`, the text is not repeated here.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tdisplayReason() {\n\t\t\tconst reason = this.reason || (this.content && this.content.reason) || ''\n\t\t\tif (reason) {\n\t\t\t\treturn this.fillValue(this.tl(reason))\n\t\t\t}\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\t// A card that sets `text` to the same words as its `title` says\n\t\t\t// them once.\n\t\t\treturn title && this.displayText !== this.displayTitle ? this.displayText : ''\n\t\t},\n\n\t\t/**\n\t\t * The attention card's actions: the first `actionCap` that have a label, each\n\t\t * with its href and whether it is the primary one.\n\t\t *\n\t\t * @return {Array<{key: string, id: (string|undefined), label: string, href: string, target: (object|null), primary: boolean, index: number}>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\t/**\n\t\t * How many actions the attention card shows: `maxActions` (prop, then\n\t\t * content), else four under the board look and two without it.\n\t\t *\n\t\t * @return {number} The cap.\n\t\t * @spec openspec/changes/screens-banner-actions-parity/specs/banner-actions/spec.md#requirement-the-board-attention-card-shows-the-boards-actions\n\t\t */\n\t\tactionCap() {\n\t\t\tconst explicit = this.maxActions ?? (this.content ? this.content.maxActions : undefined)\n\t\t\tif (Number.isInteger(explicit) && explicit > 0) {\n\t\t\t\treturn explicit\n\t\t\t}\n\t\t\treturn normalizeLook(this.cnLook) === 'board' ? MAX_ACTIONS_BOARD : MAX_ACTIONS\n\t\t},\n\n\t\tresolvedActions() {\n\t\t\tconst raw = this.actions || (this.content && this.content.actions) || []\n\t\t\tif (!Array.isArray(raw)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst usable = raw\n\t\t\t\t.filter((action) => action && typeof action === 'object' && typeof action.label === 'string' && action.label !== '')\n\t\t\t\t.slice(0, this.actionCap)\n\t\t\tconst declaredPrimary = usable.findIndex((action) => action.primary === true)\n\t\t\tconst primaryIndex = declaredPrimary === -1 ? 0 : declaredPrimary\n\t\t\treturn usable.map((action, index) => {\n\t\t\t\tlet target = null\n\t\t\t\tlet href = ''\n\t\t\t\tif (action.route) {\n\t\t\t\t\ttarget = typeof action.route === 'string' ? { name: action.route } : action.route\n\t\t\t\t\thref = resolveHref(target, this.$router)\n\t\t\t\t} else if (typeof action.href === 'string' && action.href !== '') {\n\t\t\t\t\tconst safe = safeHref(action.href)\n\t\t\t\t\thref = safe === '#' ? '' : safe\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tkey: `${index}-${action.id || action.label}`,\n\t\t\t\t\tid: action.id,\n\t\t\t\t\tlabel: this.tl(action.label),\n\t\t\t\t\thref,\n\t\t\t\t\ttarget,\n\t\t\t\t\tprimary: index === primaryIndex,\n\t\t\t\t\tindex,\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Whether the banner renders: no condition = always; else the evaluated outcome.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tvisible() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\tif (this.resolvedText === '' && !(this.isAttention && title !== '')) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (!this.resolvedVisibleWhen) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn this.conditionMet === true\n\t\t},\n\n\t\t/**\n\t\t * Whether to say the check failed: an attention card, with words to\n\t\t * show, whose `visibleWhen` request failed. A plain banner stays\n\t\t * hidden on failure, as it always did.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tcheckFailed() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\treturn this.isAttention\n\t\t\t\t&& !!this.resolvedVisibleWhen\n\t\t\t\t&& this.conditionFailed\n\t\t\t\t&& (title !== '' || this.resolvedText !== '')\n\t\t},\n\n\t\t/**\n\t\t * The one line shown when the check failed. It names the card by its\n\t\t * title, unless the title needs the value that could not be read.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tuncheckedText() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || this.resolvedText\n\t\t\tif (!title || title.includes('{value}')) {\n\t\t\t\treturn t('nextcloud-vue', 'Could not check')\n\t\t\t}\n\t\t\t// `escape: false`: the result is rendered through `{{ }}`, which escapes.\n\t\t\treturn t('nextcloud-vue', 'Could not check: {subject}', { subject: title }, undefined, { escape: false })\n\t\t},\n\n\t\t/**\n\t\t * The reason the check failed, for the tooltip and for assistive\n\t\t * technology.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tfailureTooltip() {\n\t\t\tif (!this.conditionFailureReason) {\n\t\t\t\treturn t('nextcloud-vue', 'The check failed.')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'The check failed: {reason}', { reason: this.conditionFailureReason }, undefined, { escape: false })\n\t\t},\n\n\t\t/** Whether the banner navigates on click (route set + router present). */\n\t\tclickable() {\n\t\t\treturn !!this.resolvedRoute && !!this.$router\n\t\t},\n\n\t\t/** The click-through route as a router location (a string is a route name). */\n\t\trouteLocation() {\n\t\t\tconst route = this.resolvedRoute\n\t\t\treturn typeof route === 'string' ? { name: route } : route\n\t\t},\n\n\t\t/** The href of the click-through link. */\n\t\trouteHref() {\n\t\t\treturn resolveHref(this.routeLocation, this.$router)\n\t\t},\n\t},\n\n\twatch: {\n\t\tresolvedVisibleWhen: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tthis.evaluateCondition()\n\t\t\t},\n\t\t},\n\n\t\tresolvedConditionOutcome() {\n\t\t\tthis.evaluateCondition()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * A manifest string through the host's label lookup (`cnTranslate`), so a key\n\t\t * such as \"Mine\" reads in the user's language.\n\t\t *\n\t\t * @param {string} text The text as written in the manifest.\n\t\t * @return {string} The translated text.\n\t\t */\n\t\ttl(text) {\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/**\n\t\t * Replace `{value}` in a text by the value the predicate read, once\n\t\t * one is known.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The text with the value filled in.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tfillValue(text) {\n\t\t\tif (this.conditionValue === null || !text.includes('{value}')) {\n\t\t\t\treturn text\n\t\t\t}\n\t\t\treturn text.replaceAll('{value}', String(this.conditionValue))\n\t\t},\n\n\t\t/**\n\t\t * Run an attention-card action: route an in-app link, leave an\n\t\t * external link to the browser, and emit `action` for a button.\n\t\t *\n\t\t * @param {MouseEvent} event The click.\n\t\t * @param {object} action The resolved action.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tonActionClick(event, action) {\n\t\t\tif (action.target) {\n\t\t\t\tfollowLinkClick(event, action.target, this.$router)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!action.href) {\n\t\t\t\tthis.$emit('action', { id: action.id, label: action.label, index: action.index })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve the `visibleWhen` verdict. A host-injected\n\t\t * `conditionOutcome` wins outright — the host already ran the\n\t\t * predicate, so re-fetching would double the request and flash the\n\t\t * banner hidden until its own copy resolved. Otherwise evaluate\n\t\t * through the shared util primitives (endpoint / OpenRegister-source\n\t\t * modes — the Wave-1 banner shape is the canonical one, extracted to\n\t\t * `utils/visibleWhen.js` in Wave 3 so manifest actions reuse it),\n\t\t * keeping the read VALUE for the `{value}` text placeholder.\n\t\t * Fail-safe: any fetch/shape error leaves the banner hidden. The\n\t\t * failure is kept (`conditionFailed`, with its reason) so an\n\t\t * attention card can say it could not check.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tasync evaluateCondition() {\n\t\t\tconst outcome = this.resolvedConditionOutcome\n\t\t\tif (outcome) {\n\t\t\t\tthis.conditionMet = outcome.met === true\n\t\t\t\tthis.conditionValue = outcome.value !== undefined ? outcome.value : null\n\t\t\t\tthis.conditionFailed = outcome.failed === true\n\t\t\t\tthis.conditionFailureReason = outcome.failed === true && typeof outcome.reason === 'string' ? outcome.reason : ''\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst cond = this.resolvedVisibleWhen\n\t\t\tif (!cond) {\n\t\t\t\tthis.conditionMet = null\n\t\t\t\tthis.conditionValue = null\n\t\t\t\tthis.conditionFailed = false\n\t\t\t\tthis.conditionFailureReason = ''\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst value = await readVisibleWhenValue(cond)\n\t\t\t\tthis.conditionValue = value\n\t\t\t\tthis.conditionMet = compareVisibleWhen(value, cond.op || 'eq', cond.value)\n\t\t\t\tthis.conditionFailed = false\n\t\t\t\tthis.conditionFailureReason = ''\n\t\t\t} catch (error) {\n\t\t\t\t// Still hidden: a banner that cannot tell whether it applies\n\t\t\t\t// should not claim attention. But not silently, because a\n\t\t\t\t// failed count and \"nothing to report\" look the same on screen.\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnBannerWidget] visibleWhen could not be evaluated, banner hidden:', error?.message || error)\n\t\t\t\tthis.conditionMet = false\n\t\t\t\tthis.conditionValue = null\n\t\t\t\tthis.conditionFailed = true\n\t\t\t\tthis.conditionFailureReason = typeof error?.message === 'string' ? error.message : String(error ?? '')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Route a plain click on the link; a modified click opens the href\n\t\t * in the browser.\n\t\t *\n\t\t * @param {MouseEvent} event The click event.\n\t\t * @return {void}\n\t\t */\n\t\tonClick(event) {\n\t\t\tif (!this.clickable) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfollowLinkClick(event, this.routeLocation, this.$router)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-banner-widget {\n\twidth: 100%;\n}\n\n.cn-banner-widget__card {\n\tmargin: 0;\n}\n\n.cn-banner-widget__text--clickable {\n\tcursor: pointer;\n\ttext-decoration: underline;\n\tcolor: inherit;\n}\n\n/* The check failed. Muted text, no fill, no severity colour: it is a note\n that something is not known, not a warning. */\n.cn-banner-widget--unchecked {\n\tbox-sizing: border-box;\n\tmargin: 0;\n\tpadding: 8px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tcursor: help;\n}\n\n/* The reason, for assistive technology; sighted readers get the tooltip. */\n.cn-banner-widget__unchecked-reason {\n\tposition: absolute;\n\twidth: 1px;\n\theight: 1px;\n\tmargin: -1px;\n\tpadding: 0;\n\toverflow: hidden;\n\tclip-path: inset(50%);\n\twhite-space: nowrap;\n\tborder: 0;\n}\n\n/* Attention card. The severity edge is an inset shadow, so it takes no width. */\n.cn-banner-widget--attention {\n\t--cn-banner-accent: var(--color-primary-element);\n\t--cn-banner-accent-text: var(--color-primary-element);\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 16px 24px;\n\tbox-sizing: border-box;\n\tpadding: 20px 24px 20px 28px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-container, var(--border-radius-large, 12px));\n\tbackground: var(--color-main-background);\n\tbox-shadow: inset 4px 0 0 0 var(--cn-banner-accent);\n}\n\n.cn-banner-widget--attention.cn-banner-widget--warning {\n\t--cn-banner-accent: var(--color-element-warning, var(--color-warning));\n\t--cn-banner-accent-text: var(--color-text-warning, var(--color-warning-text, var(--color-main-text)));\n}\n\n.cn-banner-widget--attention.cn-banner-widget--error {\n\t--cn-banner-accent: var(--color-element-error, var(--color-error));\n\t--cn-banner-accent-text: var(--color-text-error, var(--color-error-text));\n}\n\n.cn-banner-widget--attention.cn-banner-widget--success {\n\t--cn-banner-accent: var(--color-element-success, var(--color-success));\n\t--cn-banner-accent-text: var(--color-text-success, var(--color-success-text));\n}\n\n.cn-banner-widget__body {\n\tdisplay: flex;\n\tflex: 1 1 320px;\n\tflex-direction: column;\n\tgap: 6px;\n\tmin-width: 0;\n}\n\n.cn-banner-widget__kicker {\n\tmargin: 0;\n\tfont-size: 0.85em;\n\tfont-weight: 700;\n\tletter-spacing: 0.06em;\n\ttext-transform: uppercase;\n\tcolor: var(--cn-banner-accent-text);\n}\n\n.cn-banner-widget__title {\n\tmargin: 0;\n\tfont-size: 1.3em;\n\tfont-weight: 700;\n\tline-height: 1.3;\n\tcolor: var(--color-main-text);\n}\n\n.cn-banner-widget__reason {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-banner-widget__actions {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 10px;\n}\n\n.cn-banner-widget__action {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\tbox-sizing: border-box;\n\tmin-height: var(--default-clickable-area, 40px);\n\tmargin: 0;\n\tpadding: 0 16px;\n\tborder: 1px solid var(--color-border-maxcontrast);\n\tborder-radius: var(--border-radius-element, var(--border-radius-large, 8px));\n\tfont: inherit;\n\tfont-weight: 600;\n\ttext-decoration: none;\n\tcursor: pointer;\n}\n\n.cn-banner-widget__action--secondary {\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n}\n\n.cn-banner-widget__action--secondary:hover {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-banner-widget__action--primary {\n\tborder-color: var(--color-primary-element);\n\tbackground: var(--color-primary-element);\n\tcolor: var(--color-primary-element-text);\n}\n\n.cn-banner-widget__action--primary:hover {\n\tborder-color: var(--color-primary-element-hover);\n\tbackground: var(--color-primary-element-hover);\n}\n\n.cn-banner-widget__action:focus-visible {\n\toutline: 2px solid var(--color-main-text);\n\toutline-offset: 2px;\n}\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createElementVNode","_toDisplayString","_openBlock","_Fragment","_renderList","_resolveDynamicComponent","_createTextVNode","_createVNode","_createBlock"],"mappings":"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"}
1
+ {"version":3,"file":"CnBannerWidget.vue3.js","sources":["../../../../src/components/CnBannerWidget/CnBannerWidget.vue?vue&type=template&id=3531692f&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<section\n\t\tv-if=\"visible && isAttention\"\n\t\tclass=\"cn-banner-widget cn-banner-widget--attention\"\n\t\t:class=\"['cn-banner-widget--' + resolvedVariant, { 'cn-banner-widget--stacked': isStacked }]\"\n\t\t:aria-labelledby=\"headingId\"\n\t\tdata-testid=\"cn-banner-widget-attention\">\n\t\t<div class=\"cn-banner-widget__body\">\n\t\t\t<p v-if=\"resolvedKicker\" class=\"cn-banner-widget__kicker\">\n\t\t\t\t{{ resolvedKicker }}\n\t\t\t</p>\n\t\t\t<h3 :id=\"headingId\" class=\"cn-banner-widget__title\">\n\t\t\t\t{{ displayTitle }}\n\t\t\t</h3>\n\t\t\t<p v-if=\"displayReason\" class=\"cn-banner-widget__reason\">\n\t\t\t\t{{ displayReason }}\n\t\t\t</p>\n\t\t</div>\n\t\t<div v-if=\"resolvedActions.length > 0\" class=\"cn-banner-widget__actions\">\n\t\t\t<component\n\t\t\t\t:is=\"action.href ? 'a' : 'button'\"\n\t\t\t\tv-for=\"action in resolvedActions\"\n\t\t\t\t:key=\"action.key\"\n\t\t\t\tclass=\"cn-banner-widget__action\"\n\t\t\t\t:class=\"action.primary ? 'cn-banner-widget__action--primary' : 'cn-banner-widget__action--secondary'\"\n\t\t\t\t:href=\"action.href || null\"\n\t\t\t\t:type=\"action.href ? null : 'button'\"\n\t\t\t\tdata-testid=\"cn-banner-widget-action\"\n\t\t\t\t@click=\"onActionClick($event, action)\">\n\t\t\t\t{{ action.label }}\n\t\t\t</component>\n\t\t</div>\n\t</section>\n\t<!-- The count could not be read. One quiet line, not the card and not an\n\t alarm: hidden would read as \"nothing to report\", which is not known. -->\n\t<p\n\t\tv-else-if=\"checkFailed\"\n\t\tclass=\"cn-banner-widget cn-banner-widget--unchecked\"\n\t\t:title=\"failureTooltip\"\n\t\tdata-testid=\"cn-banner-widget-unchecked\">\n\t\t<span class=\"cn-banner-widget__unchecked-text\">{{ uncheckedText }}</span>\n\t\t<span class=\"cn-banner-widget__unchecked-reason\">{{ failureTooltip }}</span>\n\t</p>\n\t<div v-else-if=\"visible\" class=\"cn-banner-widget\">\n\t\t<NcNoteCard :type=\"resolvedVariant\" class=\"cn-banner-widget__card\">\n\t\t\t<component\n\t\t\t\t:is=\"clickable ? 'a' : 'span'\"\n\t\t\t\tclass=\"cn-banner-widget__text\"\n\t\t\t\t:class=\"{ 'cn-banner-widget__text--clickable': clickable }\"\n\t\t\t\t:href=\"clickable ? routeHref : null\"\n\t\t\t\tdata-testid=\"cn-banner-widget-text\"\n\t\t\t\t@click=\"onClick\">\n\t\t\t\t{{ displayText }}\n\t\t\t</component>\n\t\t</NcNoteCard>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcNoteCard } from '@nextcloud/vue'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport { fillTemplate } from '../../utils/fillTemplate.js'\nimport { followLinkClick, resolveHref } from '../../utils/linkNavigation.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { safeHref } from '../../utils/safeHref.js'\nimport { nextUid } from '../../utils/uid.js'\nimport { compareVisibleWhen, readVisibleWhenValue } from '../../utils/visibleWhen.js'\n\n/** Variants understood by NcNoteCard. */\nconst VARIANTS = ['info', 'warning', 'error', 'success']\n\n/** Layouts: the note card (default) or the attention card. */\nconst LAYOUTS = ['banner', 'attention']\n\n/** An attention card offers a primary and a secondary action, no more. */\nconst MAX_ACTIONS = 2\n\n/**\n * Under the board look the card takes what the boards draw: DqMijnWerk's\n * \"Vandaag eerst\" has three secondary actions and the primary one.\n */\nconst MAX_ACTIONS_BOARD = 4\n\n/**\n * CnBannerWidget — declarative notice banner for dashboards and v2 pages\n * (`banner` widget type, Wave 1 of nextcloud-vue#91).\n *\n * Renders a themed NcNoteCard with a variant (`info | warning | error`),\n * a text, an optional click-through route, and an optional `visibleWhen`\n * condition: a simple `{ field, op, value }` predicate evaluated against\n * an endpoint response or an OpenRegister source (the doriath\n * migration-banner case — \"show while `pending > 0`\").\n *\n * Config arrives either as flat props (v2 grid spreads `props`) or as a\n * stored `content` blob (CnDashboardPage's registry branch) — explicit\n * flat props win on collision.\n *\n * ```json\n * {\n * \"widgetKey\": \"banner\",\n * \"props\": {\n * \"variant\": \"warning\",\n * \"text\": \"Migrations pending — open the migration overview.\",\n * \"visibleWhen\": { \"endpoint\": \"/apps/doriath/api/migrations/status\", \"field\": \"pending\", \"op\": \"gt\", \"value\": 0 },\n * \"route\": \"migrations\"\n * }\n * }\n * ```\n *\n * Fail-safe: with a `visibleWhen`, the banner stays HIDDEN until the\n * condition evaluates true — a failed fetch never breaks (or spams) a\n * dashboard.\n *\n * An attention card (`layout: \"attention\"`) whose `visibleWhen` request\n * FAILS renders one quiet line instead, \"Could not check\", with the reason as\n * a tooltip. A hidden card reads as \"nothing needs attention\", and a failed\n * count does not know that. A request that succeeds with a value that does\n * not meet the condition still renders nothing.\n *\n * `layout: \"attention\"` turns the banner into an attention card: a card with\n * a coloured edge by severity, a `kicker` label, a `title`, a `reason` line\n * and up to two `actions` (the first is the primary one unless an action says\n * `primary` itself). `visibleWhen` works the same way. Without `layout` the\n * note card above renders, as it always did.\n *\n * ```json\n * {\n * \"widgetKey\": \"banner\",\n * \"props\": {\n * \"layout\": \"attention\",\n * \"variant\": \"error\",\n * \"kicker\": \"First today\",\n * \"title\": \"Parking permits city centre\",\n * \"reason\": \"The deadline ends today.\",\n * \"actions\": [\n * { \"label\": \"Open case\", \"route\": { \"name\": \"CaseDetail\", \"params\": { \"id\": \"2026-0061\" } } },\n * { \"label\": \"Suspend deadline\", \"route\": \"CaseSuspend\" }\n * ]\n * }\n * }\n * ```\n */\nexport default {\n\tname: 'CnBannerWidget',\n\n\tcomponents: { NcNoteCard },\n\n\tinject: {\n\t\t/**\n\t\t * The host's label lookup (CnAppRoot provides it); identity without one, so a\n\t\t * label that is not a key renders as written.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The app's look, provided by CnAppRoot or CnPageRenderer (`nextcloud` or `board`). */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Banner severity variant: `info | warning | error` (plus `success`\n\t\t * for completeness — NcNoteCard's set). Empty falls back to the\n\t\t * `content` blob, then `info`.\n\t\t */\n\t\tvariant: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t\tvalidator: (v) => v === '' || VARIANTS.includes(v),\n\t\t},\n\n\t\t/** Pre-translated banner text. Empty falls back to the `content` blob. */\n\t\ttext: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Optional visibility condition. Shape:\n\t\t * `{ endpoint?, source?, field?, op?, value }` — exactly one of\n\t\t * `endpoint` (a same-origin URL returning JSON) or `source`\n\t\t * (`{ register, schema, filter? }`, an OpenRegister object query whose\n\t\t * `filter` supports the shared @-token grammar). `field` is a\n\t\t * dot-path into the response (for a `source`, into the first result;\n\t\t * omit it — or use `@total` — to compare the collection total).\n\t\t * `op` is `eq | neq | gt | gte | lt | lte` (default `eq`); `value` is\n\t\t * the literal right-hand side. `null` (the default) shows the banner\n\t\t * unconditionally.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tvisibleWhen: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Optional click-through route: a vue-router route NAME (string) or\n\t\t * a full location object. When set, the banner text is a link to that\n\t\t * route. `null` renders static text.\n\t\t *\n\t\t * @type {string|object|null}\n\t\t */\n\t\troute: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * How the banner renders: `banner` (the note card, default) or\n\t\t * `attention` (a card with a severity edge, kicker, title, reason and\n\t\t * up to two actions). Empty falls back to the `content` blob, then\n\t\t * `banner`.\n\t\t *\n\t\t * @type {''|'banner'|'attention'}\n\t\t */\n\t\tlayout: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t\tvalidator: (v) => v === '' || LAYOUTS.includes(v),\n\t\t},\n\n\t\t/** Attention card: the small label above the title (\"First today\"). */\n\t\tkicker: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Attention card: the title. Falls back to `text` when empty. */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Attention card: the line under the title that says why this needs attention. `{value}` is replaced like in `text`. */\n\t\treason: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Attention card: its actions, two at most (four under the board look,\n\t\t * or `maxActions`). Each is `{ label, route?,\n\t\t * href?, primary?, id? }`: `route` is a route name or location,\n\t\t * `href` an URL. An action with neither renders a button that emits\n\t\t * `action`. The first action is the primary one unless one sets\n\t\t * `primary: true`.\n\t\t *\n\t\t * @type {Array<{label: string, route?: (string|object), href?: string, primary?: boolean, id?: string}>|null}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Attention card: how many actions it shows at most. Null takes the\n\t\t * default: two, four under the board look.\n\t\t *\n\t\t * @spec openspec/changes/screens-banner-actions-parity/specs/banner-actions/spec.md#requirement-the-board-attention-card-shows-the-boards-actions\n\t\t */\n\t\tmaxActions: {\n\t\t\ttype: Number,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Attention card: stack the actions under the text instead of beside it,\n\t\t * the \"Begin hier\" card of PqMijnWerk (16px title, the action under the\n\t\t * reason). Falls back to the `content` blob.\n\t\t *\n\t\t * @spec openspec/changes/screens-start-here-banner/specs/start-here-banner/spec.md#requirement-a-card-can-stack-its-action-under-the-text\n\t\t */\n\t\tstacked: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Stored content blob (CnDashboardPage registry branch) carrying the\n\t\t * same keys as the flat props: `{ variant, text, visibleWhen, route,\n\t\t * layout, kicker, title, reason, actions }`. Explicit flat props win\n\t\t * on collision.\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Pre-evaluated `visibleWhen` outcome `{ met, value }`, injected by a\n\t\t * host that already ran the predicate (CnDashboardPage evaluates it\n\t\t * to know whether to collapse the banner's grid cell). When present,\n\t\t * the banner renders from this verdict instead of fetching again —\n\t\t * no duplicate request and no hidden-until-self-evaluated flash —\n\t\t * and `value` feeds the `{value}` text placeholder. `null` (the\n\t\t * default) keeps the banner self-evaluating. A host whose request\n\t\t * failed passes `{ met: false, value: null, failed: true, reason }`,\n\t\t * and an attention card then says it could not check.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tconditionOutcome: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted when an attention-card action without a `route` or `href`\n\t\t * is clicked, so the host can run it.\n\t\t *\n\t\t * @event action\n\t\t * @type {{id: (string|undefined), label: string, index: number}}\n\t\t */\n\t\t'action',\n\t],\n\n\t/**\n\t * The banner's own state: the evaluated condition, the value it read,\n\t * and whether the request for it failed.\n\t *\n\t * @return {object} The state.\n\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t */\n\tdata() {\n\t\treturn {\n\t\t\t/** Id that ties the attention card's region to its title. */\n\t\t\theadingId: `cn-banner-title-${nextUid()}`,\n\t\t\t/** Evaluated visibleWhen outcome (null = not yet evaluated). */\n\t\t\tconditionMet: null,\n\t\t\t/**\n\t\t\t * The raw field value the predicate read (from the injected\n\t\t\t * outcome or the banner's own evaluation) — interpolated into\n\t\t\t * the text wherever it says `{value}`.\n\t\t\t */\n\t\t\tconditionValue: null,\n\t\t\t/**\n\t\t\t * The first row of the `visibleWhen` source query (its `order`\n\t\t\t * applied), the card's subject: `{subject.<field>}` in the texts\n\t\t\t * and `@subject.<field>` in an action route read it.\n\t\t\t */\n\t\t\tconditionRow: null,\n\t\t\t/** Whether the visibleWhen request itself failed (not: answered \"no\"). */\n\t\t\tconditionFailed: false,\n\t\t\t/** Why it failed, as the request reported it. */\n\t\t\tconditionFailureReason: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** The effective variant (prop → content → 'info'). */\n\t\tresolvedVariant() {\n\t\t\tconst v = this.variant || (this.content && this.content.variant) || 'info'\n\t\t\treturn VARIANTS.includes(v) ? v : 'info'\n\t\t},\n\n\t\t/** The effective banner text (prop → content → ''). */\n\t\tresolvedText() {\n\t\t\treturn this.text || (this.content && this.content.text) || ''\n\t\t},\n\n\t\t/** The effective visibleWhen condition (prop → content → null). */\n\t\tresolvedVisibleWhen() {\n\t\t\treturn this.visibleWhen || (this.content && this.content.visibleWhen) || null\n\t\t},\n\n\t\t/** The effective injected outcome (prop → content → null). */\n\t\tresolvedConditionOutcome() {\n\t\t\treturn this.conditionOutcome || (this.content && this.content.conditionOutcome) || null\n\t\t},\n\n\t\t/**\n\t\t * The rendered text: `resolvedText` with `{value}` replaced by the\n\t\t * predicate's field value once one is known. Without a known value\n\t\t * the text renders as written — a `{value}` placeholder only makes\n\t\t * sense on a conditional banner, which stays hidden until its\n\t\t * predicate (and therefore its value) resolves.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tdisplayText() {\n\t\t\treturn this.fillValue(this.resolvedText)\n\t\t},\n\n\t\t/** The effective click-through route (prop → content → null). */\n\t\tresolvedRoute() {\n\t\t\treturn this.route || (this.content && this.content.route) || null\n\t\t},\n\n\t\t/**\n\t\t * Whether the attention card renders instead of the note card.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tisAttention() {\n\t\t\treturn (this.layout || (this.content && this.content.layout) || 'banner') === 'attention'\n\t\t},\n\n\t\t/**\n\t\t * Whether the actions sit under the text.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisStacked() {\n\t\t\treturn this.stacked || Boolean(this.content && this.content.stacked)\n\t\t},\n\n\t\t/**\n\t\t * The attention card's kicker (prop, else content).\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tresolvedKicker() {\n\t\t\tconst kicker = this.kicker || (this.content && this.content.kicker) || ''\n\t\t\treturn kicker ? this.fillValue(this.tl(kicker)) : ''\n\t\t},\n\n\t\t/**\n\t\t * The attention card's title: `title`, else `text`, with `{value}` filled in.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tdisplayTitle() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\treturn title ? this.fillValue(this.tl(title)) : this.displayText\n\t\t},\n\n\t\t/**\n\t\t * The attention card's reason line. When the title already used\n\t\t * `text`, the text is not repeated here.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tdisplayReason() {\n\t\t\tconst reason = this.reason || (this.content && this.content.reason) || ''\n\t\t\tif (reason) {\n\t\t\t\treturn this.fillValue(this.tl(reason))\n\t\t\t}\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\t// A card that sets `text` to the same words as its `title` says\n\t\t\t// them once.\n\t\t\treturn title && this.displayText !== this.displayTitle ? this.displayText : ''\n\t\t},\n\n\t\t/**\n\t\t * The attention card's actions: the first `actionCap` that have a label, each\n\t\t * with its href and whether it is the primary one.\n\t\t *\n\t\t * @return {Array<{key: string, id: (string|undefined), label: string, href: string, target: (object|null), primary: boolean, index: number}>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\t/**\n\t\t * How many actions the attention card shows: `maxActions` (prop, then\n\t\t * content), else four under the board look and two without it.\n\t\t *\n\t\t * @return {number} The cap.\n\t\t * @spec openspec/changes/screens-banner-actions-parity/specs/banner-actions/spec.md#requirement-the-board-attention-card-shows-the-boards-actions\n\t\t */\n\t\tactionCap() {\n\t\t\tconst explicit = this.maxActions ?? (this.content ? this.content.maxActions : undefined)\n\t\t\tif (Number.isInteger(explicit) && explicit > 0) {\n\t\t\t\treturn explicit\n\t\t\t}\n\t\t\treturn normalizeLook(this.cnLook) === 'board' ? MAX_ACTIONS_BOARD : MAX_ACTIONS\n\t\t},\n\n\t\tresolvedActions() {\n\t\t\tconst raw = this.actions || (this.content && this.content.actions) || []\n\t\t\tif (!Array.isArray(raw)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst usable = raw\n\t\t\t\t.filter((action) => action && typeof action === 'object' && typeof action.label === 'string' && action.label !== '')\n\t\t\t\t.slice(0, this.actionCap)\n\t\t\tconst declaredPrimary = usable.findIndex((action) => action.primary === true)\n\t\t\tconst primaryIndex = declaredPrimary === -1 ? 0 : declaredPrimary\n\t\t\treturn usable.map((action, index) => {\n\t\t\t\tlet target = null\n\t\t\t\tlet href = ''\n\t\t\t\tif (action.route) {\n\t\t\t\t\ttarget = typeof action.route === 'string' ? { name: action.route } : this.resolveSubject(action.route)\n\t\t\t\t\thref = resolveHref(target, this.$router)\n\t\t\t\t} else if (typeof action.href === 'string' && action.href !== '') {\n\t\t\t\t\tconst safe = safeHref(action.href)\n\t\t\t\t\thref = safe === '#' ? '' : safe\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tkey: `${index}-${action.id || action.label}`,\n\t\t\t\t\tid: action.id,\n\t\t\t\t\tlabel: this.tl(action.label),\n\t\t\t\t\thref,\n\t\t\t\t\ttarget,\n\t\t\t\t\tprimary: index === primaryIndex,\n\t\t\t\t\tindex,\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Whether the banner renders: no condition = always; else the evaluated outcome.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tvisible() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\tif (this.resolvedText === '' && !(this.isAttention && title !== '')) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (!this.resolvedVisibleWhen) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn this.conditionMet === true\n\t\t},\n\n\t\t/**\n\t\t * Whether to say the check failed: an attention card, with words to\n\t\t * show, whose `visibleWhen` request failed. A plain banner stays\n\t\t * hidden on failure, as it always did.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tcheckFailed() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || ''\n\t\t\treturn this.isAttention\n\t\t\t\t&& !!this.resolvedVisibleWhen\n\t\t\t\t&& this.conditionFailed\n\t\t\t\t&& (title !== '' || this.resolvedText !== '')\n\t\t},\n\n\t\t/**\n\t\t * The one line shown when the check failed. It names the card by its\n\t\t * title, unless the title needs the value that could not be read.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tuncheckedText() {\n\t\t\tconst title = this.title || (this.content && this.content.title) || this.resolvedText\n\t\t\tif (!title || title.includes('{value}') || title.includes('{subject.')) {\n\t\t\t\treturn t('nextcloud-vue', 'Could not check')\n\t\t\t}\n\t\t\t// `escape: false`: the result is rendered through `{{ }}`, which escapes.\n\t\t\treturn t('nextcloud-vue', 'Could not check: {subject}', { subject: title }, undefined, { escape: false })\n\t\t},\n\n\t\t/**\n\t\t * The reason the check failed, for the tooltip and for assistive\n\t\t * technology.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tfailureTooltip() {\n\t\t\tif (!this.conditionFailureReason) {\n\t\t\t\treturn t('nextcloud-vue', 'The check failed.')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'The check failed: {reason}', { reason: this.conditionFailureReason }, undefined, { escape: false })\n\t\t},\n\n\t\t/** Whether the banner navigates on click (route set + router present). */\n\t\tclickable() {\n\t\t\treturn !!this.resolvedRoute && !!this.$router\n\t\t},\n\n\t\t/** The click-through route as a router location (a string is a route name). */\n\t\trouteLocation() {\n\t\t\tconst route = this.resolvedRoute\n\t\t\treturn typeof route === 'string' ? { name: route } : route\n\t\t},\n\n\t\t/** The href of the click-through link. */\n\t\trouteHref() {\n\t\t\treturn resolveHref(this.routeLocation, this.$router)\n\t\t},\n\t},\n\n\twatch: {\n\t\tresolvedVisibleWhen: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tthis.evaluateCondition()\n\t\t\t},\n\t\t},\n\n\t\tresolvedConditionOutcome() {\n\t\t\tthis.evaluateCondition()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * A manifest string through the host's label lookup (`cnTranslate`), so a key\n\t\t * such as \"Mine\" reads in the user's language.\n\t\t *\n\t\t * @param {string} text The text as written in the manifest.\n\t\t * @return {string} The translated text.\n\t\t */\n\t\ttl(text) {\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/**\n\t\t * Replace `{value}` in a text by the value the predicate read, once\n\t\t * one is known.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The text with the value filled in.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tfillValue(text) {\n\t\t\tlet out = text\n\t\t\tif (this.conditionValue !== null && out.includes('{value}')) {\n\t\t\t\tout = out.replaceAll('{value}', String(this.conditionValue))\n\t\t\t}\n\t\t\treturn this.fillSubject(out)\n\t\t},\n\n\t\t/**\n\t\t * Fill `{subject.<field>}` (and `{subject.<field>|date}`, `|days`) from\n\t\t * the card's subject row. A token with no value empties the whole text,\n\t\t * as in a record list. Text without a subject token is untouched.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The filled text.\n\t\t * @spec openspec/changes/screens-start-here-banner/specs/start-here-banner/spec.md#requirement-a-banner-can-name-the-first-row-of-a-query\n\t\t */\n\t\tfillSubject(text) {\n\t\t\tif (typeof text !== 'string' || !text.includes('{subject.')) {\n\t\t\t\treturn text\n\t\t\t}\n\t\t\t// `{value}` is not a subject token: keep it out of the template fill.\n\t\t\tconst guard = '\\u0000value\\u0000'\n\t\t\tconst filled = fillTemplate(text.replaceAll('{value}', guard), { subject: this.conditionRow || {} })\n\t\t\treturn filled.replaceAll(guard, '{value}')\n\t\t},\n\n\t\t/**\n\t\t * Replace `@subject.<field>` strings anywhere in an action route\n\t\t * (params, query) by the subject's value.\n\t\t *\n\t\t * @param {unknown} node A route name, location or any nested part of one.\n\t\t * @return {unknown} The same shape with the tokens resolved.\n\t\t * @spec openspec/changes/screens-start-here-banner/specs/start-here-banner/spec.md#requirement-a-banner-can-name-the-first-row-of-a-query\n\t\t */\n\t\tresolveSubject(node) {\n\t\t\tif (typeof node === 'string') {\n\t\t\t\tif (!node.startsWith('@subject.')) {\n\t\t\t\t\treturn node\n\t\t\t\t}\n\t\t\t\tconst value = this.conditionRow ? readPath(this.conditionRow, node.slice('@subject.'.length)) : undefined\n\t\t\t\treturn value === undefined || value === null ? '' : String(value)\n\t\t\t}\n\t\t\tif (Array.isArray(node)) {\n\t\t\t\treturn node.map((item) => this.resolveSubject(item))\n\t\t\t}\n\t\t\tif (node && typeof node === 'object') {\n\t\t\t\treturn Object.fromEntries(Object.entries(node).map(([key, value]) => [key, this.resolveSubject(value)]))\n\t\t\t}\n\t\t\treturn node\n\t\t},\n\n\t\t/**\n\t\t * Run an attention-card action: route an in-app link, leave an\n\t\t * external link to the browser, and emit `action` for a button.\n\t\t *\n\t\t * @param {MouseEvent} event The click.\n\t\t * @param {object} action The resolved action.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-attention-card\n\t\t */\n\t\tonActionClick(event, action) {\n\t\t\tif (action.target) {\n\t\t\t\tfollowLinkClick(event, action.target, this.$router)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!action.href) {\n\t\t\t\tthis.$emit('action', { id: action.id, label: action.label, index: action.index })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve the `visibleWhen` verdict. A host-injected\n\t\t * `conditionOutcome` wins outright — the host already ran the\n\t\t * predicate, so re-fetching would double the request and flash the\n\t\t * banner hidden until its own copy resolved. Otherwise evaluate\n\t\t * through the shared util primitives (endpoint / OpenRegister-source\n\t\t * modes — the Wave-1 banner shape is the canonical one, extracted to\n\t\t * `utils/visibleWhen.js` in Wave 3 so manifest actions reuse it),\n\t\t * keeping the read VALUE for the `{value}` text placeholder.\n\t\t * Fail-safe: any fetch/shape error leaves the banner hidden. The\n\t\t * failure is kept (`conditionFailed`, with its reason) so an\n\t\t * attention card can say it could not check.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/live-check-follow-ups/specs/live-check-follow-ups/spec.md#requirement-an-attention-card-says-when-it-could-not-check\n\t\t */\n\t\tasync evaluateCondition() {\n\t\t\tconst outcome = this.resolvedConditionOutcome\n\t\t\tif (outcome) {\n\t\t\t\tthis.conditionMet = outcome.met === true\n\t\t\t\tthis.conditionValue = outcome.value !== undefined ? outcome.value : null\n\t\t\t\tthis.conditionRow = outcome.row && typeof outcome.row === 'object' ? outcome.row : null\n\t\t\t\tthis.conditionFailed = outcome.failed === true\n\t\t\t\tthis.conditionFailureReason = outcome.failed === true && typeof outcome.reason === 'string' ? outcome.reason : ''\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst cond = this.resolvedVisibleWhen\n\t\t\tif (!cond) {\n\t\t\t\tthis.conditionMet = null\n\t\t\t\tthis.conditionValue = null\n\t\t\t\tthis.conditionRow = null\n\t\t\t\tthis.conditionFailed = false\n\t\t\t\tthis.conditionFailureReason = ''\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst sink = {}\n\t\t\t\tconst value = await readVisibleWhenValue(cond, undefined, sink)\n\t\t\t\tthis.conditionRow = sink.row || null\n\t\t\t\tthis.conditionValue = value\n\t\t\t\tthis.conditionMet = compareVisibleWhen(value, cond.op || 'eq', cond.value)\n\t\t\t\tthis.conditionFailed = false\n\t\t\t\tthis.conditionFailureReason = ''\n\t\t\t} catch (error) {\n\t\t\t\t// Still hidden: a banner that cannot tell whether it applies\n\t\t\t\t// should not claim attention. But not silently, because a\n\t\t\t\t// failed count and \"nothing to report\" look the same on screen.\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnBannerWidget] visibleWhen could not be evaluated, banner hidden:', error?.message || error)\n\t\t\t\tthis.conditionMet = false\n\t\t\t\tthis.conditionValue = null\n\t\t\t\tthis.conditionRow = null\n\t\t\t\tthis.conditionFailed = true\n\t\t\t\tthis.conditionFailureReason = typeof error?.message === 'string' ? error.message : String(error ?? '')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Route a plain click on the link; a modified click opens the href\n\t\t * in the browser.\n\t\t *\n\t\t * @param {MouseEvent} event The click event.\n\t\t * @return {void}\n\t\t */\n\t\tonClick(event) {\n\t\t\tif (!this.clickable) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfollowLinkClick(event, this.routeLocation, this.$router)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-banner-widget {\n\twidth: 100%;\n}\n\n.cn-banner-widget__card {\n\tmargin: 0;\n}\n\n.cn-banner-widget__text--clickable {\n\tcursor: pointer;\n\ttext-decoration: underline;\n\tcolor: inherit;\n}\n\n/* The check failed. Muted text, no fill, no severity colour: it is a note\n that something is not known, not a warning. */\n.cn-banner-widget--unchecked {\n\tbox-sizing: border-box;\n\tmargin: 0;\n\tpadding: 8px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tcursor: help;\n}\n\n/* The reason, for assistive technology; sighted readers get the tooltip. */\n.cn-banner-widget__unchecked-reason {\n\tposition: absolute;\n\twidth: 1px;\n\theight: 1px;\n\tmargin: -1px;\n\tpadding: 0;\n\toverflow: hidden;\n\tclip-path: inset(50%);\n\twhite-space: nowrap;\n\tborder: 0;\n}\n\n/* Attention card. The severity edge is an inset shadow, so it takes no width. */\n.cn-banner-widget--attention {\n\t--cn-banner-accent: var(--color-primary-element);\n\t--cn-banner-accent-text: var(--color-primary-element);\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 16px 24px;\n\tbox-sizing: border-box;\n\tpadding: 20px 24px 20px 28px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-container, var(--border-radius-large, 12px));\n\tbackground: var(--color-main-background);\n\tbox-shadow: inset 4px 0 0 0 var(--cn-banner-accent);\n}\n\n.cn-banner-widget--attention.cn-banner-widget--warning {\n\t--cn-banner-accent: var(--color-element-warning, var(--color-warning));\n\t--cn-banner-accent-text: var(--color-text-warning, var(--color-warning-text, var(--color-main-text)));\n}\n\n.cn-banner-widget--attention.cn-banner-widget--error {\n\t--cn-banner-accent: var(--color-element-error, var(--color-error));\n\t--cn-banner-accent-text: var(--color-text-error, var(--color-error-text));\n}\n\n.cn-banner-widget--attention.cn-banner-widget--success {\n\t--cn-banner-accent: var(--color-element-success, var(--color-success));\n\t--cn-banner-accent-text: var(--color-text-success, var(--color-success-text));\n}\n\n.cn-banner-widget__body {\n\tdisplay: flex;\n\tflex: 1 1 320px;\n\tflex-direction: column;\n\tgap: 6px;\n\tmin-width: 0;\n}\n\n.cn-banner-widget__kicker {\n\tmargin: 0;\n\tfont-size: 0.85em;\n\tfont-weight: 700;\n\tletter-spacing: 0.06em;\n\ttext-transform: uppercase;\n\tcolor: var(--cn-banner-accent-text);\n}\n\n.cn-banner-widget__title {\n\tmargin: 0;\n\tfont-size: 1.3em;\n\tfont-weight: 700;\n\tline-height: 1.3;\n\tcolor: var(--color-main-text);\n}\n\n.cn-banner-widget__reason {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* Stacked (PqMijnWerk \"Begin hier\"): the text, then the action under it. */\n.cn-banner-widget--stacked {\n\tflex-direction: column;\n\talign-items: flex-start;\n\tgap: 10px;\n}\n\n.cn-banner-widget--stacked .cn-banner-widget__body {\n\tflex: 0 0 auto;\n\tgap: 10px;\n}\n\n.cn-banner-widget--stacked .cn-banner-widget__title {\n\tfont-size: 16px;\n\tline-height: 1.4;\n}\n\n.cn-banner-widget--stacked .cn-banner-widget__reason {\n\tfont-size: 14px;\n}\n\n.cn-banner-widget--stacked .cn-banner-widget__action {\n\tmin-height: var(--cn-board-control-height, 40px);\n\tfont-size: 14px;\n}\n\n.cn-banner-widget__actions {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 10px;\n}\n\n.cn-banner-widget__action {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\tbox-sizing: border-box;\n\tmin-height: var(--default-clickable-area, 40px);\n\tmargin: 0;\n\tpadding: 0 16px;\n\tborder: 1px solid var(--color-border-maxcontrast);\n\tborder-radius: var(--border-radius-element, var(--border-radius-large, 8px));\n\tfont: inherit;\n\tfont-weight: 600;\n\ttext-decoration: none;\n\tcursor: pointer;\n}\n\n.cn-banner-widget__action--secondary {\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n}\n\n.cn-banner-widget__action--secondary:hover {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-banner-widget__action--primary {\n\tborder-color: var(--color-primary-element);\n\tbackground: var(--color-primary-element);\n\tcolor: var(--color-primary-element-text);\n}\n\n.cn-banner-widget__action--primary:hover {\n\tborder-color: var(--color-primary-element-hover);\n\tbackground: var(--color-primary-element-hover);\n}\n\n.cn-banner-widget__action:focus-visible {\n\toutline: 2px solid var(--color-main-text);\n\toutline-offset: 2px;\n}\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createElementVNode","_toDisplayString","_openBlock","_Fragment","_renderList","_resolveDynamicComponent","_createTextVNode","_createVNode","_createBlock"],"mappings":"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"}
@@ -633,11 +633,14 @@ var script = {
633
633
  * a `{ value: variant }` map (e.g. `{ placed: 'primary', delivered: 'success' }`)
634
634
  * that CnStatusBadge resolves per label. Null when unset (falls back to
635
635
  * `badgeVariant`). Lets a manifest colour a status column without code.
636
+ * A column-level `colorMap` (it reaches the cell as the property's
637
+ * `colorMap`) works too, so `{ widget: 'badge', colorMap }` and
638
+ * `{ widget: 'badge', widgetProps: { colorMap } }` read the same.
636
639
  *
637
640
  * @return {object|null}
638
641
  */
639
642
  badgeColorMap() {
640
- return (this.widgetProps && this.widgetProps.colorMap) || null
643
+ return (this.widgetProps && this.widgetProps.colorMap) || this.enumColorMap || null
641
644
  },
642
645
 
643
646
  /**