@semiont/react-ui 0.4.2 → 0.4.4

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/{PdfAnnotationCanvas.client-PVTVPDBQ.mjs → PdfAnnotationCanvas.client-LF6DDTCV.mjs} +3 -3
  2. package/dist/{ar-5REA6P4J.mjs → ar-RJLR7NDD.mjs} +13 -11
  3. package/dist/ar-RJLR7NDD.mjs.map +1 -0
  4. package/dist/{bn-YHRYHHPD.mjs → bn-4NU2UAZ7.mjs} +13 -11
  5. package/dist/bn-4NU2UAZ7.mjs.map +1 -0
  6. package/dist/{chunk-VVCCMJS7.mjs → chunk-2VWLGQIO.mjs} +13 -11
  7. package/dist/chunk-2VWLGQIO.mjs.map +1 -0
  8. package/dist/chunk-5JZFKRLW.mjs +62 -0
  9. package/dist/chunk-5JZFKRLW.mjs.map +1 -0
  10. package/dist/{chunk-PFQYNPQJ.mjs → chunk-EKQMINCV.mjs} +61 -92
  11. package/dist/chunk-EKQMINCV.mjs.map +1 -0
  12. package/dist/{chunk-ZPV43WN2.mjs → chunk-XMCUHQ2Y.mjs} +72 -3
  13. package/dist/chunk-XMCUHQ2Y.mjs.map +1 -0
  14. package/dist/{cs-JTJXTX2T.mjs → cs-6UJZFF55.mjs} +13 -11
  15. package/dist/cs-6UJZFF55.mjs.map +1 -0
  16. package/dist/{da-MK37SJB6.mjs → da-CFFBBOH3.mjs} +13 -11
  17. package/dist/da-CFFBBOH3.mjs.map +1 -0
  18. package/dist/{de-LGBCWERA.mjs → de-2KAG6KMX.mjs} +13 -11
  19. package/dist/de-2KAG6KMX.mjs.map +1 -0
  20. package/dist/{el-FKJMFCWY.mjs → el-KVPQ7VNF.mjs} +13 -11
  21. package/dist/el-KVPQ7VNF.mjs.map +1 -0
  22. package/dist/{en-AOSMPC2M.mjs → en-ZW4UKTVX.mjs} +2 -2
  23. package/dist/{es-LVDPIXWU.mjs → es-ZU2ECPVG.mjs} +13 -11
  24. package/dist/es-ZU2ECPVG.mjs.map +1 -0
  25. package/dist/{fa-3VA2PIUD.mjs → fa-CBIVKDIQ.mjs} +13 -11
  26. package/dist/fa-CBIVKDIQ.mjs.map +1 -0
  27. package/dist/{fi-3WM75ZLR.mjs → fi-5HUVT7IK.mjs} +13 -11
  28. package/dist/fi-5HUVT7IK.mjs.map +1 -0
  29. package/dist/{fr-NK4A72WA.mjs → fr-O5WU2US2.mjs} +13 -11
  30. package/dist/fr-O5WU2US2.mjs.map +1 -0
  31. package/dist/{he-IACZDZMB.mjs → he-WP4C2SNJ.mjs} +13 -11
  32. package/dist/he-WP4C2SNJ.mjs.map +1 -0
  33. package/dist/{hi-JZ7MGMMS.mjs → hi-HLQXNRWI.mjs} +13 -11
  34. package/dist/hi-HLQXNRWI.mjs.map +1 -0
  35. package/dist/{id-P3KDQGNK.mjs → id-DTJT2ZA6.mjs} +13 -11
  36. package/dist/id-DTJT2ZA6.mjs.map +1 -0
  37. package/dist/index.css +213 -0
  38. package/dist/index.css.map +1 -1
  39. package/dist/index.d.mts +48 -50
  40. package/dist/index.mjs +1492 -1474
  41. package/dist/index.mjs.map +1 -1
  42. package/dist/{it-LQS33SUY.mjs → it-2DK57IMT.mjs} +13 -11
  43. package/dist/it-2DK57IMT.mjs.map +1 -0
  44. package/dist/{ja-G4FKZPWD.mjs → ja-52ZNY72Y.mjs} +13 -11
  45. package/dist/ja-52ZNY72Y.mjs.map +1 -0
  46. package/dist/{ko-2XWKQ7BA.mjs → ko-4Z2IMYZO.mjs} +13 -11
  47. package/dist/ko-4Z2IMYZO.mjs.map +1 -0
  48. package/dist/{ms-2SNONIUD.mjs → ms-HKU73KEX.mjs} +13 -11
  49. package/dist/ms-HKU73KEX.mjs.map +1 -0
  50. package/dist/{nl-BMZUAJ7J.mjs → nl-HPHQMXPT.mjs} +13 -11
  51. package/dist/nl-HPHQMXPT.mjs.map +1 -0
  52. package/dist/{no-6J3WIZ6L.mjs → no-Q7G4PVFT.mjs} +13 -11
  53. package/dist/no-Q7G4PVFT.mjs.map +1 -0
  54. package/dist/{pl-QQ7DAUVK.mjs → pl-D43C2CE3.mjs} +13 -11
  55. package/dist/pl-D43C2CE3.mjs.map +1 -0
  56. package/dist/{pt-MU3GN7MW.mjs → pt-V3GFSY7B.mjs} +13 -11
  57. package/dist/pt-V3GFSY7B.mjs.map +1 -0
  58. package/dist/{ro-6GBE72QK.mjs → ro-3UIRV3OA.mjs} +13 -11
  59. package/dist/ro-3UIRV3OA.mjs.map +1 -0
  60. package/dist/{sv-NQIL7PEM.mjs → sv-BMW26A7M.mjs} +13 -11
  61. package/dist/sv-BMW26A7M.mjs.map +1 -0
  62. package/dist/test-utils.mjs +4 -4
  63. package/dist/{th-6OCNZQBE.mjs → th-CMND2QET.mjs} +13 -11
  64. package/dist/th-CMND2QET.mjs.map +1 -0
  65. package/dist/{tr-XWJ5P3SC.mjs → tr-P3AD7MYA.mjs} +13 -11
  66. package/dist/tr-P3AD7MYA.mjs.map +1 -0
  67. package/dist/{uk-AKSN6DGW.mjs → uk-QPONRQ5O.mjs} +13 -11
  68. package/dist/uk-QPONRQ5O.mjs.map +1 -0
  69. package/dist/{vi-23GHQ45M.mjs → vi-G2OMVMCI.mjs} +13 -11
  70. package/dist/vi-G2OMVMCI.mjs.map +1 -0
  71. package/dist/{zh-ITT4QBSN.mjs → zh-HTDN4LKE.mjs} +13 -11
  72. package/dist/zh-HTDN4LKE.mjs.map +1 -0
  73. package/package.json +3 -5
  74. package/src/components/CodeMirrorRenderer.tsx +9 -9
  75. package/src/components/ResourceTagsInline.tsx +1 -1
  76. package/src/components/__tests__/ResourceTagsInline.test.tsx +3 -3
  77. package/src/components/modals/ContextSummary.tsx +56 -165
  78. package/src/components/modals/GatherContextStep.tsx +124 -42
  79. package/src/components/modals/ReferenceWizardModal.tsx +8 -25
  80. package/src/components/modals/SearchModal.css +241 -0
  81. package/src/components/resource/AnnotateView.tsx +3 -3
  82. package/src/components/resource/BrowseView.tsx +2 -2
  83. package/src/components/resource/ResourceViewer.tsx +4 -4
  84. package/src/components/resource/__tests__/annotation-interactions.test.tsx +1 -1
  85. package/src/components/resource/panels/AssessmentPanel.tsx +3 -0
  86. package/src/components/resource/panels/AssistSection.tsx +4 -1
  87. package/src/components/resource/panels/CommentsPanel.tsx +3 -0
  88. package/src/components/resource/panels/ResourceInfoPanel.tsx +82 -0
  89. package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +4 -0
  90. package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +94 -0
  91. package/src/features/resource-viewer/__tests__/AnnotationCreationPending.test.tsx +6 -6
  92. package/src/features/resource-viewer/__tests__/AnnotationProgressDismissal.test.tsx +4 -4
  93. package/src/features/resource-viewer/__tests__/BindFlowIntegration.test.tsx +23 -17
  94. package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +4 -4
  95. package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +14 -14
  96. package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +2 -2
  97. package/src/features/resource-viewer/__tests__/YieldFlowIntegration.test.tsx +2 -2
  98. package/src/features/resource-viewer/__tests__/annotation-progress-flow.test.tsx +4 -4
  99. package/src/features/resource-viewer/components/ResourceViewerPage.tsx +12 -10
  100. package/translations/ar.json +12 -10
  101. package/translations/bn.json +12 -10
  102. package/translations/cs.json +12 -10
  103. package/translations/da.json +12 -10
  104. package/translations/de.json +12 -10
  105. package/translations/el.json +12 -10
  106. package/translations/en.json +13 -11
  107. package/translations/es.json +12 -10
  108. package/translations/fa.json +12 -10
  109. package/translations/fi.json +12 -10
  110. package/translations/fr.json +12 -10
  111. package/translations/he.json +12 -10
  112. package/translations/hi.json +12 -10
  113. package/translations/id.json +12 -10
  114. package/translations/it.json +12 -10
  115. package/translations/ja.json +12 -10
  116. package/translations/ko.json +12 -10
  117. package/translations/ms.json +12 -10
  118. package/translations/nl.json +12 -10
  119. package/translations/no.json +12 -10
  120. package/translations/pl.json +12 -10
  121. package/translations/pt.json +12 -10
  122. package/translations/ro.json +12 -10
  123. package/translations/sv.json +12 -10
  124. package/translations/th.json +12 -10
  125. package/translations/tr.json +12 -10
  126. package/translations/uk.json +12 -10
  127. package/translations/vi.json +12 -10
  128. package/translations/zh.json +12 -10
  129. package/dist/ar-5REA6P4J.mjs.map +0 -1
  130. package/dist/bn-YHRYHHPD.mjs.map +0 -1
  131. package/dist/chunk-2HGWOLVN.mjs +0 -31
  132. package/dist/chunk-2HGWOLVN.mjs.map +0 -1
  133. package/dist/chunk-PFQYNPQJ.mjs.map +0 -1
  134. package/dist/chunk-VVCCMJS7.mjs.map +0 -1
  135. package/dist/chunk-ZPV43WN2.mjs.map +0 -1
  136. package/dist/cs-JTJXTX2T.mjs.map +0 -1
  137. package/dist/da-MK37SJB6.mjs.map +0 -1
  138. package/dist/de-LGBCWERA.mjs.map +0 -1
  139. package/dist/el-FKJMFCWY.mjs.map +0 -1
  140. package/dist/es-LVDPIXWU.mjs.map +0 -1
  141. package/dist/fa-3VA2PIUD.mjs.map +0 -1
  142. package/dist/fi-3WM75ZLR.mjs.map +0 -1
  143. package/dist/fr-NK4A72WA.mjs.map +0 -1
  144. package/dist/he-IACZDZMB.mjs.map +0 -1
  145. package/dist/hi-JZ7MGMMS.mjs.map +0 -1
  146. package/dist/id-P3KDQGNK.mjs.map +0 -1
  147. package/dist/it-LQS33SUY.mjs.map +0 -1
  148. package/dist/ja-G4FKZPWD.mjs.map +0 -1
  149. package/dist/ko-2XWKQ7BA.mjs.map +0 -1
  150. package/dist/ms-2SNONIUD.mjs.map +0 -1
  151. package/dist/nl-BMZUAJ7J.mjs.map +0 -1
  152. package/dist/no-6J3WIZ6L.mjs.map +0 -1
  153. package/dist/pl-QQ7DAUVK.mjs.map +0 -1
  154. package/dist/pt-MU3GN7MW.mjs.map +0 -1
  155. package/dist/ro-6GBE72QK.mjs.map +0 -1
  156. package/dist/sv-NQIL7PEM.mjs.map +0 -1
  157. package/dist/th-6OCNZQBE.mjs.map +0 -1
  158. package/dist/tr-XWJ5P3SC.mjs.map +0 -1
  159. package/dist/uk-AKSN6DGW.mjs.map +0 -1
  160. package/dist/vi-23GHQ45M.mjs.map +0 -1
  161. package/dist/zh-ITT4QBSN.mjs.map +0 -1
  162. /package/dist/{PdfAnnotationCanvas.client-PVTVPDBQ.mjs.map → PdfAnnotationCanvas.client-LF6DDTCV.mjs.map} +0 -0
  163. /package/dist/{en-AOSMPC2M.mjs.map → en-ZW4UKTVX.mjs.map} +0 -0
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import { useState, useEffect, useRef } from 'react';
3
4
  import type { GatheredContext } from '@semiont/core';
4
5
  import { ContextSummary } from './ContextSummary';
5
6
  import type { ContextSummaryTranslations } from './ContextSummary';
@@ -10,7 +11,6 @@ export interface GatherContextStepProps {
10
11
  contextError: Error | null;
11
12
  userHint: string;
12
13
  onUserHintChange: (value: string) => void;
13
- onCancel: () => void;
14
14
  onBind: () => void;
15
15
  onGenerate: () => void;
16
16
  onCompose: () => void;
@@ -18,10 +18,10 @@ export interface GatherContextStepProps {
18
18
  title: string;
19
19
  loadingContext: string;
20
20
  failedContext: string;
21
- cancel: string;
22
21
  search: string;
23
22
  generate: string;
24
23
  compose: string;
24
+ resolutionStrategyLabel: string;
25
25
  } & ContextSummaryTranslations;
26
26
  }
27
27
 
@@ -31,19 +31,34 @@ export function GatherContextStep({
31
31
  contextError,
32
32
  userHint,
33
33
  onUserHintChange,
34
- onCancel,
35
34
  onBind,
36
35
  onGenerate,
37
36
  onCompose,
38
37
  translations: t,
39
38
  }: GatherContextStepProps) {
39
+ const [sourceExpanded, setSourceExpanded] = useState(false);
40
40
  const contextReady = !contextLoading && !contextError && !!context;
41
+ const sourceContext = context?.sourceContext;
42
+ const highlightRef = useRef<HTMLSpanElement>(null);
43
+
44
+ // Scroll the highlighted term into view when context loads
45
+ useEffect(() => {
46
+ if (highlightRef.current) {
47
+ highlightRef.current.scrollIntoView({ block: 'center', behavior: 'instant' });
48
+ }
49
+ }, [context]);
41
50
 
42
51
  return (
43
- <>
52
+ <div className="semiont-gather__outer">
53
+ {/* Loading / error states */}
44
54
  {contextLoading && (
45
- <div className="semiont-modal__empty-state" style={{ textAlign: 'center', padding: '1rem 0' }}>
46
- {t.loadingContext}
55
+ <div className="semiont-gather__loading">
56
+ <div style={{ display: 'flex', gap: '0.5rem' }}>
57
+ <span className="semiont-gather__loading-dot" />
58
+ <span className="semiont-gather__loading-dot" />
59
+ <span className="semiont-gather__loading-dot" />
60
+ </div>
61
+ <span className="semiont-gather__loading-text">{t.loadingContext}</span>
47
62
  </div>
48
63
  )}
49
64
  {!!contextError && (
@@ -52,42 +67,109 @@ export function GatherContextStep({
52
67
  </div>
53
68
  )}
54
69
 
55
- {context && <ContextSummary context={context} userHint={userHint} onUserHintChange={onUserHintChange} translations={t} />}
70
+ {context && (
71
+ <>
72
+ {/* Full-width source context strip */}
73
+ {sourceContext && (
74
+ <div className="semiont-gather__source-strip">
75
+ <label className="semiont-form__label" style={{ marginBottom: '0.375rem' }}>
76
+ {t.sourceContextLabel}{context.sourceResource?.name ? ` "${context.sourceResource.name}"` : ''}
77
+ </label>
78
+ <div className={`semiont-gather__source-box${sourceExpanded ? ' semiont-gather__source-box--expanded' : ''}`}>
79
+ <div className="semiont-gather__source-context">
80
+ <div style={{ fontSize: 'var(--semiont-text-sm)', fontFamily: 'monospace', whiteSpace: 'pre-wrap', color: 'var(--semiont-text-secondary)' }}>
81
+ {sourceContext.before && <span>{sourceContext.before}</span>}
82
+ <span
83
+ ref={highlightRef}
84
+ style={{
85
+ backgroundColor: 'var(--semiont-color-primary-100)',
86
+ padding: '0 0.25rem',
87
+ fontWeight: 600,
88
+ color: 'var(--semiont-color-primary-900)',
89
+ }}
90
+ >
91
+ {sourceContext.selected}
92
+ </span>
93
+ {(context.metadata?.entityTypes ?? []).map(et => (
94
+ <span key={et} className="semiont-chip" style={{ fontSize: 'var(--semiont-text-xs)', padding: '0.125rem 0.375rem', fontWeight: 400, verticalAlign: 'middle', marginLeft: '0.25rem' }}>
95
+ {et}
96
+ </span>
97
+ ))}
98
+ {context.annotation?.motivation && (
99
+ <span className="semiont-chip" style={{ fontSize: 'var(--semiont-text-xs)', padding: '0.125rem 0.375rem', fontWeight: 400, verticalAlign: 'middle', marginLeft: '0.25rem' }}>
100
+ {context.annotation.motivation}
101
+ </span>
102
+ )}
103
+ {sourceContext.after && <span>{sourceContext.after}</span>}
104
+ </div>
105
+ </div>
106
+ <button
107
+ type="button"
108
+ className="semiont-gather__expand-btn"
109
+ onClick={() => setSourceExpanded(v => !v)}
110
+ >
111
+ {sourceExpanded ? '▲ less' : '▼ more'}
112
+ </button>
113
+ </div>
114
+ </div>
115
+ )}
116
+
117
+ {/* Two-column body */}
118
+ <div className="semiont-gather__body">
119
+ {/* Left: annotation metadata */}
120
+ <div className="semiont-gather__left">
121
+ <ContextSummary context={context} translations={t} />
122
+ </div>
56
123
 
57
- {/* Action Buttons */}
58
- <div className="semiont-modal__actions" style={{ paddingTop: '0.5rem' }}>
59
- <button
60
- type="button"
61
- onClick={onCancel}
62
- className="semiont-button--secondary semiont-button--flex"
63
- >
64
- {t.cancel}
65
- </button>
66
- <button
67
- type="button"
68
- onClick={onBind}
69
- disabled={!contextReady}
70
- className="semiont-button--primary semiont-button--flex"
71
- >
72
- 🔍 {t.search}…
73
- </button>
74
- <button
75
- type="button"
76
- onClick={onGenerate}
77
- disabled={!contextReady}
78
- className="semiont-button--primary semiont-button--flex"
79
- >
80
- {t.generate}
81
- </button>
82
- <button
83
- type="button"
84
- onClick={onCompose}
85
- disabled={!contextReady}
86
- className="semiont-button--secondary semiont-button--flex"
87
- >
88
- ✍️ {t.compose}
89
- </button>
90
- </div>
91
- </>
124
+ {/* Right: hint textarea */}
125
+ <div className="semiont-gather__right">
126
+ <div className="semiont-form__field">
127
+ <label className="semiont-form__label">
128
+ {t.userHintLabel}
129
+ </label>
130
+ <textarea
131
+ value={userHint}
132
+ onChange={(e) => onUserHintChange(e.target.value)}
133
+ placeholder={t.userHintPlaceholder}
134
+ className="semiont-search-modal__search-input semiont-gather__hint-textarea"
135
+ style={{ resize: 'vertical', fontFamily: 'inherit' }}
136
+ />
137
+ </div>
138
+ </div>
139
+ </div>
140
+
141
+ {/* Full-width footer: resolution strategy */}
142
+ <div className="semiont-gather__footer">
143
+ <div className="semiont-gather__footer-label">{t.resolutionStrategyLabel}</div>
144
+ <div className="semiont-gather__actions">
145
+ <button
146
+ type="button"
147
+ onClick={onBind}
148
+ disabled={!contextReady}
149
+ className="semiont-button--primary semiont-button--flex"
150
+ >
151
+ 🔍 {t.search}
152
+ </button>
153
+ <button
154
+ type="button"
155
+ onClick={onGenerate}
156
+ disabled={!contextReady}
157
+ className="semiont-button--primary semiont-button--flex"
158
+ >
159
+ ✨ {t.generate}…
160
+ </button>
161
+ <button
162
+ type="button"
163
+ onClick={onCompose}
164
+ disabled={!contextReady}
165
+ className="semiont-button--secondary semiont-button--flex"
166
+ >
167
+ ✍️ {t.compose}
168
+ </button>
169
+ </div>
170
+ </div>
171
+ </>
172
+ )}
173
+ </div>
92
174
  );
93
175
  }
@@ -46,15 +46,9 @@ export interface ReferenceWizardModalProps {
46
46
  configureGenerationTitle: string;
47
47
  configureSearchTitle: string;
48
48
  searchResultsTitle: string;
49
- annotationLabel: string;
50
- sourceResourceLabel: string;
51
- motivationLabel: string;
52
49
  sourceContextLabel: string;
53
- entityTypesLabel: string;
54
- graphContextLabel: string;
55
50
  connectionsLabel: string;
56
51
  citedByLabel: string;
57
- siblingTypesLabel: string;
58
52
  userHintLabel: string;
59
53
  userHintPlaceholder: string;
60
54
  loadingContext: string;
@@ -64,6 +58,7 @@ export interface ReferenceWizardModalProps {
64
58
  searching: string;
65
59
  generate: string;
66
60
  compose: string;
61
+ resolutionStrategyLabel: string;
67
62
  back: string;
68
63
  link: string;
69
64
  score: string;
@@ -119,10 +114,11 @@ export function ReferenceWizardModal({
119
114
  useEffect(() => {
120
115
  if (!isOpen) return;
121
116
 
122
- const subscription = eventBus.get('bind:search-results').subscribe((event) => {
123
- if (annotationId && event.referenceId === annotationId) {
117
+ const subscription = eventBus.get('match:search-results').subscribe((event) => {
118
+ const e = event as { referenceId: string; results: ScoredResult[] };
119
+ if (annotationId && e.referenceId === annotationId) {
124
120
  setIsSearching(false);
125
- setWizardStep({ step: 'search-results', results: event.results as ScoredResult[] });
121
+ setWizardStep({ step: 'search-results', results: e.results });
126
122
  }
127
123
  });
128
124
 
@@ -152,7 +148,7 @@ export function ReferenceWizardModal({
152
148
  if (!annotationId || !context) return;
153
149
  setIsSearching(true);
154
150
  const contextWithHint = userHint ? { ...context, userHint } : context;
155
- eventBus.get('bind:search-requested').next({
151
+ eventBus.get('match:search-requested').next({
156
152
  referenceId: annotationId,
157
153
  context: contextWithHint,
158
154
  limit: config.limit,
@@ -208,7 +204,7 @@ export function ReferenceWizardModal({
208
204
  leaveFrom="opacity-100 scale-100"
209
205
  leaveTo="opacity-0 scale-95"
210
206
  >
211
- <DialogPanel className={`semiont-search-modal__panel semiont-search-modal__panel--with-border${wizardStep.step === 'search-results' ? ' semiont-search-modal__panel--wide' : ''}`}>
207
+ <DialogPanel className={`semiont-search-modal__panel semiont-search-modal__panel--with-border${wizardStep.step === 'search-results' ? ' semiont-search-modal__panel--wide' : ''}${wizardStep.step === 'gather' ? ' semiont-search-modal__panel--gather' : ''}`}>
212
208
  <div className="semiont-search-modal__header">
213
209
  <DialogTitle className="semiont-search-modal__title">
214
210
  {stepTitle}
@@ -229,29 +225,22 @@ export function ReferenceWizardModal({
229
225
  contextError={contextError}
230
226
  userHint={userHint}
231
227
  onUserHintChange={setUserHint}
232
- onCancel={onClose}
233
228
  onBind={handleBind}
234
229
  onGenerate={handleGenerate}
235
230
  onCompose={handleCompose}
236
231
  translations={{
237
232
  title: t.gatherTitle,
238
- annotationLabel: t.annotationLabel,
239
- sourceResourceLabel: t.sourceResourceLabel,
240
- motivationLabel: t.motivationLabel,
241
233
  sourceContextLabel: t.sourceContextLabel,
242
- entityTypesLabel: t.entityTypesLabel,
243
- graphContextLabel: t.graphContextLabel,
244
234
  connectionsLabel: t.connectionsLabel,
245
235
  citedByLabel: t.citedByLabel,
246
- siblingTypesLabel: t.siblingTypesLabel,
247
236
  userHintLabel: t.userHintLabel,
248
237
  userHintPlaceholder: t.userHintPlaceholder,
249
238
  loadingContext: t.loadingContext,
250
239
  failedContext: t.failedContext,
251
- cancel: t.cancel,
252
240
  search: t.search,
253
241
  generate: t.generate,
254
242
  compose: t.compose,
243
+ resolutionStrategyLabel: t.resolutionStrategyLabel,
255
244
  }}
256
245
  />
257
246
  )}
@@ -314,15 +303,9 @@ export function ReferenceWizardModal({
314
303
  back: t.back,
315
304
  cancel: t.cancel,
316
305
  score: t.score,
317
- annotationLabel: t.annotationLabel,
318
- sourceResourceLabel: t.sourceResourceLabel,
319
- motivationLabel: t.motivationLabel,
320
306
  sourceContextLabel: t.sourceContextLabel,
321
- entityTypesLabel: t.entityTypesLabel,
322
- graphContextLabel: t.graphContextLabel,
323
307
  connectionsLabel: t.connectionsLabel,
324
308
  citedByLabel: t.citedByLabel,
325
- siblingTypesLabel: t.siblingTypesLabel,
326
309
  userHintLabel: t.userHintLabel,
327
310
  userHintPlaceholder: t.userHintPlaceholder,
328
311
  }}
@@ -467,6 +467,247 @@
467
467
  max-width: 1100px;
468
468
  }
469
469
 
470
+ /* Gather-context step: wider modal */
471
+ .semiont-search-modal__panel--gather {
472
+ max-width: min(90vw, 820px);
473
+ max-height: min(90vh, 680px);
474
+ display: flex;
475
+ flex-direction: column;
476
+ overflow: hidden;
477
+ }
478
+
479
+ /* Outer flex column: source strip + two-column body */
480
+ .semiont-gather__outer {
481
+ display: flex;
482
+ flex-direction: column;
483
+ flex: 1;
484
+ min-height: 0;
485
+ gap: 0.75rem;
486
+ overflow: hidden;
487
+ }
488
+
489
+ /* Loading state */
490
+ .semiont-gather__loading {
491
+ display: flex;
492
+ flex-direction: column;
493
+ align-items: center;
494
+ justify-content: center;
495
+ gap: 1rem;
496
+ flex: 1;
497
+ padding: 2rem 0;
498
+ }
499
+
500
+ .semiont-gather__loading-text {
501
+ font-size: 1.125rem;
502
+ color: var(--semiont-text-secondary);
503
+ font-weight: 500;
504
+ }
505
+
506
+ [data-theme="dark"] .semiont-gather__loading-text {
507
+ color: var(--semiont-text-secondary);
508
+ }
509
+
510
+ .semiont-gather__loading-dot {
511
+ display: inline-block;
512
+ width: 0.625rem;
513
+ height: 0.625rem;
514
+ border-radius: 50%;
515
+ background-color: var(--semiont-color-primary-500);
516
+ animation: semiont-gather-pulse 1.2s ease-in-out infinite;
517
+ }
518
+
519
+ [data-theme="dark"] .semiont-gather__loading-dot {
520
+ background-color: var(--semiont-color-primary-400);
521
+ }
522
+
523
+ .semiont-gather__loading-dot:nth-child(1) { animation-delay: 0s; }
524
+ .semiont-gather__loading-dot:nth-child(2) { animation-delay: 0.2s; }
525
+ .semiont-gather__loading-dot:nth-child(3) { animation-delay: 0.4s; }
526
+
527
+ @keyframes semiont-gather-pulse {
528
+ 0%, 80%, 100% { transform: scale(0.6); opacity: 0.4; }
529
+ 40% { transform: scale(1); opacity: 1; }
530
+ }
531
+
532
+ /* Full-width source context strip */
533
+ .semiont-gather__source-strip {
534
+ flex-shrink: 0;
535
+ padding-top: 0.25rem;
536
+ }
537
+
538
+ /* Two-column body below the strip */
539
+ .semiont-gather__body {
540
+ display: flex;
541
+ flex: 1;
542
+ min-height: 0;
543
+ overflow: hidden;
544
+ }
545
+
546
+ .semiont-gather__left {
547
+ flex: 0 0 55%;
548
+ overflow-y: auto;
549
+ padding-right: 1.25rem;
550
+ display: flex;
551
+ flex-direction: column;
552
+ gap: 0.75rem;
553
+ border-right: 1px solid var(--semiont-border-primary);
554
+ }
555
+
556
+ [data-theme="dark"] .semiont-gather__left {
557
+ border-right-color: var(--semiont-border-primary);
558
+ }
559
+
560
+ .semiont-gather__right {
561
+ flex: 0 0 45%;
562
+ padding-left: 1.25rem;
563
+ display: flex;
564
+ flex-direction: column;
565
+ gap: 0.75rem;
566
+ overflow-y: auto;
567
+ }
568
+
569
+ .semiont-gather__right .semiont-form__field {
570
+ display: flex;
571
+ flex-direction: column;
572
+ flex: 1;
573
+ min-height: 0;
574
+ }
575
+
576
+ .semiont-gather__hint-textarea {
577
+ width: 100%;
578
+ flex: 1;
579
+ min-height: 0;
580
+ }
581
+
582
+ .semiont-gather__hint-textarea:focus-visible {
583
+ outline: 2px solid var(--semiont-color-primary-500);
584
+ outline-offset: 0;
585
+ }
586
+
587
+ .semiont-gather__footer {
588
+ flex-shrink: 0;
589
+ padding-top: 0.75rem;
590
+ border-top: 1px solid var(--semiont-border-primary);
591
+ }
592
+
593
+ .semiont-gather__footer-label {
594
+ font-size: var(--semiont-text-xs);
595
+ font-weight: 700;
596
+ text-transform: uppercase;
597
+ letter-spacing: 0.06em;
598
+ color: var(--semiont-text-tertiary);
599
+ margin-bottom: 0.5rem;
600
+ }
601
+
602
+ [data-theme="dark"] .semiont-gather__footer-label {
603
+ color: var(--semiont-text-tertiary);
604
+ }
605
+
606
+ .semiont-gather__actions {
607
+ display: flex;
608
+ flex-flow: row nowrap;
609
+ gap: 0.5rem;
610
+ }
611
+
612
+ /* Source context collapsible */
613
+ .semiont-gather__source-box {
614
+ padding: 0.75rem;
615
+ background-color: var(--semiont-bg-secondary);
616
+ border-radius: var(--semiont-radius-md);
617
+ border: 1px solid var(--semiont-border-primary);
618
+ position: relative;
619
+ }
620
+
621
+ [data-theme="dark"] .semiont-gather__source-box {
622
+ background-color: var(--semiont-bg-secondary);
623
+ }
624
+
625
+ .semiont-gather__source-box::after {
626
+ content: '';
627
+ position: absolute;
628
+ bottom: 2rem; /* above the expand button */
629
+ left: 0;
630
+ right: 0;
631
+ height: 2rem;
632
+ background: linear-gradient(to bottom, transparent, var(--semiont-bg-secondary));
633
+ pointer-events: none;
634
+ border-radius: 0 0 var(--semiont-radius-md) var(--semiont-radius-md);
635
+ transition: opacity 0.15s ease;
636
+ }
637
+
638
+ .semiont-gather__source-box--expanded::after {
639
+ opacity: 0;
640
+ }
641
+
642
+ .semiont-gather__source-context {
643
+ max-height: 8rem;
644
+ overflow: hidden;
645
+ transition: max-height 0.2s ease;
646
+ }
647
+
648
+ .semiont-gather__source-box--expanded .semiont-gather__source-context {
649
+ max-height: 16rem;
650
+ }
651
+
652
+ .semiont-gather__expand-btn {
653
+ font-size: var(--semiont-text-xs);
654
+ color: var(--semiont-text-tertiary);
655
+ background: none;
656
+ border: none;
657
+ cursor: pointer;
658
+ padding: 0.125rem 0;
659
+ margin-top: 0.25rem;
660
+ text-decoration: none;
661
+ outline: none;
662
+ }
663
+
664
+ .semiont-gather__expand-btn:hover {
665
+ color: var(--semiont-color-primary-600);
666
+ }
667
+
668
+ [data-theme="dark"] .semiont-gather__expand-btn {
669
+ color: var(--semiont-text-tertiary);
670
+ }
671
+
672
+ [data-theme="dark"] .semiont-gather__expand-btn:hover {
673
+ color: var(--semiont-color-primary-400);
674
+ }
675
+
676
+ /* Narrow screens: stack vertically */
677
+ @media (max-width: 640px) {
678
+ .semiont-search-modal__panel--gather {
679
+ max-width: 100%;
680
+ max-height: 100dvh;
681
+ }
682
+
683
+ .semiont-gather__outer {
684
+ overflow-y: auto;
685
+ }
686
+
687
+ .semiont-gather__body {
688
+ flex-direction: column;
689
+ overflow: visible;
690
+ }
691
+
692
+ .semiont-gather__left {
693
+ flex: none;
694
+ border-right: none;
695
+ border-bottom: 1px solid var(--semiont-border-primary);
696
+ padding-right: 0;
697
+ padding-bottom: 0.75rem;
698
+ }
699
+
700
+ .semiont-gather__right {
701
+ flex: none;
702
+ padding-left: 0;
703
+ padding-top: 0.75rem;
704
+ }
705
+
706
+ .semiont-gather__actions {
707
+ flex-flow: row wrap;
708
+ }
709
+ }
710
+
470
711
  /* Two-pane layout for search results + context */
471
712
  .semiont-search-results__two-pane {
472
713
  display: flex;
@@ -37,7 +37,7 @@ interface Props {
37
37
  onUIStateChange?: (state: Partial<AnnotationUIState>) => void;
38
38
  editable?: boolean;
39
39
  enableWidgets?: boolean;
40
- getTargetDocumentName?: (documentId: string) => string | undefined;
40
+ getTargetResourceName?: (resourceId: string) => string | undefined;
41
41
  generatingReferenceId?: string | null;
42
42
  showLineNumbers?: boolean;
43
43
  hoverDelayMs?: number;
@@ -61,7 +61,7 @@ export function AnnotateView({
61
61
  uiState,
62
62
  onUIStateChange,
63
63
  enableWidgets = false,
64
- getTargetDocumentName,
64
+ getTargetResourceName,
65
65
  generatingReferenceId,
66
66
  showLineNumbers = false,
67
67
  hoverDelayMs = 150,
@@ -220,7 +220,7 @@ export function AnnotateView({
220
220
  hoverDelayMs={hoverDelayMs}
221
221
  enableWidgets={enableWidgets}
222
222
  eventBus={eventBus}
223
- {...(getTargetDocumentName && { getTargetDocumentName })}
223
+ {...(getTargetResourceName && { getTargetResourceName })}
224
224
  {...(generatingReferenceId !== undefined && { generatingReferenceId })}
225
225
  />
226
226
 
@@ -196,8 +196,8 @@ export const BrowseView = memo(function BrowseView({
196
196
  scrollToAnnotation(annotationId);
197
197
  }, [scrollToAnnotation]);
198
198
 
199
- const handleAnnotationFocus = useCallback(({ annotationId }: { annotationId: string | null }) => {
200
- scrollToAnnotation(annotationId, true);
199
+ const handleAnnotationFocus = useCallback(({ annotationId }: { annotationId?: string | null }) => {
200
+ scrollToAnnotation(annotationId ?? null, true);
201
201
  }, [scrollToAnnotation]);
202
202
 
203
203
  useEventSubscriptions({
@@ -374,10 +374,10 @@ export function ResourceViewer({
374
374
  scrollToAnnotationId
375
375
  };
376
376
 
377
- // Define getTargetDocumentName callback OUTSIDE the conditional
377
+ // Define getTargetResourceName callback OUTSIDE the conditional
378
378
  // IMPORTANT: This must be defined before the return statement to avoid hook ordering violations
379
- const getTargetDocumentName = useCallback((documentId: string) => {
380
- const referencedResource = references.find((a: Annotation) => getBodySource(a.body) === documentId);
379
+ const getTargetResourceName = useCallback((resourceId: string) => {
380
+ const referencedResource = references.find((a: Annotation) => getBodySource(a.body) === resourceId);
381
381
  return referencedResource ? getExactText(getTargetSelector(referencedResource.target)) : undefined;
382
382
  }, [references]);
383
383
 
@@ -397,7 +397,7 @@ export function ResourceViewer({
397
397
  if ('selectedShape' in updates) setSelectedShape(updates.selectedShape!);
398
398
  }}
399
399
  enableWidgets={true}
400
- getTargetDocumentName={getTargetDocumentName}
400
+ getTargetResourceName={getTargetResourceName}
401
401
  {...(generatingReferenceId !== undefined && { generatingReferenceId })}
402
402
  showLineNumbers={showLineNumbers}
403
403
  hoverDelayMs={hoverDelayMs}
@@ -40,7 +40,7 @@ describe('Annotation Interaction Transitions', () => {
40
40
  // When: User clicks on the reference text
41
41
  // Then: Should navigate to the referenced document
42
42
  // - In both Browse mode and Annotate mode
43
- // - Router should push to /know/resource/{documentId}
43
+ // - Router should push to /know/resource/{resourceId}
44
44
  expect(true).toBe(true); // Placeholder - implement actual test
45
45
  });
46
46
 
@@ -46,6 +46,7 @@ interface AssessmentPanelProps {
46
46
  percentage?: number;
47
47
  message?: string;
48
48
  } | null;
49
+ locale?: string;
49
50
  annotateMode?: boolean;
50
51
  scrollToAnnotationId?: string | null;
51
52
  onScrollCompleted?: () => void;
@@ -64,6 +65,7 @@ export function AssessmentPanel({
64
65
  pendingAnnotation,
65
66
  isAssisting = false,
66
67
  progress,
68
+ locale,
67
69
  annotateMode = true,
68
70
  scrollToAnnotationId,
69
71
  onScrollCompleted,
@@ -236,6 +238,7 @@ export function AssessmentPanel({
236
238
  <AssistSection
237
239
  annotationType="assessment"
238
240
  isAssisting={isAssisting}
241
+ locale={locale}
239
242
  progress={progress}
240
243
  />
241
244
  )}
@@ -9,6 +9,7 @@ import './AssistSection.css';
9
9
  interface AssistSectionProps {
10
10
  annotationType: 'highlight' | 'assessment' | 'comment';
11
11
  isAssisting: boolean;
12
+ locale?: string;
12
13
  progress?: {
13
14
  status: string;
14
15
  percentage?: number;
@@ -34,6 +35,7 @@ interface AssistSectionProps {
34
35
  export function AssistSection({
35
36
  annotationType,
36
37
  isAssisting,
38
+ locale,
37
39
  progress,
38
40
  }: AssistSectionProps) {
39
41
 
@@ -77,13 +79,14 @@ export function AssistSection({
77
79
  instructions: instructions.trim() || undefined,
78
80
  tone: (annotationType === 'comment' || annotationType === 'assessment') && tone ? tone : undefined,
79
81
  density: (annotationType === 'comment' || annotationType === 'assessment' || annotationType === 'highlight') && useDensity ? density : undefined,
82
+ language: (annotationType === 'comment' || annotationType === 'assessment') ? locale : undefined,
80
83
  },
81
84
  });
82
85
 
83
86
  setInstructions('');
84
87
  setTone('');
85
88
  // Don't reset density/useDensity - persist across assists
86
- }, [annotationType, instructions, tone, useDensity, density]); // eventBus is stable singleton - never in deps
89
+ }, [annotationType, instructions, tone, useDensity, density, locale]); // eventBus is stable singleton - never in deps
87
90
 
88
91
  const handleDismissProgress = useCallback(() => {
89
92
  eventBus.get('mark:progress-dismiss').next(undefined);