@conduction/nextcloud-vue 2.96.0 → 2.98.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 (163) hide show
  1. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +17 -0
  2. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  3. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  4. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue.js +1 -1
  5. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +33 -1
  6. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  7. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +383 -359
  8. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  9. package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
  10. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +106 -2
  11. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
  12. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
  13. package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue.js +9 -0
  14. package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue.js.map +1 -0
  15. package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue2.js +964 -0
  16. package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue2.js.map +1 -0
  17. package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue3.js +282 -0
  18. package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue3.js.map +1 -0
  19. package/dist/esm/components/CnChecklistRunWidget/index.js +28 -0
  20. package/dist/esm/components/CnChecklistRunWidget/index.js.map +1 -0
  21. package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js +1 -1
  22. package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js +17 -3
  23. package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js.map +1 -1
  24. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js +61 -43
  25. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js.map +1 -1
  26. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue.js +1 -1
  27. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +159 -18
  28. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -1
  29. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js +9 -7
  30. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -1
  31. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
  32. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +1 -0
  33. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  34. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  35. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +26 -8
  36. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  37. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  38. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +188 -5
  39. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  40. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  41. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
  42. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +48 -3
  43. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  44. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +46 -12
  45. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  46. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue.js +1 -1
  47. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js +3 -3
  48. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
  49. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
  50. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue.js +1 -1
  51. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue2.js +27 -0
  52. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue2.js.map +1 -1
  53. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue3.js +16 -2
  54. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue3.js.map +1 -1
  55. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
  56. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +144 -0
  57. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  58. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +488 -399
  59. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  60. package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js +8 -2
  61. package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js.map +1 -1
  62. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +344 -21
  63. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  64. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +29 -7
  65. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  66. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js +1 -1
  67. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js +2 -2
  68. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js.map +1 -1
  69. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js +2 -2
  70. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js.map +1 -1
  71. package/dist/esm/components/CnIndexPage/useNamedSource.js +1 -1
  72. package/dist/esm/components/CnIndexPage/useNamedSource.js.map +1 -1
  73. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue.js +1 -1
  74. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +13 -0
  75. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  76. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +5 -1
  77. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  78. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue.js +9 -0
  79. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue.js.map +1 -0
  80. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js +220 -0
  81. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js.map +1 -0
  82. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js +121 -0
  83. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js.map +1 -0
  84. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue.js +1 -1
  85. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue2.js +83 -9
  86. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue2.js.map +1 -1
  87. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue3.js +80 -11
  88. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue3.js.map +1 -1
  89. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  90. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  91. package/dist/esm/composables/indexSources.js +420 -5
  92. package/dist/esm/composables/indexSources.js.map +1 -1
  93. package/dist/esm/composables/useOrganisationStore.js +115 -0
  94. package/dist/esm/composables/useOrganisationStore.js.map +1 -0
  95. package/dist/esm/index.js +2 -0
  96. package/dist/esm/index.js.map +1 -1
  97. package/dist/esm/l10n/en.json.js +21 -1
  98. package/dist/esm/l10n/en.json.js.map +1 -1
  99. package/dist/esm/l10n/nl.json.js +36 -1
  100. package/dist/esm/l10n/nl.json.js.map +1 -1
  101. package/dist/esm/library-style.js +1 -1
  102. package/dist/esm/nextcloud-vue.css +725 -383
  103. package/dist/esm/utils/builtInFormatters.js +34 -1
  104. package/dist/esm/utils/builtInFormatters.js.map +1 -1
  105. package/dist/esm/utils/libraryWidgetKeys.js +1 -0
  106. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  107. package/dist/esm/utils/matrixCells.js +158 -0
  108. package/dist/esm/utils/matrixCells.js.map +1 -0
  109. package/dist/esm/utils/nextcloudDirectory.js +121 -0
  110. package/dist/esm/utils/nextcloudDirectory.js.map +1 -0
  111. package/dist/esm/utils/queryPresets.js +13 -2
  112. package/dist/esm/utils/queryPresets.js.map +1 -1
  113. package/dist/esm/utils/scopeListLayout.js +37 -1
  114. package/dist/esm/utils/scopeListLayout.js.map +1 -1
  115. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  116. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  117. package/dist/nextcloud-vue.cjs.js +14443 -10519
  118. package/dist/nextcloud-vue.cjs.js.map +2 -2
  119. package/dist/nextcloud-vue.css +725 -383
  120. package/l10n/en.json +21 -1
  121. package/l10n/nl.json +36 -1
  122. package/package.json +2 -2
  123. package/scripts/merge-l10n.js +196 -0
  124. package/src/components/CnAppRoot/CnAppRoot.md +1 -0
  125. package/src/components/CnAppRoot/CnAppRoot.vue +17 -0
  126. package/src/components/CnCellRenderer/CnCellRenderer.vue +57 -1
  127. package/src/components/CnChartWidget/CnChartWidget.md +2 -1
  128. package/src/components/CnChartWidget/CnChartWidget.vue +106 -2
  129. package/src/components/CnChecklistRunWidget/CnChecklistRunWidget.vue +1269 -0
  130. package/src/components/CnChecklistRunWidget/index.js +27 -0
  131. package/src/components/CnConversationThread/CnConversationThread.md +12 -0
  132. package/src/components/CnConversationThread/CnConversationThread.vue +30 -4
  133. package/src/components/CnConversationThread/CnConversationWidget.vue +167 -24
  134. package/src/components/CnDashboardPage/CnDashboardPage.vue +1 -0
  135. package/src/components/CnDataTable/CnDataTable.vue +26 -8
  136. package/src/components/CnDetailPage/CnDetailPage.md +48 -0
  137. package/src/components/CnDetailPage/CnDetailPage.vue +188 -5
  138. package/src/components/CnFactsWidget/CnFactsWidget.vue +114 -3
  139. package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +3 -3
  140. package/src/components/CnFeaturesBoard/CnFeaturesBoard.vue +38 -2
  141. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +508 -236
  142. package/src/components/CnFilesBrowser/filesBrowserColumns.js +8 -2
  143. package/src/components/CnIndexPage/CnIndexPage.md +28 -0
  144. package/src/components/CnIndexPage/CnIndexPage.vue +367 -27
  145. package/src/components/CnIndexPage/CnIndexPageWidgets.vue +13 -4
  146. package/src/components/CnIndexPage/useNamedSource.js +1 -1
  147. package/src/components/CnObjectSidebar/CnFilesTab.vue +17 -0
  148. package/src/components/CnOrderedListField/CnOrderedListField.vue +386 -0
  149. package/src/components/CnOrderedListField/index.js +7 -0
  150. package/src/components/CnUserSettingsDialog/CnUserSettingsDialog.vue +139 -10
  151. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  152. package/src/components/index.js +2 -0
  153. package/src/composables/indexSources.js +419 -4
  154. package/src/composables/useOrganisationStore.js +111 -0
  155. package/src/index.js +2 -0
  156. package/src/schemas/app-manifest-v2.schema.json +42 -5
  157. package/src/utils/builtInFormatters.js +33 -0
  158. package/src/utils/libraryWidgetKeys.js +1 -0
  159. package/src/utils/matrixCells.js +154 -0
  160. package/src/utils/nextcloudDirectory.js +117 -0
  161. package/src/utils/queryPresets.js +13 -2
  162. package/src/utils/scopeListLayout.js +37 -1
  163. package/src/utils/validateManifestV2.compiled.js +3 -3
@@ -35,11 +35,13 @@ import __sfc_main$8 from '../CnSegmentedControl/CnSegmentedControl.vue.js';
35
35
  import __sfc_main$d from '../CnSummaryAggregates/CnSummaryAggregates.vue.js';
36
36
  import __sfc_main$i from '../CnTranslatedBadge/CnTranslatedBadge.vue.js';
37
37
  import __sfc_main$e from './CnDetailHeaderChips.vue.js';
38
+ import { resolveRecordSource } from '../../composables/indexSources.js';
38
39
  import { useIntegrationRegistry } from '../../composables/useIntegrationRegistry.js';
39
40
  import { normalizeLook } from '../../composables/useLook.js';
40
41
  import { useObjectLock } from '../../composables/useObjectLock.js';
41
42
  import { useObjectPresence } from '../../composables/useObjectPresence.js';
42
43
  import { useObjectSubscription } from '../../composables/useObjectSubscription.js';
44
+ import { createRefLabelResolver } from '../../composables/useRefLabels.js';
43
45
  import { gridLayout } from '../../mixins/gridLayout.js';
44
46
  import { pageViews } from '../../mixins/pageViews.js';
45
47
  import { isAppInstalled } from '../../utils/appInstalled.js';
@@ -692,6 +694,23 @@ var script = {
692
694
  default: null,
693
695
  },
694
696
 
697
+ /**
698
+ * Name of a registered entity source that reads ONE record (manifest
699
+ * `config.entitySource`), for a record that is not a register/schema
700
+ * object. The same registry and the same name as `CnIndexPage`'s
701
+ * `entitySource`: `organisations` shows the OpenRegister organisation
702
+ * whose uuid is `objectId`. The record feeds the header, pills, facts,
703
+ * widgets and the `@objectId` / `@object.*` tokens exactly as a fetched
704
+ * object does. Wins over `register` + `schema`. Empty (the default)
705
+ * leaves the page on its object store.
706
+ *
707
+ * @type {string}
708
+ */
709
+ entitySource: {
710
+ type: String,
711
+ default: '',
712
+ },
713
+
695
714
  /**
696
715
  * OpenRegister register slug. Pair with `schema` to opt into the
697
716
  * schema-driven mode: the page fuses the two into an internal
@@ -1646,6 +1665,10 @@ var script = {
1646
1665
  pillCounts: {},
1647
1666
  /** Sequence of the last pill count round, so a late answer does not overwrite a newer one. */
1648
1667
  pillCountSeq: 0,
1668
+ /** The labels of the records pills with `resolve` point at, by pill key. */
1669
+ pillRefLabels: {},
1670
+ /** Sequence of the last pill label lookup, so a late answer for another record is dropped. */
1671
+ pillRefSeq: 0,
1649
1672
  /** `register/schema/id` the read-state call was last made for, so it is sent once per load. */
1650
1673
  readStateSentFor: '',
1651
1674
  /** Whether the user is arranging the chosen view (`userLayout`). */
@@ -2167,7 +2190,16 @@ var script = {
2167
2190
  const pill = pillCountSource(config)
2168
2191
  ? resolveCountPill(config, this.pillCounts[key], (text) => this.effectiveTranslate(text))
2169
2192
  : resolvePill(config, this.resolvedObject);
2170
- return pill ? { ...pill, key, label: this.effectiveTranslate(pill.label) } : null
2193
+ if (!pill) {
2194
+ return null
2195
+ }
2196
+ // `resolve`: the referenced record's label replaces the id, and
2197
+ // keys the colour too ("Woo-verzoek", not a uuid).
2198
+ const resolved = this.pillRefLabels[key];
2199
+ if (typeof resolved === 'string' && resolved !== '') {
2200
+ return { ...pill, key, label: this.effectiveTranslate(resolved), colorKey: resolved }
2201
+ }
2202
+ return { ...pill, key, label: this.effectiveTranslate(pill.label) }
2171
2203
  })
2172
2204
  .filter(Boolean)
2173
2205
  },
@@ -2179,6 +2211,34 @@ var script = {
2179
2211
  * @return {string} The key.
2180
2212
  * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-header-pill-can-be-a-constant-or-a-count
2181
2213
  */
2214
+ /**
2215
+ * What the resolved pills depend on, as one key: a change looks again.
2216
+ *
2217
+ * @return {string}
2218
+ * @spec openspec/changes/detail-pill-resolve/specs/detail-page/spec.md#requirement-a-header-pill-can-show-a-referenced-record-s-label
2219
+ */
2220
+ pillResolveKey() {
2221
+ return JSON.stringify(this.pillResolveTargets)
2222
+ },
2223
+
2224
+ /**
2225
+ * The pills with `resolve: { register, schema, labelField? }` and the
2226
+ * id each one shows: `[key, register, schema, labelField, id]`. Empty
2227
+ * while the field is empty.
2228
+ *
2229
+ * @return {Array<Array<string>>}
2230
+ * @spec openspec/changes/detail-pill-resolve/specs/detail-page/spec.md#requirement-a-header-pill-can-show-a-referenced-record-s-label
2231
+ */
2232
+ pillResolveTargets() {
2233
+ return [['type', this.typePill], ['status', this.statusPill]]
2234
+ .filter(([, config]) => config && config.resolve && config.resolve.register && config.resolve.schema && typeof config.field === 'string')
2235
+ .map(([key, config]) => {
2236
+ const pill = resolvePill({ field: config.field }, this.resolvedObject);
2237
+ return pill ? [key, config.resolve.register, config.resolve.schema, config.resolve.labelField || '', pill.colorKey] : null
2238
+ })
2239
+ .filter(Boolean)
2240
+ },
2241
+
2182
2242
  pillCountKey() {
2183
2243
  return JSON.stringify([this.objectId, pillCountSource(this.typePill), pillCountSource(this.statusPill)])
2184
2244
  },
@@ -2381,6 +2441,9 @@ var script = {
2381
2441
  * @return {boolean}
2382
2442
  */
2383
2443
  awaitingObject() {
2444
+ if (this.hasRecordSourceFetch) {
2445
+ return this.objectFetchPending && !this.currentObject
2446
+ }
2384
2447
  return this.hasSchemaDrivenFetch
2385
2448
  && this.objectFetchPending
2386
2449
  && typeof this.effectiveObjectStore?.fetchObject === 'function'
@@ -2454,7 +2517,45 @@ var script = {
2454
2517
  * the fetch path entirely.
2455
2518
  */
2456
2519
  hasSchemaDrivenFetch() {
2457
- return Boolean(this.register && this.schema && this.objectId)
2520
+ return !this.entitySource && Boolean(this.register && this.schema && this.objectId)
2521
+ },
2522
+
2523
+ /**
2524
+ * The record source named by `entitySource`, or null. An unknown name,
2525
+ * or a source that cannot read one record, is null with a warning.
2526
+ *
2527
+ * @return {object|null}
2528
+ */
2529
+ recordSource() {
2530
+ if (!this.entitySource) {
2531
+ return null
2532
+ }
2533
+ try {
2534
+ return resolveRecordSource(this.entitySource)
2535
+ } catch (err) {
2536
+ // eslint-disable-next-line no-console -- the console is the only channel a host app can read the detail on
2537
+ console.warn('[CnDetailPage] the record source could not start; no record is shown.', err);
2538
+ return null
2539
+ }
2540
+ },
2541
+
2542
+ /**
2543
+ * Whether the page reads its record through a record source.
2544
+ *
2545
+ * @return {boolean}
2546
+ */
2547
+ hasRecordSourceFetch() {
2548
+ return Boolean(this.recordSource && this.objectId)
2549
+ },
2550
+
2551
+ /**
2552
+ * Whether the page reads its own record, from the object store or a
2553
+ * record source, so Refresh and `cn:page:refresh` have something to do.
2554
+ *
2555
+ * @return {boolean}
2556
+ */
2557
+ selfFetchesRecord() {
2558
+ return this.hasSchemaDrivenFetch || this.hasRecordSourceFetch
2458
2559
  },
2459
2560
 
2460
2561
  /**
@@ -2669,7 +2770,7 @@ var script = {
2669
2770
  }
2670
2771
  // `$.vnode.props`, not `$attrs`: `refresh` is a declared emit, and
2671
2772
  // Vue keeps declared emits out of `$attrs`.
2672
- return Boolean(this.$.vnode.props?.onRefresh) || this.hasSchemaDrivenFetch
2773
+ return Boolean(this.$.vnode.props?.onRefresh) || this.selfFetchesRecord
2673
2774
  },
2674
2775
 
2675
2776
  /**
@@ -2713,6 +2814,9 @@ var script = {
2713
2814
  * @return {object|null}
2714
2815
  */
2715
2816
  currentObject() {
2817
+ if (this.recordSource) {
2818
+ return this.objectId ? (this.recordSource.record(this.objectId) ?? null) : null
2819
+ }
2716
2820
  const store = this.effectiveObjectStore;
2717
2821
  if (!store) {
2718
2822
  return null
@@ -3388,6 +3492,14 @@ var script = {
3388
3492
  },
3389
3493
  },
3390
3494
 
3495
+ // A pill with `resolve` looks its record up again for another value.
3496
+ pillResolveKey: {
3497
+ immediate: true,
3498
+ handler() {
3499
+ this.refreshPillRefLabels();
3500
+ },
3501
+ },
3502
+
3391
3503
  // The header's count pills count again for another record or other lists.
3392
3504
  pillCountKey: {
3393
3505
  immediate: true,
@@ -3811,7 +3923,7 @@ var script = {
3811
3923
  // Schema-driven (manifest) detail pages self-fetch — without this
3812
3924
  // the Refresh action has no host listener to act on and does
3813
3925
  // nothing. Re-fetch the object + schema and spin the action.
3814
- if (!this.hasSchemaDrivenFetch) {
3926
+ if (!this.selfFetchesRecord) {
3815
3927
  return
3816
3928
  }
3817
3929
  this.internalRefreshing = true;
@@ -3824,6 +3936,42 @@ var script = {
3824
3936
  }
3825
3937
  },
3826
3938
 
3939
+ /**
3940
+ * Look up the records the `resolve` pills point at, through the same
3941
+ * reference-label resolver the index tables use. A failed lookup keeps
3942
+ * the raw value.
3943
+ *
3944
+ * @return {Promise<void>}
3945
+ * @spec openspec/changes/detail-pill-resolve/specs/detail-page/spec.md#requirement-a-header-pill-can-show-a-referenced-record-s-label
3946
+ */
3947
+ async refreshPillRefLabels() {
3948
+ const targets = this.pillResolveTargets;
3949
+ const seq = ++this.pillRefSeq;
3950
+ if (targets.length === 0) {
3951
+ this.pillRefLabels = {};
3952
+ return
3953
+ }
3954
+ if (!this._pillRefResolver) {
3955
+ this._pillRefResolver = createRefLabelResolver(() => {
3956
+ try {
3957
+ return this.objectStore || useObjectStore()
3958
+ } catch {
3959
+ return null
3960
+ }
3961
+ });
3962
+ }
3963
+ const labels = {};
3964
+ await Promise.all(targets.map(async ([key, register, schema, labelField, id]) => {
3965
+ const found = await this._pillRefResolver.resolve(register, schema, [id], labelField).catch(() => ({}));
3966
+ if (found && typeof found[id] === 'string') {
3967
+ labels[key] = found[id];
3968
+ }
3969
+ }));
3970
+ if (seq === this.pillRefSeq) {
3971
+ this.pillRefLabels = labels;
3972
+ }
3973
+ },
3974
+
3827
3975
  /**
3828
3976
  * Count the lists behind the header's count pills (`countFrom`): one
3829
3977
  * list request each (`_limit=1`, the `total`). A failed count, or a
@@ -3866,7 +4014,7 @@ var script = {
3866
4014
  */
3867
4015
  onPageRefreshBus(payload) {
3868
4016
  this.refreshPillCounts();
3869
- if (!this.hasSchemaDrivenFetch) {
4017
+ if (!this.selfFetchesRecord) {
3870
4018
  return
3871
4019
  }
3872
4020
  if (this.internalRefreshing) {
@@ -4091,6 +4239,10 @@ var script = {
4091
4239
  },
4092
4240
 
4093
4241
  async fetchObjectIfNeeded() {
4242
+ if (this.recordSource) {
4243
+ await this.fetchSourceRecord();
4244
+ return
4245
+ }
4094
4246
  // Create archetype: no object to fetch, but the create form needs
4095
4247
  // the schema — register the type and fetch its schema, then stop.
4096
4248
  if (!this.hasSchemaDrivenFetch) {
@@ -4154,6 +4306,37 @@ var script = {
4154
4306
  }
4155
4307
  },
4156
4308
 
4309
+ /**
4310
+ * Read the record through the record source. A refusal (403) or a
4311
+ * missing record (404) shows the not-found state; which records the
4312
+ * viewer may read is the server's decision.
4313
+ *
4314
+ * @return {Promise<void>}
4315
+ */
4316
+ async fetchSourceRecord() {
4317
+ const source = this.recordSource;
4318
+ const objectId = this.objectId;
4319
+ if (!objectId) {
4320
+ this.objectFetchPending = false;
4321
+ return
4322
+ }
4323
+ this.objectFetchPending = true;
4324
+ this.objectNotFound = false;
4325
+ try {
4326
+ await source.loadRecord(String(objectId));
4327
+ } catch (err) {
4328
+ // eslint-disable-next-line no-console -- the failure is already handled; the console is the only channel a host app can read the detail on
4329
+ console.error('[CnDetailPage] record source fetch failed:', err);
4330
+ } finally {
4331
+ // A newer fetch for another id owns the flags now.
4332
+ if (objectId === this.objectId) {
4333
+ const status = typeof source.recordError === 'function' ? source.recordError(String(objectId)) : null;
4334
+ this.objectNotFound = !this.currentObject && (status === 403 || status === 404);
4335
+ this.objectFetchPending = false;
4336
+ }
4337
+ }
4338
+ },
4339
+
4157
4340
  /**
4158
4341
  * Whether a grid-layout item resolves to an integration-typed
4159
4342
  * widget — `def.type === 'integration'` with a string