@semiont/react-ui 0.4.14 → 0.4.16
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.
- package/README.md +18 -12
- package/dist/KnowledgeBaseSessionContext-BNNunwzO.d.mts +175 -0
- package/dist/{PdfAnnotationCanvas.client-CW6SKH2U.mjs → PdfAnnotationCanvas.client-CHDCGQBR.mjs} +3 -3
- package/dist/{ar-R4CRNXEF.mjs → ar-3W37O3R3.mjs} +9 -3
- package/dist/ar-3W37O3R3.mjs.map +1 -0
- package/dist/{bn-CZKGRHTA.mjs → bn-JZTJLMVE.mjs} +9 -3
- package/dist/bn-JZTJLMVE.mjs.map +1 -0
- package/dist/chunk-FAI3S4BM.mjs +865 -0
- package/dist/chunk-FAI3S4BM.mjs.map +1 -0
- package/dist/{chunk-HVMAGUFA.mjs → chunk-NOD3NCXE.mjs} +3 -1
- package/dist/chunk-NOD3NCXE.mjs.map +1 -0
- package/dist/{chunk-HNZOXH4L.mjs → chunk-OZICDVH7.mjs} +5 -3
- package/dist/chunk-OZICDVH7.mjs.map +1 -0
- package/dist/{chunk-BQJWOK4C.mjs → chunk-VN5NY4SN.mjs} +9 -8
- package/dist/chunk-VN5NY4SN.mjs.map +1 -0
- package/dist/{cs-4WIB2IHH.mjs → cs-XYHH7HNE.mjs} +9 -3
- package/dist/cs-XYHH7HNE.mjs.map +1 -0
- package/dist/{da-JWYEUYPX.mjs → da-MZKIECVT.mjs} +9 -3
- package/dist/da-MZKIECVT.mjs.map +1 -0
- package/dist/{de-GWUQZGER.mjs → de-AYXTMRQW.mjs} +9 -3
- package/dist/de-AYXTMRQW.mjs.map +1 -0
- package/dist/{el-DM2GT7P5.mjs → el-A6CVQWAW.mjs} +9 -3
- package/dist/el-A6CVQWAW.mjs.map +1 -0
- package/dist/{en-IUV4ZXKH.mjs → en-YPQQBI4T.mjs} +2 -2
- package/dist/{es-6LVQIM3D.mjs → es-M2HXLJGT.mjs} +9 -3
- package/dist/es-M2HXLJGT.mjs.map +1 -0
- package/dist/{fa-IRUJY3QI.mjs → fa-V6JZJDYP.mjs} +9 -3
- package/dist/fa-V6JZJDYP.mjs.map +1 -0
- package/dist/{fi-53FBOEVT.mjs → fi-ONDTZ5H7.mjs} +9 -3
- package/dist/fi-ONDTZ5H7.mjs.map +1 -0
- package/dist/{fr-Q5KY7QL6.mjs → fr-PAPV4H4G.mjs} +9 -3
- package/dist/fr-PAPV4H4G.mjs.map +1 -0
- package/dist/{he-HJNKULBY.mjs → he-F6VTLJLW.mjs} +9 -3
- package/dist/he-F6VTLJLW.mjs.map +1 -0
- package/dist/{hi-UYZ4X6CR.mjs → hi-CFUAV4BF.mjs} +9 -3
- package/dist/hi-CFUAV4BF.mjs.map +1 -0
- package/dist/{id-UAQMH6U2.mjs → id-NBKLCCI7.mjs} +9 -3
- package/dist/id-NBKLCCI7.mjs.map +1 -0
- package/dist/index.d.mts +141 -169
- package/dist/index.mjs +2394 -2116
- package/dist/index.mjs.map +1 -1
- package/dist/{it-C7QEBNFA.mjs → it-SLSOWVVU.mjs} +9 -3
- package/dist/it-SLSOWVVU.mjs.map +1 -0
- package/dist/{ja-THS6AOSJ.mjs → ja-L5IG4ECE.mjs} +9 -3
- package/dist/ja-L5IG4ECE.mjs.map +1 -0
- package/dist/{ko-XKK3TWQG.mjs → ko-QYMTULKK.mjs} +9 -3
- package/dist/ko-QYMTULKK.mjs.map +1 -0
- package/dist/{ms-GSK7LIF7.mjs → ms-5DGSFKM2.mjs} +9 -3
- package/dist/ms-5DGSFKM2.mjs.map +1 -0
- package/dist/{nl-KUBWITGY.mjs → nl-VZPCGONO.mjs} +9 -3
- package/dist/nl-VZPCGONO.mjs.map +1 -0
- package/dist/{no-ECWZUHT6.mjs → no-MF6F352I.mjs} +9 -3
- package/dist/no-MF6F352I.mjs.map +1 -0
- package/dist/{pl-PLVWSZWS.mjs → pl-WIK72JUO.mjs} +9 -3
- package/dist/pl-WIK72JUO.mjs.map +1 -0
- package/dist/{pt-AL74ZTKB.mjs → pt-RRP5ZF6A.mjs} +9 -3
- package/dist/pt-RRP5ZF6A.mjs.map +1 -0
- package/dist/{ro-WTPHLHGS.mjs → ro-XHQLC3T7.mjs} +9 -3
- package/dist/ro-XHQLC3T7.mjs.map +1 -0
- package/dist/{sv-QCLI7SG4.mjs → sv-EWULDN6E.mjs} +9 -3
- package/dist/sv-EWULDN6E.mjs.map +1 -0
- package/dist/test-utils.d.mts +13 -62
- package/dist/test-utils.mjs +41 -22
- package/dist/test-utils.mjs.map +1 -1
- package/dist/{th-WCKVZU6U.mjs → th-TGOBHFG4.mjs} +9 -3
- package/dist/th-TGOBHFG4.mjs.map +1 -0
- package/dist/{tr-2CAFS2XS.mjs → tr-LMMPBMV7.mjs} +9 -3
- package/dist/tr-LMMPBMV7.mjs.map +1 -0
- package/dist/{uk-TDE4JLCY.mjs → uk-IPGRRJY6.mjs} +9 -3
- package/dist/uk-IPGRRJY6.mjs.map +1 -0
- package/dist/{vi-KKXZ4PCX.mjs → vi-Q676OJQS.mjs} +9 -3
- package/dist/vi-Q676OJQS.mjs.map +1 -0
- package/dist/{zh-VH4XN5PV.mjs → zh-F3MTWQDX.mjs} +9 -3
- package/dist/zh-F3MTWQDX.mjs.map +1 -0
- package/package.json +5 -3
- package/src/components/ProtectedErrorBoundary.tsx +95 -0
- package/src/components/__tests__/ProtectedErrorBoundary.test.tsx +197 -0
- package/src/components/modals/PermissionDeniedModal.tsx +140 -0
- package/src/components/modals/ReferenceWizardModal.tsx +3 -2
- package/src/components/modals/SessionExpiredModal.tsx +101 -0
- package/src/components/modals/__tests__/PermissionDeniedModal.test.tsx +150 -0
- package/src/components/modals/__tests__/SessionExpiredModal.test.tsx +115 -0
- package/src/components/resource/AnnotationHistory.tsx +5 -6
- package/src/components/resource/HistoryEvent.tsx +9 -8
- package/src/components/resource/__tests__/AnnotationHistory.test.tsx +33 -34
- package/src/components/resource/__tests__/HistoryEvent.test.tsx +18 -19
- package/src/components/resource/__tests__/event-formatting.test.ts +70 -94
- package/src/components/resource/event-formatting.ts +92 -56
- package/src/components/resource/panels/ReferenceEntry.tsx +7 -5
- package/src/components/resource/panels/ResourceInfoPanel.tsx +18 -6
- package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +12 -12
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +24 -0
- package/src/features/resource-compose/components/ResourceComposePage.tsx +10 -1
- package/src/features/resource-viewer/__tests__/AnnotationCreationPending.test.tsx +1 -1
- package/src/features/resource-viewer/__tests__/AnnotationDeletionIntegration.test.tsx +4 -4
- package/src/features/resource-viewer/__tests__/AnnotationProgressDismissal.test.tsx +5 -10
- package/src/features/resource-viewer/__tests__/BindFlowIntegration.test.tsx +23 -54
- package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +6 -6
- package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +7 -19
- package/src/features/resource-viewer/__tests__/ToastNotifications.test.tsx +1 -1
- package/src/features/resource-viewer/__tests__/YieldFlowIntegration.test.tsx +18 -44
- package/src/features/resource-viewer/__tests__/annotation-progress-flow.test.tsx +6 -6
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +26 -26
- package/src/styles/features/compose.css +63 -0
- package/translations/ar.json +6 -2
- package/translations/bn.json +6 -2
- package/translations/cs.json +6 -2
- package/translations/da.json +6 -2
- package/translations/de.json +6 -2
- package/translations/el.json +6 -2
- package/translations/en.json +2 -0
- package/translations/es.json +6 -2
- package/translations/fa.json +6 -2
- package/translations/fi.json +6 -2
- package/translations/fr.json +6 -2
- package/translations/he.json +6 -2
- package/translations/hi.json +6 -2
- package/translations/id.json +6 -2
- package/translations/it.json +6 -2
- package/translations/ja.json +6 -2
- package/translations/ko.json +6 -2
- package/translations/ms.json +6 -2
- package/translations/nl.json +6 -2
- package/translations/no.json +6 -2
- package/translations/pl.json +6 -2
- package/translations/pt.json +6 -2
- package/translations/ro.json +6 -2
- package/translations/sv.json +6 -2
- package/translations/th.json +6 -2
- package/translations/tr.json +6 -2
- package/translations/uk.json +6 -2
- package/translations/vi.json +6 -2
- package/translations/zh.json +6 -2
- package/dist/TranslationManager-CudgH3gw.d.mts +0 -107
- package/dist/ar-R4CRNXEF.mjs.map +0 -1
- package/dist/bn-CZKGRHTA.mjs.map +0 -1
- package/dist/chunk-BQJWOK4C.mjs.map +0 -1
- package/dist/chunk-HNZOXH4L.mjs.map +0 -1
- package/dist/chunk-HVMAGUFA.mjs.map +0 -1
- package/dist/chunk-OL5UST25.mjs +0 -413
- package/dist/chunk-OL5UST25.mjs.map +0 -1
- package/dist/cs-4WIB2IHH.mjs.map +0 -1
- package/dist/da-JWYEUYPX.mjs.map +0 -1
- package/dist/de-GWUQZGER.mjs.map +0 -1
- package/dist/el-DM2GT7P5.mjs.map +0 -1
- package/dist/es-6LVQIM3D.mjs.map +0 -1
- package/dist/fa-IRUJY3QI.mjs.map +0 -1
- package/dist/fi-53FBOEVT.mjs.map +0 -1
- package/dist/fr-Q5KY7QL6.mjs.map +0 -1
- package/dist/he-HJNKULBY.mjs.map +0 -1
- package/dist/hi-UYZ4X6CR.mjs.map +0 -1
- package/dist/id-UAQMH6U2.mjs.map +0 -1
- package/dist/it-C7QEBNFA.mjs.map +0 -1
- package/dist/ja-THS6AOSJ.mjs.map +0 -1
- package/dist/ko-XKK3TWQG.mjs.map +0 -1
- package/dist/ms-GSK7LIF7.mjs.map +0 -1
- package/dist/nl-KUBWITGY.mjs.map +0 -1
- package/dist/no-ECWZUHT6.mjs.map +0 -1
- package/dist/pl-PLVWSZWS.mjs.map +0 -1
- package/dist/pt-AL74ZTKB.mjs.map +0 -1
- package/dist/ro-WTPHLHGS.mjs.map +0 -1
- package/dist/sv-QCLI7SG4.mjs.map +0 -1
- package/dist/th-WCKVZU6U.mjs.map +0 -1
- package/dist/tr-2CAFS2XS.mjs.map +0 -1
- package/dist/uk-TDE4JLCY.mjs.map +0 -1
- package/dist/vi-KKXZ4PCX.mjs.map +0 -1
- package/dist/zh-VH4XN5PV.mjs.map +0 -1
- /package/dist/{PdfAnnotationCanvas.client-CW6SKH2U.mjs.map → PdfAnnotationCanvas.client-CHDCGQBR.mjs.map} +0 -0
- /package/dist/{en-IUV4ZXKH.mjs.map → en-YPQQBI4T.mjs.map} +0 -0
|
@@ -26,41 +26,41 @@ describe('event-formatting', () => {
|
|
|
26
26
|
|
|
27
27
|
describe('formatEventType', () => {
|
|
28
28
|
it('returns translation key for resource events', () => {
|
|
29
|
-
expect(formatEventType('
|
|
30
|
-
expect(formatEventType('
|
|
31
|
-
expect(formatEventType('
|
|
32
|
-
expect(formatEventType('
|
|
29
|
+
expect(formatEventType('yield:created', t)).toBe('resourceCreated');
|
|
30
|
+
expect(formatEventType('yield:cloned', t)).toBe('resourceCloned');
|
|
31
|
+
expect(formatEventType('mark:archived', t)).toBe('resourceArchived');
|
|
32
|
+
expect(formatEventType('mark:unarchived', t)).toBe('resourceUnarchived');
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
it('returns motivation-specific key for
|
|
36
|
-
expect(formatEventType('
|
|
37
|
-
expect(formatEventType('
|
|
38
|
-
expect(formatEventType('
|
|
39
|
-
expect(formatEventType('
|
|
35
|
+
it('returns motivation-specific key for mark:added', () => {
|
|
36
|
+
expect(formatEventType('mark:added', t, { annotation: { motivation: 'highlighting' } })).toBe('highlightAdded');
|
|
37
|
+
expect(formatEventType('mark:added', t, { annotation: { motivation: 'linking' } })).toBe('referenceCreated');
|
|
38
|
+
expect(formatEventType('mark:added', t, { annotation: { motivation: 'assessing' } })).toBe('assessmentAdded');
|
|
39
|
+
expect(formatEventType('mark:added', t, { annotation: { motivation: 'commenting' } })).toBe('annotationAdded');
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
it('returns annotationRemoved for
|
|
43
|
-
expect(formatEventType('
|
|
42
|
+
it('returns annotationRemoved for mark:removed', () => {
|
|
43
|
+
expect(formatEventType('mark:removed', t)).toBe('annotationRemoved');
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
it('returns annotationBodyUpdated for
|
|
47
|
-
expect(formatEventType('
|
|
46
|
+
it('returns annotationBodyUpdated for mark:body-updated', () => {
|
|
47
|
+
expect(formatEventType('mark:body-updated', t)).toBe('annotationBodyUpdated');
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
it('returns entitytag keys', () => {
|
|
51
|
-
expect(formatEventType('
|
|
52
|
-
expect(formatEventType('
|
|
51
|
+
expect(formatEventType('mark:entity-tag-added', t)).toBe('entitytagAdded');
|
|
52
|
+
expect(formatEventType('mark:entity-tag-removed', t)).toBe('entitytagRemoved');
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
it('returns jobEvent for job types', () => {
|
|
56
|
-
expect(formatEventType('job
|
|
57
|
-
expect(formatEventType('job
|
|
58
|
-
expect(formatEventType('job
|
|
56
|
+
expect(formatEventType('job:completed', t)).toBe('jobEvent');
|
|
57
|
+
expect(formatEventType('job:started', t)).toBe('jobEvent');
|
|
58
|
+
expect(formatEventType('job:failed', t)).toBe('jobEvent');
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
it('returns representationEvent for representation types', () => {
|
|
62
|
-
expect(formatEventType('representation
|
|
63
|
-
expect(formatEventType('representation
|
|
61
|
+
it('returns representationEvent for yield:representation types', () => {
|
|
62
|
+
expect(formatEventType('yield:representation-added', t)).toBe('representationEvent');
|
|
63
|
+
expect(formatEventType('yield:representation-removed', t)).toBe('representationEvent');
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
it('returns raw type for unknown event types', () => {
|
|
@@ -70,33 +70,33 @@ describe('event-formatting', () => {
|
|
|
70
70
|
|
|
71
71
|
describe('getEventEmoji', () => {
|
|
72
72
|
it('returns document emoji for resource events', () => {
|
|
73
|
-
expect(getEventEmoji('
|
|
74
|
-
expect(getEventEmoji('
|
|
73
|
+
expect(getEventEmoji('yield:created')).toBe('📄');
|
|
74
|
+
expect(getEventEmoji('yield:cloned')).toBe('📄');
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
-
it('returns motivation-specific emoji for
|
|
78
|
-
expect(getEventEmoji('
|
|
79
|
-
expect(getEventEmoji('
|
|
80
|
-
expect(getEventEmoji('
|
|
77
|
+
it('returns motivation-specific emoji for mark:added', () => {
|
|
78
|
+
expect(getEventEmoji('mark:added', { annotation: { motivation: 'highlighting' } })).toBe('🟡');
|
|
79
|
+
expect(getEventEmoji('mark:added', { annotation: { motivation: 'linking' } })).toBeTruthy();
|
|
80
|
+
expect(getEventEmoji('mark:added', { annotation: { motivation: 'assessing' } })).toBe('🔴');
|
|
81
81
|
});
|
|
82
82
|
|
|
83
|
-
it('returns trash emoji for
|
|
84
|
-
expect(getEventEmoji('
|
|
83
|
+
it('returns trash emoji for mark:removed', () => {
|
|
84
|
+
expect(getEventEmoji('mark:removed')).toBe('🗑️');
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
-
it('returns pencil emoji for
|
|
88
|
-
expect(getEventEmoji('
|
|
87
|
+
it('returns pencil emoji for mark:body-updated', () => {
|
|
88
|
+
expect(getEventEmoji('mark:body-updated')).toBe('✏️');
|
|
89
89
|
});
|
|
90
90
|
|
|
91
91
|
it('returns tag emoji for entitytag events', () => {
|
|
92
|
-
expect(getEventEmoji('
|
|
93
|
-
expect(getEventEmoji('
|
|
92
|
+
expect(getEventEmoji('mark:entity-tag-added')).toBe('🏷️');
|
|
93
|
+
expect(getEventEmoji('mark:entity-tag-removed')).toBe('🏷️');
|
|
94
94
|
});
|
|
95
95
|
|
|
96
96
|
it('returns appropriate emoji for job events', () => {
|
|
97
|
-
expect(getEventEmoji('job
|
|
98
|
-
expect(getEventEmoji('job
|
|
99
|
-
expect(getEventEmoji('job
|
|
97
|
+
expect(getEventEmoji('job:completed')).toBe('🔗');
|
|
98
|
+
expect(getEventEmoji('job:started')).toBe('⚙️');
|
|
99
|
+
expect(getEventEmoji('job:failed')).toBe('❌');
|
|
100
100
|
});
|
|
101
101
|
|
|
102
102
|
it('returns default emoji for unknown', () => {
|
|
@@ -137,55 +137,43 @@ describe('event-formatting', () => {
|
|
|
137
137
|
});
|
|
138
138
|
|
|
139
139
|
describe('getEventDisplayContent', () => {
|
|
140
|
-
it('returns resource name for
|
|
141
|
-
const event = {
|
|
142
|
-
event: { type: 'resource.created' as const, payload: { name: 'My Document' }, userId: 'u1', timestamp: '' },
|
|
143
|
-
} as any;
|
|
140
|
+
it('returns resource name for yield:created', () => {
|
|
141
|
+
const event = { type: 'yield:created' as const, payload: { name: 'My Document' }, userId: 'u1', timestamp: '' } as any;
|
|
144
142
|
const result = getEventDisplayContent(event, [], []);
|
|
145
143
|
expect(result).toEqual({ exact: 'My Document', isQuoted: false, isTag: false });
|
|
146
144
|
});
|
|
147
145
|
|
|
148
|
-
it('returns resource name for
|
|
149
|
-
const event = {
|
|
150
|
-
event: { type: 'resource.cloned' as const, payload: { name: 'Cloned Doc' }, userId: 'u1', timestamp: '' },
|
|
151
|
-
} as any;
|
|
146
|
+
it('returns resource name for yield:cloned', () => {
|
|
147
|
+
const event = { type: 'yield:cloned' as const, payload: { name: 'Cloned Doc' }, userId: 'u1', timestamp: '' } as any;
|
|
152
148
|
const result = getEventDisplayContent(event, [], []);
|
|
153
149
|
expect(result).toEqual({ exact: 'Cloned Doc', isQuoted: false, isTag: false });
|
|
154
150
|
});
|
|
155
151
|
|
|
156
152
|
it('returns entity type for entitytag events', () => {
|
|
157
|
-
const event = {
|
|
158
|
-
event: { type: 'entitytag.added' as const, payload: { entityType: 'Person' }, userId: 'u1', timestamp: '' },
|
|
159
|
-
} as any;
|
|
153
|
+
const event = { type: 'mark:entity-tag-added' as const, payload: { entityType: 'Person' }, userId: 'u1', timestamp: '' } as any;
|
|
160
154
|
const result = getEventDisplayContent(event, [], []);
|
|
161
155
|
expect(result).toEqual({ exact: 'Person', isQuoted: false, isTag: true });
|
|
162
156
|
});
|
|
163
157
|
|
|
164
|
-
it('returns null for job
|
|
165
|
-
const event = {
|
|
166
|
-
event: { type: 'job.started' as const, payload: {}, userId: 'u1', timestamp: '' },
|
|
167
|
-
} as any;
|
|
158
|
+
it('returns null for job:started', () => {
|
|
159
|
+
const event = { type: 'job:started' as const, payload: {}, userId: 'u1', timestamp: '' } as any;
|
|
168
160
|
expect(getEventDisplayContent(event, [], [])).toBeNull();
|
|
169
161
|
});
|
|
170
162
|
|
|
171
|
-
it('returns null for representation events', () => {
|
|
172
|
-
const event = {
|
|
173
|
-
event: { type: 'representation.added' as const, payload: {}, userId: 'u1', timestamp: '' },
|
|
174
|
-
} as any;
|
|
163
|
+
it('returns null for yield:representation events', () => {
|
|
164
|
+
const event = { type: 'yield:representation-added' as const, payload: {}, userId: 'u1', timestamp: '' } as any;
|
|
175
165
|
expect(getEventDisplayContent(event, [], [])).toBeNull();
|
|
176
166
|
});
|
|
177
167
|
});
|
|
178
168
|
|
|
179
169
|
describe('getEventEntityTypes', () => {
|
|
180
|
-
it('returns entity types from
|
|
170
|
+
it('returns entity types from mark:added with linking motivation', () => {
|
|
181
171
|
const event = {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
body: { entityTypes: ['Person', 'Place'] },
|
|
188
|
-
},
|
|
172
|
+
type: 'mark:added' as const,
|
|
173
|
+
payload: {
|
|
174
|
+
annotation: {
|
|
175
|
+
motivation: 'linking',
|
|
176
|
+
body: { entityTypes: ['Person', 'Place'] },
|
|
189
177
|
},
|
|
190
178
|
},
|
|
191
179
|
} as any;
|
|
@@ -194,33 +182,27 @@ describe('event-formatting', () => {
|
|
|
194
182
|
|
|
195
183
|
it('returns empty array for non-linking annotations', () => {
|
|
196
184
|
const event = {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
annotation: { motivation: 'highlighting', body: null },
|
|
201
|
-
},
|
|
185
|
+
type: 'mark:added' as const,
|
|
186
|
+
payload: {
|
|
187
|
+
annotation: { motivation: 'highlighting', body: null },
|
|
202
188
|
},
|
|
203
189
|
} as any;
|
|
204
190
|
expect(getEventEntityTypes(event)).toEqual([]);
|
|
205
191
|
});
|
|
206
192
|
|
|
207
193
|
it('returns empty array for non-annotation events', () => {
|
|
208
|
-
const event = {
|
|
209
|
-
event: { type: 'resource.created' as const, payload: { name: 'test' } },
|
|
210
|
-
} as any;
|
|
194
|
+
const event = { type: 'yield:created' as const, payload: { name: 'test' } } as any;
|
|
211
195
|
expect(getEventEntityTypes(event)).toEqual([]);
|
|
212
196
|
});
|
|
213
197
|
});
|
|
214
198
|
|
|
215
199
|
describe('getResourceCreationDetails', () => {
|
|
216
|
-
it('returns created details for
|
|
200
|
+
it('returns created details for yield:created', () => {
|
|
217
201
|
const event = {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
timestamp: '',
|
|
223
|
-
},
|
|
202
|
+
type: 'yield:created' as const,
|
|
203
|
+
payload: { name: 'Doc', creationMethod: 'upload' },
|
|
204
|
+
userId: 'user-1',
|
|
205
|
+
timestamp: '',
|
|
224
206
|
} as any;
|
|
225
207
|
const result = getResourceCreationDetails(event);
|
|
226
208
|
expect(result).toEqual({
|
|
@@ -231,14 +213,12 @@ describe('event-formatting', () => {
|
|
|
231
213
|
});
|
|
232
214
|
});
|
|
233
215
|
|
|
234
|
-
it('returns cloned details for
|
|
216
|
+
it('returns cloned details for yield:cloned', () => {
|
|
235
217
|
const event = {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
timestamp: '',
|
|
241
|
-
},
|
|
218
|
+
type: 'yield:cloned' as const,
|
|
219
|
+
payload: { name: 'Clone', creationMethod: 'clone', parentResourceId: 'parent-1' },
|
|
220
|
+
userId: 'user-2',
|
|
221
|
+
timestamp: '',
|
|
242
222
|
} as any;
|
|
243
223
|
const result = getResourceCreationDetails(event);
|
|
244
224
|
expect(result).toEqual({
|
|
@@ -253,20 +233,16 @@ describe('event-formatting', () => {
|
|
|
253
233
|
|
|
254
234
|
it('uses fallback method when creationMethod missing', () => {
|
|
255
235
|
const event = {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
timestamp: '',
|
|
261
|
-
},
|
|
236
|
+
type: 'yield:created' as const,
|
|
237
|
+
payload: { name: 'Doc' },
|
|
238
|
+
userId: 'u1',
|
|
239
|
+
timestamp: '',
|
|
262
240
|
} as any;
|
|
263
241
|
expect(getResourceCreationDetails(event)?.method).toBe('unknown');
|
|
264
242
|
});
|
|
265
243
|
|
|
266
244
|
it('returns null for non-creation events', () => {
|
|
267
|
-
const event = {
|
|
268
|
-
event: { type: 'annotation.added' as const, payload: {} },
|
|
269
|
-
} as any;
|
|
245
|
+
const event = { type: 'mark:added' as const, payload: {} } as any;
|
|
270
246
|
expect(getResourceCreationDetails(event)).toBeNull();
|
|
271
247
|
});
|
|
272
248
|
});
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* No React dependencies - safe to use in any JavaScript environment.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { StoredEventLike,
|
|
8
|
+
import type { StoredEventLike, PersistedEventType } from '@semiont/core';
|
|
9
9
|
import type { components } from '@semiont/core';
|
|
10
10
|
import { getExactText, getTargetSelector } from '@semiont/api-client';
|
|
11
11
|
import { ANNOTATORS } from '../../lib/annotation-registry';
|
|
@@ -13,6 +13,36 @@ import { ANNOTATORS } from '../../lib/annotation-registry';
|
|
|
13
13
|
type Annotation = components['schemas']['Annotation'];
|
|
14
14
|
type TranslateFn = (key: string, params?: Record<string, string | number>) => string;
|
|
15
15
|
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// USER ID DISPLAY
|
|
18
|
+
// =============================================================================
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Format a DID or user ID for display.
|
|
22
|
+
*
|
|
23
|
+
* did:web:example.com:users:admin%40example.com → admin@example.com
|
|
24
|
+
* did:web:system:smelter → Smelter
|
|
25
|
+
* plain-string → plain-string
|
|
26
|
+
*/
|
|
27
|
+
export function formatUserId(userId: string): string {
|
|
28
|
+
if (!userId.startsWith('did:')) return userId;
|
|
29
|
+
|
|
30
|
+
// System actors: did:web:system:smelter → Smelter
|
|
31
|
+
const systemMatch = userId.match(/^did:web:system:(.+)$/);
|
|
32
|
+
if (systemMatch) {
|
|
33
|
+
const name = systemMatch[1];
|
|
34
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// User DIDs: did:web:example.com:users:admin%40example.com → admin@example.com
|
|
38
|
+
const userMatch = userId.match(/^did:web:[^:]+:users:(.+)$/);
|
|
39
|
+
if (userMatch) {
|
|
40
|
+
return decodeURIComponent(userMatch[1]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return userId;
|
|
44
|
+
}
|
|
45
|
+
|
|
16
46
|
// =============================================================================
|
|
17
47
|
// EVENT FORMATTING AND DISPLAY
|
|
18
48
|
// =============================================================================
|
|
@@ -20,46 +50,49 @@ type TranslateFn = (key: string, params?: Record<string, string | number>) => st
|
|
|
20
50
|
/**
|
|
21
51
|
* Format event type for display with i18n support
|
|
22
52
|
*/
|
|
23
|
-
export function formatEventType(type:
|
|
53
|
+
export function formatEventType(type: PersistedEventType, t: TranslateFn, payload?: any): string {
|
|
24
54
|
switch (type) {
|
|
25
|
-
case '
|
|
55
|
+
case 'yield:created':
|
|
26
56
|
return t('resourceCreated');
|
|
27
|
-
case '
|
|
57
|
+
case 'yield:cloned':
|
|
28
58
|
return t('resourceCloned');
|
|
29
|
-
case '
|
|
59
|
+
case 'mark:archived':
|
|
30
60
|
return t('resourceArchived');
|
|
31
|
-
case '
|
|
61
|
+
case 'mark:unarchived':
|
|
32
62
|
return t('resourceUnarchived');
|
|
33
63
|
|
|
34
|
-
case '
|
|
64
|
+
case 'mark:added': {
|
|
35
65
|
const motivation = payload?.annotation?.motivation;
|
|
36
66
|
if (motivation === 'highlighting') return t('highlightAdded');
|
|
37
67
|
if (motivation === 'linking') return t('referenceCreated');
|
|
38
68
|
if (motivation === 'assessing') return t('assessmentAdded');
|
|
39
69
|
return t('annotationAdded');
|
|
40
70
|
}
|
|
41
|
-
case '
|
|
71
|
+
case 'mark:removed': {
|
|
42
72
|
return t('annotationRemoved');
|
|
43
73
|
}
|
|
44
|
-
case '
|
|
74
|
+
case 'mark:body-updated': {
|
|
45
75
|
return t('annotationBodyUpdated');
|
|
46
76
|
}
|
|
47
77
|
|
|
48
|
-
case '
|
|
78
|
+
case 'mark:entity-tag-added':
|
|
49
79
|
return t('entitytagAdded');
|
|
50
|
-
case '
|
|
80
|
+
case 'mark:entity-tag-removed':
|
|
51
81
|
return t('entitytagRemoved');
|
|
52
82
|
|
|
53
|
-
case 'job
|
|
54
|
-
case 'job
|
|
55
|
-
case 'job
|
|
56
|
-
case 'job
|
|
83
|
+
case 'job:completed':
|
|
84
|
+
case 'job:started':
|
|
85
|
+
case 'job:progress':
|
|
86
|
+
case 'job:failed':
|
|
57
87
|
return t('jobEvent');
|
|
58
88
|
|
|
59
|
-
case 'representation
|
|
60
|
-
case 'representation
|
|
89
|
+
case 'yield:representation-added':
|
|
90
|
+
case 'yield:representation-removed':
|
|
61
91
|
return t('representationEvent');
|
|
62
92
|
|
|
93
|
+
case 'embedding:computed':
|
|
94
|
+
return t('embeddingComputed');
|
|
95
|
+
|
|
63
96
|
default:
|
|
64
97
|
return type;
|
|
65
98
|
}
|
|
@@ -69,15 +102,15 @@ export function formatEventType(type: ResourceEventType, t: TranslateFn, payload
|
|
|
69
102
|
* Get emoji for event type
|
|
70
103
|
* For unified annotation events, pass the payload to determine motivation
|
|
71
104
|
*/
|
|
72
|
-
export function getEventEmoji(type:
|
|
105
|
+
export function getEventEmoji(type: PersistedEventType, payload?: any): string {
|
|
73
106
|
switch (type) {
|
|
74
|
-
case '
|
|
75
|
-
case '
|
|
76
|
-
case '
|
|
77
|
-
case '
|
|
107
|
+
case 'yield:created':
|
|
108
|
+
case 'yield:cloned':
|
|
109
|
+
case 'mark:archived':
|
|
110
|
+
case 'mark:unarchived':
|
|
78
111
|
return '📄';
|
|
79
112
|
|
|
80
|
-
case '
|
|
113
|
+
case 'mark:added': {
|
|
81
114
|
const motivation = payload?.annotation?.motivation;
|
|
82
115
|
// Use annotation registry as single source of truth for emojis
|
|
83
116
|
if (motivation === 'highlighting') return ANNOTATORS.highlight.iconEmoji || '📝';
|
|
@@ -85,29 +118,32 @@ export function getEventEmoji(type: ResourceEventType, payload?: any): string {
|
|
|
85
118
|
if (motivation === 'assessing') return ANNOTATORS.assessment.iconEmoji || '📝';
|
|
86
119
|
return '📝';
|
|
87
120
|
}
|
|
88
|
-
case '
|
|
121
|
+
case 'mark:removed': {
|
|
89
122
|
return '🗑️';
|
|
90
123
|
}
|
|
91
|
-
case '
|
|
124
|
+
case 'mark:body-updated': {
|
|
92
125
|
return '✏️';
|
|
93
126
|
}
|
|
94
127
|
|
|
95
|
-
case '
|
|
96
|
-
case '
|
|
128
|
+
case 'mark:entity-tag-added':
|
|
129
|
+
case 'mark:entity-tag-removed':
|
|
97
130
|
return '🏷️';
|
|
98
131
|
|
|
99
|
-
case 'job
|
|
132
|
+
case 'job:completed':
|
|
100
133
|
return '🔗'; // Link emoji for linked document creation
|
|
101
|
-
case 'job
|
|
102
|
-
case 'job
|
|
134
|
+
case 'job:started':
|
|
135
|
+
case 'job:progress':
|
|
103
136
|
return '⚙️'; // Gear for job processing
|
|
104
|
-
case 'job
|
|
137
|
+
case 'job:failed':
|
|
105
138
|
return '❌'; // X mark for failed jobs
|
|
106
139
|
|
|
107
|
-
case 'representation
|
|
108
|
-
case 'representation
|
|
140
|
+
case 'yield:representation-added':
|
|
141
|
+
case 'yield:representation-removed':
|
|
109
142
|
return '📄';
|
|
110
143
|
|
|
144
|
+
case 'embedding:computed':
|
|
145
|
+
return '🧮';
|
|
146
|
+
|
|
111
147
|
default:
|
|
112
148
|
return '📝';
|
|
113
149
|
}
|
|
@@ -148,18 +184,18 @@ export function getEventDisplayContent(
|
|
|
148
184
|
annotations: Annotation[], // Unified annotations array (all types)
|
|
149
185
|
allEvents: StoredEventLike[]
|
|
150
186
|
): { exact: string; isQuoted: boolean; isTag: boolean } | null {
|
|
151
|
-
const eventData = event
|
|
187
|
+
const eventData = event;
|
|
152
188
|
const payload = eventData.payload as any;
|
|
153
189
|
|
|
154
190
|
// Use type discriminators for proper narrowing
|
|
155
191
|
switch (eventData.type) {
|
|
156
|
-
case '
|
|
157
|
-
case '
|
|
192
|
+
case 'yield:created':
|
|
193
|
+
case 'yield:cloned': {
|
|
158
194
|
return { exact: payload.name, isQuoted: false, isTag: false };
|
|
159
195
|
}
|
|
160
196
|
|
|
161
197
|
// Unified annotation events
|
|
162
|
-
case '
|
|
198
|
+
case 'mark:body-updated': {
|
|
163
199
|
// Find current annotation to get its text
|
|
164
200
|
// payload.annotationId is just the UUID, but annotation.id is the full URI
|
|
165
201
|
const annotation = annotations.find(a =>
|
|
@@ -180,16 +216,16 @@ export function getEventDisplayContent(
|
|
|
180
216
|
return null;
|
|
181
217
|
}
|
|
182
218
|
|
|
183
|
-
case '
|
|
219
|
+
case 'mark:removed': {
|
|
184
220
|
// Find the original annotation.added event to get the text
|
|
185
221
|
// payload.annotationId is just the UUID, but annotation.id in the added event is the full URI
|
|
186
222
|
const addedEvent = allEvents.find(e =>
|
|
187
|
-
e.
|
|
188
|
-
(e.
|
|
223
|
+
e.type === 'mark:added' &&
|
|
224
|
+
(e.payload as any).annotation.id.endsWith(`/annotations/${payload.annotationId}`)
|
|
189
225
|
);
|
|
190
|
-
if (addedEvent && addedEvent.
|
|
226
|
+
if (addedEvent && addedEvent.type === 'mark:added') {
|
|
191
227
|
try {
|
|
192
|
-
const target = (addedEvent.
|
|
228
|
+
const target = (addedEvent.payload as any).annotation.target;
|
|
193
229
|
if (typeof target !== 'string' && target.selector) {
|
|
194
230
|
const exact = getExactText(target.selector);
|
|
195
231
|
if (exact) {
|
|
@@ -203,7 +239,7 @@ export function getEventDisplayContent(
|
|
|
203
239
|
return null;
|
|
204
240
|
}
|
|
205
241
|
|
|
206
|
-
case '
|
|
242
|
+
case 'mark:added': {
|
|
207
243
|
// New unified event structure - annotation is in payload
|
|
208
244
|
try {
|
|
209
245
|
const target = payload.annotation.target;
|
|
@@ -219,12 +255,12 @@ export function getEventDisplayContent(
|
|
|
219
255
|
return null;
|
|
220
256
|
}
|
|
221
257
|
|
|
222
|
-
case '
|
|
223
|
-
case '
|
|
258
|
+
case 'mark:entity-tag-added':
|
|
259
|
+
case 'mark:entity-tag-removed': {
|
|
224
260
|
return { exact: payload.entityType, isQuoted: false, isTag: true };
|
|
225
261
|
}
|
|
226
262
|
|
|
227
|
-
case 'job
|
|
263
|
+
case 'job:completed': {
|
|
228
264
|
// Find the annotation that was used to generate the resource
|
|
229
265
|
if (payload.annotationUri) {
|
|
230
266
|
const annotation = annotations.find(a =>
|
|
@@ -246,11 +282,11 @@ export function getEventDisplayContent(
|
|
|
246
282
|
return null;
|
|
247
283
|
}
|
|
248
284
|
|
|
249
|
-
case 'job
|
|
250
|
-
case 'job
|
|
251
|
-
case 'job
|
|
252
|
-
case 'representation
|
|
253
|
-
case 'representation
|
|
285
|
+
case 'job:started':
|
|
286
|
+
case 'job:progress':
|
|
287
|
+
case 'job:failed':
|
|
288
|
+
case 'yield:representation-added':
|
|
289
|
+
case 'yield:representation-removed':
|
|
254
290
|
return null;
|
|
255
291
|
|
|
256
292
|
default:
|
|
@@ -262,10 +298,10 @@ export function getEventDisplayContent(
|
|
|
262
298
|
* Get entity types from event payload
|
|
263
299
|
*/
|
|
264
300
|
export function getEventEntityTypes(event: StoredEventLike): string[] {
|
|
265
|
-
const eventData = event
|
|
301
|
+
const eventData = event;
|
|
266
302
|
const payload = eventData.payload as any;
|
|
267
303
|
|
|
268
|
-
if (eventData.type === '
|
|
304
|
+
if (eventData.type === 'mark:added') {
|
|
269
305
|
const motivation = payload.annotation.motivation;
|
|
270
306
|
const body = payload.annotation.body;
|
|
271
307
|
if (motivation === 'linking' && body && 'entityTypes' in body) {
|
|
@@ -292,10 +328,10 @@ export interface ResourceCreationDetails {
|
|
|
292
328
|
* Get resource creation details from event
|
|
293
329
|
*/
|
|
294
330
|
export function getResourceCreationDetails(event: StoredEventLike): ResourceCreationDetails | null {
|
|
295
|
-
const eventData = event
|
|
331
|
+
const eventData = event;
|
|
296
332
|
const payload = eventData.payload as any;
|
|
297
333
|
|
|
298
|
-
if (eventData.type === '
|
|
334
|
+
if (eventData.type === 'yield:created') {
|
|
299
335
|
return {
|
|
300
336
|
type: 'created',
|
|
301
337
|
method: payload.creationMethod || 'unknown',
|
|
@@ -304,7 +340,7 @@ export function getResourceCreationDetails(event: StoredEventLike): ResourceCrea
|
|
|
304
340
|
};
|
|
305
341
|
}
|
|
306
342
|
|
|
307
|
-
if (eventData.type === '
|
|
343
|
+
if (eventData.type === 'yield:cloned') {
|
|
308
344
|
return {
|
|
309
345
|
type: 'cloned',
|
|
310
346
|
method: payload.creationMethod || 'clone',
|
|
@@ -9,6 +9,7 @@ import { getAnnotationExactText, isBodyResolved, getBodySource, getFragmentSelec
|
|
|
9
9
|
import { getEntityTypes } from '@semiont/ontology';
|
|
10
10
|
import { getResourceIcon } from '../../../lib/resource-utils';
|
|
11
11
|
import { useEventBus } from '../../../contexts/EventBusContext';
|
|
12
|
+
import { useApiClient } from '../../../contexts/ApiClientContext';
|
|
12
13
|
import { useObservableExternalNavigation } from '../../../hooks/useObservableBrowse';
|
|
13
14
|
import { useHoverEmitter } from '../../../hooks/useBeckonFlow';
|
|
14
15
|
|
|
@@ -41,6 +42,7 @@ export function ReferenceEntry({
|
|
|
41
42
|
}: ReferenceEntryProps) {
|
|
42
43
|
const t = useTranslations('ReferencesPanel');
|
|
43
44
|
const eventBus = useEventBus();
|
|
45
|
+
const semiont = useApiClient();
|
|
44
46
|
const navigate = useObservableExternalNavigation();
|
|
45
47
|
const hoverProps = useHoverEmitter(reference.id);
|
|
46
48
|
|
|
@@ -74,11 +76,11 @@ export function ReferenceEntry({
|
|
|
74
76
|
|
|
75
77
|
const handleUnlink = () => {
|
|
76
78
|
if (source && resolvedResourceUri) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
});
|
|
79
|
+
semiont.bind.body(
|
|
80
|
+
resourceId(source),
|
|
81
|
+
annotationId(reference.id),
|
|
82
|
+
[{ op: 'remove', item: { type: 'SpecificResource', source: resolvedResourceUri } }],
|
|
83
|
+
).catch(() => { /* error handled by events-stream */ });
|
|
82
84
|
}
|
|
83
85
|
};
|
|
84
86
|
|