@conduction/nextcloud-vue 2.137.0 → 2.139.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue.js +1 -1
  2. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +6 -4
  3. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  4. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  5. package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
  6. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
  7. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
  8. package/dist/esm/components/CnDetailCard/CnDetailCard.vue.js +1 -1
  9. package/dist/esm/components/CnDetailCard/CnDetailCard.vue2.js +6 -0
  10. package/dist/esm/components/CnDetailCard/CnDetailCard.vue2.js.map +1 -1
  11. package/dist/esm/components/CnDetailCard/CnDetailCard.vue3.js +24 -7
  12. package/dist/esm/components/CnDetailCard/CnDetailCard.vue3.js.map +1 -1
  13. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
  14. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +2 -0
  15. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  16. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  17. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
  18. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  19. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  20. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
  21. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +43 -1
  22. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  23. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  24. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +133 -6
  25. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  26. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +25 -17
  27. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  28. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +2 -0
  29. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
  30. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js +13 -2
  31. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
  32. package/dist/esm/components/CnMailTab/dashboardRegistration.js +4 -0
  33. package/dist/esm/components/CnMailTab/dashboardRegistration.js.map +1 -1
  34. package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue.js +1 -1
  35. package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue2.js.map +1 -1
  36. package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue3.js.map +1 -1
  37. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue.js +1 -1
  38. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +1 -1
  39. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  40. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  41. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue.js +1 -1
  42. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +2 -0
  43. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  44. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +2 -1
  45. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  46. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue.js +1 -1
  47. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js +6 -0
  48. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js.map +1 -1
  49. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js +3 -3
  50. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js.map +1 -1
  51. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
  52. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +16 -0
  53. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  54. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +11 -5
  55. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  56. package/dist/esm/components/CnSavedViewMenuSettings/CnSavedViewMenuSettings.vue.js +1 -1
  57. package/dist/esm/components/CnSavedViewMenuSettings/CnSavedViewMenuSettings.vue2.js.map +1 -1
  58. package/dist/esm/components/CnSavedViewMenuSettings/CnSavedViewMenuSettings.vue3.js +1 -0
  59. package/dist/esm/components/CnSavedViewMenuSettings/CnSavedViewMenuSettings.vue3.js.map +1 -1
  60. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue.js +1 -1
  61. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue2.js.map +1 -1
  62. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue3.js.map +1 -1
  63. package/dist/esm/composables/indexSources.js +78 -12
  64. package/dist/esm/composables/indexSources.js.map +1 -1
  65. package/dist/esm/l10n/en.json.js +12 -1
  66. package/dist/esm/l10n/en.json.js.map +1 -1
  67. package/dist/esm/l10n/nl.json.js +12 -1
  68. package/dist/esm/l10n/nl.json.js.map +1 -1
  69. package/dist/esm/library-style.js +1 -1
  70. package/dist/esm/nextcloud-vue.css +685 -512
  71. package/dist/esm/utils/resolveFilterTokens.js +9 -0
  72. package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
  73. package/dist/esm/utils/sentinelTokens.js +1 -1
  74. package/dist/esm/utils/sentinelTokens.js.map +1 -1
  75. package/dist/nextcloud-vue.cjs.js +580 -236
  76. package/dist/nextcloud-vue.cjs.js.map +2 -2
  77. package/dist/nextcloud-vue.css +685 -512
  78. package/l10n/en.json +12 -1
  79. package/l10n/nl.json +12 -1
  80. package/package.json +1 -1
  81. package/src/components/CnCellRenderer/CnCellRenderer.vue +6 -4
  82. package/src/components/CnChartWidget/CnChartWidget.vue +4 -0
  83. package/src/components/CnDetailCard/CnDetailCard.md +1 -0
  84. package/src/components/CnDetailCard/CnDetailCard.vue +25 -1
  85. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +2 -0
  86. package/src/components/CnFactsWidget/CnFactsWidget.vue +6 -4
  87. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +54 -1
  88. package/src/components/CnIndexPage/CnIndexPage.vue +135 -7
  89. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +10 -2
  90. package/src/components/CnMailTab/dashboardRegistration.js +4 -0
  91. package/src/components/CnNotificationMatrix/CnNotificationMatrix.vue +4 -0
  92. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +1 -1
  93. package/src/components/CnObjectSidebar/CnFilesTab.vue +3 -0
  94. package/src/components/CnOrderedListField/CnOrderedListField.vue +11 -1
  95. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +22 -3
  96. package/src/components/CnSavedViewMenuSettings/CnSavedViewMenuSettings.vue +1 -0
  97. package/src/components/CnUserSettingsDialog/CnUserSettingsDialog.vue +3 -0
  98. package/src/composables/indexSources.js +78 -12
  99. package/src/css/dialog.css +26 -0
  100. package/src/css/index-page.css +5 -0
  101. package/src/css/look-board-detail.css +36 -0
  102. package/src/css/look-board-index.css +21 -0
  103. package/src/css/look-board.css +47 -0
  104. package/src/utils/resolveFilterTokens.js +9 -0
  105. package/src/utils/sentinelTokens.js +1 -1
@@ -3601,7 +3601,10 @@ var translations$s = {
3601
3601
  "Talk to {agent}": "Talk to {agent}",
3602
3602
  "Search a person or group": "Search a person or group",
3603
3603
  "Now: by {order}.": "Now: by {order}.",
3604
- "You save the order along with a view.": "You save the order along with a view."
3604
+ "You save the order along with a view.": "You save the order along with a view.",
3605
+ me: "me",
3606
+ "Open profile": "Open profile",
3607
+ "Link a contact from your address book.": "Link a contact from your address book."
3605
3608
  };
3606
3609
  var plurals$1 = {
3607
3610
  "{count} member": [
@@ -3802,6 +3805,14 @@ var plurals$1 = {
3802
3805
  "within %n day": [
3803
3806
  "within %n day",
3804
3807
  "within %n days"
3808
+ ],
3809
+ "%n other": [
3810
+ "%n other",
3811
+ "%n others"
3812
+ ],
3813
+ "{names} has no location, so it is not on the map.": [
3814
+ "{names} has no location, so it is not on the map.",
3815
+ "{names} have no location, so they are not on the map."
3805
3816
  ]
3806
3817
  };
3807
3818
  var en = {
@@ -6602,7 +6613,10 @@ var translations$c = {
6602
6613
  "Talk to {agent}": "Praten met {agent}",
6603
6614
  "Search a person or group": "Zoek een persoon of groep",
6604
6615
  "Now: by {order}.": "Nu: op {order}.",
6605
- "You save the order along with a view.": "De sortering bewaart u mee in een weergave."
6616
+ "You save the order along with a view.": "De sortering bewaart u mee in een weergave.",
6617
+ me: "ik",
6618
+ "Open profile": "Profiel openen",
6619
+ "Link a contact from your address book.": "Koppel een contact uit je adresboek."
6606
6620
  };
6607
6621
  var plurals = {
6608
6622
  "{count} member": [
@@ -6820,6 +6834,14 @@ var plurals = {
6820
6834
  "within %n day": [
6821
6835
  "binnen %n dag",
6822
6836
  "binnen %n dagen"
6837
+ ],
6838
+ "%n other": [
6839
+ "%n andere",
6840
+ "%n andere"
6841
+ ],
6842
+ "{names} has no location, so it is not on the map.": [
6843
+ "{names} heeft geen locatie, dus staat niet op de kaart.",
6844
+ "{names} hebben geen locatie, dus staan ze niet op de kaart."
6823
6845
  ]
6824
6846
  };
6825
6847
  var nl = {
@@ -7256,9 +7278,9 @@ const _hoisted_4$6l = {
7256
7278
  "data-testid": "cn-ai-history-list-edit"
7257
7279
  };
7258
7280
  const _hoisted_5$5D = { class: "cn-ai-history-list__edit-actions" };
7259
- const _hoisted_6$5b = ["onClick"];
7260
- const _hoisted_7$4J = { class: "cn-ai-history-list__item-title" };
7261
- const _hoisted_8$4h = {
7281
+ const _hoisted_6$5c = ["onClick"];
7282
+ const _hoisted_7$4K = { class: "cn-ai-history-list__item-title" };
7283
+ const _hoisted_8$4i = {
7262
7284
  key: 0,
7263
7285
  class: "cn-ai-history-list__item-desc"
7264
7286
  };
@@ -7360,12 +7382,12 @@ function render$8G(_ctx, _cache, $props, $setup, $data, $options) {
7360
7382
  class: "cn-ai-history-list__item-button",
7361
7383
  onClick: $event => (_ctx.$emit('select', conv.uuid))
7362
7384
  }, [
7363
- vue$1.createElementVNode("span", _hoisted_7$4J, vue$1.toDisplayString(conv.title || $options.cnTranslate('New conversation')), 1 /* TEXT */),
7385
+ vue$1.createElementVNode("span", _hoisted_7$4K, vue$1.toDisplayString(conv.title || $options.cnTranslate('New conversation')), 1 /* TEXT */),
7364
7386
  (conv.description)
7365
- ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_8$4h, vue$1.toDisplayString(conv.description), 1 /* TEXT */))
7387
+ ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_8$4i, vue$1.toDisplayString(conv.description), 1 /* TEXT */))
7366
7388
  : vue$1.createCommentVNode("v-if", true),
7367
7389
  vue$1.createElementVNode("span", _hoisted_9$3Y, vue$1.toDisplayString($options.formatRelative(conv.updatedAt || conv.createdAt)), 1 /* TEXT */)
7368
- ], 8 /* PROPS */, _hoisted_6$5b),
7390
+ ], 8 /* PROPS */, _hoisted_6$5c),
7369
7391
  vue$1.createElementVNode("button", {
7370
7392
  type: "button",
7371
7393
  class: "cn-ai-history-list__item-edit-btn",
@@ -10005,9 +10027,9 @@ const _hoisted_2$7U = {
10005
10027
  const _hoisted_3$73 = { class: "cn-ai-input__chip-name" };
10006
10028
  const _hoisted_4$6k = ["aria-label", "disabled", "data-testid", "onClick"];
10007
10029
  const _hoisted_5$5C = { class: "cn-ai-input__row" };
10008
- const _hoisted_6$5a = ["aria-label", "title", "aria-pressed", "aria-disabled", "disabled", "data-engine"];
10009
- const _hoisted_7$4I = ["aria-label", "title", "aria-pressed", "disabled"];
10010
- const _hoisted_8$4g = ["aria-label", "disabled", "placeholder"];
10030
+ const _hoisted_6$5b = ["aria-label", "title", "aria-pressed", "aria-disabled", "disabled", "data-engine"];
10031
+ const _hoisted_7$4J = ["aria-label", "title", "aria-pressed", "disabled"];
10032
+ const _hoisted_8$4h = ["aria-label", "disabled", "placeholder"];
10011
10033
  const _hoisted_9$3X = ["aria-label", "disabled"];
10012
10034
 
10013
10035
  function render$8F(_ctx, _cache, $props, $setup, $data, $options) {
@@ -10111,7 +10133,7 @@ function render$8F(_ctx, _cache, $props, $setup, $data, $options) {
10111
10133
  key: 2,
10112
10134
  size: 20
10113
10135
  }))
10114
- ], 10 /* CLASS, PROPS */, _hoisted_6$5a))
10136
+ ], 10 /* CLASS, PROPS */, _hoisted_6$5b))
10115
10137
  : vue$1.createCommentVNode("v-if", true),
10116
10138
  ($options.conversationButtonVisible)
10117
10139
  ? (vue$1.openBlock(), vue$1.createElementBlock("button", {
@@ -10134,7 +10156,7 @@ function render$8F(_ctx, _cache, $props, $setup, $data, $options) {
10134
10156
  key: 1,
10135
10157
  size: 20
10136
10158
  }))
10137
- ], 10 /* CLASS, PROPS */, _hoisted_7$4I))
10159
+ ], 10 /* CLASS, PROPS */, _hoisted_7$4J))
10138
10160
  : vue$1.createCommentVNode("v-if", true),
10139
10161
  vue$1.createVNode(_component_NcActions, {
10140
10162
  class: "cn-ai-input__attach-menu",
@@ -10205,7 +10227,7 @@ function render$8F(_ctx, _cache, $props, $setup, $data, $options) {
10205
10227
  _cache[5] || (_cache[5] = vue$1.withKeys(vue$1.withModifiers((...args) => ($options.handleShiftEnter && $options.handleShiftEnter(...args)), ["shift","exact"]), ["enter"]))
10206
10228
  ],
10207
10229
  onInput: _cache[6] || (_cache[6] = (...args) => ($options.autoGrow && $options.autoGrow(...args)))
10208
- }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_8$4g), [
10230
+ }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_8$4h), [
10209
10231
  [vue$1.vModelText, $data.inputText]
10210
10232
  ]),
10211
10233
  vue$1.createElementVNode("button", {
@@ -10431,13 +10453,13 @@ const _hoisted_4$6j = {
10431
10453
  class: "cn-ai-message-list__bubble cn-ai-message-list__bubble--user"
10432
10454
  };
10433
10455
  const _hoisted_5$5B = { class: "cn-ai-message-list__user-text" };
10434
- const _hoisted_6$59 = {
10456
+ const _hoisted_6$5a = {
10435
10457
  key: 0,
10436
10458
  class: "cn-ai-message-list__attachments",
10437
10459
  "data-testid": "cn-ai-message-attachments"
10438
10460
  };
10439
- const _hoisted_7$4H = ["href"];
10440
- const _hoisted_8$4f = {
10461
+ const _hoisted_7$4I = ["href"];
10462
+ const _hoisted_8$4g = {
10441
10463
  key: 1,
10442
10464
  class: "cn-ai-message-list__chip"
10443
10465
  };
@@ -10501,7 +10523,7 @@ function render$8E(_ctx, _cache, $props, $setup, $data, $options) {
10501
10523
  ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_4$6j, [
10502
10524
  vue$1.createElementVNode("span", _hoisted_5$5B, vue$1.toDisplayString(message.content), 1 /* TEXT */),
10503
10525
  ($options.hasAttachments(message))
10504
- ? (vue$1.openBlock(), vue$1.createElementBlock("ul", _hoisted_6$59, [
10526
+ ? (vue$1.openBlock(), vue$1.createElementBlock("ul", _hoisted_6$5a, [
10505
10527
  (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList(message.attachments, (file, fIdx) => {
10506
10528
  return (vue$1.openBlock(), vue$1.createElementBlock("li", {
10507
10529
  key: fIdx,
@@ -10514,8 +10536,8 @@ function render$8E(_ctx, _cache, $props, $setup, $data, $options) {
10514
10536
  href: $options.fileHref(file),
10515
10537
  target: "_blank",
10516
10538
  rel: "noopener noreferrer"
10517
- }, vue$1.toDisplayString(file.name), 9 /* TEXT, PROPS */, _hoisted_7$4H))
10518
- : (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_8$4f, vue$1.toDisplayString(file.name), 1 /* TEXT */))
10539
+ }, vue$1.toDisplayString(file.name), 9 /* TEXT, PROPS */, _hoisted_7$4I))
10540
+ : (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_8$4g, vue$1.toDisplayString(file.name), 1 /* TEXT */))
10519
10541
  ]))
10520
10542
  }), 128 /* KEYED_FRAGMENT */))
10521
10543
  ]))
@@ -11707,12 +11729,12 @@ const _hoisted_2$7S = {
11707
11729
  const _hoisted_3$71 = ["title"];
11708
11730
  const _hoisted_4$6i = { class: "cn-ai-chat-window__identity-name" };
11709
11731
  const _hoisted_5$5A = { class: "cn-ai-chat-window__controls" };
11710
- const _hoisted_6$58 = {
11732
+ const _hoisted_6$59 = {
11711
11733
  key: 1,
11712
11734
  class: "cn-ai-chat-window__body"
11713
11735
  };
11714
- const _hoisted_7$4G = { class: "cn-ai-chat-window__messages" };
11715
- const _hoisted_8$4e = {
11736
+ const _hoisted_7$4H = { class: "cn-ai-chat-window__messages" };
11737
+ const _hoisted_8$4f = {
11716
11738
  class: "cn-ai-chat-window__start",
11717
11739
  "data-testid": "cn-ai-chat-tab-start"
11718
11740
  };
@@ -11952,15 +11974,15 @@ function render$8D(_ctx, _cache, $props, $setup, $data, $options) {
11952
11974
  ]))
11953
11975
  : vue$1.createCommentVNode("v-if", true),
11954
11976
  ($data.activeView === 'chat')
11955
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_6$58, [
11956
- vue$1.createElementVNode("div", _hoisted_7$4G, [
11977
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_6$59, [
11978
+ vue$1.createElementVNode("div", _hoisted_7$4H, [
11957
11979
  vue$1.createVNode(_component_CnAiMessageList, {
11958
11980
  messages: $props.streamState.messages,
11959
11981
  currentText: $props.streamState.currentText,
11960
11982
  isStreaming: $props.streamState.isStreaming
11961
11983
  }, {
11962
11984
  empty: vue$1.withCtx(() => [
11963
- vue$1.createElementVNode("div", _hoisted_8$4e, [
11985
+ vue$1.createElementVNode("div", _hoisted_8$4f, [
11964
11986
  vue$1.createVNode(_component_NcEmptyContent, { name: $options.agentLabel }, {
11965
11987
  icon: vue$1.withCtx(() => [
11966
11988
  vue$1.createVNode(_component_Creation, { size: 40 })
@@ -14750,6 +14772,34 @@ function fillMyTeamToken(text, name) {
14750
14772
  .replaceAll('{myTeam}', name)
14751
14773
  }
14752
14774
 
14775
+ /**
14776
+ * SPDX-FileCopyrightText: 2026 Conduction B.V.
14777
+ * SPDX-License-Identifier: EUPL-1.2
14778
+ *
14779
+ * readPath: read a dot-path off an object.
14780
+ *
14781
+ * Its own module, with no imports, so the rules that read a record's fields
14782
+ * (stage, checklist, due date) do not hang on a module a host or a test may
14783
+ * replace.
14784
+ *
14785
+ * @module utils/readPath
14786
+ */
14787
+
14788
+ /**
14789
+ * Read a dot-path (`'a.b.c'`) off an object.
14790
+ *
14791
+ * @param {object|null} data The source object.
14792
+ * @param {string} [path] Dot-path into the object. Empty returns the object itself.
14793
+ * @return {unknown} The value at the path, or undefined when a segment is missing.
14794
+ * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage
14795
+ */
14796
+ function readPath$8(data, path) {
14797
+ if (!path) {
14798
+ return data
14799
+ }
14800
+ return String(path).split('.').reduce((value, key) => (value === null || value === undefined ? value : value[key]), data)
14801
+ }
14802
+
14753
14803
  /**
14754
14804
  * SPDX-FileCopyrightText: 2026 Conduction B.V.
14755
14805
  * SPDX-License-Identifier: EUPL-1.2
@@ -14940,7 +14990,7 @@ const SENTINEL_VOCABULARY = Object.freeze({
14940
14990
  members: ['@subject.<field>'],
14941
14991
  },
14942
14992
  context: {
14943
- resolver: 'CnDetailPage visibilityContext (detailWidgetVisibility), read by a grid widget\'s or a tab\'s visibleWhen',
14993
+ resolver: 'CnDetailPage visibilityContext (detailWidgetVisibility), read by a grid widget\'s or a tab\'s visibleWhen, and by a widget\'s props (CnDetailWidgetHost)',
14944
14994
  description: 'A record the detail page reads beside the loaded one, named in `config.visibilityContext` (a case\'s case type). A `visibleWhen` field reads it as `@context.<name>.<field>`, dot paths allowed (`@context.caseType.category`).',
14945
14995
  members: ['@context.<name>.<field>'],
14946
14996
  },
@@ -15268,6 +15318,9 @@ function resetGroupsPeek() {
15268
15318
  * - `@object.<field>` → a field off the current detail-page object (needs `ctx`)
15269
15319
  * - `@workspace.<key>` → a value off the page-level workspace context (needs `ctx.workspace`)
15270
15320
  * - `@config.<key>` → a value off the page-level app config (needs `ctx.config`)
15321
+ * - `@context.<name>.<path>` → a value off a record the detail page reads beside
15322
+ * the loaded one (needs `ctx.context`); left as written
15323
+ * while that record is not read
15271
15324
  *
15272
15325
  * `@objectId` / `@object.<field>` are OBJECT-CONTEXT tokens: they resolve only
15273
15326
  * when a `ctx` `{ objectId, object }` is supplied (a detail page provides it via
@@ -15414,6 +15467,11 @@ function resolveFilterValue(v, ctx) {
15414
15467
  }
15415
15468
  return v
15416
15469
  }
15470
+ if (v.startsWith('@context.')) {
15471
+ // @spec openspec/changes/files-second-line-fields/specs/files-second-line-fields/spec.md#requirement-widget-props-can-read-the-detail-pages-context-records
15472
+ const found = ctx && ctx.context ? readPath$8(ctx.context, v.slice('@context.'.length)) : undefined;
15473
+ return found === undefined ? v : found
15474
+ }
15417
15475
  if (v.startsWith('@workspace.')) {
15418
15476
  // A trailing `?` marks the token OPTIONAL: when unset the caller drops the
15419
15477
  // filter key (show all) instead of waiting (see hasUnresolvedTokens /
@@ -21892,34 +21950,6 @@ function normalizeAllowed(answer) {
21892
21950
  return { keys, reasons }
21893
21951
  }
21894
21952
 
21895
- /**
21896
- * SPDX-FileCopyrightText: 2026 Conduction B.V.
21897
- * SPDX-License-Identifier: EUPL-1.2
21898
- *
21899
- * readPath: read a dot-path off an object.
21900
- *
21901
- * Its own module, with no imports, so the rules that read a record's fields
21902
- * (stage, checklist, due date) do not hang on a module a host or a test may
21903
- * replace.
21904
- *
21905
- * @module utils/readPath
21906
- */
21907
-
21908
- /**
21909
- * Read a dot-path (`'a.b.c'`) off an object.
21910
- *
21911
- * @param {object|null} data The source object.
21912
- * @param {string} [path] Dot-path into the object. Empty returns the object itself.
21913
- * @return {unknown} The value at the path, or undefined when a segment is missing.
21914
- * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage
21915
- */
21916
- function readPath$8(data, path) {
21917
- if (!path) {
21918
- return data
21919
- }
21920
- return String(path).split('.').reduce((value, key) => (value === null || value === undefined ? value : value[key]), data)
21921
- }
21922
-
21923
21953
  /**
21924
21954
  * SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>
21925
21955
  * SPDX-License-Identifier: EUPL-1.2
@@ -26651,12 +26681,12 @@ const _hoisted_4$6h = {
26651
26681
  "data-testid": "cn-chart-widget-bar-list"
26652
26682
  };
26653
26683
  const _hoisted_5$5z = { class: "cn-chart-widget__bar-list" };
26654
- const _hoisted_6$57 = { class: "cn-chart-widget__bar-label" };
26655
- const _hoisted_7$4F = {
26684
+ const _hoisted_6$58 = { class: "cn-chart-widget__bar-label" };
26685
+ const _hoisted_7$4G = {
26656
26686
  class: "cn-chart-widget__bar-track",
26657
26687
  "aria-hidden": "true"
26658
26688
  };
26659
- const _hoisted_8$4d = { class: "cn-chart-widget__bar-value" };
26689
+ const _hoisted_8$4e = { class: "cn-chart-widget__bar-value" };
26660
26690
  const _hoisted_9$3U = {
26661
26691
  key: 0,
26662
26692
  class: "cn-chart-widget__footnote"
@@ -26759,14 +26789,14 @@ function render$8y(_ctx, _cache, $props, $setup, $data, $options) {
26759
26789
  onClick: $event => (row.drill ? $options.onDataPointSelection(null, null, { dataPointIndex: row.index }) : null)
26760
26790
  }, {
26761
26791
  default: vue$1.withCtx(() => [
26762
- vue$1.createElementVNode("span", _hoisted_6$57, vue$1.toDisplayString(row.label), 1 /* TEXT */),
26763
- vue$1.createElementVNode("span", _hoisted_7$4F, [
26792
+ vue$1.createElementVNode("span", _hoisted_6$58, vue$1.toDisplayString(row.label), 1 /* TEXT */),
26793
+ vue$1.createElementVNode("span", _hoisted_7$4G, [
26764
26794
  vue$1.createElementVNode("span", {
26765
26795
  class: "cn-chart-widget__bar-fill",
26766
26796
  style: vue$1.normalizeStyle({ width: `${row.share}%` })
26767
26797
  }, null, 4 /* STYLE */)
26768
26798
  ]),
26769
- vue$1.createElementVNode("span", _hoisted_8$4d, vue$1.toDisplayString(row.text), 1 /* TEXT */)
26799
+ vue$1.createElementVNode("span", _hoisted_8$4e, vue$1.toDisplayString(row.text), 1 /* TEXT */)
26770
26800
  ]),
26771
26801
  _: 2 /* DYNAMIC */
26772
26802
  }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["type", "class", "onClick"]))
@@ -26845,7 +26875,7 @@ function render$8y(_ctx, _cache, $props, $setup, $data, $options) {
26845
26875
  }
26846
26876
 
26847
26877
  script$8z.render = render$8y;
26848
- script$8z.__scopeId = "data-v-8e52ae5c";
26878
+ script$8z.__scopeId = "data-v-56aabaf0";
26849
26879
  const __sfc_main$8t = script$8z;
26850
26880
 
26851
26881
  /**
@@ -27771,15 +27801,15 @@ const _hoisted_2$7N = { class: "cn-chart-widget-form__section" };
27771
27801
  const _hoisted_3$6_ = { class: "cn-chart-widget-form__row2" };
27772
27802
  const _hoisted_4$6g = { class: "cn-chart-widget-form__section" };
27773
27803
  const _hoisted_5$5y = { class: "cn-chart-widget-form__row2" };
27774
- const _hoisted_6$56 = {
27804
+ const _hoisted_6$57 = {
27775
27805
  key: 0,
27776
27806
  class: "cn-chart-widget-form__row2"
27777
27807
  };
27778
- const _hoisted_7$4E = {
27808
+ const _hoisted_7$4F = {
27779
27809
  key: 1,
27780
27810
  class: "cn-chart-widget-form__row2"
27781
27811
  };
27782
- const _hoisted_8$4c = { class: "cn-chart-widget-form__row2" };
27812
+ const _hoisted_8$4d = { class: "cn-chart-widget-form__row2" };
27783
27813
  const _hoisted_9$3T = {
27784
27814
  key: 2,
27785
27815
  class: "cn-chart-widget-form__hint"
@@ -27828,7 +27858,7 @@ function render$8u(_ctx, _cache, $props, $setup, $data, $options) {
27828
27858
  }, null, 8 /* PROPS */, ["register", "schema"])
27829
27859
  ]),
27830
27860
  ($data.mode === 'timeseries')
27831
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_6$56, [
27861
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_6$57, [
27832
27862
  vue$1.createVNode(_component_CnFieldPicker, {
27833
27863
  value: $data.bucket.field,
27834
27864
  label: $options.t('nextcloud-vue', 'Date field'),
@@ -27844,7 +27874,7 @@ function render$8u(_ctx, _cache, $props, $setup, $data, $options) {
27844
27874
  "onUpdate:modelValue": _cache[5] || (_cache[5] = $event => ($options.updateBucket('interval', $event)))
27845
27875
  }, null, 8 /* PROPS */, ["modelValue", "options", "inputLabel"])
27846
27876
  ]))
27847
- : (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_7$4E, [
27877
+ : (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_7$4F, [
27848
27878
  vue$1.createVNode(_component_CnFieldPicker, {
27849
27879
  value: $data.group.field,
27850
27880
  label: $options.t('nextcloud-vue', 'Group by field'),
@@ -27866,7 +27896,7 @@ function render$8u(_ctx, _cache, $props, $setup, $data, $options) {
27866
27896
  "onUpdate:modelValue": _cache[8] || (_cache[8] = $event => ($options.updateGroup('limit', Number($event) || 0)))
27867
27897
  }, null, 8 /* PROPS */, ["modelValue", "label"])
27868
27898
  ])),
27869
- vue$1.createElementVNode("div", _hoisted_8$4c, [
27899
+ vue$1.createElementVNode("div", _hoisted_8$4d, [
27870
27900
  vue$1.createVNode(_component_NcSelect, {
27871
27901
  modelValue: $data.metric,
27872
27902
  options: $options.metricOptions,
@@ -29259,9 +29289,9 @@ const _hoisted_5$5x = {
29259
29289
  key: 0,
29260
29290
  class: "cn-quick-filter-bar__label"
29261
29291
  };
29262
- const _hoisted_6$55 = ["role", "aria-label"];
29263
- const _hoisted_7$4D = ["role", "aria-selected", "aria-pressed", "onClick"];
29264
- const _hoisted_8$4b = { class: "cn-quick-filter-bar__label" };
29292
+ const _hoisted_6$56 = ["role", "aria-label"];
29293
+ const _hoisted_7$4E = ["role", "aria-selected", "aria-pressed", "onClick"];
29294
+ const _hoisted_8$4c = { class: "cn-quick-filter-bar__label" };
29265
29295
  const _hoisted_9$3S = {
29266
29296
  key: 1,
29267
29297
  class: "cn-quick-filter-bar__count",
@@ -29360,13 +29390,13 @@ function render$8r(_ctx, _cache, $props, $setup, $data, $options) {
29360
29390
  class: "cn-quick-filter-bar__icon"
29361
29391
  }, null, 8 /* PROPS */, ["name"]))
29362
29392
  : vue$1.createCommentVNode("v-if", true),
29363
- vue$1.createElementVNode("span", _hoisted_8$4b, vue$1.toDisplayString($options.tl(entry.tab.label)), 1 /* TEXT */),
29393
+ vue$1.createElementVNode("span", _hoisted_8$4c, vue$1.toDisplayString($options.tl(entry.tab.label)), 1 /* TEXT */),
29364
29394
  ($options.countOf(entry) !== null)
29365
29395
  ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_9$3S, vue$1.toDisplayString(' ' + $options.countOf(entry)), 1 /* TEXT */))
29366
29396
  : vue$1.createCommentVNode("v-if", true)
29367
- ], 10 /* CLASS, PROPS */, _hoisted_7$4D))
29397
+ ], 10 /* CLASS, PROPS */, _hoisted_7$4E))
29368
29398
  }), 128 /* KEYED_FRAGMENT */))
29369
- ], 8 /* PROPS */, _hoisted_6$55),
29399
+ ], 8 /* PROPS */, _hoisted_6$56),
29370
29400
  ($options.overflowEntries.length > 0)
29371
29401
  ? (vue$1.openBlock(), vue$1.createBlock(_component_NcPopover, {
29372
29402
  key: 1,
@@ -29890,18 +29920,18 @@ const _hoisted_5$5w = {
29890
29920
  key: 1,
29891
29921
  class: "cn-conversation-thread__header-actions"
29892
29922
  };
29893
- const _hoisted_6$54 = {
29923
+ const _hoisted_6$55 = {
29894
29924
  key: 2,
29895
29925
  class: "cn-conversation-thread__empty",
29896
29926
  role: "status",
29897
29927
  "data-testid": "cn-conversation-loading"
29898
29928
  };
29899
- const _hoisted_7$4C = {
29929
+ const _hoisted_7$4D = {
29900
29930
  key: 3,
29901
29931
  class: "cn-conversation-thread__empty",
29902
29932
  "data-testid": "cn-conversation-empty"
29903
29933
  };
29904
- const _hoisted_8$4a = {
29934
+ const _hoisted_8$4b = {
29905
29935
  key: 4,
29906
29936
  class: "cn-conversation-thread__empty",
29907
29937
  "data-testid": "cn-conversation-channel-empty"
@@ -30011,11 +30041,11 @@ function render$8q(_ctx, _cache, $props, $setup, $data, $options) {
30011
30041
  }, null, 8 /* PROPS */, ["tabs", "activeIndex", "onUpdate:activeIndex"]))
30012
30042
  : vue$1.createCommentVNode("v-if", true),
30013
30043
  ($props.loading && $options.rows.length === 0)
30014
- ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_6$54, vue$1.toDisplayString($options.loadingText), 1 /* TEXT */))
30044
+ ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_6$55, vue$1.toDisplayString($options.loadingText), 1 /* TEXT */))
30015
30045
  : ($options.rows.length === 0)
30016
- ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_7$4C, vue$1.toDisplayString($props.emptyText), 1 /* TEXT */))
30046
+ ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_7$4D, vue$1.toDisplayString($props.emptyText), 1 /* TEXT */))
30017
30047
  : ($options.shownRows.length === 0)
30018
- ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_8$4a, vue$1.toDisplayString($props.emptyText), 1 /* TEXT */))
30048
+ ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_8$4b, vue$1.toDisplayString($props.emptyText), 1 /* TEXT */))
30019
30049
  : (vue$1.openBlock(), vue$1.createElementBlock("ol", _hoisted_9$3R, [
30020
30050
  (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList($options.shownRows, (row) => {
30021
30051
  return (vue$1.openBlock(), vue$1.createElementBlock("li", {
@@ -32306,9 +32336,9 @@ const _hoisted_5$5u = {
32306
32336
  key: 2,
32307
32337
  class: "cn-document-review-list__rows"
32308
32338
  };
32309
- const _hoisted_6$53 = { class: "cn-document-review-list__text" };
32310
- const _hoisted_7$4B = ["href"];
32311
- const _hoisted_8$49 = {
32339
+ const _hoisted_6$54 = { class: "cn-document-review-list__text" };
32340
+ const _hoisted_7$4C = ["href"];
32341
+ const _hoisted_8$4a = {
32312
32342
  key: 1,
32313
32343
  class: "cn-document-review-list__name"
32314
32344
  };
@@ -32361,14 +32391,14 @@ function render$8k(_ctx, _cache, $props, $setup, $data, $options) {
32361
32391
  size: 22,
32362
32392
  "aria-hidden": "true"
32363
32393
  }),
32364
- vue$1.createElementVNode("span", _hoisted_6$53, [
32394
+ vue$1.createElementVNode("span", _hoisted_6$54, [
32365
32395
  (row.href)
32366
32396
  ? (vue$1.openBlock(), vue$1.createElementBlock("a", {
32367
32397
  key: 0,
32368
32398
  class: "cn-document-review-list__name",
32369
32399
  href: row.href
32370
- }, vue$1.toDisplayString(row.name), 9 /* TEXT, PROPS */, _hoisted_7$4B))
32371
- : (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_8$49, vue$1.toDisplayString(row.name), 1 /* TEXT */)),
32400
+ }, vue$1.toDisplayString(row.name), 9 /* TEXT, PROPS */, _hoisted_7$4C))
32401
+ : (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_8$4a, vue$1.toDisplayString(row.name), 1 /* TEXT */)),
32372
32402
  (row.meta)
32373
32403
  ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_9$3Q, vue$1.toDisplayString(row.meta), 1 /* TEXT */))
32374
32404
  : vue$1.createCommentVNode("v-if", true)
@@ -37010,9 +37040,9 @@ const _hoisted_5$5t = {
37010
37040
  key: 2,
37011
37041
  class: "cn-calendar-event-picker__calendar-list"
37012
37042
  };
37013
- const _hoisted_6$52 = ["onClick", "onKeyup"];
37014
- const _hoisted_7$4A = { class: "cn-calendar-event-picker__calendar-name" };
37015
- const _hoisted_8$48 = {
37043
+ const _hoisted_6$53 = ["onClick", "onKeyup"];
37044
+ const _hoisted_7$4B = { class: "cn-calendar-event-picker__calendar-name" };
37045
+ const _hoisted_8$49 = {
37016
37046
  key: 3,
37017
37047
  class: "cn-calendar-event-picker__empty"
37018
37048
  };
@@ -37107,11 +37137,11 @@ function render$8h(_ctx, _cache, $props, $setup, $data, $options) {
37107
37137
  class: "cn-calendar-event-picker__color-swatch",
37108
37138
  style: vue$1.normalizeStyle({ backgroundColor: cal.color || 'var(--color-primary-element)' })
37109
37139
  }, null, 4 /* STYLE */),
37110
- vue$1.createElementVNode("span", _hoisted_7$4A, vue$1.toDisplayString(cal.displayName), 1 /* TEXT */)
37111
- ], 40 /* PROPS, NEED_HYDRATION */, _hoisted_6$52))
37140
+ vue$1.createElementVNode("span", _hoisted_7$4B, vue$1.toDisplayString(cal.displayName), 1 /* TEXT */)
37141
+ ], 40 /* PROPS, NEED_HYDRATION */, _hoisted_6$53))
37112
37142
  }), 128 /* KEYED_FRAGMENT */))
37113
37143
  ]))
37114
- : (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_8$48, vue$1.toDisplayString($props.noCalendarsLabel), 1 /* TEXT */))
37144
+ : (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_8$49, vue$1.toDisplayString($props.noCalendarsLabel), 1 /* TEXT */))
37115
37145
  ]))
37116
37146
  : ($data.step === 'events')
37117
37147
  ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_9$3P, [
@@ -37550,16 +37580,16 @@ const _hoisted_3$6Q = {
37550
37580
  };
37551
37581
  const _hoisted_4$69 = { class: "cn-events-card__summary" };
37552
37582
  const _hoisted_5$5s = { class: "cn-events-card__when" };
37553
- const _hoisted_6$51 = {
37583
+ const _hoisted_6$52 = {
37554
37584
  key: 0,
37555
37585
  class: "cn-events-card__where"
37556
37586
  };
37557
- const _hoisted_7$4z = {
37587
+ const _hoisted_7$4A = {
37558
37588
  key: 3,
37559
37589
  class: "cn-events-card__muted",
37560
37590
  "data-testid": "cn-events-card-empty"
37561
37591
  };
37562
- const _hoisted_8$47 = {
37592
+ const _hoisted_8$48 = {
37563
37593
  key: 4,
37564
37594
  class: "cn-events-card__muted",
37565
37595
  role: "alert",
@@ -37590,14 +37620,14 @@ function render$8g(_ctx, _cache, $props, $setup, $data, $options) {
37590
37620
  vue$1.createElementVNode("span", _hoisted_4$69, vue$1.toDisplayString($options.summaryOf(event)), 1 /* TEXT */),
37591
37621
  vue$1.createElementVNode("span", _hoisted_5$5s, vue$1.toDisplayString($options.whenOf(event)), 1 /* TEXT */),
37592
37622
  (event.location)
37593
- ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_6$51, vue$1.toDisplayString(event.location), 1 /* TEXT */))
37623
+ ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_6$52, vue$1.toDisplayString(event.location), 1 /* TEXT */))
37594
37624
  : vue$1.createCommentVNode("v-if", true)
37595
37625
  ]))
37596
37626
  }), 128 /* KEYED_FRAGMENT */))
37597
37627
  ]))
37598
- : (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_7$4z, vue$1.toDisplayString($options.emptyText), 1 /* TEXT */)),
37628
+ : (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_7$4A, vue$1.toDisplayString($options.emptyText), 1 /* TEXT */)),
37599
37629
  ($data.linkError)
37600
- ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_8$47, vue$1.toDisplayString($data.linkError), 1 /* TEXT */))
37630
+ ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_8$48, vue$1.toDisplayString($data.linkError), 1 /* TEXT */))
37601
37631
  : vue$1.createCommentVNode("v-if", true),
37602
37632
  ($data.showPicker)
37603
37633
  ? (vue$1.openBlock(), vue$1.createBlock(_component_CnCalendarEventPicker, {
@@ -37916,9 +37946,9 @@ const _hoisted_5$5r = {
37916
37946
  key: 3,
37917
37947
  class: "cn-macros-widget__list"
37918
37948
  };
37919
- const _hoisted_6$50 = ["data-testid"];
37920
- const _hoisted_7$4y = { class: "cn-macros-widget__text" };
37921
- const _hoisted_8$46 = { class: "cn-macros-widget__name" };
37949
+ const _hoisted_6$51 = ["data-testid"];
37950
+ const _hoisted_7$4z = { class: "cn-macros-widget__text" };
37951
+ const _hoisted_8$47 = { class: "cn-macros-widget__name" };
37922
37952
  const _hoisted_9$3O = {
37923
37953
  key: 0,
37924
37954
  class: "cn-macros-widget__hint"
@@ -37943,8 +37973,8 @@ function render$8f(_ctx, _cache, $props, $setup, $data, $options) {
37943
37973
  class: "cn-macros-widget__row",
37944
37974
  "data-testid": `cn-macros-widget-row-${macro.key}`
37945
37975
  }, [
37946
- vue$1.createElementVNode("div", _hoisted_7$4y, [
37947
- vue$1.createElementVNode("span", _hoisted_8$46, vue$1.toDisplayString($options.tr(macro.name)), 1 /* TEXT */),
37976
+ vue$1.createElementVNode("div", _hoisted_7$4z, [
37977
+ vue$1.createElementVNode("span", _hoisted_8$47, vue$1.toDisplayString($options.tr(macro.name)), 1 /* TEXT */),
37948
37978
  (macro.description)
37949
37979
  ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_9$3O, vue$1.toDisplayString($options.tr(macro.description)), 1 /* TEXT */))
37950
37980
  : vue$1.createCommentVNode("v-if", true)
@@ -37959,7 +37989,7 @@ function render$8f(_ctx, _cache, $props, $setup, $data, $options) {
37959
37989
  ]),
37960
37990
  _: 2 /* DYNAMIC */
37961
37991
  }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["disabled", "data-testid", "onClick"])
37962
- ], 8 /* PROPS */, _hoisted_6$50))
37992
+ ], 8 /* PROPS */, _hoisted_6$51))
37963
37993
  }), 128 /* KEYED_FRAGMENT */))
37964
37994
  ])),
37965
37995
  ($data.error)
@@ -39929,7 +39959,7 @@ const _hoisted_5$5q = {
39929
39959
  class: "cn-map-widget__note",
39930
39960
  "data-testid": "cn-map-unplotted"
39931
39961
  };
39932
- const _hoisted_6$4$ = {
39962
+ const _hoisted_6$50 = {
39933
39963
  key: 4,
39934
39964
  class: "cn-map-widget__legend"
39935
39965
  };
@@ -39965,7 +39995,7 @@ function render$8e(_ctx, _cache, $props, $setup, $data, $options) {
39965
39995
  ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_5$5q, vue$1.toDisplayString($options.unplottedLabel), 1 /* TEXT */))
39966
39996
  : vue$1.createCommentVNode("v-if", true),
39967
39997
  (_ctx.$slots.legend || _ctx.$slots.legend)
39968
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_6$4$, [
39998
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_6$50, [
39969
39999
  vue$1.renderSlot(_ctx.$slots, "legend", {
39970
40000
  layers: $options.cfg.layers,
39971
40001
  markers: $options.cfg.markers
@@ -40182,7 +40212,7 @@ const _hoisted_2$7A = {
40182
40212
  const _hoisted_3$6N = { class: "cn-map-widget-form__field" };
40183
40213
  const _hoisted_4$66 = { class: "cn-map-widget-form__label" };
40184
40214
  const _hoisted_5$5p = ["value"];
40185
- const _hoisted_6$4_ = { class: "cn-map-widget-form__row" };
40215
+ const _hoisted_6$4$ = { class: "cn-map-widget-form__row" };
40186
40216
 
40187
40217
  function render$8d(_ctx, _cache, $props, $setup, $data, $options) {
40188
40218
  const _component_CnRegisterSchemaSelect = vue$1.resolveComponent("CnRegisterSchemaSelect");
@@ -40229,7 +40259,7 @@ function render$8d(_ctx, _cache, $props, $setup, $data, $options) {
40229
40259
  placeholder: "400px",
40230
40260
  "onUpdate:modelValue": _cache[4] || (_cache[4] = $event => ($data.height = $event))
40231
40261
  }, null, 8 /* PROPS */, ["modelValue", "label"]),
40232
- vue$1.createElementVNode("div", _hoisted_6$4_, [
40262
+ vue$1.createElementVNode("div", _hoisted_6$4$, [
40233
40263
  vue$1.createVNode(_component_NcTextField, {
40234
40264
  modelValue: String($data.center[0]),
40235
40265
  type: "number",
@@ -40680,12 +40710,12 @@ const _hoisted_4$65 = {
40680
40710
  "aria-hidden": "true"
40681
40711
  };
40682
40712
  const _hoisted_5$5o = { class: "cn-next-step-card__label" };
40683
- const _hoisted_6$4Z = {
40713
+ const _hoisted_6$4_ = {
40684
40714
  key: 0,
40685
40715
  class: "cn-next-step-card__hint"
40686
40716
  };
40687
- const _hoisted_7$4x = { class: "cn-next-step-card__state" };
40688
- const _hoisted_8$45 = {
40717
+ const _hoisted_7$4y = { class: "cn-next-step-card__state" };
40718
+ const _hoisted_8$46 = {
40689
40719
  key: 0,
40690
40720
  class: "cn-next-step-card__side"
40691
40721
  };
@@ -40739,15 +40769,15 @@ function render$8a(_ctx, _cache, $props, $setup, $data, $options) {
40739
40769
  ]),
40740
40770
  vue$1.createElementVNode("span", _hoisted_5$5o, vue$1.toDisplayString(item.label), 1 /* TEXT */),
40741
40771
  (item.hint)
40742
- ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_6$4Z, vue$1.toDisplayString(item.hint), 1 /* TEXT */))
40772
+ ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_6$4_, vue$1.toDisplayString(item.hint), 1 /* TEXT */))
40743
40773
  : vue$1.createCommentVNode("v-if", true),
40744
- vue$1.createElementVNode("span", _hoisted_7$4x, vue$1.toDisplayString(item.done ? $props.doneLabel : $props.todoLabel), 1 /* TEXT */)
40774
+ vue$1.createElementVNode("span", _hoisted_7$4y, vue$1.toDisplayString(item.done ? $props.doneLabel : $props.todoLabel), 1 /* TEXT */)
40745
40775
  ], 2 /* CLASS */))
40746
40776
  }), 128 /* KEYED_FRAGMENT */))
40747
40777
  ])
40748
40778
  ]),
40749
40779
  ($props.actionLabel || _ctx.$slots.action || $props.after)
40750
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_8$45, [
40780
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_8$46, [
40751
40781
  vue$1.renderSlot(_ctx.$slots, "action", {}, () => [
40752
40782
  ($props.actionLabel)
40753
40783
  ? (vue$1.openBlock(), vue$1.createBlock(_component_NcButton, {
@@ -42040,12 +42070,12 @@ const _hoisted_5$5n = {
42040
42070
  "aria-disabled": "true",
42041
42071
  "data-testid": "cn-pagination-next"
42042
42072
  };
42043
- const _hoisted_6$4Y = {
42073
+ const _hoisted_6$4Z = {
42044
42074
  key: 1,
42045
42075
  class: "cn-pagination__info"
42046
42076
  };
42047
- const _hoisted_7$4w = { class: "cn-pagination__page-info" };
42048
- const _hoisted_8$44 = {
42077
+ const _hoisted_7$4x = { class: "cn-pagination__page-info" };
42078
+ const _hoisted_8$45 = {
42049
42079
  key: 2,
42050
42080
  class: "cn-pagination__nav cn-pagination__nav--compact"
42051
42081
  };
@@ -42118,12 +42148,12 @@ function render$88(_ctx, _cache, $props, $setup, $data, $options) {
42118
42148
  ], 64 /* STABLE_FRAGMENT */))
42119
42149
  : vue$1.createCommentVNode("v-if", true),
42120
42150
  (!$options.isBoardVariant)
42121
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_6$4Y, [
42122
- vue$1.createElementVNode("span", _hoisted_7$4w, vue$1.toDisplayString($options.pageInfoText), 1 /* TEXT */)
42151
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_6$4Z, [
42152
+ vue$1.createElementVNode("span", _hoisted_7$4x, vue$1.toDisplayString($options.pageInfoText), 1 /* TEXT */)
42123
42153
  ]))
42124
42154
  : vue$1.createCommentVNode("v-if", true),
42125
42155
  (!$options.isBoardVariant && $props.compact && $props.totalPages > 1)
42126
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_8$44, [
42156
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_8$45, [
42127
42157
  vue$1.createVNode(_component_NcButton, {
42128
42158
  "aria-label": $props.previousLabel,
42129
42159
  disabled: $props.currentPage === 1,
@@ -42539,13 +42569,13 @@ const _hoisted_5$5m = {
42539
42569
  class: "cn-column-filter__hint",
42540
42570
  role: "status"
42541
42571
  };
42542
- const _hoisted_6$4X = {
42572
+ const _hoisted_6$4Y = {
42543
42573
  key: 2,
42544
42574
  class: "cn-column-filter__hint",
42545
42575
  role: "status"
42546
42576
  };
42547
- const _hoisted_7$4v = { class: "cn-column-filter__options" };
42548
- const _hoisted_8$43 = { class: "cn-column-filter__sr-only" };
42577
+ const _hoisted_7$4w = { class: "cn-column-filter__options" };
42578
+ const _hoisted_8$44 = { class: "cn-column-filter__sr-only" };
42549
42579
  const _hoisted_9$3L = ["value", "checked", "onChange"];
42550
42580
  const _hoisted_10$3l = {
42551
42581
  key: 0,
@@ -42614,10 +42644,10 @@ function render$87(_ctx, _cache, $props, $setup, $data, $options) {
42614
42644
  ($props.def.kind === 'reference' && $data.referenceLoading)
42615
42645
  ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_5$5m, vue$1.toDisplayString($options.loadingLabel), 1 /* TEXT */))
42616
42646
  : ($props.def.kind === 'reference' && $data.referenceError)
42617
- ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_6$4X, vue$1.toDisplayString($data.referenceError), 1 /* TEXT */))
42647
+ ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_6$4Y, vue$1.toDisplayString($data.referenceError), 1 /* TEXT */))
42618
42648
  : vue$1.createCommentVNode("v-if", true),
42619
- vue$1.createElementVNode("fieldset", _hoisted_7$4v, [
42620
- vue$1.createElementVNode("legend", _hoisted_8$43, vue$1.toDisplayString($options.headingText), 1 /* TEXT */),
42649
+ vue$1.createElementVNode("fieldset", _hoisted_7$4w, [
42650
+ vue$1.createElementVNode("legend", _hoisted_8$44, vue$1.toDisplayString($options.headingText), 1 /* TEXT */),
42621
42651
  (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList($options.listOptions, (option) => {
42622
42652
  return (vue$1.openBlock(), vue$1.createElementBlock("label", {
42623
42653
  key: option.value,
@@ -44569,7 +44599,7 @@ var script$85 = {
44569
44599
  * type-aware rendering, so a manifest column can opt into currency or a
44570
44600
  * colour swatch without registering a function.
44571
44601
  *
44572
- * @type {{style?: 'currency'|'number'|'percent'|'duration'|'swatch', currency?: string, decimals?: number, unit?: 'milliseconds'|'seconds'|'minutes'|'hours', prefix?: string, suffix?: string, colorField?: string}}
44602
+ * @type {{style?: 'currency'|'number'|'decimal'|'percent'|'duration'|'swatch', currency?: string, decimals?: number, unit?: 'milliseconds'|'seconds'|'minutes'|'hours', prefix?: string, suffix?: string, colorField?: string}}
44573
44603
  */
44574
44604
  format: {
44575
44605
  type: Object,
@@ -45639,9 +45669,9 @@ var script$85 = {
45639
45669
  return typeof s === 'string' ? s : null
45640
45670
  },
45641
45671
 
45642
- /** True when a built-in NUMERIC format (currency/number/percent/duration) applies. */
45672
+ /** True when a built-in NUMERIC format (currency/number/decimal/percent/duration) applies. */
45643
45673
  hasBuiltinFormat() {
45644
- return ['currency', 'number', 'percent', 'duration'].includes(this.formatStyle)
45674
+ return ['currency', 'number', 'decimal', 'percent', 'duration'].includes(this.formatStyle)
45645
45675
  },
45646
45676
 
45647
45677
  /** True when this cell renders as a colour swatch (`format.style:"swatch"`). */
@@ -45846,9 +45876,11 @@ var script$85 = {
45846
45876
  if (!Number.isFinite(num)) {
45847
45877
  return formatValue$1(this.value, this.property, { truncate: this.truncate })
45848
45878
  }
45879
+ // `decimal` defaults to one fraction digit, as formatMetricValue does.
45880
+ const defaultDecimals = { currency: 2, decimal: 1 };
45849
45881
  const decimals = Number.isFinite(fmt.decimals)
45850
45882
  ? fmt.decimals
45851
- : (fmt.style === 'currency' ? 2 : 0);
45883
+ : (defaultDecimals[fmt.style] ?? 0);
45852
45884
  let body;
45853
45885
  if (fmt.style === 'currency') {
45854
45886
  // `format.currencyField` reads the code from the row's own field.
@@ -45931,13 +45963,13 @@ const _hoisted_4$62 = {
45931
45963
  class: "cn-cell-renderer__groups"
45932
45964
  };
45933
45965
  const _hoisted_5$5l = { key: 0 };
45934
- const _hoisted_6$4W = {
45966
+ const _hoisted_6$4X = {
45935
45967
  key: 3,
45936
45968
  class: "cn-cell-renderer__actions",
45937
45969
  "data-testid": "cn-cell-actions"
45938
45970
  };
45939
- const _hoisted_7$4u = ["title"];
45940
- const _hoisted_8$42 = {
45971
+ const _hoisted_7$4v = ["title"];
45972
+ const _hoisted_8$43 = {
45941
45973
  key: 1,
45942
45974
  class: "cn-cell-renderer__dash"
45943
45975
  };
@@ -46080,7 +46112,7 @@ const _hoisted_60$4 = {
46080
46112
  key: 0,
46081
46113
  class: "cn-cell-renderer__dash"
46082
46114
  };
46083
- const _hoisted_61$3 = ["title"];
46115
+ const _hoisted_61$4 = ["title"];
46084
46116
  const _hoisted_62$3 = ["title"];
46085
46117
 
46086
46118
  function render$84(_ctx, _cache, $props, $setup, $data, $options) {
@@ -46137,7 +46169,7 @@ function render$84(_ctx, _cache, $props, $setup, $data, $options) {
46137
46169
  ]))
46138
46170
  ], 64 /* STABLE_FRAGMENT */))
46139
46171
  : ($props.widget === 'actions')
46140
- ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_6$4W, [
46172
+ ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_6$4X, [
46141
46173
  (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList($options.actionItems, (item) => {
46142
46174
  return (vue$1.openBlock(), vue$1.createBlock(_component_NcButton, {
46143
46175
  key: item.key,
@@ -46160,8 +46192,8 @@ function render$84(_ctx, _cache, $props, $setup, $data, $options) {
46160
46192
  ? (vue$1.openBlock(), vue$1.createElementBlock("span", {
46161
46193
  key: 0,
46162
46194
  title: $options.rawTitle
46163
- }, vue$1.toDisplayString($options.labelledValue), 9 /* TEXT, PROPS */, _hoisted_7$4u))
46164
- : (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_8$42, "—"))
46195
+ }, vue$1.toDisplayString($options.labelledValue), 9 /* TEXT, PROPS */, _hoisted_7$4v))
46196
+ : (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_8$43, "—"))
46165
46197
  ], 64 /* STABLE_FRAGMENT */))
46166
46198
  : ($props.widget === 'badge')
46167
46199
  ? (vue$1.openBlock(), vue$1.createElementBlock(vue$1.Fragment, { key: 5 }, [
@@ -46568,7 +46600,7 @@ function render$84(_ctx, _cache, $props, $setup, $data, $options) {
46568
46600
  : (vue$1.openBlock(), vue$1.createElementBlock("span", {
46569
46601
  key: 1,
46570
46602
  title: Array.isArray($props.value) ? $props.value.join(', ') : ''
46571
- }, vue$1.toDisplayString($options.formattedValue), 9 /* TEXT, PROPS */, _hoisted_61$3))
46603
+ }, vue$1.toDisplayString($options.formattedValue), 9 /* TEXT, PROPS */, _hoisted_61$4))
46572
46604
  ], 64 /* STABLE_FRAGMENT */))
46573
46605
  : (vue$1.openBlock(), vue$1.createElementBlock("span", {
46574
46606
  key: 26,
@@ -46578,7 +46610,7 @@ function render$84(_ctx, _cache, $props, $setup, $data, $options) {
46578
46610
  }
46579
46611
 
46580
46612
  script$85.render = render$84;
46581
- script$85.__scopeId = "data-v-178f1a90";
46613
+ script$85.__scopeId = "data-v-5e132788";
46582
46614
  const __sfc_main$7$ = script$85;
46583
46615
 
46584
46616
  /**
@@ -49030,13 +49062,13 @@ const _hoisted_3$6H = {
49030
49062
  };
49031
49063
  const _hoisted_4$61 = ["aria-label"];
49032
49064
  const _hoisted_5$5k = ["aria-label", "onClick"];
49033
- const _hoisted_6$4V = {
49065
+ const _hoisted_6$4W = {
49034
49066
  key: 3,
49035
49067
  class: "cn-table-loading",
49036
49068
  "data-testid": "cn-object-list-loading"
49037
49069
  };
49038
- const _hoisted_7$4t = ["tabindex", "role", "aria-label"];
49039
- const _hoisted_8$41 = {
49070
+ const _hoisted_7$4u = ["tabindex", "role", "aria-label"];
49071
+ const _hoisted_8$42 = {
49040
49072
  key: 0,
49041
49073
  class: "cn-data-table__hidden-head"
49042
49074
  };
@@ -49207,7 +49239,7 @@ function render$81(_ctx, _cache, $props, $setup, $data, $options) {
49207
49239
  }, null, 8 /* PROPS */, ["def", "state", "label", "anchorRect", "register", "onClose"]))
49208
49240
  : vue$1.createCommentVNode("v-if", true),
49209
49241
  ($options.isLoading)
49210
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_6$4V, [
49242
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_6$4W, [
49211
49243
  vue$1.createVNode(_component_NcLoadingIcon, {
49212
49244
  size: 32,
49213
49245
  "aria-hidden": "true"
@@ -49227,7 +49259,7 @@ function render$81(_ctx, _cache, $props, $setup, $data, $options) {
49227
49259
  "data-testid": "cn-object-list-table"
49228
49260
  }, [
49229
49261
  ($props.hideHeader)
49230
- ? (vue$1.openBlock(), vue$1.createElementBlock("thead", _hoisted_8$41, [
49262
+ ? (vue$1.openBlock(), vue$1.createElementBlock("thead", _hoisted_8$42, [
49231
49263
  vue$1.createElementVNode("tr", null, [
49232
49264
  ($props.selectable)
49233
49265
  ? (vue$1.openBlock(), vue$1.createElementBlock("th", _hoisted_9$3J))
@@ -49540,7 +49572,7 @@ function render$81(_ctx, _cache, $props, $setup, $data, $options) {
49540
49572
  }), 128 /* KEYED_FRAGMENT */))
49541
49573
  ])
49542
49574
  ], 2 /* CLASS */)
49543
- ], 8 /* PROPS */, _hoisted_7$4t)),
49575
+ ], 8 /* PROPS */, _hoisted_7$4u)),
49544
49576
  (_ctx.$slots.footer || ($props.viewAllRoute && $options.totalRowCount > $options.effectiveRows.length))
49545
49577
  ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_39$g, [
49546
49578
  vue$1.renderSlot(_ctx.$slots, "footer", {
@@ -50146,9 +50178,9 @@ const _hoisted_5$5j = {
50146
50178
  key: 0,
50147
50179
  scope: "col"
50148
50180
  };
50149
- const _hoisted_6$4U = { class: "cn-child-records-field__sr" };
50150
- const _hoisted_7$4s = { key: 0 };
50151
- const _hoisted_8$40 = ["colspan"];
50181
+ const _hoisted_6$4V = { class: "cn-child-records-field__sr" };
50182
+ const _hoisted_7$4t = { key: 0 };
50183
+ const _hoisted_8$41 = ["colspan"];
50152
50184
  const _hoisted_9$3I = { key: 1 };
50153
50185
  const _hoisted_10$3i = {
50154
50186
  key: 0,
@@ -50204,18 +50236,18 @@ function render$7$(_ctx, _cache, $props, $setup, $data, $options) {
50204
50236
  }), 128 /* KEYED_FRAGMENT */)),
50205
50237
  (!$props.disabled)
50206
50238
  ? (vue$1.openBlock(), vue$1.createElementBlock("th", _hoisted_5$5j, [
50207
- vue$1.createElementVNode("span", _hoisted_6$4U, vue$1.toDisplayString($options.t('nextcloud-vue', 'Row actions')), 1 /* TEXT */)
50239
+ vue$1.createElementVNode("span", _hoisted_6$4V, vue$1.toDisplayString($options.t('nextcloud-vue', 'Row actions')), 1 /* TEXT */)
50208
50240
  ]))
50209
50241
  : vue$1.createCommentVNode("v-if", true)
50210
50242
  ])
50211
50243
  ]),
50212
50244
  vue$1.createElementVNode("tbody", null, [
50213
50245
  ($options.list.length === 0)
50214
- ? (vue$1.openBlock(), vue$1.createElementBlock("tr", _hoisted_7$4s, [
50246
+ ? (vue$1.openBlock(), vue$1.createElementBlock("tr", _hoisted_7$4t, [
50215
50247
  vue$1.createElementVNode("td", {
50216
50248
  colspan: $options.columns.length + 1,
50217
50249
  class: "cn-child-records-field__empty"
50218
- }, vue$1.toDisplayString($options.t('nextcloud-vue', 'No rows yet.')), 9 /* TEXT, PROPS */, _hoisted_8$40)
50250
+ }, vue$1.toDisplayString($options.t('nextcloud-vue', 'No rows yet.')), 9 /* TEXT, PROPS */, _hoisted_8$41)
50219
50251
  ]))
50220
50252
  : vue$1.createCommentVNode("v-if", true),
50221
50253
  (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList($options.list, (row, index) => {
@@ -51930,13 +51962,13 @@ const _hoisted_5$5g = {
51930
51962
  key: 0,
51931
51963
  class: "cn-file-field__hint"
51932
51964
  };
51933
- const _hoisted_6$4T = {
51965
+ const _hoisted_6$4U = {
51934
51966
  key: 2,
51935
51967
  class: "cn-file-field__list",
51936
51968
  "data-testid": "cn-file-field-list"
51937
51969
  };
51938
- const _hoisted_7$4r = { class: "cn-file-field__item-name" };
51939
- const _hoisted_8$3$ = {
51970
+ const _hoisted_7$4s = { class: "cn-file-field__item-name" };
51971
+ const _hoisted_8$40 = {
51940
51972
  key: 0,
51941
51973
  class: "cn-file-field__item-size"
51942
51974
  };
@@ -52039,16 +52071,16 @@ function render$7V(_ctx, _cache, $props, $setup, $data, $options) {
52039
52071
  }, null, 8 /* PROPS */, ["facing", "onCapture"]))
52040
52072
  : vue$1.createCommentVNode("v-if", true),
52041
52073
  ($props.multiple && $options.items.length > 0)
52042
- ? (vue$1.openBlock(), vue$1.createElementBlock("ul", _hoisted_6$4T, [
52074
+ ? (vue$1.openBlock(), vue$1.createElementBlock("ul", _hoisted_6$4U, [
52043
52075
  (vue$1.openBlock(true), vue$1.createElementBlock(vue$1.Fragment, null, vue$1.renderList($options.items, (item, index) => {
52044
52076
  return (vue$1.openBlock(), vue$1.createElementBlock("li", {
52045
52077
  key: index,
52046
52078
  class: "cn-file-field__item",
52047
52079
  "data-testid": "cn-file-field-item"
52048
52080
  }, [
52049
- vue$1.createElementVNode("span", _hoisted_7$4r, vue$1.toDisplayString(item.name), 1 /* TEXT */),
52081
+ vue$1.createElementVNode("span", _hoisted_7$4s, vue$1.toDisplayString(item.name), 1 /* TEXT */),
52050
52082
  (item.size)
52051
- ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_8$3$, vue$1.toDisplayString($options.formatSize(item.size)), 1 /* TEXT */))
52083
+ ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_8$40, vue$1.toDisplayString($options.formatSize(item.size)), 1 /* TEXT */))
52052
52084
  : vue$1.createCommentVNode("v-if", true),
52053
52085
  (item.held)
52054
52086
  ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_9$3H, vue$1.toDisplayString($options.t('nextcloud-vue', 'Uploads when saved')), 1 /* TEXT */))
@@ -52132,7 +52164,7 @@ const _hoisted_3$6A = {
52132
52164
  };
52133
52165
  const _hoisted_4$5Y = ["id"];
52134
52166
  const _hoisted_5$5f = { class: "cn-form-field__error-text" };
52135
- const _hoisted_6$4S = { class: "cn-form-field__sr" };
52167
+ const _hoisted_6$4T = { class: "cn-form-field__sr" };
52136
52168
 
52137
52169
  function render$7U(_ctx, _cache, $props, $setup, $data, $options) {
52138
52170
  const _component_AlertCircleOutline = vue$1.resolveComponent("AlertCircleOutline");
@@ -52160,7 +52192,7 @@ function render$7U(_ctx, _cache, $props, $setup, $data, $options) {
52160
52192
  "aria-hidden": "true"
52161
52193
  }),
52162
52194
  vue$1.createElementVNode("span", _hoisted_5$5f, [
52163
- vue$1.createElementVNode("span", _hoisted_6$4S, vue$1.toDisplayString($props.errorPrefix), 1 /* TEXT */),
52195
+ vue$1.createElementVNode("span", _hoisted_6$4T, vue$1.toDisplayString($props.errorPrefix), 1 /* TEXT */),
52164
52196
  vue$1.createTextVNode(vue$1.toDisplayString($props.error), 1 /* TEXT */)
52165
52197
  ])
52166
52198
  ], 8 /* PROPS */, _hoisted_4$5Y))
@@ -85163,9 +85195,9 @@ const _hoisted_5$5e = {
85163
85195
  class: "cn-icon-browser-panel__preview-svg",
85164
85196
  viewBox: "0 0 24 24"
85165
85197
  };
85166
- const _hoisted_6$4R = ["d"];
85167
- const _hoisted_7$4q = { class: "cn-icon-browser-panel__preview-label" };
85168
- const _hoisted_8$3_ = ["title", "aria-label"];
85198
+ const _hoisted_6$4S = ["d"];
85199
+ const _hoisted_7$4r = { class: "cn-icon-browser-panel__preview-label" };
85200
+ const _hoisted_8$3$ = ["title", "aria-label"];
85169
85201
  const _hoisted_9$3G = ["aria-label"];
85170
85202
  const _hoisted_10$3g = ["id", "aria-selected", "aria-controls", "tabindex", "onClick", "onKeydown"];
85171
85203
  const _hoisted_11$2W = ["id", "role", "aria-labelledby"];
@@ -85259,11 +85291,11 @@ function render$7S(_ctx, _cache, $props, $setup, $data, $options) {
85259
85291
  }))
85260
85292
  : ($options.currentPath)
85261
85293
  ? (vue$1.openBlock(), vue$1.createElementBlock("svg", _hoisted_5$5e, [
85262
- vue$1.createElementVNode("path", { d: $options.currentPath }, null, 8 /* PROPS */, _hoisted_6$4R)
85294
+ vue$1.createElementVNode("path", { d: $options.currentPath }, null, 8 /* PROPS */, _hoisted_6$4S)
85263
85295
  ]))
85264
85296
  : vue$1.createCommentVNode("v-if", true)
85265
85297
  ]),
85266
- vue$1.createElementVNode("span", _hoisted_7$4q, vue$1.toDisplayString($options.selectedLabel || $options.t('nextcloud-vue', 'No icon selected')), 1 /* TEXT */),
85298
+ vue$1.createElementVNode("span", _hoisted_7$4r, vue$1.toDisplayString($options.selectedLabel || $options.t('nextcloud-vue', 'No icon selected')), 1 /* TEXT */),
85267
85299
  ($props.clearable && $props.value)
85268
85300
  ? (vue$1.openBlock(), vue$1.createElementBlock("button", {
85269
85301
  key: 0,
@@ -85274,7 +85306,7 @@ function render$7S(_ctx, _cache, $props, $setup, $data, $options) {
85274
85306
  onClick: _cache[0] || (_cache[0] = (...args) => ($options.clearIcon && $options.clearIcon(...args)))
85275
85307
  }, [
85276
85308
  vue$1.createVNode(_component_Cancel, { size: 16 })
85277
- ], 8 /* PROPS */, _hoisted_8$3_))
85309
+ ], 8 /* PROPS */, _hoisted_8$3$))
85278
85310
  : vue$1.createCommentVNode("v-if", true)
85279
85311
  ]),
85280
85312
  ($options.tabs.length > 1)
@@ -87695,11 +87727,11 @@ const _hoisted_4$5W = {
87695
87727
  viewBox: "0 0 24 24"
87696
87728
  };
87697
87729
  const _hoisted_5$5d = ["d"];
87698
- const _hoisted_6$4Q = {
87730
+ const _hoisted_6$4R = {
87699
87731
  key: 3,
87700
87732
  class: "cn-icon-browser__trigger-placeholder"
87701
87733
  };
87702
- const _hoisted_7$4p = ["aria-label"];
87734
+ const _hoisted_7$4q = ["aria-label"];
87703
87735
 
87704
87736
  function render$7R(_ctx, _cache, $props, $setup, $data, $options) {
87705
87737
  const _component_CnIconBrowserPanel = vue$1.resolveComponent("CnIconBrowserPanel");
@@ -87764,7 +87796,7 @@ function render$7R(_ctx, _cache, $props, $setup, $data, $options) {
87764
87796
  ? (vue$1.openBlock(), vue$1.createElementBlock("svg", _hoisted_4$5W, [
87765
87797
  vue$1.createElementVNode("path", { d: $options.currentPath }, null, 8 /* PROPS */, _hoisted_5$5d)
87766
87798
  ]))
87767
- : (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_6$4Q, vue$1.toDisplayString($options.t('nextcloud-vue', 'Icon')), 1 /* TEXT */))
87799
+ : (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_6$4R, vue$1.toDisplayString($options.t('nextcloud-vue', 'Icon')), 1 /* TEXT */))
87768
87800
  ], 16 /* FULL_PROPS */, _hoisted_2$7h)
87769
87801
  ])
87770
87802
  ]),
@@ -87782,7 +87814,7 @@ function render$7R(_ctx, _cache, $props, $setup, $data, $options) {
87782
87814
  ]),
87783
87815
  _: 3 /* FORWARDED */
87784
87816
  }, 16 /* FULL_PROPS */)
87785
- ], 8 /* PROPS */, _hoisted_7$4p)
87817
+ ], 8 /* PROPS */, _hoisted_7$4q)
87786
87818
  ]),
87787
87819
  _: 3 /* FORWARDED */
87788
87820
  }, 8 /* PROPS */, ["shown"]))
@@ -94785,6 +94817,12 @@ var script$7L = {
94785
94817
  default: '',
94786
94818
  },
94787
94819
 
94820
+ /** A muted line under the title, as given (translate before passing). */
94821
+ subtitle: {
94822
+ type: String,
94823
+ default: '',
94824
+ },
94825
+
94788
94826
  /** Optional MDI icon component for the header */
94789
94827
  icon: {
94790
94828
  type: [Object, Function],
@@ -94849,10 +94887,22 @@ var script$7L = {
94849
94887
  };
94850
94888
 
94851
94889
  const _hoisted_1$7o = { class: "cn-detail-card__header-left" };
94852
- const _hoisted_2$7a = { class: "cn-detail-card__title" };
94853
- const _hoisted_3$6u = { class: "cn-detail-card__header-right" };
94854
- const _hoisted_4$5T = ["aria-label"];
94890
+ const _hoisted_2$7a = {
94891
+ key: 0,
94892
+ class: "cn-detail-card__heading"
94893
+ };
94894
+ const _hoisted_3$6u = { class: "cn-detail-card__title" };
94895
+ const _hoisted_4$5T = {
94896
+ class: "cn-detail-card__subtitle",
94897
+ "data-testid": "cn-detail-card-subtitle"
94898
+ };
94855
94899
  const _hoisted_5$5c = {
94900
+ key: 1,
94901
+ class: "cn-detail-card__title"
94902
+ };
94903
+ const _hoisted_6$4Q = { class: "cn-detail-card__header-right" };
94904
+ const _hoisted_7$4p = ["aria-label"];
94905
+ const _hoisted_8$3_ = {
94856
94906
  key: 1,
94857
94907
  class: "cn-detail-card__footer"
94858
94908
  };
@@ -94880,9 +94930,14 @@ function render$7K(_ctx, _cache, $props, $setup, $data, $options) {
94880
94930
  }))
94881
94931
  : vue$1.createCommentVNode("v-if", true)
94882
94932
  ]),
94883
- vue$1.createElementVNode("h3", _hoisted_2$7a, vue$1.toDisplayString($props.title), 1 /* TEXT */)
94933
+ ($props.subtitle)
94934
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_2$7a, [
94935
+ vue$1.createElementVNode("h3", _hoisted_3$6u, vue$1.toDisplayString($props.title), 1 /* TEXT */),
94936
+ vue$1.createElementVNode("span", _hoisted_4$5T, vue$1.toDisplayString($props.subtitle), 1 /* TEXT */)
94937
+ ]))
94938
+ : (vue$1.openBlock(), vue$1.createElementBlock("h3", _hoisted_5$5c, vue$1.toDisplayString($props.title), 1 /* TEXT */))
94884
94939
  ]),
94885
- vue$1.createElementVNode("div", _hoisted_3$6u, [
94940
+ vue$1.createElementVNode("div", _hoisted_6$4Q, [
94886
94941
  ($options.declaredHeaderActions.length > 0)
94887
94942
  ? (vue$1.openBlock(), vue$1.createBlock(_component_CnWidgetHeaderActionButtons, {
94888
94943
  key: 0,
@@ -94900,7 +94955,7 @@ function render$7K(_ctx, _cache, $props, $setup, $data, $options) {
94900
94955
  size: 20,
94901
94956
  class: vue$1.normalizeClass({ 'cn-detail-card__chevron--rotated': $data.isCollapsed })
94902
94957
  }, null, 8 /* PROPS */, ["class"])
94903
- ], 8 /* PROPS */, _hoisted_4$5T))
94958
+ ], 8 /* PROPS */, _hoisted_7$4p))
94904
94959
  : vue$1.createCommentVNode("v-if", true)
94905
94960
  ])
94906
94961
  ], 2 /* CLASS */))
@@ -94913,7 +94968,7 @@ function render$7K(_ctx, _cache, $props, $setup, $data, $options) {
94913
94968
  [vue$1.vShow, !$data.isCollapsed]
94914
94969
  ]),
94915
94970
  (_ctx.$slots.footer)
94916
- ? vue$1.withDirectives((vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_5$5c, [
94971
+ ? vue$1.withDirectives((vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_8$3_, [
94917
94972
  vue$1.renderSlot(_ctx.$slots, "footer")
94918
94973
  ], 512 /* NEED_PATCH */)), [
94919
94974
  [vue$1.vShow, !$data.isCollapsed]
@@ -94923,7 +94978,7 @@ function render$7K(_ctx, _cache, $props, $setup, $data, $options) {
94923
94978
  }
94924
94979
 
94925
94980
  script$7L.render = render$7K;
94926
- script$7L.__scopeId = "data-v-01384e36";
94981
+ script$7L.__scopeId = "data-v-26edbe9e";
94927
94982
  const __sfc_main$7F = script$7L;
94928
94983
 
94929
94984
  /**
@@ -116478,6 +116533,21 @@ var script$71 = {
116478
116533
  default: '',
116479
116534
  },
116480
116535
 
116536
+ /**
116537
+ * The second line as a list of fields joined with " · ", a field
116538
+ * without a value left out: the `nameSecondary` tokens without their
116539
+ * braces (`["type", "size", "modified|date"]`, a row-data field such as
116540
+ * `"senderName"`). Or a list of variants `{ when, fields }`: the first
116541
+ * whose `when` is `true`, or that has no `when`, is used (a case type
116542
+ * flag read through `@context`). Takes precedence over `nameSecondary`.
116543
+ *
116544
+ * @spec openspec/changes/files-second-line-fields/specs/files-second-line-fields/spec.md#requirement-a-files-second-line-is-a-list-of-fields
116545
+ */
116546
+ secondLine: {
116547
+ type: Array,
116548
+ default: null,
116549
+ },
116550
+
116481
116551
  /** The line `dropHint` shows. */
116482
116552
  dropHintLabel: {
116483
116553
  type: String,
@@ -116876,6 +116946,26 @@ var script$71 = {
116876
116946
  *
116877
116947
  * @return {Array<object>} The file nodes.
116878
116948
  */
116949
+ /**
116950
+ * The fields `secondLine` names for this browser: the list itself, or
116951
+ * the fields of its first variant whose `when` is `true` or absent.
116952
+ * Null without `secondLine` or a matching variant.
116953
+ *
116954
+ * @return {Array<string>|null}
116955
+ * @spec openspec/changes/files-second-line-fields/specs/files-second-line-fields/spec.md#requirement-a-files-second-line-is-a-list-of-fields
116956
+ */
116957
+ secondLineFields() {
116958
+ const list = Array.isArray(this.secondLine) ? this.secondLine : [];
116959
+ if (list.length === 0) {
116960
+ return null
116961
+ }
116962
+ if (list.every((item) => typeof item === 'string')) {
116963
+ return list
116964
+ }
116965
+ const variant = list.find((item) => item && typeof item === 'object' && Array.isArray(item.fields) && (!('when' in item) || item.when === true));
116966
+ return variant ? variant.fields.filter((field) => typeof field === 'string') : null
116967
+ },
116968
+
116879
116969
  /**
116880
116970
  * The bulk bar's count text.
116881
116971
  *
@@ -117429,7 +117519,8 @@ var script$71 = {
117429
117519
  * @return {string}
117430
117520
  */
117431
117521
  nameSecondaryOf(node) {
117432
- if (!this.nameSecondary || this.isFolder(node)) {
117522
+ const fields = this.secondLineFields;
117523
+ if ((!this.nameSecondary && fields === null) || this.isFolder(node)) {
117433
117524
  return ''
117434
117525
  }
117435
117526
  const byId = this.resolvedRowData && typeof this.resolvedRowData === 'object' ? this.resolvedRowData : {};
@@ -117443,6 +117534,12 @@ var script$71 = {
117443
117534
  type: this.typeLabelOf(node),
117444
117535
  ...(owner ? { owner } : {}),
117445
117536
  };
117537
+ if (fields !== null) {
117538
+ return fields
117539
+ .map((field) => fillTemplate(`{${field}}`, object))
117540
+ .filter((text) => text !== '')
117541
+ .join(' · ')
117542
+ }
117446
117543
  return fillTemplate(this.cnTranslate(this.nameSecondary), object)
117447
117544
  },
117448
117545
 
@@ -119618,7 +119715,7 @@ function render$70(_ctx, _cache, $props, $setup, $data, $options) {
119618
119715
  }
119619
119716
 
119620
119717
  script$71.render = render$70;
119621
- script$71.__scopeId = "data-v-541e44c4";
119718
+ script$71.__scopeId = "data-v-ad907164";
119622
119719
  const __sfc_main$6X = script$71;
119623
119720
 
119624
119721
  /**
@@ -120053,6 +120150,8 @@ var script$70 = {
120053
120150
  footnote: { type: String, default: '' },
120054
120151
  /** Forwarded to CnFilesBrowser: the second line under each file's name. */
120055
120152
  nameSecondary: { type: String, default: '' },
120153
+ /** Forwarded to CnFilesBrowser: the second line as a list of fields, or variants. */
120154
+ secondLine: { type: Array, default: null },
120056
120155
  /** A dotted path to the list of records in the `rowDataUrl` answer; empty reads the answer itself. */
120057
120156
  rowDataPath: { type: String, default: '' },
120058
120157
  /** The record field that holds the Nextcloud file id. */
@@ -120757,6 +120856,7 @@ function render$6$(_ctx, _cache, $props, $setup, $data, $options) {
120757
120856
  reviewSummary: $props.reviewSummary,
120758
120857
  footnote: $props.footnote,
120759
120858
  nameSecondary: $props.nameSecondary,
120859
+ secondLine: $props.secondLine,
120760
120860
  linkedItems: $data.linkedItems,
120761
120861
  columns: $props.columns,
120762
120862
  rowData: $options.rowDataSource,
@@ -120771,7 +120871,7 @@ function render$6$(_ctx, _cache, $props, $setup, $data, $options) {
120771
120871
  groupBy: $props.groupBy,
120772
120872
  facets: $props.facets,
120773
120873
  onChanged: $options.onBrowserChanged
120774
- }, null, 8 /* PROPS */, ["rootPath", "rootLabel", "source", "deleteLabel", "uploadButton", "newMenu", "folderButton", "dropOverlay", "dropLabel", "dropDetail", "dropHint", "rowActions", "newActions", "headerActions", "rowButton", "reviewSummary", "footnote", "nameSecondary", "linkedItems", "columns", "rowData", "preferenceApp", "preferenceKey", "bulkActions", "selectionLabel", "selectionLabelOne", "showClearSelection", "sideCards", "sideCardsPlacement", "groupBy", "facets", "onChanged"]))
120874
+ }, null, 8 /* PROPS */, ["rootPath", "rootLabel", "source", "deleteLabel", "uploadButton", "newMenu", "folderButton", "dropOverlay", "dropLabel", "dropDetail", "dropHint", "rowActions", "newActions", "headerActions", "rowButton", "reviewSummary", "footnote", "nameSecondary", "secondLine", "linkedItems", "columns", "rowData", "preferenceApp", "preferenceKey", "bulkActions", "selectionLabel", "selectionLabelOne", "showClearSelection", "sideCards", "sideCardsPlacement", "groupBy", "facets", "onChanged"]))
120775
120875
  : (vue$1.openBlock(), vue$1.createElementBlock(vue$1.Fragment, { key: 1 }, [
120776
120876
  ($data.uploadError)
120777
120877
  ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_2$6w, vue$1.toDisplayString($data.uploadError), 1 /* TEXT */))
@@ -121022,7 +121122,7 @@ function render$6$(_ctx, _cache, $props, $setup, $data, $options) {
121022
121122
  }
121023
121123
 
121024
121124
  script$70.render = render$6$;
121025
- script$70.__scopeId = "data-v-043f08e2";
121125
+ script$70.__scopeId = "data-v-89d80864";
121026
121126
  const __sfc_main$6W = script$70;
121027
121127
 
121028
121128
  /**
@@ -141943,7 +142043,7 @@ function fillRowTemplate(template, row) {
141943
142043
  * and its count (DqZoekresultaten).
141944
142044
  *
141945
142045
  * `sourceConfig.kinds` lists what is searched:
141946
- * `{ id, label, register, schema, titleField?, subtitleField?, statusField?, statusResolve?, linkedField?, linkedTemplate?, href?, limit? }`
142046
+ * `{ id, label, register, schema, titleField?, subtitleField?, subtitleTemplate?, statusField?, statusResolve?, linkedField?, linkedTemplate?, linkedUser?, href?, limit? }`
141947
142047
  * for objects (OpenRegister's `_search`, so the reader sees what they may
141948
142048
  * read), or `{ id, label, files: true, limit? }` for the reader's files
141949
142049
  * (Nextcloud's unified search). The term is `sourceConfig.term`, else the
@@ -141957,6 +142057,10 @@ function fillRowTemplate(template, row) {
141957
142057
  * coloured by `sourceConfig.statusColorMap`. `linkedTemplate` (a field
141958
142058
  * template, "{case.title}") or `linkedField` (a path) fills the "Linked"
141959
142059
  * column, drawn when a kind declares one; `sourceConfig.linkedLabel` names it.
142060
+ * `subtitleTemplate` ("Case {identifier}", translated) fills the line under a
142061
+ * result, else `subtitleField`.
142062
+ * `linkedUser: true` reads that value as a user id and shows the user's
142063
+ * display name. `sourceConfig.columnWidths` sets a width per column key.
141960
142064
  * The page's title and count line can name the term as `{term}`, and the
141961
142065
  * count is the active kind's total. Kind, link and status are filterable.
141962
142066
  *
@@ -141966,6 +142070,23 @@ function fillRowTemplate(template, row) {
141966
142070
  * @return {object} The source adapter.
141967
142071
  * @spec openspec/changes/index-search-source/specs/index-page/spec.md#requirement-an-index-page-can-search-several-schemas-and-files
141968
142072
  */
142073
+ /**
142074
+ * Columns with the widths a page names per column key
142075
+ * (`sourceConfig.columnWidths`, `{ title: '250px' }`); a column it does not
142076
+ * name keeps none.
142077
+ *
142078
+ * @param {Array<object>} columns The columns.
142079
+ * @param {object|undefined} widths The widths by key.
142080
+ * @return {Array<object>} The columns.
142081
+ * @spec openspec/changes/search-linked-user-and-widths/specs/search-linked-user-and-widths/spec.md
142082
+ */
142083
+ function withColumnWidths(columns, widths) {
142084
+ if (!widths || typeof widths !== 'object') {
142085
+ return columns
142086
+ }
142087
+ return columns.map((col) => (typeof widths[col.key] === 'string' && widths[col.key] !== '' ? { ...col, width: widths[col.key] } : col))
142088
+ }
142089
+
141969
142090
  function searchSource(config = {}, context = {}) {
141970
142091
  const translate = typeof context.translate === 'function' ? context.translate : (text) => text;
141971
142092
  const kinds = (Array.isArray(config && config.kinds) ? config.kinds : [])
@@ -142054,12 +142175,27 @@ function searchSource(config = {}, context = {}) {
142054
142175
  return ''
142055
142176
  };
142056
142177
 
142178
+ const subtitleOf = (kind, object) => {
142179
+ if (kind.subtitleTemplate) {
142180
+ return fillTemplate(translate(kind.subtitleTemplate), object)
142181
+ }
142182
+ return kind.subtitleField ? String(readPath$8(object, kind.subtitleField) ?? '') : ''
142183
+ };
142184
+
142185
+ // `linkedUser`: the Linked cell holds a user id; name the user instead.
142186
+ const linkedUserNames = async (kind, objects) => {
142187
+ const ids = [...new Set(objects.map((object) => linkedOf(kind, object)).filter((id) => id !== ''))];
142188
+ const options = await Promise.all(ids.map((id) => resolveNextcloudUser(id).catch(() => null)));
142189
+ return new Map(ids.map((id, index) => [id, (options[index] && options[index].label) || id]))
142190
+ };
142191
+
142057
142192
  const searchObjects = async (axios, kind, term) => {
142058
142193
  const url = router.generateUrl('/apps/openregister/api/objects/{register}/{schema}', { register: kind.register, schema: kind.schema });
142059
142194
  const res = await axios.get(url, { params: { _search: term, _limit: kind.limit || 20 } });
142060
142195
  const body = (res && res.data) || {};
142061
142196
  const results = Array.isArray(body.results) ? body.results : (Array.isArray(body) ? body : []);
142062
142197
  const labels = kind.statusResolve && kind.statusField ? await statusLabels(kind) : null;
142198
+ const userNames = kind.linkedUser === true ? await linkedUserNames(kind, results) : null;
142063
142199
  return {
142064
142200
  total: typeof body.total === 'number' ? body.total : results.length,
142065
142201
  rows: results.map((object) => {
@@ -142074,9 +142210,9 @@ function searchSource(config = {}, context = {}) {
142074
142210
  kindLabel: kind.label,
142075
142211
  kindText: kindTextOf(kind, object),
142076
142212
  title: String(readPath$8(object, kind.titleField || 'title') ?? object.name ?? id ?? ''),
142077
- subtitle: kind.subtitleField ? String(readPath$8(object, kind.subtitleField) ?? '') : '',
142213
+ subtitle: subtitleOf(kind, object),
142078
142214
  status,
142079
- linked: linkedOf(kind, object),
142215
+ linked: userNames ? (userNames.get(linkedOf(kind, object)) || linkedOf(kind, object)) : linkedOf(kind, object),
142080
142216
  href: kind.href ? router.generateUrl(fillRowTemplate(kind.href, { ...object, id })) : '',
142081
142217
  object,
142082
142218
  }
@@ -142184,7 +142320,7 @@ function searchSource(config = {}, context = {}) {
142184
142320
  */
142185
142321
  loading: () => state.loading,
142186
142322
 
142187
- columns: [
142323
+ columns: withColumnWidths([
142188
142324
  { key: 'title', label: l10n.translate('nextcloud-vue', 'Result'), secondary: 'subtitle', sortable: true },
142189
142325
  // `kindAsText`: what one row is, as plain text ("Zaak", "Lantaarnpaal",
142190
142326
  // DqZoekresultaten), instead of the plural kind as a pill.
@@ -142199,7 +142335,7 @@ function searchSource(config = {}, context = {}) {
142199
142335
  widgetProps: { colorMap: (config && config.statusColorMap) || {} },
142200
142336
  sortable: true,
142201
142337
  },
142202
- ],
142338
+ ], config && config.columnWidths),
142203
142339
 
142204
142340
  /**
142205
142341
  * Every column header carries a filter button (DqZoekresultaten); the
@@ -142261,14 +142397,24 @@ function searchSource(config = {}, context = {}) {
142261
142397
  * the pager asks for.
142262
142398
  *
142263
142399
  * @param {Array<object>} members The members, by name.
142264
- * @param {object} config `search`, `sortKey`, `sortOrder`, `limit`, `offset`.
142400
+ * @param {object} config `search`, `nameLike`, `rolesLike`, `emailLike`, `today`, `sortKey`, `sortOrder`, `limit`, `offset`.
142265
142401
  * @return {{rows: Array<object>, total: number}} The page and how many match.
142266
142402
  */
142267
142403
  function membersPage(members, config = {}) {
142404
+ const has = (member, fields, text) => fields.some((field) => String(member[field] || '').toLowerCase().includes(text));
142268
142405
  const term = String(config.search || '').trim().toLowerCase();
142269
- let rows = term === ''
142270
- ? members.slice()
142271
- : members.filter((member) => ['displayname', 'function', 'roles', 'email'].some((field) => String(member[field] || '').toLowerCase().includes(term)));
142406
+ let rows = term === '' ? members.slice() : members.filter((member) => has(member, ['displayname', 'function', 'roles', 'email'], term));
142407
+ // The table heads' filters: text contains, and the Today values picked.
142408
+ for (const [param, fields] of [['nameLike', ['displayname', 'function']], ['rolesLike', ['roles']], ['emailLike', ['email']]]) {
142409
+ const text = String(config[param] || '').trim().toLowerCase();
142410
+ if (text !== '') {
142411
+ rows = rows.filter((member) => has(member, fields, text));
142412
+ }
142413
+ }
142414
+ const today = (Array.isArray(config.today) ? config.today : (config.today ? [config.today] : [])).map(String);
142415
+ if (today.length > 0) {
142416
+ rows = rows.filter((member) => today.includes(member.today));
142417
+ }
142272
142418
  if (config.sortKey) {
142273
142419
  const sign = config.sortOrder === 'desc' ? -1 : 1;
142274
142420
  rows = rows.sort((a, b) => sign * String(a[config.sortKey] ?? '').localeCompare(String(b[config.sortKey] ?? '')));
@@ -142330,10 +142476,11 @@ function usersSource() {
142330
142476
  */
142331
142477
  load: async (config) => {
142332
142478
  const group = String((config && (config.group || config.defaultGroup)) || '');
142333
- const { search, sortKey, sortOrder, limit, offset, ...request } = config || {};
142479
+ const { search, sortKey, sortOrder, limit, offset, nameLike, rolesLike, emailLike, today, ...request } = config || {};
142334
142480
  const key = JSON.stringify({ ...request, group });
142335
- // A new page, term or order of the members already read: no request.
142336
- if (key === membersKey && (offset !== undefined || search !== undefined || sortKey !== undefined)) {
142481
+ const narrows = [offset, search, sortKey, nameLike, rolesLike, emailLike, today].some((value) => value !== undefined);
142482
+ // A new page, term, order or head filter of the members already read: no request.
142483
+ if (key === membersKey && narrows) {
142337
142484
  const page = membersPage(readMembers, config);
142338
142485
  state.rows = page.rows;
142339
142486
  state.total = page.total;
@@ -142429,8 +142576,24 @@ function usersSource() {
142429
142576
 
142430
142577
  searchFields: {
142431
142578
  group: { param: 'group', single: true },
142579
+ 'displayname[like]': { param: 'nameLike', single: true },
142580
+ 'roles[like]': { param: 'rolesLike', single: true },
142581
+ 'email[like]': { param: 'emailLike', single: true },
142582
+ today: { param: 'today' },
142432
142583
  },
142433
142584
 
142585
+ /** A filter button on every head (DqAfdelingen); the members read are narrowed here. */
142586
+ headerFilters: true,
142587
+
142588
+ /**
142589
+ * A member's row menu (DqAfdelingen): their Nextcloud profile, and an
142590
+ * e-mail when they have an address. Both are links.
142591
+ */
142592
+ rowActions: [
142593
+ { id: 'open-profile', label: l10n.translate('nextcloud-vue', 'Open profile'), icon: AccountOutline, href: (row) => router.generateUrl(`/u/${encodeURIComponent(row.id)}`) },
142594
+ { id: 'send-email', label: l10n.translate('nextcloud-vue', 'Send email'), icon: EmailOutline, href: (row) => `mailto:${row.email}`, visible: (row) => Boolean(row && row.email) },
142595
+ ],
142596
+
142434
142597
  showAdd: false,
142435
142598
  }
142436
142599
  }
@@ -156939,7 +157102,7 @@ var script$6b = {
156939
157102
  // `secondary` (a second, muted line under the cell) rides
156940
157103
  // along too: the table draws it, and dropping it here took the
156941
157104
  // list rows' second line away (zuiddrecht-pixel-gaps-3).
156942
- for (const k of ['format', 'widget', 'widgetProps', 'formatter', 'align', 'width', 'type', 'enum', 'enumLabels', 'sortable', 'secondary', 'colorMap', 'grow']) {
157105
+ for (const k of ['format', 'widget', 'widgetProps', 'formatter', 'align', 'width', 'type', 'enum', 'enumLabels', 'sortable', 'secondary', 'colorMap', 'grow', 'cellClass']) {
156943
157106
  if (c[k] !== undefined) {
156944
157107
  out[k] = c[k];
156945
157108
  }
@@ -159489,7 +159652,7 @@ function render$6a(_ctx, _cache, $props, $setup, $data, $options) {
159489
159652
  }
159490
159653
 
159491
159654
  script$6b.render = render$6a;
159492
- script$6b.__scopeId = "data-v-445e213c";
159655
+ script$6b.__scopeId = "data-v-3a0165c8";
159493
159656
  const __sfc_main$65 = script$6b;
159494
159657
 
159495
159658
  const DEFAULT_CONTENT$s = Object.freeze({
@@ -164955,7 +165118,7 @@ const _hoisted_60$3 = {
164955
165118
  key: 1,
164956
165119
  class: "cn-related-objects-widget__footer-actions"
164957
165120
  };
164958
- const _hoisted_61$2 = { class: "cn-related-objects-widget__footer" };
165121
+ const _hoisted_61$3 = { class: "cn-related-objects-widget__footer" };
164959
165122
  const _hoisted_62$2 = {
164960
165123
  key: 2,
164961
165124
  class: "cn-related-objects-widget__add-error"
@@ -165444,7 +165607,7 @@ function render$64(_ctx, _cache, $props, $setup, $data, $options) {
165444
165607
  ? {
165445
165608
  name: "footer",
165446
165609
  fn: vue$1.withCtx(() => [
165447
- vue$1.createElementVNode("div", _hoisted_61$2, [
165610
+ vue$1.createElementVNode("div", _hoisted_61$3, [
165448
165611
  ($options.soleAddable && $options.soleAddable.key === 'files')
165449
165612
  ? (vue$1.openBlock(), vue$1.createBlock(_component_NcButton, {
165450
165613
  key: 0,
@@ -199856,6 +200019,7 @@ var script$5e = {
199856
200019
  objectId: this.objectId,
199857
200020
  object: this.object,
199858
200021
  config: unwrapAppConfig(this.cnAppConfig),
200022
+ context: (this.cnDetailVisibilityRows && this.cnDetailVisibilityRows.value) || null,
199859
200023
  })
199860
200024
  },
199861
200025
 
@@ -199922,6 +200086,7 @@ var script$5e = {
199922
200086
  // prints its title as given.
199923
200087
  title: this.translateLabel(this.widget?.title || ''),
199924
200088
  ...this.leafTitleLabel,
200089
+ ...(this.widgetSubtitle ? { subtitle: this.translateLabel(this.widgetSubtitle) } : {}),
199925
200090
  // A provider opting into `bareWidget` is being rendered where the
199926
200091
  // tab strip already supplies the card and the title, so tell it to
199927
200092
  // drop its own. Without this the panel gets a titled card inside
@@ -201013,7 +201178,7 @@ function render$5d(_ctx, _cache, $props, $setup, $data, $options) {
201013
201178
  }
201014
201179
 
201015
201180
  script$5e.render = render$5d;
201016
- script$5e.__scopeId = "data-v-32a82d6a";
201181
+ script$5e.__scopeId = "data-v-10c797a6";
201017
201182
  const __sfc_main$58 = script$5e;
201018
201183
 
201019
201184
  /**
@@ -238064,7 +238229,7 @@ function render$47(_ctx, _cache, $props, $setup, $data, $options) {
238064
238229
  }
238065
238230
 
238066
238231
  script$48.render = render$47;
238067
- script$48.__scopeId = "data-v-e3954500";
238232
+ script$48.__scopeId = "data-v-4fd0d81c";
238068
238233
  const __sfc_main$48 = script$48;
238069
238234
 
238070
238235
  /**
@@ -242345,6 +242510,10 @@ registerDashboardWidget('mail', {
242345
242510
  displayName: 'Mail',
242346
242511
  icon: 'EmailOutline',
242347
242512
  surfaces: ['detail-page'],
242513
+ // Draws its own title row; on a card surface it stands in an untitled card.
242514
+ // @spec openspec/changes/mail-widget-card/specs/mail-widget-card/spec.md#requirement-the-mail-widget-stands-in-a-card-on-a-card-surface
242515
+ ownsTitle: true,
242516
+ chrome: 'card',
242348
242517
  });
242349
242518
 
242350
242519
  /**
@@ -251259,6 +251428,18 @@ var script$3Q = {
251259
251428
  * @return {string}
251260
251429
  * @spec openspec/changes/record-list-table-row-actions/specs/record-list-table/spec.md#requirement-a-table-row-can-carry-a-second-line-and-an-actions-menu
251261
251430
  */
251431
+ /**
251432
+ * Whether the row action is drawn as an outlined button sized to its
251433
+ * label (`rowAction.variant: "outlined"`, DqMijnTeam "Oppakken") instead
251434
+ * of the 150px pill (DcMijnActies "Stem uitbrengen").
251435
+ *
251436
+ * @return {boolean} True for the outlined variant.
251437
+ * @spec openspec/changes/board-row-action-outlined/specs/board-row-action-outlined/spec.md#requirement-a-dashboard-rows-action-is-an-outlined-button
251438
+ */
251439
+ rowActionOutlined() {
251440
+ return this.content?.rowAction?.variant === 'outlined'
251441
+ },
251442
+
251262
251443
  rowActionsHeader() {
251263
251444
  const own = this.content?.rowActionsHeader;
251264
251445
  return typeof own === 'string' && own !== '' ? this.translate(own) : l10n.translate('nextcloud-vue', 'Actions')
@@ -251301,6 +251482,10 @@ var script$3Q = {
251301
251482
  if (this.conditionalEmptyText !== '') {
251302
251483
  return this.conditionalEmptyText
251303
251484
  }
251485
+ // `emptyText: ""` says nothing on an empty list.
251486
+ if (this.content?.emptyText === '') {
251487
+ return ''
251488
+ }
251304
251489
  return this.content?.emptyText ? this.translate(this.content.emptyText) : l10n.translate('nextcloud-vue', 'Nothing here yet')
251305
251490
  },
251306
251491
 
@@ -252622,7 +252807,7 @@ const _hoisted_4$2W = {
252622
252807
  class: "cn-record-list__loading"
252623
252808
  };
252624
252809
  const _hoisted_5$2x = {
252625
- key: 4,
252810
+ key: 0,
252626
252811
  class: "cn-record-list__empty",
252627
252812
  "data-testid": "cn-record-list-empty"
252628
252813
  };
@@ -252812,7 +252997,7 @@ const _hoisted_60$2 = {
252812
252997
  class: "cn-record-list__footnote",
252813
252998
  "data-testid": "cn-record-list-footnote"
252814
252999
  };
252815
- const _hoisted_61$1 = ["href"];
253000
+ const _hoisted_61$2 = ["href"];
252816
253001
  const _hoisted_62$1 = {
252817
253002
  key: 15,
252818
253003
  class: "cn-record-list__group-head",
@@ -252862,7 +253047,11 @@ function render$3P(_ctx, _cache, $props, $setup, $data, $options) {
252862
253047
  }, null, 8 /* PROPS */, ["name"])
252863
253048
  ]))
252864
253049
  : ($data.rows.length === 0)
252865
- ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_5$2x, vue$1.toDisplayString($options.emptyLabel), 1 /* TEXT */))
253050
+ ? (vue$1.openBlock(), vue$1.createElementBlock(vue$1.Fragment, { key: 4 }, [
253051
+ ($options.emptyLabel !== '')
253052
+ ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_5$2x, vue$1.toDisplayString($options.emptyLabel), 1 /* TEXT */))
253053
+ : vue$1.createCommentVNode("v-if", true)
253054
+ ], 64 /* STABLE_FRAGMENT */))
252866
253055
  : ($options.presentation === 'notice')
252867
253056
  ? (vue$1.openBlock(), vue$1.createElementBlock("div", {
252868
253057
  key: 5,
@@ -253172,12 +253361,14 @@ function render$3P(_ctx, _cache, $props, $setup, $data, $options) {
253172
253361
  (item.trailing)
253173
253362
  ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_31$d, vue$1.toDisplayString(item.trailing), 1 /* TEXT */))
253174
253363
  : vue$1.createCommentVNode("v-if", true),
253175
- (vue$1.openBlock(), vue$1.createBlock(vue$1.resolveDynamicComponent(item.action.tag), vue$1.mergeProps({ class: "cn-record-list__action" }, { ref_for: true }, item.action.attrs, { "data-testid": "cn-record-list-action" }, vue$1.toHandlers(item.action.tag === 'button' ? { click: () => $options.runRowAction(item.row) } : {})), {
253364
+ (vue$1.openBlock(), vue$1.createBlock(vue$1.resolveDynamicComponent(item.action.tag), vue$1.mergeProps({
253365
+ class: ["cn-record-list__action", { 'cn-record-list__action--outlined': $options.rowActionOutlined }]
253366
+ }, { ref_for: true }, item.action.attrs, { "data-testid": "cn-record-list-action" }, vue$1.toHandlers(item.action.tag === 'button' ? { click: () => $options.runRowAction(item.row) } : {})), {
253176
253367
  default: vue$1.withCtx(() => [
253177
253368
  vue$1.createTextVNode(vue$1.toDisplayString(item.action.label), 1 /* TEXT */)
253178
253369
  ]),
253179
253370
  _: 2 /* DYNAMIC */
253180
- }, 1040 /* FULL_PROPS, DYNAMIC_SLOTS */))
253371
+ }, 1040 /* FULL_PROPS, DYNAMIC_SLOTS */, ["class"]))
253181
253372
  ]))
253182
253373
  : ($options.presentation === 'rows')
253183
253374
  ? (vue$1.openBlock(), vue$1.createBlock(vue$1.resolveDynamicComponent(item.tag), vue$1.mergeProps({
@@ -253510,7 +253701,7 @@ function render$3P(_ctx, _cache, $props, $setup, $data, $options) {
253510
253701
  class: "cn-record-list__footer-link",
253511
253702
  href: $options.footerLink.href,
253512
253703
  "data-testid": "cn-record-list-footer-link"
253513
- }, vue$1.toDisplayString($options.footerLink.label), 9 /* TEXT, PROPS */, _hoisted_61$1))
253704
+ }, vue$1.toDisplayString($options.footerLink.label), 9 /* TEXT, PROPS */, _hoisted_61$2))
253514
253705
  : vue$1.createCommentVNode("v-if", true),
253515
253706
  ($options.footerGroupHead && $data.rows.length === 0 && !$data.error && !$data.loading)
253516
253707
  ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_62$1, [
@@ -253601,7 +253792,7 @@ function render$3P(_ctx, _cache, $props, $setup, $data, $options) {
253601
253792
  }
253602
253793
 
253603
253794
  script$3Q.render = render$3P;
253604
- script$3Q.__scopeId = "data-v-3c8e615c";
253795
+ script$3Q.__scopeId = "data-v-38566d36";
253605
253796
  const __sfc_main$3Q = script$3Q;
253606
253797
 
253607
253798
  /**
@@ -325338,6 +325529,12 @@ var script$2u = {
325338
325529
  default: '',
325339
325530
  },
325340
325531
 
325532
+ /** Keep the label for assistive technology only, when a heading above already says it. */
325533
+ hideLabel: {
325534
+ type: Boolean,
325535
+ default: false,
325536
+ },
325537
+
325341
325538
  /** The picker's label. */
325342
325539
  addLabel: {
325343
325540
  type: String,
@@ -325683,8 +325880,8 @@ function render$2t(_ctx, _cache, $props, $setup, $data, $options) {
325683
325880
  ? (vue$1.openBlock(), vue$1.createElementBlock("p", {
325684
325881
  key: 0,
325685
325882
  id: $data.labelId,
325686
- class: "cn-ordered-list-field__label"
325687
- }, vue$1.toDisplayString($props.label), 9 /* TEXT, PROPS */, _hoisted_2$2p))
325883
+ class: vue$1.normalizeClass(["cn-ordered-list-field__label", { 'hidden-visually': $props.hideLabel }])
325884
+ }, vue$1.toDisplayString($props.label), 11 /* TEXT, CLASS, PROPS */, _hoisted_2$2p))
325688
325885
  : vue$1.createCommentVNode("v-if", true),
325689
325886
  ($options.rows.length > 0)
325690
325887
  ? (vue$1.openBlock(), vue$1.createElementBlock("ol", {
@@ -325833,7 +326030,7 @@ function render$2t(_ctx, _cache, $props, $setup, $data, $options) {
325833
326030
  }
325834
326031
 
325835
326032
  script$2u.render = render$2t;
325836
- script$2u.__scopeId = "data-v-8f7a2df6";
326033
+ script$2u.__scopeId = "data-v-54034c6f";
325837
326034
  const __sfc_main$2u = script$2u;
325838
326035
 
325839
326036
  // SPDX-FileCopyrightText: 2026 Conduction B.V.
@@ -326744,6 +326941,7 @@ function render$2s(_ctx, _cache, $props, $setup, $data, $options) {
326744
326941
  modelValue: $options.values,
326745
326942
  options: $options.options,
326746
326943
  label: $options.t('nextcloud-vue', 'Views in my menu'),
326944
+ hideLabel: true,
326747
326945
  addLabel: $options.t('nextcloud-vue', 'Add view'),
326748
326946
  addPlaceholder: $options.t('nextcloud-vue', 'Pick a saved view'),
326749
326947
  addHint: $options.t('nextcloud-vue', 'You see your own views, the views shared with you and the default views. A new view goes to the bottom.'),
@@ -326757,7 +326955,7 @@ function render$2s(_ctx, _cache, $props, $setup, $data, $options) {
326757
326955
  }
326758
326956
 
326759
326957
  script$2t.render = render$2s;
326760
- script$2t.__scopeId = "data-v-dfcd17c4";
326958
+ script$2t.__scopeId = "data-v-962f985c";
326761
326959
  const __sfc_main$2t = script$2t;
326762
326960
 
326763
326961
  /**
@@ -343924,6 +344122,7 @@ var script$2d = {
343924
344122
  Pin,
343925
344123
  PinOutline,
343926
344124
  InformationOutline,
344125
+ Magnify,
343927
344126
  },
343928
344127
 
343929
344128
  inject: {
@@ -345012,6 +345211,7 @@ const _hoisted_29$a = { class: "cn-index-sidebar__section" };
345012
345211
  function render$2c(_ctx, _cache, $props, $setup, $data, $options) {
345013
345212
  const _component_CnIcon = vue$1.resolveComponent("CnIcon");
345014
345213
  const _component_FilterOutline = vue$1.resolveComponent("FilterOutline");
345214
+ const _component_Magnify = vue$1.resolveComponent("Magnify");
345015
345215
  const _component_NcTextField = vue$1.resolveComponent("NcTextField");
345016
345216
  const _component_NcSelect = vue$1.resolveComponent("NcSelect");
345017
345217
  const _component_ArrowUp = vue$1.resolveComponent("ArrowUp");
@@ -345068,9 +345268,19 @@ function render$2c(_ctx, _cache, $props, $setup, $data, $options) {
345068
345268
  vue$1.createVNode(_component_NcTextField, {
345069
345269
  modelValue: $props.searchValue || '',
345070
345270
  placeholder: $props.searchPlaceholder,
345071
- label: $props.searchLabel,
345271
+ label: $options.isBoardLook ? $props.searchPlaceholder : $props.searchLabel,
345072
345272
  "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => (_ctx.$emit('search', $event)))
345073
- }, null, 8 /* PROPS */, ["modelValue", "placeholder", "label"])
345273
+ }, vue$1.createSlots({ _: 2 /* DYNAMIC */ }, [
345274
+ ($options.isBoardLook)
345275
+ ? {
345276
+ name: "icon",
345277
+ fn: vue$1.withCtx(() => [
345278
+ vue$1.createVNode(_component_Magnify, { size: 18 })
345279
+ ]),
345280
+ key: "0"
345281
+ }
345282
+ : undefined
345283
+ ]), 1032 /* PROPS, DYNAMIC_SLOTS */, ["modelValue", "placeholder", "label"])
345074
345284
  ]),
345075
345285
  ($props.sortOptions.length > 0)
345076
345286
  ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_5$1z, [
@@ -351694,6 +351904,21 @@ var script$20 = {
351694
351904
  default: '',
351695
351905
  },
351696
351906
 
351907
+ /**
351908
+ * Board look: the count line while a saved view is applied (manifest
351909
+ * `config.savedViewCountText`), a template as `countText` that may name
351910
+ * the view's conditions as `{view}`: its description, else its filters
351911
+ * in words (DqFilterInMenu "3 van 48 lopende zaken · termijn binnen 7
351912
+ * dagen, van mij · gesorteerd op termijn"). A view's own `countText`
351913
+ * override and a scope's come first.
351914
+ *
351915
+ * @spec openspec/changes/applied-view-lights-its-lens/specs/applied-view-lights-its-lens/spec.md
351916
+ */
351917
+ savedViewCountText: {
351918
+ type: String,
351919
+ default: '',
351920
+ },
351921
+
351697
351922
  /**
351698
351923
  * Board look: a count line per view (manifest `config.countTextByView`),
351699
351924
  * `{ table?, cards?, list?, board?, map? }`, each a template as
@@ -354034,6 +354259,8 @@ var script$20 = {
354034
354259
  savedViewCounts: null,
354035
354260
  // The id (or slug) of the saved view that is applied; its chip shows selected under the board look.
354036
354261
  appliedSavedViewId: '',
354262
+ /** Whether the reader picked a lens chip after the applied view (then their chip shows). */
354263
+ lensChosenByHand: false,
354037
354264
  // The sort option the applied view's `overrides.sortOption` selected, so leaving the view can undo it.
354038
354265
  viewSortOption: '',
354039
354266
  // The applied view's filters as one comparable key, so the address keeps naming the view until they change.
@@ -355873,13 +356100,53 @@ var script$20 = {
355873
356100
  * @spec openspec/changes/working-list-row-actions/specs/index-page/spec.md
355874
356101
  */
355875
356102
  /**
355876
- * The chip the strip lights: the active tab, else the default tab of
355877
- * tabs that arrived after setup (`quickFiltersFrom`), which is the
355878
- * one the fetch applies.
356103
+ * The lens chip an applied saved view narrows within: the non-default
356104
+ * tab whose every condition the view's own conditions (its filters
356105
+ * over its locked filters, tokens as written) hold, the most specific
356106
+ * first (DqFilterInMenu: "Termijn deze week" lit for the view of that
356107
+ * name). Null without an applied view or a fitting tab.
356108
+ *
356109
+ * @return {number|null} The tab index.
356110
+ * @spec openspec/changes/applied-view-lights-its-lens/specs/applied-view-lights-its-lens/spec.md
356111
+ */
356112
+ appliedViewLensIndex() {
356113
+ const view = this.appliedSavedView;
356114
+ const tabs = Array.isArray(this.tabStripEntries) ? this.tabStripEntries : [];
356115
+ if (!view || tabs.length === 0) {
356116
+ return null
356117
+ }
356118
+ const query = view.query && typeof view.query === 'object' ? view.query : {};
356119
+ const locked = query.overrides && typeof query.overrides.lockedFilters === 'object' ? query.overrides.lockedFilters : {};
356120
+ const conditions = { ...(locked || {}), ...(query.filters || view.filters || {}) };
356121
+ const same = (a, b) => JSON.stringify(Array.isArray(a) ? a.map(String) : String(a)) === JSON.stringify(Array.isArray(b) ? b.map(String) : String(b));
356122
+ let best = null;
356123
+ let bestSize = 0;
356124
+ tabs.forEach((tab, index) => {
356125
+ const filter = tab && tab.filter && typeof tab.filter === 'object' ? tab.filter : {};
356126
+ const keys = Object.keys(filter);
356127
+ if (tab.default === true || keys.length === 0 || keys.length <= bestSize) {
356128
+ return
356129
+ }
356130
+ if (keys.every((key) => Object.hasOwn(conditions, key) && same(conditions[key], filter[key]))) {
356131
+ best = index;
356132
+ bestSize = keys.length;
356133
+ }
356134
+ });
356135
+ return best
356136
+ },
356137
+
356138
+ /**
356139
+ * The chip the strip lights: the lens an applied saved view narrows
356140
+ * within (until the reader picks a chip), else the active tab, else
356141
+ * the default tab of tabs that arrived after setup
356142
+ * (`quickFiltersFrom`), which is the one the fetch applies.
355879
356143
  *
355880
356144
  * @return {number|null} The tab index, or null without tabs.
355881
356145
  */
355882
356146
  shownQuickFilterIndex() {
356147
+ if (!this.quickFilterMultiple && !this.lensChosenByHand && this.appliedViewLensIndex !== null) {
356148
+ return this.appliedViewLensIndex
356149
+ }
355883
356150
  if (this.activeQuickFilterIndex !== null && this.activeQuickFilterIndex !== undefined) {
355884
356151
  return this.activeQuickFilterIndex
355885
356152
  }
@@ -356673,6 +356940,31 @@ var script$20 = {
356673
356940
  return l10n.translatePlural('nextcloud-vue', '%n without an address', '%n without an address', this.mapList.missing.length)
356674
356941
  },
356675
356942
 
356943
+ /**
356944
+ * The sentence on that card naming the rows without a place by the
356945
+ * list's title template, three at most and the rest counted: "A, B and
356946
+ * C have no location, so they are not on the map." Empty without a
356947
+ * title template.
356948
+ *
356949
+ * @spec openspec/changes/map-missing-card-names-the-cases/specs/map-missing-card-names-the-cases/spec.md#requirement-the-missing-address-card-names-the-cases-it-counts
356950
+ * @return {string} The sentence.
356951
+ */
356952
+ mapMissingNote() {
356953
+ const list = this.mapConfig && this.mapConfig.list;
356954
+ const template = list && typeof list.title === 'string' ? this.cnTranslate(list.title) : '';
356955
+ const missing = this.mapList.missing;
356956
+ if (template === '' || missing.length === 0) {
356957
+ return ''
356958
+ }
356959
+ const shown = missing.slice(0, 3).map((row) => fillTemplate(template, row, undefined, this.cnFormatters ? { formatters: this.cnFormatters } : null)).filter((title) => title !== '');
356960
+ if (shown.length === 0) {
356961
+ return ''
356962
+ }
356963
+ const rest = missing.length - shown.length;
356964
+ const names = listText(rest > 0 ? [...shown, l10n.translatePlural('nextcloud-vue', '%n other', '%n others', rest)] : shown, l10n.getCanonicalLocale() || undefined);
356965
+ return l10n.translatePlural('nextcloud-vue', '{names} has no location, so it is not on the map.', '{names} have no location, so they are not on the map.', missing.length, { names })
356966
+ },
356967
+
356676
356968
  /** @return {string} The link from that card to the table. */
356677
356969
  mapMissingLink() {
356678
356970
  return l10n.translate('nextcloud-vue', 'Show them in the table')
@@ -358244,6 +358536,11 @@ var script$20 = {
358244
358536
  * @return {string}
358245
358537
  */
358246
358538
  presentedCountText() {
358539
+ const viewLine = this.savedViewCountText;
358540
+ const view = this.appliedSavedView;
358541
+ if (view && typeof viewLine === 'string' && viewLine !== '' && this.activeScopePresentation.countText === undefined && !savedViewOverrides(view)?.countText) {
358542
+ return viewLine
358543
+ }
358247
358544
  if (this.activeScopePresentation.countText === undefined) {
358248
358545
  const byView = this.countTextByView;
358249
358546
  const own = byView && typeof byView === 'object' ? byView[this.currentViewMode] : undefined;
@@ -358254,6 +358551,23 @@ var script$20 = {
358254
358551
  return this.presented('countText')
358255
358552
  },
358256
358553
 
358554
+ /**
358555
+ * The applied saved view's conditions for the count line's `{view}`:
358556
+ * its description, else its filters in words, starting lower case.
358557
+ *
358558
+ * @return {string}
358559
+ * @spec openspec/changes/applied-view-lights-its-lens/specs/applied-view-lights-its-lens/spec.md
358560
+ */
358561
+ appliedViewConditionsText() {
358562
+ const view = this.appliedSavedView;
358563
+ if (!view) {
358564
+ return ''
358565
+ }
358566
+ const own = typeof view.description === 'string' ? view.description.trim() : '';
358567
+ const text = own !== '' ? own : String(this.viewFilterSummary(view) || '');
358568
+ return text.charAt(0).toLowerCase() + text.slice(1)
358569
+ },
358570
+
358257
358571
  headerDescriptionBase() {
358258
358572
  const total = this.effectivePagination?.total;
358259
358573
  // Board look: the count line is a template over the rows on this page
@@ -358277,6 +358591,7 @@ var script$20 = {
358277
358591
  .replace('{shown}', String(shown))
358278
358592
  .replace('{total}', String(total))
358279
358593
  .replace(/\{term\}/g, this.sourceTerm)
358594
+ .replace(/\{view\}/g, this.appliedViewConditionsText)
358280
358595
  }
358281
358596
  return this.countLine(this.countSubtitle, total, shown)
358282
358597
  }
@@ -358342,6 +358657,17 @@ var script$20 = {
358342
358657
  return !!this.resolvedSidebar.enabled && this.resolvedSidebar.show !== false
358343
358658
  },
358344
358659
 
358660
+ /**
358661
+ * Whether the page's side panel is open now; the board look then lays
358662
+ * the header out narrower (DqZakenSorteren).
358663
+ *
358664
+ * @spec openspec/changes/board-header-buttons-under-title-beside-side-panel/specs/board-header-buttons-under-title-beside-side-panel/spec.md#requirement-the-header-buttons-sit-under-the-title-while-the-side-panel-is-open
358665
+ * @return {boolean}
358666
+ */
358667
+ sidebarPanelOpen() {
358668
+ return Boolean(this.sidebarOpen && this.hasSidebar)
358669
+ },
358670
+
358345
358671
  /**
358346
358672
  * Whether the board toolbar draws the Filter button as a menu: the
358347
358673
  * board look, `filterMenu` on, something to filter by, and no filter
@@ -358384,7 +358710,9 @@ var script$20 = {
358384
358710
 
358385
358711
  /** Search props forwarded to the embedded CnIndexSidebar (defaults applied per CnIndexSidebar). */
358386
358712
  sidebarSearchProps() {
358387
- return (this.sidebar && this.sidebar.search) || {}
358713
+ // The page's own search hint, unless `sidebar.search` names one.
358714
+ const own = this.shownSearchPlaceholder ? { searchPlaceholder: this.shownSearchPlaceholder } : {};
358715
+ return { ...own, ...((this.sidebar && this.sidebar.search) || {}) }
358388
358716
  },
358389
358717
 
358390
358718
  /**
@@ -360207,6 +360535,7 @@ var script$20 = {
360207
360535
  // `activeQuickFilterIndex` is a setup-returned ref; the
360208
360536
  // Vue 2 ref-unwrap proxy makes plain assignment work.
360209
360537
  this.activeQuickFilterIndex = index;
360538
+ this.lensChosenByHand = true;
360210
360539
  this.$emit('quick-filter-change', index);
360211
360540
  },
360212
360541
 
@@ -360975,10 +361304,16 @@ var script$20 = {
360975
361304
  const bucket = facet && Array.isArray(facet.values)
360976
361305
  ? facet.values.find((fv) => fv && typeof fv === 'object' && String(fv.value) === id)
360977
361306
  : null;
360978
- if (bucket && typeof bucket.label === 'string' && bucket.label !== '') {
361307
+ // A facet that only echoes the value names nothing; a group or an
361308
+ // option below may.
361309
+ if (bucket && typeof bucket.label === 'string' && bucket.label !== '' && bucket.label !== id) {
360979
361310
  return this.cnTranslate(bucket.label)
360980
361311
  }
360981
361312
  const prop = this.effectiveSchema?.properties?.[key];
361313
+ // The reader on a user filter is "me" (DqFilterInMenu "Behandelaar: ik").
361314
+ if (prop && prop.referenceType === 'nextcloud-user' && id === (auth.getCurrentUser()?.uid ?? '')) {
361315
+ return l10n.translate('nextcloud-vue', 'me')
361316
+ }
360982
361317
  if (prop && prop.referenceType === 'nextcloud-group') {
360983
361318
  const name = groupDisplayName(id);
360984
361319
  if (name === null) {
@@ -362636,6 +362971,7 @@ var script$20 = {
362636
362971
  */
362637
362972
  onApplySavedView(view, { keepSavedViewKey = false } = {}) {
362638
362973
  this.appliedSavedViewId = view && (view.id || view.slug) ? String(view.id || view.slug) : '';
362974
+ this.lensChosenByHand = false;
362639
362975
  const state = extractViewState(view);
362640
362976
  // A view's filters may carry tokens (`@me`, `@myTeam`): resolved here,
362641
362977
  // and an optional one that answers nothing (no team) is dropped. A
@@ -363692,35 +364028,40 @@ const _hoisted_52$1 = {
363692
364028
  "data-testid": "cn-index-map-missing"
363693
364029
  };
363694
364030
  const _hoisted_53$1 = {
364031
+ key: 0,
364032
+ class: "cn-index-page__map-missing-note",
364033
+ "data-testid": "cn-index-map-missing-note"
364034
+ };
364035
+ const _hoisted_54$1 = {
363695
364036
  key: 16,
363696
364037
  class: "cn-index-page__content-search-note",
363697
364038
  "data-testid": "cn-index-content-search-note"
363698
364039
  };
363699
- const _hoisted_54$1 = {
364040
+ const _hoisted_55$1 = {
363700
364041
  key: 17,
363701
364042
  class: "cn-index-page__map-count",
363702
364043
  "data-testid": "cn-index-map-count"
363703
364044
  };
363704
- const _hoisted_55$1 = {
364045
+ const _hoisted_56$1 = {
363705
364046
  key: 0,
363706
364047
  "data-testid": "cn-index-map-count-line"
363707
364048
  };
363708
- const _hoisted_56$1 = {
364049
+ const _hoisted_57$1 = {
363709
364050
  key: 1,
363710
364051
  class: "cn-index-page__map-cap",
363711
364052
  "data-testid": "cn-index-map-cap"
363712
364053
  };
363713
- const _hoisted_57$1 = {
364054
+ const _hoisted_58$1 = {
363714
364055
  key: 18,
363715
364056
  class: "cn-index-page__board-load",
363716
364057
  "data-testid": "cn-index-board-load"
363717
364058
  };
363718
- const _hoisted_58$1 = {
364059
+ const _hoisted_59$1 = {
363719
364060
  class: "cn-index-page__board-load-count",
363720
364061
  "data-testid": "cn-index-board-load-count"
363721
364062
  };
363722
- const _hoisted_59$1 = ["data-split-layout"];
363723
- const _hoisted_60$1 = ["href", "title"];
364063
+ const _hoisted_60$1 = ["data-split-layout"];
364064
+ const _hoisted_61$1 = ["href", "title"];
363724
364065
 
363725
364066
  function render$1$(_ctx, _cache, $props, $setup, $data, $options) {
363726
364067
  const _component_CnBuildiqEditButton = vue$1.resolveComponent("CnBuildiqEditButton");
@@ -363783,7 +364124,7 @@ function render$1$(_ctx, _cache, $props, $setup, $data, $options) {
363783
364124
  const _component_CnIndexSidebar = vue$1.resolveComponent("CnIndexSidebar");
363784
364125
 
363785
364126
  return (vue$1.openBlock(), vue$1.createElementBlock("div", {
363786
- class: vue$1.normalizeClass(["cn-index-page", [$setup.lookClass, { 'cn-index-page--with-side-panel': $options.hasSidePanel, 'cn-index-page--quick-filters-toolbar': $options.effectiveQuickFilterPlacement === 'toolbar' }]]),
364127
+ class: vue$1.normalizeClass(["cn-index-page", [$setup.lookClass, { 'cn-index-page--with-side-panel': $options.hasSidePanel, 'cn-index-page--sidebar-open': $options.sidebarPanelOpen, 'cn-index-page--quick-filters-toolbar': $options.effectiveQuickFilterPlacement === 'toolbar' }]]),
363787
364128
  "data-testid": "cn-index-page",
363788
364129
  onKeydown: _cache[34] || (_cache[34] = (...args) => ($options.onListKeydown && $options.onListKeydown(...args)))
363789
364130
  }, [
@@ -364023,7 +364364,7 @@ function render$1$(_ctx, _cache, $props, $setup, $data, $options) {
364023
364364
  maxVisible: $props.quickFilterMaxVisible,
364024
364365
  fit: false,
364025
364366
  multiple: $props.quickFilterMultiple,
364026
- activeIndex: $setup.activeQuickFilterIndex,
364367
+ activeIndex: $options.shownQuickFilterIndex,
364027
364368
  selectedIndices: $setup.selectedQuickFilterIndices,
364028
364369
  counts: $options.effectiveTabCounts,
364029
364370
  "onUpdate:activeIndex": $options.onQuickFilterChange,
@@ -365033,6 +365374,9 @@ function render$1$(_ctx, _cache, $props, $setup, $data, $options) {
365033
365374
  ($options.mapList.missing.length > 0)
365034
365375
  ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_52$1, [
365035
365376
  vue$1.createElementVNode("strong", null, vue$1.toDisplayString($options.mapMissingTitle), 1 /* TEXT */),
365377
+ ($options.mapMissingNote)
365378
+ ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_53$1, vue$1.toDisplayString($options.mapMissingNote), 1 /* TEXT */))
365379
+ : vue$1.createCommentVNode("v-if", true),
365036
365380
  vue$1.createVNode(_component_NcButton, {
365037
365381
  variant: "tertiary-no-background",
365038
365382
  "data-testid": "cn-index-map-missing-table",
@@ -365272,21 +365616,21 @@ function render$1$(_ctx, _cache, $props, $setup, $data, $options) {
365272
365616
  onClose: $setup.closeContextMenu
365273
365617
  }, null, 8 /* PROPS */, ["open", "actions", "targetItem", "onAction", "onClose"]),
365274
365618
  ($props.searchInFiles && $setup.contentSearch)
365275
- ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_53$1, vue$1.toDisplayString(_ctx.t('nextcloud-vue', 'File matches are limited to the best 50.')), 1 /* TEXT */))
365619
+ ? (vue$1.openBlock(), vue$1.createElementBlock("p", _hoisted_54$1, vue$1.toDisplayString(_ctx.t('nextcloud-vue', 'File matches are limited to the best 50.')), 1 /* TEXT */))
365276
365620
  : vue$1.createCommentVNode("v-if", true),
365277
365621
  ($options.mapCountLine || $options.mapCapNote)
365278
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_54$1, [
365622
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_55$1, [
365279
365623
  ($options.mapCountLine)
365280
- ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_55$1, vue$1.toDisplayString($options.mapCountLine), 1 /* TEXT */))
365624
+ ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_56$1, vue$1.toDisplayString($options.mapCountLine), 1 /* TEXT */))
365281
365625
  : vue$1.createCommentVNode("v-if", true),
365282
365626
  ($options.mapCapNote)
365283
- ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_56$1, vue$1.toDisplayString($options.mapCapNote), 1 /* TEXT */))
365627
+ ? (vue$1.openBlock(), vue$1.createElementBlock("span", _hoisted_57$1, vue$1.toDisplayString($options.mapCapNote), 1 /* TEXT */))
365284
365628
  : vue$1.createCommentVNode("v-if", true)
365285
365629
  ]))
365286
365630
  : vue$1.createCommentVNode("v-if", true),
365287
365631
  ($options.boardLoadsAll && $options.boardLoadCounts.total > 0)
365288
- ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_57$1, [
365289
- vue$1.createElementVNode("span", _hoisted_58$1, vue$1.toDisplayString(_ctx.t('nextcloud-vue', 'Showing {count} of {total}', { count: $options.boardLoadCounts.shown, total: $options.boardLoadCounts.total })), 1 /* TEXT */),
365632
+ ? (vue$1.openBlock(), vue$1.createElementBlock("div", _hoisted_58$1, [
365633
+ vue$1.createElementVNode("span", _hoisted_59$1, vue$1.toDisplayString(_ctx.t('nextcloud-vue', 'Showing {count} of {total}', { count: $options.boardLoadCounts.shown, total: $options.boardLoadCounts.total })), 1 /* TEXT */),
365290
365634
  ($options.isPaged)
365291
365635
  ? (vue$1.openBlock(), vue$1.createBlock(_component_NcButton, {
365292
365636
  key: 0,
@@ -365358,7 +365702,7 @@ function render$1$(_ctx, _cache, $props, $setup, $data, $options) {
365358
365702
  }, [
365359
365703
  vue$1.createVNode(_component_Close, { size: 16 }),
365360
365704
  vue$1.createElementVNode("span", null, vue$1.toDisplayString($options.splitCloseTitle), 1 /* TEXT */)
365361
- ], 8 /* PROPS */, _hoisted_60$1))
365705
+ ], 8 /* PROPS */, _hoisted_61$1))
365362
365706
  : vue$1.createCommentVNode("v-if", true),
365363
365707
  vue$1.renderSlot(_ctx.$slots, "split-pane", {
365364
365708
  id: $props.splitId,
@@ -365366,7 +365710,7 @@ function render$1$(_ctx, _cache, $props, $setup, $data, $options) {
365366
365710
  close: $options.closeSplitPane,
365367
365711
  saved: $options.onSplitPaneSaved
365368
365712
  })
365369
- ], 14 /* CLASS, STYLE, PROPS */, _hoisted_59$1))
365713
+ ], 14 /* CLASS, STYLE, PROPS */, _hoisted_60$1))
365370
365714
  : vue$1.createCommentVNode("v-if", true)
365371
365715
  ], 2 /* CLASS */),
365372
365716
  ($props.notice && $props.notice.text && $options.noticeOnPage)
@@ -431370,7 +431714,7 @@ function render$o(_ctx, _cache, $props, $setup, $data, $options) {
431370
431714
  }
431371
431715
 
431372
431716
  script$o.render = render$o;
431373
- script$o.__scopeId = "data-v-9a3971e2";
431717
+ script$o.__scopeId = "data-v-2743a776";
431374
431718
  const __sfc_main$o = script$o;
431375
431719
 
431376
431720
  /**
@@ -441325,7 +441669,7 @@ function render$d(_ctx, _cache, $props, $setup, $data, $options) {
441325
441669
  }
441326
441670
 
441327
441671
  script$d.render = render$d;
441328
- script$d.__scopeId = "data-v-85a3fe56";
441672
+ script$d.__scopeId = "data-v-ebb6783e";
441329
441673
  const __sfc_main$d = script$d;
441330
441674
 
441331
441675
  let titleIdCounter = 0;