@semiont/react-ui 0.5.3 → 0.5.5

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 (148) hide show
  1. package/dist/{PdfAnnotationCanvas.client-5QESNO5H.mjs → PdfAnnotationCanvas.client-CN3C3S55.js} +2 -2
  2. package/dist/{ar-UUMMNQKF.mjs → ar-U2EXWUMQ.js} +2 -5
  3. package/dist/ar-U2EXWUMQ.js.map +1 -0
  4. package/dist/{bn-AL5BJSR3.mjs → bn-DRJGV772.js} +2 -5
  5. package/dist/bn-DRJGV772.js.map +1 -0
  6. package/dist/{chunk-EBBL3VJI.mjs → chunk-3Q3TUKWP.js} +32 -32
  7. package/dist/{chunk-OJSRLEER.mjs → chunk-GERSK2DY.js} +2 -5
  8. package/dist/chunk-GERSK2DY.js.map +1 -0
  9. package/dist/{chunk-D4GAAQMM.mjs → chunk-K6BJDL2I.js} +1 -1
  10. package/dist/{cs-UMINALSU.mjs → cs-PTWDM23V.js} +2 -5
  11. package/dist/{cs-UMINALSU.mjs.map → cs-PTWDM23V.js.map} +1 -1
  12. package/dist/{da-FKUX6CDL.mjs → da-KSNIKYSS.js} +2 -5
  13. package/dist/da-KSNIKYSS.js.map +1 -0
  14. package/dist/{de-XSJ3E25S.mjs → de-F2XBEWFY.js} +2 -5
  15. package/dist/de-F2XBEWFY.js.map +1 -0
  16. package/dist/{el-UJXNRCBP.mjs → el-DLD2GWAP.js} +2 -5
  17. package/dist/el-DLD2GWAP.js.map +1 -0
  18. package/dist/{en-J5DHKLQ5.mjs → en-L45VK7BS.js} +3 -3
  19. package/dist/{es-VURP62BU.mjs → es-WLPYWGB5.js} +2 -5
  20. package/dist/es-WLPYWGB5.js.map +1 -0
  21. package/dist/{fa-TIT5ZPZY.mjs → fa-BAXHSDZG.js} +2 -5
  22. package/dist/fa-BAXHSDZG.js.map +1 -0
  23. package/dist/{fi-F7VTGT4H.mjs → fi-FCHSYVOT.js} +2 -5
  24. package/dist/fi-FCHSYVOT.js.map +1 -0
  25. package/dist/{fr-2ZR26VF7.mjs → fr-3UERBSL6.js} +2 -5
  26. package/dist/fr-3UERBSL6.js.map +1 -0
  27. package/dist/{he-BXP2KYVZ.mjs → he-F6F3FV2K.js} +2 -5
  28. package/dist/he-F6F3FV2K.js.map +1 -0
  29. package/dist/{hi-PSWTP3NC.mjs → hi-4BK6IK7Q.js} +2 -5
  30. package/dist/hi-4BK6IK7Q.js.map +1 -0
  31. package/dist/{id-HO6TXGTO.mjs → id-7ECCWP3J.js} +2 -5
  32. package/dist/id-7ECCWP3J.js.map +1 -0
  33. package/dist/{index.d.mts → index.d.ts} +25 -10
  34. package/dist/{index.mjs → index.js} +43 -46
  35. package/dist/index.js.map +1 -0
  36. package/dist/{it-AGTDMBL3.mjs → it-234Z6XK6.js} +2 -5
  37. package/dist/it-234Z6XK6.js.map +1 -0
  38. package/dist/{ja-TTGOVF5K.mjs → ja-PJWQI4OQ.js} +2 -5
  39. package/dist/ja-PJWQI4OQ.js.map +1 -0
  40. package/dist/{ko-FF77IQ7N.mjs → ko-APUEW2RS.js} +2 -5
  41. package/dist/ko-APUEW2RS.js.map +1 -0
  42. package/dist/{ms-UPQWWIL4.mjs → ms-PJBZWZWD.js} +2 -5
  43. package/dist/ms-PJBZWZWD.js.map +1 -0
  44. package/dist/{nl-W75HEPFL.mjs → nl-L4C3ZBCU.js} +2 -5
  45. package/dist/nl-L4C3ZBCU.js.map +1 -0
  46. package/dist/{no-R4W7W7ZU.mjs → no-QE5N5KNG.js} +2 -5
  47. package/dist/no-QE5N5KNG.js.map +1 -0
  48. package/dist/{pl-GQC2ELWO.mjs → pl-5Q2D23PD.js} +2 -5
  49. package/dist/pl-5Q2D23PD.js.map +1 -0
  50. package/dist/{pt-YGVT62RU.mjs → pt-AIGUOIOC.js} +2 -5
  51. package/dist/pt-AIGUOIOC.js.map +1 -0
  52. package/dist/{ro-TST6XS6X.mjs → ro-T56CSHTY.js} +2 -5
  53. package/dist/ro-T56CSHTY.js.map +1 -0
  54. package/dist/{sv-TQLF6HV7.mjs → sv-L4TJQ2UH.js} +2 -5
  55. package/dist/sv-L4TJQ2UH.js.map +1 -0
  56. package/dist/{test-utils.d.mts → test-utils.d.ts} +20 -5
  57. package/dist/{test-utils.mjs → test-utils.js} +5 -5
  58. package/dist/{th-HJUIETVR.mjs → th-6O7Y6O2Q.js} +2 -5
  59. package/dist/th-6O7Y6O2Q.js.map +1 -0
  60. package/dist/{tr-CW3C46TW.mjs → tr-D4CQCSNO.js} +2 -5
  61. package/dist/tr-D4CQCSNO.js.map +1 -0
  62. package/dist/{uk-WTHZQB2U.mjs → uk-2HMQG6ND.js} +2 -5
  63. package/dist/uk-2HMQG6ND.js.map +1 -0
  64. package/dist/{vi-PHWHJLKP.mjs → vi-XVJ4RUEJ.js} +2 -5
  65. package/dist/{vi-PHWHJLKP.mjs.map → vi-XVJ4RUEJ.js.map} +1 -1
  66. package/dist/{zh-MO3FCUD6.mjs → zh-K2KDPGHK.js} +2 -5
  67. package/dist/zh-K2KDPGHK.js.map +1 -0
  68. package/package.json +22 -19
  69. package/src/components/resource/HistoryEvent.tsx +0 -3
  70. package/src/components/resource/__tests__/HistoryEvent.test.tsx +1 -4
  71. package/src/components/resource/__tests__/event-formatting.test.ts +2 -14
  72. package/src/components/resource/event-formatting.ts +3 -4
  73. package/src/components/resource/panels/AssessmentEntry.tsx +4 -1
  74. package/src/components/resource/panels/CommentEntry.tsx +4 -1
  75. package/src/components/resource/panels/HighlightEntry.tsx +4 -1
  76. package/src/components/resource/panels/ReferenceEntry.tsx +4 -1
  77. package/src/components/resource/panels/ResourceInfoPanel.tsx +11 -23
  78. package/src/components/resource/panels/TagEntry.tsx +4 -1
  79. package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +2 -2
  80. package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +12 -16
  81. package/src/components/resource/panels/__tests__/agent-label.test.ts +93 -0
  82. package/src/components/resource/panels/agent-label.ts +20 -0
  83. package/src/features/resource-compose/state/compose-page-state-unit.ts +0 -1
  84. package/src/features/resource-viewer/components/ResourceViewerPage.tsx +0 -1
  85. package/translations/ar.json +0 -3
  86. package/translations/bn.json +0 -3
  87. package/translations/cs.json +0 -3
  88. package/translations/da.json +0 -3
  89. package/translations/de.json +0 -3
  90. package/translations/el.json +0 -3
  91. package/translations/en.json +2 -5
  92. package/translations/es.json +0 -3
  93. package/translations/fa.json +0 -3
  94. package/translations/fi.json +0 -3
  95. package/translations/fr.json +0 -3
  96. package/translations/he.json +0 -3
  97. package/translations/hi.json +0 -3
  98. package/translations/id.json +0 -3
  99. package/translations/it.json +0 -3
  100. package/translations/ja.json +0 -3
  101. package/translations/ko.json +0 -3
  102. package/translations/ms.json +0 -3
  103. package/translations/nl.json +0 -3
  104. package/translations/no.json +0 -3
  105. package/translations/pl.json +0 -3
  106. package/translations/pt.json +0 -3
  107. package/translations/ro.json +0 -3
  108. package/translations/sv.json +0 -3
  109. package/translations/th.json +0 -3
  110. package/translations/tr.json +0 -3
  111. package/translations/uk.json +0 -3
  112. package/translations/vi.json +0 -3
  113. package/translations/zh.json +0 -3
  114. package/dist/TranslationManager-9Xj3MIWQ.d.mts +0 -16
  115. package/dist/ar-UUMMNQKF.mjs.map +0 -1
  116. package/dist/bn-AL5BJSR3.mjs.map +0 -1
  117. package/dist/chunk-OJSRLEER.mjs.map +0 -1
  118. package/dist/da-FKUX6CDL.mjs.map +0 -1
  119. package/dist/de-XSJ3E25S.mjs.map +0 -1
  120. package/dist/el-UJXNRCBP.mjs.map +0 -1
  121. package/dist/es-VURP62BU.mjs.map +0 -1
  122. package/dist/fa-TIT5ZPZY.mjs.map +0 -1
  123. package/dist/fi-F7VTGT4H.mjs.map +0 -1
  124. package/dist/fr-2ZR26VF7.mjs.map +0 -1
  125. package/dist/he-BXP2KYVZ.mjs.map +0 -1
  126. package/dist/hi-PSWTP3NC.mjs.map +0 -1
  127. package/dist/id-HO6TXGTO.mjs.map +0 -1
  128. package/dist/index.mjs.map +0 -1
  129. package/dist/it-AGTDMBL3.mjs.map +0 -1
  130. package/dist/ja-TTGOVF5K.mjs.map +0 -1
  131. package/dist/ko-FF77IQ7N.mjs.map +0 -1
  132. package/dist/ms-UPQWWIL4.mjs.map +0 -1
  133. package/dist/nl-W75HEPFL.mjs.map +0 -1
  134. package/dist/no-R4W7W7ZU.mjs.map +0 -1
  135. package/dist/pl-GQC2ELWO.mjs.map +0 -1
  136. package/dist/pt-YGVT62RU.mjs.map +0 -1
  137. package/dist/ro-TST6XS6X.mjs.map +0 -1
  138. package/dist/sv-TQLF6HV7.mjs.map +0 -1
  139. package/dist/th-HJUIETVR.mjs.map +0 -1
  140. package/dist/tr-CW3C46TW.mjs.map +0 -1
  141. package/dist/uk-WTHZQB2U.mjs.map +0 -1
  142. package/dist/zh-MO3FCUD6.mjs.map +0 -1
  143. /package/dist/{PdfAnnotationCanvas.client-5QESNO5H.mjs.map → PdfAnnotationCanvas.client-CN3C3S55.js.map} +0 -0
  144. /package/dist/{chunk-EBBL3VJI.mjs.map → chunk-3Q3TUKWP.js.map} +0 -0
  145. /package/dist/{chunk-D4GAAQMM.mjs.map → chunk-K6BJDL2I.js.map} +0 -0
  146. /package/dist/{en-J5DHKLQ5.mjs.map → en-L45VK7BS.js.map} +0 -0
  147. /package/dist/{test-utils.mjs.map → test-utils.js.map} +0 -0
  148. /package/src/integrations/{tailwind-plugin.js → tailwind-plugin.cjs} +0 -0
@@ -204,14 +204,13 @@ describe('event-formatting', () => {
204
204
  it('returns created details for yield:created', () => {
205
205
  const event = {
206
206
  type: 'yield:created' as const,
207
- payload: { name: 'Doc', creationMethod: 'upload' },
207
+ payload: { name: 'Doc' },
208
208
  userId: 'user-1',
209
209
  timestamp: '',
210
210
  } as any;
211
211
  const result = getResourceCreationDetails(event);
212
212
  expect(result).toEqual({
213
213
  type: 'created',
214
- method: 'upload',
215
214
  userId: 'user-1',
216
215
  metadata: undefined,
217
216
  });
@@ -220,14 +219,13 @@ describe('event-formatting', () => {
220
219
  it('returns cloned details for yield:cloned', () => {
221
220
  const event = {
222
221
  type: 'yield:cloned' as const,
223
- payload: { name: 'Clone', creationMethod: 'clone', parentResourceId: 'parent-1' },
222
+ payload: { name: 'Clone', parentResourceId: 'parent-1' },
224
223
  userId: 'user-2',
225
224
  timestamp: '',
226
225
  } as any;
227
226
  const result = getResourceCreationDetails(event);
228
227
  expect(result).toEqual({
229
228
  type: 'cloned',
230
- method: 'clone',
231
229
  userId: 'user-2',
232
230
  sourceDocId: 'parent-1',
233
231
  parentResourceId: 'parent-1',
@@ -235,16 +233,6 @@ describe('event-formatting', () => {
235
233
  });
236
234
  });
237
235
 
238
- it('uses fallback method when creationMethod missing', () => {
239
- const event = {
240
- type: 'yield:created' as const,
241
- payload: { name: 'Doc' },
242
- userId: 'u1',
243
- timestamp: '',
244
- } as any;
245
- expect(getResourceCreationDetails(event)?.method).toBe('unknown');
246
- });
247
-
248
236
  it('returns null for non-creation events', () => {
249
237
  const event = { type: 'mark:added' as const, payload: {} } as any;
250
238
  expect(getResourceCreationDetails(event)).toBeNull();
@@ -306,11 +306,12 @@ export function getEventEntityTypes(event: StoredEventLike): string[] {
306
306
  }
307
307
 
308
308
  /**
309
- * Resource creation details
309
+ * Resource creation details derived from a yield:created or yield:cloned
310
+ * event. The creation kind (`type`) is read from the event channel —
311
+ * the redundant `creationMethod` payload field is gone.
310
312
  */
311
313
  export interface ResourceCreationDetails {
312
314
  type: 'created' | 'cloned';
313
- method: string;
314
315
  userId?: string;
315
316
  sourceDocId?: string; // For cloned resources
316
317
  parentResourceId?: string;
@@ -327,7 +328,6 @@ export function getResourceCreationDetails(event: StoredEventLike): ResourceCrea
327
328
  if (eventData.type === 'yield:created') {
328
329
  return {
329
330
  type: 'created',
330
- method: payload.creationMethod || 'unknown',
331
331
  userId: eventData.userId,
332
332
  metadata: undefined,
333
333
  };
@@ -336,7 +336,6 @@ export function getResourceCreationDetails(event: StoredEventLike): ResourceCrea
336
336
  if (eventData.type === 'yield:cloned') {
337
337
  return {
338
338
  type: 'cloned',
339
- method: payload.creationMethod || 'clone',
340
339
  userId: eventData.userId,
341
340
  sourceDocId: payload.parentResourceId,
342
341
  parentResourceId: payload.parentResourceId,
@@ -6,6 +6,7 @@ import { getAnnotationExactText } from '@semiont/core';
6
6
  import { useSemiont } from '../../../session/SemiontProvider';
7
7
  import { useObservable } from '../../../hooks/useObservable';
8
8
  import { useHoverEmitter } from '../../../hooks/useHoverEmitter';
9
+ import { renderAgentLabel } from './agent-label';
9
10
 
10
11
  // W3C Annotation TextualBody type
11
12
  interface TextualBody {
@@ -110,7 +111,9 @@ export function AssessmentEntry({
110
111
  </div>
111
112
  {assessment.generator && (
112
113
  <div className="semiont-annotation-entry__metadata">
113
- Via {typeof assessment.generator === 'string' ? assessment.generator : assessment.generator.name}
114
+ Via {Array.isArray(assessment.generator)
115
+ ? assessment.generator.map(renderAgentLabel).join(', ')
116
+ : renderAgentLabel(assessment.generator)}
114
117
  </div>
115
118
  )}
116
119
  </div>
@@ -7,6 +7,7 @@ import { getAnnotationExactText, getCommentText } from '@semiont/core';
7
7
  import { useSemiont } from '../../../session/SemiontProvider';
8
8
  import { useObservable } from '../../../hooks/useObservable';
9
9
  import { useHoverEmitter } from '../../../hooks/useHoverEmitter';
10
+ import { renderAgentLabel } from './agent-label';
10
11
 
11
12
  interface CommentEntryProps {
12
13
  comment: Annotation;
@@ -140,7 +141,9 @@ export function CommentEntry({
140
141
  </div>
141
142
  {comment.generator && (
142
143
  <div className="semiont-annotation-entry__metadata">
143
- Via {typeof comment.generator === 'string' ? comment.generator : comment.generator.name}
144
+ Via {Array.isArray(comment.generator)
145
+ ? comment.generator.map(renderAgentLabel).join(', ')
146
+ : renderAgentLabel(comment.generator)}
144
147
  </div>
145
148
  )}
146
149
  {annotateMode && (
@@ -6,6 +6,7 @@ import { getAnnotationExactText } from '@semiont/core';
6
6
  import { useSemiont } from '../../../session/SemiontProvider';
7
7
  import { useObservable } from '../../../hooks/useObservable';
8
8
  import { useHoverEmitter } from '../../../hooks/useHoverEmitter';
9
+ import { renderAgentLabel } from './agent-label';
9
10
 
10
11
  interface HighlightEntryProps {
11
12
  highlight: Annotation;
@@ -66,7 +67,9 @@ export function HighlightEntry({
66
67
  </div>
67
68
  {highlight.generator && (
68
69
  <div className="semiont-annotation-entry__metadata">
69
- Via {typeof highlight.generator === 'string' ? highlight.generator : highlight.generator.name}
70
+ Via {Array.isArray(highlight.generator)
71
+ ? highlight.generator.map(renderAgentLabel).join(', ')
72
+ : renderAgentLabel(highlight.generator)}
70
73
  </div>
71
74
  )}
72
75
  </div>
@@ -10,6 +10,7 @@ import { getEntityTypes } from '@semiont/ontology';
10
10
  import { getResourceIcon } from '../../../lib/resource-utils';
11
11
  import { useSemiont } from '../../../session/SemiontProvider';
12
12
  import { useObservable } from '../../../hooks/useObservable';
13
+ import { renderAgentLabel } from './agent-label';
13
14
  import { useObservableExternalNavigation } from '../../../hooks/useObservableBrowse';
14
15
  import { useHoverEmitter } from '../../../hooks/useHoverEmitter';
15
16
 
@@ -171,7 +172,9 @@ export function ReferenceEntry({
171
172
  )}
172
173
  {reference.generator && (
173
174
  <div className="semiont-annotation-entry__metadata">
174
- Via {typeof reference.generator === 'string' ? reference.generator : reference.generator.name}
175
+ Via {Array.isArray(reference.generator)
176
+ ? reference.generator.map(renderAgentLabel).join(', ')
177
+ : renderAgentLabel(reference.generator)}
175
178
  </div>
176
179
  )}
177
180
  </div>
@@ -5,6 +5,7 @@ import { useSemiont } from '../../../session/SemiontProvider';
5
5
  import { useObservable } from '../../../hooks/useObservable';
6
6
  import { formatLocaleDisplay } from '@semiont/core';
7
7
  import { resourceId as makeResourceId, type components } from '@semiont/core';
8
+ import { renderAgentLabel } from './agent-label';
8
9
  import './ResourceInfoPanel.css';
9
10
 
10
11
  type Agent = components['schemas']['Agent'];
@@ -19,7 +20,6 @@ interface Props {
19
20
  isArchived?: boolean;
20
21
  dateCreated?: string | undefined;
21
22
  dateModified?: string | undefined;
22
- creationMethod?: string | undefined;
23
23
  wasAttributedTo?: Agent | Agent[] | undefined;
24
24
  wasDerivedFrom?: string | string[] | undefined;
25
25
  generator?: Agent | Agent[] | undefined;
@@ -42,7 +42,6 @@ export function ResourceInfoPanel({
42
42
  isArchived = false,
43
43
  dateCreated,
44
44
  dateModified,
45
- creationMethod,
46
45
  wasAttributedTo,
47
46
  wasDerivedFrom,
48
47
  generator,
@@ -50,6 +49,13 @@ export function ResourceInfoPanel({
50
49
  const t = useTranslations('ResourceInfoPanel');
51
50
  const session = useObservable(useSemiont().activeSession$);
52
51
 
52
+ // Single attribution surface. `wasAttributedTo` is the canonical list
53
+ // of responsible parties; if a producer set only `generator` we
54
+ // render that as the attribution chain.
55
+ const attribution: Agent[] = wasAttributedTo
56
+ ? (Array.isArray(wasAttributedTo) ? wasAttributedTo : [wasAttributedTo])
57
+ : (generator ? (Array.isArray(generator) ? generator : [generator]) : []);
58
+
53
59
  return (
54
60
  <div className="semiont-resource-info-panel">
55
61
  {/* Panel Title */}
@@ -105,7 +111,7 @@ export function ResourceInfoPanel({
105
111
  )}
106
112
 
107
113
  {/* Provenance Section */}
108
- {(dateCreated || dateModified || creationMethod || wasAttributedTo || wasDerivedFrom || generator) && (
114
+ {(dateCreated || dateModified || attribution.length > 0 || wasDerivedFrom) && (
109
115
  <div className="semiont-resource-info-panel__section">
110
116
  <h3 className="semiont-resource-info-panel__heading">{t('provenance')}</h3>
111
117
  <div className="semiont-resource-info-panel__field-group">
@@ -125,19 +131,11 @@ export function ResourceInfoPanel({
125
131
  </span>
126
132
  </div>
127
133
  )}
128
- {creationMethod && (
129
- <div>
130
- <span className="semiont-resource-info-panel__label">{t('creationMethod')}</span>
131
- <span className="semiont-resource-info-panel__value">{creationMethod}</span>
132
- </div>
133
- )}
134
- {wasAttributedTo && (
134
+ {attribution.length > 0 && (
135
135
  <div>
136
136
  <span className="semiont-resource-info-panel__label">{t('attributedTo')}</span>
137
137
  <span className="semiont-resource-info-panel__value">
138
- {(Array.isArray(wasAttributedTo) ? wasAttributedTo : [wasAttributedTo])
139
- .map(a => a.name)
140
- .join(', ')}
138
+ {attribution.map(renderAgentLabel).join(', ')}
141
139
  </span>
142
140
  </div>
143
141
  )}
@@ -157,16 +155,6 @@ export function ResourceInfoPanel({
157
155
  </span>
158
156
  </div>
159
157
  )}
160
- {generator && (
161
- <div>
162
- <span className="semiont-resource-info-panel__label">{t('generatedBy')}</span>
163
- <span className="semiont-resource-info-panel__value">
164
- {(Array.isArray(generator) ? generator : [generator])
165
- .map(a => a.name)
166
- .join(', ')}
167
- </span>
168
- </div>
169
- )}
170
158
  </div>
171
159
  </div>
172
160
  )}
@@ -8,6 +8,7 @@ import { getTagCategory, getTagSchemaId } from '@semiont/ontology';
8
8
  import { useSemiont } from '../../../session/SemiontProvider';
9
9
  import { useObservable } from '../../../hooks/useObservable';
10
10
  import { useHoverEmitter } from '../../../hooks/useHoverEmitter';
11
+ import { renderAgentLabel } from './agent-label';
11
12
 
12
13
  interface TagEntryProps {
13
14
  tag: Annotation;
@@ -70,7 +71,9 @@ export function TagEntry({
70
71
  </div>
71
72
  {tag.generator && (
72
73
  <div className="semiont-annotation-entry__metadata">
73
- Via {typeof tag.generator === 'string' ? tag.generator : tag.generator.name}
74
+ Via {Array.isArray(tag.generator)
75
+ ? tag.generator.map(renderAgentLabel).join(', ')
76
+ : renderAgentLabel(tag.generator)}
74
77
  </div>
75
78
  )}
76
79
  </div>
@@ -81,8 +81,8 @@ const mockCommentStates = {
81
81
  }),
82
82
  withCreatorObject: createMockComment({
83
83
  creator: {
84
- id: 'user-123',
85
- type: 'Person',
84
+ '@id': 'user-123',
85
+ '@type': 'Person',
86
86
  name: 'John Doe',
87
87
  },
88
88
  }),
@@ -27,10 +27,8 @@ vi.mock('../../../../contexts/TranslationContext', () => ({
27
27
  provenance: 'Provenance',
28
28
  createdAt: 'Created',
29
29
  modifiedAt: 'Modified',
30
- creationMethod: 'How created',
31
30
  attributedTo: 'Attributed to',
32
31
  derivedFrom: 'Derived from',
33
- generatedBy: 'Generated by',
34
32
  };
35
33
  return translations[key] || key;
36
34
  }),
@@ -293,14 +291,6 @@ describe('ResourceInfoPanel Component', () => {
293
291
  expect(screen.getByText('Modified')).toBeInTheDocument();
294
292
  });
295
293
 
296
- it('should render creationMethod when provided', () => {
297
- renderWithEventBus(
298
- <ResourceInfoPanel {...defaultProps} creationMethod="generated" />
299
- );
300
- expect(screen.getByText('How created')).toBeInTheDocument();
301
- expect(screen.getByText('generated')).toBeInTheDocument();
302
- });
303
-
304
294
  it('should render a single agent attribution', () => {
305
295
  renderWithEventBus(
306
296
  <ResourceInfoPanel
@@ -336,25 +326,31 @@ describe('ResourceInfoPanel Component', () => {
336
326
  expect(screen.getByText('urn:semiont:resource:abc123')).toBeInTheDocument();
337
327
  });
338
328
 
339
- it('should render generator when provided', () => {
329
+ it('falls back to generator for attribution when wasAttributedTo is absent', () => {
340
330
  renderWithEventBus(
341
331
  <ResourceInfoPanel
342
332
  {...defaultProps}
343
- generator={{ name: 'Semiont AI' }}
333
+ generator={{ '@type': 'Software', name: 'Semiont AI' } as never}
344
334
  />
345
335
  );
346
- expect(screen.getByText('Generated by')).toBeInTheDocument();
336
+ expect(screen.getByText('Attributed to')).toBeInTheDocument();
347
337
  expect(screen.getByText('Semiont AI')).toBeInTheDocument();
348
338
  });
349
339
 
350
- it('should render multiple generators', () => {
340
+ it('renders Software peers as `${provider} ${model}` from structured fields', () => {
351
341
  renderWithEventBus(
352
342
  <ResourceInfoPanel
353
343
  {...defaultProps}
354
- generator={[{ name: 'Model A' }, { name: 'Model B' }]}
344
+ generator={{
345
+ '@type': 'Software',
346
+ '@id': 'did:web:example.com:agents:ollama:gemma2%3A27b',
347
+ name: 'ignored',
348
+ provider: 'ollama',
349
+ model: 'gemma2:27b',
350
+ } as never}
355
351
  />
356
352
  );
357
- expect(screen.getByText('Model A, Model B')).toBeInTheDocument();
353
+ expect(screen.getByText('ollama gemma2:27b')).toBeInTheDocument();
358
354
  });
359
355
  });
360
356
 
@@ -0,0 +1,93 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { renderAgentLabel } from '../agent-label';
3
+ import type { components } from '@semiont/core';
4
+
5
+ type Agent = components['schemas']['Agent'];
6
+
7
+ describe('renderAgentLabel', () => {
8
+ describe('Software peers', () => {
9
+ it('composes `${provider} ${model}` from structured fields', () => {
10
+ const agent: Agent = {
11
+ '@type': 'Software',
12
+ '@id': 'did:web:example.com:agents:ollama:gemma2%3A27b',
13
+ name: 'unused-display-name',
14
+ provider: 'ollama',
15
+ model: 'gemma2:27b',
16
+ };
17
+ expect(renderAgentLabel(agent)).toBe('ollama gemma2:27b');
18
+ });
19
+
20
+ it('falls back to model alone when provider is absent', () => {
21
+ const agent: Agent = {
22
+ '@type': 'Software',
23
+ name: 'unused',
24
+ model: 'gpt-4',
25
+ };
26
+ expect(renderAgentLabel(agent)).toBe('gpt-4');
27
+ });
28
+
29
+ it('falls back to provider alone when model is absent', () => {
30
+ const agent: Agent = {
31
+ '@type': 'Software',
32
+ name: 'unused',
33
+ provider: 'anthropic',
34
+ };
35
+ expect(renderAgentLabel(agent)).toBe('anthropic');
36
+ });
37
+
38
+ it('falls back to name when both provider and model are absent', () => {
39
+ const agent: Agent = {
40
+ '@type': 'Software',
41
+ name: 'Some Software',
42
+ };
43
+ expect(renderAgentLabel(agent)).toBe('Some Software');
44
+ });
45
+ });
46
+
47
+ describe('Person and Organization peers', () => {
48
+ it('renders Person name', () => {
49
+ const agent: Agent = {
50
+ '@type': 'Person',
51
+ '@id': 'did:web:example.com:users:alice%40example.com',
52
+ name: 'Alice',
53
+ };
54
+ expect(renderAgentLabel(agent)).toBe('Alice');
55
+ });
56
+
57
+ it('renders Organization name', () => {
58
+ const agent: Agent = {
59
+ '@type': 'Organization',
60
+ name: 'Acme Corp',
61
+ };
62
+ expect(renderAgentLabel(agent)).toBe('Acme Corp');
63
+ });
64
+ });
65
+
66
+ describe('Legacy and degraded shapes', () => {
67
+ it('renders the stored `name` for legacy SoftwareAgent shape (graceful fallback)', () => {
68
+ // Pre-migration generator with @type='SoftwareAgent' and concatenated name
69
+ const legacy = {
70
+ '@type': 'SoftwareAgent',
71
+ name: 'worker-pool / ollama gemma4:26b',
72
+ worker: 'worker-pool',
73
+ inferenceProvider: 'ollama',
74
+ model: 'gemma4:26b',
75
+ } as unknown as Agent;
76
+ expect(renderAgentLabel(legacy)).toBe('worker-pool / ollama gemma4:26b');
77
+ });
78
+
79
+ it('falls back to @id when name is empty', () => {
80
+ const agent = {
81
+ '@type': 'Person',
82
+ '@id': 'did:web:example.com:users:bob%40example.com',
83
+ name: '',
84
+ } as unknown as Agent;
85
+ expect(renderAgentLabel(agent)).toBe('did:web:example.com:users:bob%40example.com');
86
+ });
87
+
88
+ it('falls back to "unknown" when nothing identifies the agent', () => {
89
+ const agent = { '@type': 'Person', name: '' } as unknown as Agent;
90
+ expect(renderAgentLabel(agent)).toBe('unknown');
91
+ });
92
+ });
93
+ });
@@ -0,0 +1,20 @@
1
+ import type { components } from '@semiont/core';
2
+
3
+ type Agent = components['schemas']['Agent'];
4
+
5
+ /**
6
+ * Compose a display label for an Agent at render time. Software peers
7
+ * read `${provider} ${model}` from their structured fields rather than
8
+ * a producer-side concatenated `name`. Person/Organization fall back
9
+ * to `name`. Unknown shapes fall back to `name` then to `@id`.
10
+ */
11
+ export function renderAgentLabel(agent: Agent): string {
12
+ if (agent['@type'] === 'Software') {
13
+ const provider = (agent as { provider?: string }).provider;
14
+ const model = (agent as { model?: string }).model;
15
+ if (provider && model) return `${provider} ${model}`;
16
+ if (model) return model;
17
+ if (provider) return provider;
18
+ }
19
+ return agent.name || agent['@id'] || 'unknown';
20
+ }
@@ -161,7 +161,6 @@ export function createComposePageStateUnit(
161
161
  format,
162
162
  entityTypes: saveParams.entityTypes || [],
163
163
  language: saveParams.language,
164
- creationMethod: 'ui',
165
164
  storageUri: saveParams.storageUri,
166
165
  }).subscribe({
167
166
  next: (event) => {
@@ -559,7 +559,6 @@ export function ResourceViewerPage({
559
559
  isArchived={resource.archived ?? false}
560
560
  dateCreated={resource.dateCreated}
561
561
  dateModified={resource.dateModified}
562
- creationMethod={resource.creationMethod}
563
562
  wasAttributedTo={resource.wasAttributedTo}
564
563
  wasDerivedFrom={resource.wasDerivedFrom}
565
564
  generator={resource.generator as components['schemas']['Agent'] | components['schemas']['Agent'][] | undefined}
@@ -25,10 +25,8 @@
25
25
  "provenance": "المصدر والسلسلة",
26
26
  "createdAt": "تاريخ الإنشاء",
27
27
  "modifiedAt": "تاريخ التعديل",
28
- "creationMethod": "طريقة الإنشاء",
29
28
  "attributedTo": "منسوب إلى",
30
29
  "derivedFrom": "مشتق من",
31
- "generatedBy": "أُنشئ بواسطة",
32
30
  "storageUri": "التخزين"
33
31
  },
34
32
  "CollaborationPanel": {
@@ -54,7 +52,6 @@
54
52
  "history": "السجل",
55
53
  "loading": "جارٍ التحميل...",
56
54
  "user": "المستخدم",
57
- "method": "الطريقة",
58
55
  "viewOriginal": "عرض الأصل",
59
56
  "viewAnnotation": "عرض التعليق التوضيحي: {{content}}",
60
57
  "resourceCreated": "تم إنشاء المورد",
@@ -25,10 +25,8 @@
25
25
  "provenance": "উৎস ও উদ্ভব",
26
26
  "createdAt": "তৈরির তারিখ",
27
27
  "modifiedAt": "পরিবর্তনের তারিখ",
28
- "creationMethod": "তৈরির পদ্ধতি",
29
28
  "attributedTo": "সম্পাদককৃত",
30
29
  "derivedFrom": "থেকে উৎপন্ন",
31
- "generatedBy": "তৈরিকারক",
32
30
  "storageUri": "সঞ্চয়স্থান"
33
31
  },
34
32
  "CollaborationPanel": {
@@ -54,7 +52,6 @@
54
52
  "history": "ইতিহাস",
55
53
  "loading": "লোড হচ্ছে...",
56
54
  "user": "ব্যবহারকারী",
57
- "method": "পদ্ধতি",
58
55
  "viewOriginal": "মূল দেখুন",
59
56
  "viewAnnotation": "টীকা দেখুন: {{content}}",
60
57
  "resourceCreated": "সম্পদ তৈরি হয়েছে",
@@ -25,10 +25,8 @@
25
25
  "provenance": "Původ",
26
26
  "createdAt": "Vytvořeno",
27
27
  "modifiedAt": "Upraveno",
28
- "creationMethod": "Způsob vytvoření",
29
28
  "attributedTo": "Připsáno",
30
29
  "derivedFrom": "Odvozeno z",
31
- "generatedBy": "Vygenerováno pomocí",
32
30
  "storageUri": "Úložiště"
33
31
  },
34
32
  "CollaborationPanel": {
@@ -54,7 +52,6 @@
54
52
  "history": "Historie",
55
53
  "loading": "Načítání...",
56
54
  "user": "Uživatel",
57
- "method": "Metoda",
58
55
  "viewOriginal": "Zobrazit originál",
59
56
  "viewAnnotation": "Zobrazit anotaci: {{content}}",
60
57
  "resourceCreated": "Zdroj vytvořen",
@@ -25,10 +25,8 @@
25
25
  "provenance": "Oprindelse",
26
26
  "createdAt": "Oprettet",
27
27
  "modifiedAt": "Ændret",
28
- "creationMethod": "Oprettelsesmetode",
29
28
  "attributedTo": "Tilskrevet",
30
29
  "derivedFrom": "Afledt af",
31
- "generatedBy": "Genereret af",
32
30
  "storageUri": "Lagring"
33
31
  },
34
32
  "CollaborationPanel": {
@@ -54,7 +52,6 @@
54
52
  "history": "Historik",
55
53
  "loading": "Indlæser...",
56
54
  "user": "Bruger",
57
- "method": "Metode",
58
55
  "viewOriginal": "Se original",
59
56
  "viewAnnotation": "Se annotering: {{content}}",
60
57
  "resourceCreated": "Ressource oprettet",
@@ -25,10 +25,8 @@
25
25
  "provenance": "Herkunft",
26
26
  "createdAt": "Erstellt",
27
27
  "modifiedAt": "Geändert",
28
- "creationMethod": "Erstellungsmethode",
29
28
  "attributedTo": "Zugeschrieben",
30
29
  "derivedFrom": "Abgeleitet von",
31
- "generatedBy": "Erstellt von",
32
30
  "storageUri": "Speicher"
33
31
  },
34
32
  "CollaborationPanel": {
@@ -54,7 +52,6 @@
54
52
  "history": "Verlauf",
55
53
  "loading": "Wird geladen...",
56
54
  "user": "Benutzer",
57
- "method": "Methode",
58
55
  "viewOriginal": "Original ansehen",
59
56
  "viewAnnotation": "Annotation ansehen: {{content}}",
60
57
  "resourceCreated": "Ressource erstellt",
@@ -25,10 +25,8 @@
25
25
  "provenance": "Προέλευση",
26
26
  "createdAt": "Δημιουργήθηκε",
27
27
  "modifiedAt": "Τροποποιήθηκε",
28
- "creationMethod": "Μέθοδος δημιουργίας",
29
28
  "attributedTo": "Αποδίδεται σε",
30
29
  "derivedFrom": "Παράγεται από",
31
- "generatedBy": "Δημιουργήθηκε από",
32
30
  "storageUri": "Αποθήκευση"
33
31
  },
34
32
  "CollaborationPanel": {
@@ -54,7 +52,6 @@
54
52
  "history": "Ιστορικό",
55
53
  "loading": "Φόρτωση...",
56
54
  "user": "Χρήστης",
57
- "method": "Μέθοδος",
58
55
  "viewOriginal": "Προβολή πρωτοτύπου",
59
56
  "viewAnnotation": "Προβολή σχολιασμού: {{content}}",
60
57
  "resourceCreated": "Δημιουργία Πόρου",
@@ -26,10 +26,8 @@
26
26
  "provenance": "Provenance",
27
27
  "createdAt": "Created",
28
28
  "modifiedAt": "Modified",
29
- "creationMethod": "How created",
30
29
  "attributedTo": "Attributed to",
31
- "derivedFrom": "Derived from",
32
- "generatedBy": "Generated by"
30
+ "derivedFrom": "Derived from"
33
31
  },
34
32
  "CollaborationPanel": {
35
33
  "title": "Collaboration",
@@ -54,7 +52,6 @@
54
52
  "history": "History",
55
53
  "loading": "Loading...",
56
54
  "user": "User",
57
- "method": "Method",
58
55
  "viewOriginal": "View original",
59
56
  "viewAnnotation": "View annotation: {{content}}",
60
57
  "resourceCreated": "Created Resource",
@@ -361,4 +358,4 @@
361
358
  "searching": "Searching...",
362
359
  "search": "Search"
363
360
  }
364
- }
361
+ }
@@ -25,10 +25,8 @@
25
25
  "provenance": "Procedencia",
26
26
  "createdAt": "Creado",
27
27
  "modifiedAt": "Modificado",
28
- "creationMethod": "Cómo se creó",
29
28
  "attributedTo": "Atribuido a",
30
29
  "derivedFrom": "Derivado de",
31
- "generatedBy": "Generado por",
32
30
  "storageUri": "Almacenamiento"
33
31
  },
34
32
  "CollaborationPanel": {
@@ -54,7 +52,6 @@
54
52
  "history": "Historial",
55
53
  "loading": "Cargando...",
56
54
  "user": "Usuario",
57
- "method": "Método",
58
55
  "viewOriginal": "Ver original",
59
56
  "viewAnnotation": "Ver anotación: {{content}}",
60
57
  "resourceCreated": "Recurso Creado",
@@ -25,10 +25,8 @@
25
25
  "provenance": "منشأ",
26
26
  "createdAt": "ایجاد شده",
27
27
  "modifiedAt": "ویرایش شده",
28
- "creationMethod": "روش ایجاد",
29
28
  "attributedTo": "منتسب به",
30
29
  "derivedFrom": "مشتق از",
31
- "generatedBy": "تولیدشده توسط",
32
30
  "storageUri": "ذخیره‌سازی"
33
31
  },
34
32
  "CollaborationPanel": {
@@ -54,7 +52,6 @@
54
52
  "history": "تاریخچه",
55
53
  "loading": "در حال بارگذاری...",
56
54
  "user": "کاربر",
57
- "method": "روش",
58
55
  "viewOriginal": "مشاهده اصلی",
59
56
  "viewAnnotation": "مشاهده حاشیه‌نویسی: {{content}}",
60
57
  "resourceCreated": "منبع ایجاد شد",