@ltm-blueverse/alpha-semantic-hub 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1674 -0
  3. package/explorer/README.md +282 -0
  4. package/explorer/src/App.css +0 -0
  5. package/explorer/src/App.tsx +2035 -0
  6. package/explorer/src/ErrorBoundary.tsx +112 -0
  7. package/explorer/src/ExploreWorkspaceTabs.tsx +29 -0
  8. package/explorer/src/assets/hero.png +0 -0
  9. package/explorer/src/assets/react.svg +1 -0
  10. package/explorer/src/assets/vite.svg +1 -0
  11. package/explorer/src/explorerCapabilities.ts +24 -0
  12. package/explorer/src/index.css +71 -0
  13. package/explorer/src/main.tsx +7 -0
  14. package/explorer/src/store/edgePairKeys.d.ts +2 -0
  15. package/explorer/src/store/edgePairKeys.js +7 -0
  16. package/explorer/src/store/graphStore.ts +183 -0
  17. package/explorer/src/store/registryStore.ts +78 -0
  18. package/explorer/src/types.d.ts +16 -0
  19. package/explorer/src/ui/primitives.css +414 -0
  20. package/explorer/src/ui/primitives.tsx +170 -0
  21. package/explorer/src/workspaces/DecisionWorkspace/DecisionWorkspace.tsx +279 -0
  22. package/explorer/src/workspaces/DiffMergeWorkspace/DiffMergeWorkspace.tsx +121 -0
  23. package/explorer/src/workspaces/EnrichWorkspace/EntityResolutionTab.tsx +454 -0
  24. package/explorer/src/workspaces/EnrichWorkspace/RegistryTab.tsx +285 -0
  25. package/explorer/src/workspaces/GraphWorkspace/GraphCanvas.tsx +2673 -0
  26. package/explorer/src/workspaces/GraphWorkspace/GraphInspectorPanel.tsx +771 -0
  27. package/explorer/src/workspaces/GraphWorkspace/GraphLoadingOverlay.tsx +410 -0
  28. package/explorer/src/workspaces/GraphWorkspace/GraphWorkspace.tsx +3584 -0
  29. package/explorer/src/workspaces/GraphWorkspace/MarkdownContentViewer.tsx +705 -0
  30. package/explorer/src/workspaces/GraphWorkspace/SigmaSceneAdapter.tsx +55 -0
  31. package/explorer/src/workspaces/GraphWorkspace/TimelinePanel.tsx +193 -0
  32. package/explorer/src/workspaces/GraphWorkspace/behaviors/clickSelectionBehavior.ts +29 -0
  33. package/explorer/src/workspaces/GraphWorkspace/behaviors/fitViewBehavior.ts +15 -0
  34. package/explorer/src/workspaces/GraphWorkspace/behaviors/focusCameraBehavior.ts +25 -0
  35. package/explorer/src/workspaces/GraphWorkspace/behaviors/hoverActivationBehavior.ts +15 -0
  36. package/explorer/src/workspaces/GraphWorkspace/behaviors/pathHighlightBehavior.ts +52 -0
  37. package/explorer/src/workspaces/GraphWorkspace/behaviors/searchFocusBehavior.ts +36 -0
  38. package/explorer/src/workspaces/GraphWorkspace/behaviors/types.ts +41 -0
  39. package/explorer/src/workspaces/GraphWorkspace/behaviors/viewModeSwitchBehavior.ts +29 -0
  40. package/explorer/src/workspaces/GraphWorkspace/graphAnalytics.ts +779 -0
  41. package/explorer/src/workspaces/GraphWorkspace/graphColorLegend.ts +36 -0
  42. package/explorer/src/workspaces/GraphWorkspace/graphConfig.ts +102 -0
  43. package/explorer/src/workspaces/GraphWorkspace/graphEntityShape.ts +34 -0
  44. package/explorer/src/workspaces/GraphWorkspace/graphLoading.ts +84 -0
  45. package/explorer/src/workspaces/GraphWorkspace/graphSceneLayers.ts +775 -0
  46. package/explorer/src/workspaces/GraphWorkspace/graphSceneState.ts +2753 -0
  47. package/explorer/src/workspaces/GraphWorkspace/graphStructureLayer.ts +335 -0
  48. package/explorer/src/workspaces/GraphWorkspace/graphTheme.ts +1078 -0
  49. package/explorer/src/workspaces/GraphWorkspace/graphViewCopy.ts +41 -0
  50. package/explorer/src/workspaces/GraphWorkspace/localGraphTransition.ts +9 -0
  51. package/explorer/src/workspaces/GraphWorkspace/markdownEditorState.ts +114 -0
  52. package/explorer/src/workspaces/GraphWorkspace/markdownResourceClient.ts +103 -0
  53. package/explorer/src/workspaces/GraphWorkspace/markdownUrlSafety.ts +29 -0
  54. package/explorer/src/workspaces/GraphWorkspace/nodeMarkdownSync.ts +81 -0
  55. package/explorer/src/workspaces/GraphWorkspace/pluginRegistryPredicates.ts +24 -0
  56. package/explorer/src/workspaces/GraphWorkspace/plugins/explorationEffectsPlugin.tsx +353 -0
  57. package/explorer/src/workspaces/GraphWorkspace/plugins/explorationEffectsPluginPhaseC.tsx +522 -0
  58. package/explorer/src/workspaces/GraphWorkspace/plugins/index.ts +16 -0
  59. package/explorer/src/workspaces/GraphWorkspace/plugins/legendPlugin.tsx +120 -0
  60. package/explorer/src/workspaces/GraphWorkspace/plugins/neighborhoodPanelPlugin.tsx +231 -0
  61. package/explorer/src/workspaces/GraphWorkspace/plugins/temporalOverlayPlugin.tsx +142 -0
  62. package/explorer/src/workspaces/GraphWorkspace/plugins/types.ts +106 -0
  63. package/explorer/src/workspaces/GraphWorkspace/realtimeGraphAttributes.ts +50 -0
  64. package/explorer/src/workspaces/GraphWorkspace/scene.ts +79 -0
  65. package/explorer/src/workspaces/GraphWorkspace/sigmaNativeRendering.ts +421 -0
  66. package/explorer/src/workspaces/GraphWorkspace/smallGraphLayout.ts +135 -0
  67. package/explorer/src/workspaces/GraphWorkspace/temporalLifecyclePredicates.ts +31 -0
  68. package/explorer/src/workspaces/GraphWorkspace/temporalScrubberBounds.ts +46 -0
  69. package/explorer/src/workspaces/GraphWorkspace/temporalSnapshotGuards.ts +113 -0
  70. package/explorer/src/workspaces/GraphWorkspace/types.ts +395 -0
  71. package/explorer/src/workspaces/GraphWorkspace/useLoadGraph.ts +746 -0
  72. package/explorer/src/workspaces/GraphWorkspace/useMarkdownEditor.ts +177 -0
  73. package/explorer/src/workspaces/ImportExportWorkspace/ImportExportWorkspace.tsx +196 -0
  74. package/explorer/src/workspaces/LineageWorkspace/LineageDiagram.tsx +177 -0
  75. package/explorer/src/workspaces/ManageWorkspace/KGOverviewTab.tsx +291 -0
  76. package/explorer/src/workspaces/ManageWorkspace/OntologySummaryTab.tsx +346 -0
  77. package/explorer/src/workspaces/MemoryWorkspace.tsx +464 -0
  78. package/explorer/src/workspaces/OntologyWorkspace/AlignmentsTab.tsx +437 -0
  79. package/explorer/src/workspaces/OntologyWorkspace/HealthTab.tsx +214 -0
  80. package/explorer/src/workspaces/OntologyWorkspace/OntologyEditor.tsx +770 -0
  81. package/explorer/src/workspaces/OntologyWorkspace/OntologyLoader.tsx +913 -0
  82. package/explorer/src/workspaces/OntologyWorkspace/OntologyManager.tsx +930 -0
  83. package/explorer/src/workspaces/OntologyWorkspace/OntologySearch.tsx +590 -0
  84. package/explorer/src/workspaces/OntologyWorkspace/ProposalReview.tsx +429 -0
  85. package/explorer/src/workspaces/OntologyWorkspace/SKOSVocabularyManager.tsx +696 -0
  86. package/explorer/src/workspaces/OntologyWorkspace/ShaclStudio.tsx +363 -0
  87. package/explorer/src/workspaces/OntologyWorkspace/VersionsTab.tsx +546 -0
  88. package/explorer/src/workspaces/OntologyWorkspace/api.ts +164 -0
  89. package/explorer/src/workspaces/OntologyWorkspace/index.tsx +101 -0
  90. package/explorer/src/workspaces/OntologyWorkspace/ontologyEditorModel.ts +87 -0
  91. package/explorer/src/workspaces/OntologyWorkspace/ontologyUrlState.ts +94 -0
  92. package/explorer/src/workspaces/OntologyWorkspace/types.ts +119 -0
  93. package/explorer/src/workspaces/ReasoningWorkspace.tsx +243 -0
  94. package/explorer/src/workspaces/SparqlWorkspace/SparqlWorkspace.tsx +271 -0
  95. package/explorer/src/workspaces/VocabularyWorkspace/ConceptTree.tsx +97 -0
  96. package/explorer/src/workspaces/VocabularyWorkspace/ImportDropzone.tsx +95 -0
  97. package/explorer/src/workspaces/VocabularyWorkspace/PropertyPanel.tsx +116 -0
  98. package/explorer/src/workspaces/VocabularyWorkspace/Sidebar.tsx +112 -0
  99. package/explorer/src/workspaces/VocabularyWorkspace/VocabularyWorkspace.tsx +70 -0
  100. package/explorer/src/workspaces/VocabularyWorkspace/queries.ts +52 -0
  101. package/explorer/src/workspaces/VocabularyWorkspace/types.ts +24 -0
  102. package/package.json +31 -0
  103. package/plugins/.claude-plugin/README.md +262 -0
  104. package/plugins/.claude-plugin/marketplace.json +20 -0
  105. package/plugins/.claude-plugin/plugin.json +33 -0
  106. package/plugins/.cline-plugin/README.md +32 -0
  107. package/plugins/.cline-plugin/marketplace.json +17 -0
  108. package/plugins/.cline-plugin/plugin.json +35 -0
  109. package/plugins/.codex-plugin/marketplace.json +21 -0
  110. package/plugins/.codex-plugin/plugin.json +35 -0
  111. package/plugins/.continue-plugin/README.md +34 -0
  112. package/plugins/.continue-plugin/marketplace.json +17 -0
  113. package/plugins/.continue-plugin/plugin.json +33 -0
  114. package/plugins/.cursor-plugin/marketplace.json +18 -0
  115. package/plugins/.cursor-plugin/plugin.json +32 -0
  116. package/plugins/.openclaw-plugin/README.md +62 -0
  117. package/plugins/.openclaw-plugin/marketplace.json +18 -0
  118. package/plugins/.openclaw-plugin/plugin.json +46 -0
  119. package/plugins/.pi-plugin/README.md +35 -0
  120. package/plugins/.vscode-plugin/README.md +48 -0
  121. package/plugins/.vscode-plugin/marketplace.json +17 -0
  122. package/plugins/.vscode-plugin/plugin.json +35 -0
  123. package/plugins/.windsurf-plugin/README.md +39 -0
  124. package/plugins/.windsurf-plugin/marketplace.json +17 -0
  125. package/plugins/.windsurf-plugin/plugin.json +35 -0
  126. package/plugins/agents/decision-advisor.md +126 -0
  127. package/plugins/agents/explainability.md +129 -0
  128. package/plugins/agents/kg-assistant.md +73 -0
  129. package/plugins/hooks/hooks.json +12 -0
  130. package/plugins/skills/causal/SKILL.md +74 -0
  131. package/plugins/skills/change/SKILL.md +80 -0
  132. package/plugins/skills/decision/SKILL.md +197 -0
  133. package/plugins/skills/deduplicate/SKILL.md +41 -0
  134. package/plugins/skills/embed/SKILL.md +230 -0
  135. package/plugins/skills/explain/SKILL.md +44 -0
  136. package/plugins/skills/export/SKILL.md +67 -0
  137. package/plugins/skills/extract/SKILL.md +93 -0
  138. package/plugins/skills/ingest/SKILL.md +38 -0
  139. package/plugins/skills/ontology/SKILL.md +89 -0
  140. package/plugins/skills/policy/SKILL.md +66 -0
  141. package/plugins/skills/provenance/SKILL.md +70 -0
  142. package/plugins/skills/query/SKILL.md +84 -0
  143. package/plugins/skills/reason/SKILL.md +201 -0
  144. package/plugins/skills/temporal/SKILL.md +164 -0
  145. package/plugins/skills/validate/SKILL.md +228 -0
  146. package/plugins/skills/visualize/SKILL.md +249 -0
@@ -0,0 +1,429 @@
1
+ import { useCallback, useEffect, useState } from "react";
2
+ import {
3
+ GitMerge,
4
+ CheckCircle,
5
+ XCircle,
6
+ AlertCircle,
7
+ Send,
8
+ MessageSquare,
9
+ User,
10
+ Clock,
11
+ Plus,
12
+ Minus,
13
+ Edit,
14
+ } from "lucide-react";
15
+
16
+ interface Proposal {
17
+ proposal_id: string;
18
+ draft_id: string;
19
+ ontology_uri: string;
20
+ summary: string;
21
+ author: string;
22
+ reviewer: string | null;
23
+ state: "draft" | "proposed" | "approved" | "published" | "rejected";
24
+ impact_analysis: Record<string, any>;
25
+ shacl_validation: Record<string, any>;
26
+ created_at: string;
27
+ updated_at: string;
28
+ comments: Record<string, any>[];
29
+ }
30
+
31
+ interface DiffChange {
32
+ type: "added" | "removed" | "modified";
33
+ element: string;
34
+ details?: Record<string, any>;
35
+ }
36
+
37
+ export function ProposalReview({ proposalId }: { proposalId: string }) {
38
+ const [proposal, setProposal] = useState<Proposal | null>(null);
39
+ const [diff, setDiff] = useState<DiffChange[]>([]);
40
+ const [selectedElement, setSelectedElement] = useState<string | null>(null);
41
+ const [commentText, setCommentText] = useState("");
42
+
43
+ const generateDiff = useCallback((prop: Proposal) => {
44
+ const changes: DiffChange[] = [];
45
+
46
+ // Generate diff from impact analysis
47
+ if (prop.impact_analysis) {
48
+ if (prop.impact_analysis.class_adds > 0) {
49
+ changes.push({ type: "added", element: `Classes (${prop.impact_analysis.class_adds})` });
50
+ }
51
+ if (prop.impact_analysis.class_removals > 0) {
52
+ changes.push({ type: "removed", element: `Classes (${prop.impact_analysis.class_removals})` });
53
+ }
54
+ if (prop.impact_analysis.property_changes > 0) {
55
+ changes.push({ type: "modified", element: `Properties (${prop.impact_analysis.property_changes})` });
56
+ }
57
+ if (prop.impact_analysis.restriction_changes > 0) {
58
+ changes.push({ type: "modified", element: `Restrictions (${prop.impact_analysis.restriction_changes})` });
59
+ }
60
+ }
61
+
62
+ setDiff(changes);
63
+ }, []);
64
+
65
+ const loadProposal = useCallback(async () => {
66
+ try {
67
+ const response = await fetch(`/api/ontology/proposals/${proposalId}`);
68
+ if (response.ok) {
69
+ const data = await response.json();
70
+ setProposal(data);
71
+ generateDiff(data);
72
+ }
73
+ } catch (error) {
74
+ console.error("Failed to load proposal:", error);
75
+ }
76
+ }, [proposalId, generateDiff]);
77
+
78
+ useEffect(() => {
79
+ let ignore = false;
80
+ async function fetchInitial() {
81
+ try {
82
+ const response = await fetch(`/api/ontology/proposals/${proposalId}`);
83
+ if (response.ok) {
84
+ const data = await response.json();
85
+ if (!ignore) {
86
+ setProposal(data);
87
+ generateDiff(data);
88
+ }
89
+ }
90
+ } catch (error) {
91
+ console.error("Failed to load proposal:", error);
92
+ }
93
+ }
94
+ void fetchInitial();
95
+ return () => { ignore = true; };
96
+ }, [proposalId, generateDiff]);
97
+
98
+ const addComment = useCallback(async () => {
99
+ if (!selectedElement || !commentText || !proposal) return;
100
+ try {
101
+ const response = await fetch(`/api/ontology/proposals/${proposal.proposal_id}/comment`, {
102
+ method: "POST",
103
+ headers: { "Content-Type": "application/json" },
104
+ body: JSON.stringify({
105
+ element_uri: selectedElement,
106
+ text: commentText,
107
+ author: "user",
108
+ }),
109
+ });
110
+ if (response.ok) {
111
+ setCommentText("");
112
+ loadProposal();
113
+ }
114
+ } catch (error) {
115
+ console.error("Failed to add comment:", error);
116
+ alert("Failed to add comment");
117
+ }
118
+ }, [selectedElement, commentText, proposal, loadProposal]);
119
+
120
+ const approveProposal = useCallback(async () => {
121
+ if (!proposal) return;
122
+ try {
123
+ const response = await fetch(`/api/ontology/proposals/${proposal.proposal_id}/approve`, {
124
+ method: "POST",
125
+ });
126
+ if (response.ok) {
127
+ alert("Proposal approved");
128
+ loadProposal();
129
+ }
130
+ } catch (error) {
131
+ console.error("Failed to approve proposal:", error);
132
+ alert("Failed to approve proposal");
133
+ }
134
+ }, [proposal, loadProposal]);
135
+
136
+ const rejectProposal = useCallback(async () => {
137
+ if (!proposal) return;
138
+ try {
139
+ const response = await fetch(`/api/ontology/proposals/${proposal.proposal_id}/reject`, {
140
+ method: "POST",
141
+ });
142
+ if (response.ok) {
143
+ alert("Proposal rejected");
144
+ loadProposal();
145
+ }
146
+ } catch (error) {
147
+ console.error("Failed to reject proposal:", error);
148
+ alert("Failed to reject proposal");
149
+ }
150
+ }, [proposal, loadProposal]);
151
+
152
+ const publishProposal = useCallback(async () => {
153
+ if (!proposal) return;
154
+ try {
155
+ const response = await fetch(`/api/ontology/proposals/${proposal.proposal_id}/publish`, {
156
+ method: "POST",
157
+ });
158
+ if (response.ok) {
159
+ alert("Proposal published");
160
+ loadProposal();
161
+ }
162
+ } catch (error) {
163
+ console.error("Failed to publish proposal:", error);
164
+ alert("Failed to publish proposal");
165
+ }
166
+ }, [proposal, loadProposal]);
167
+
168
+ const getChangeIcon = (type: string) => {
169
+ switch (type) {
170
+ case "added":
171
+ return <Plus size={14} color="#4cc38a" />;
172
+ case "removed":
173
+ return <Minus size={14} color="#ff6b6b" />;
174
+ case "modified":
175
+ return <Edit size={14} color="#ff7a45" />;
176
+ default:
177
+ return null;
178
+ }
179
+ };
180
+
181
+ const getStateIcon = (state: string) => {
182
+ switch (state) {
183
+ case "published":
184
+ return <CheckCircle size={20} color="#4cc38a" />;
185
+ case "approved":
186
+ return <CheckCircle size={20} color="#ff2d4b" />;
187
+ case "rejected":
188
+ return <XCircle size={20} color="#ff6b6b" />;
189
+ case "proposed":
190
+ return <AlertCircle size={20} color="#ff7a45" />;
191
+ default:
192
+ return <Clock size={20} color="#c99aa3" />;
193
+ }
194
+ };
195
+
196
+ const containerStyle: React.CSSProperties = {
197
+ display: "flex",
198
+ flexDirection: "column",
199
+ height: "100%",
200
+ background: "#0b0508",
201
+ padding: "20px",
202
+ overflow: "auto",
203
+ };
204
+
205
+ const headerStyle: React.CSSProperties = {
206
+ display: "flex",
207
+ justifyContent: "space-between",
208
+ alignItems: "center",
209
+ marginBottom: "20px",
210
+ paddingBottom: "16px",
211
+ borderBottom: "1px solid rgba(255, 120, 140, 0.12)",
212
+ };
213
+
214
+ const titleStyle: React.CSSProperties = {
215
+ margin: 0,
216
+ color: "#fff0f2",
217
+ fontSize: "20px",
218
+ fontWeight: "700",
219
+ };
220
+
221
+ const contentStyle: React.CSSProperties = {
222
+ display: "flex",
223
+ gap: "20px",
224
+ flex: 1,
225
+ minHeight: 0,
226
+ };
227
+
228
+ const diffPanelStyle: React.CSSProperties = {
229
+ flex: 1,
230
+ background: "rgba(12, 6, 9, 0.8)",
231
+ borderRadius: "8px",
232
+ border: "1px solid rgba(255, 107, 129, 0.12)",
233
+ padding: "16px",
234
+ overflow: "auto",
235
+ };
236
+
237
+ const commentsPanelStyle: React.CSSProperties = {
238
+ width: "320px",
239
+ background: "rgba(12, 6, 9, 0.8)",
240
+ borderRadius: "8px",
241
+ border: "1px solid rgba(255, 107, 129, 0.12)",
242
+ padding: "16px",
243
+ display: "flex",
244
+ flexDirection: "column",
245
+ };
246
+
247
+ const diffItemStyle: React.CSSProperties = {
248
+ display: "flex",
249
+ alignItems: "center",
250
+ gap: "10px",
251
+ padding: "10px 12px",
252
+ borderRadius: "6px",
253
+ background: "rgba(6, 3, 5, 0.6)",
254
+ marginBottom: "8px",
255
+ cursor: "pointer",
256
+ transition: "160ms ease",
257
+ };
258
+
259
+ const buttonStyle: React.CSSProperties = {
260
+ display: "inline-flex",
261
+ alignItems: "center",
262
+ gap: "6px",
263
+ padding: "8px 14px",
264
+ borderRadius: "6px",
265
+ border: "1px solid rgba(255, 107, 129, 0.2)",
266
+ background: "rgba(255, 45, 75, 0.1)",
267
+ color: "#fff0f2",
268
+ fontSize: "12px",
269
+ fontWeight: "600",
270
+ cursor: "pointer",
271
+ transition: "160ms ease",
272
+ };
273
+
274
+ const textareaStyle: React.CSSProperties = {
275
+ width: "100%",
276
+ padding: "10px 12px",
277
+ borderRadius: "6px",
278
+ border: "1px solid rgba(255, 107, 129, 0.2)",
279
+ background: "rgba(6, 3, 5, 0.8)",
280
+ color: "#fff0f2",
281
+ fontSize: "13px",
282
+ resize: "vertical",
283
+ minHeight: "80px",
284
+ };
285
+
286
+ if (!proposal) {
287
+ return (
288
+ <div style={containerStyle}>
289
+ <div style={{ color: "#c99aa3", fontSize: "14px" }}>Loading proposal...</div>
290
+ </div>
291
+ );
292
+ }
293
+
294
+ return (
295
+ <div style={containerStyle}>
296
+ <div style={headerStyle}>
297
+ <div style={{ display: "flex", alignItems: "center", gap: "12px" }}>
298
+ {getStateIcon(proposal.state)}
299
+ <div>
300
+ <h1 style={titleStyle}>{proposal.summary}</h1>
301
+ <div style={{ color: "#c99aa3", fontSize: "12px", marginTop: "4px" }}>
302
+ {proposal.author} • {new Date(proposal.created_at).toLocaleString()}
303
+ </div>
304
+ </div>
305
+ </div>
306
+ <div style={{ display: "flex", gap: "8px" }}>
307
+ {proposal.state === "proposed" && (
308
+ <>
309
+ <button style={buttonStyle} onClick={approveProposal}>
310
+ <CheckCircle size={12} />
311
+ Approve
312
+ </button>
313
+ <button style={buttonStyle} onClick={rejectProposal}>
314
+ <XCircle size={12} />
315
+ Reject
316
+ </button>
317
+ </>
318
+ )}
319
+ {proposal.state === "approved" && (
320
+ <button style={buttonStyle} onClick={publishProposal}>
321
+ <Send size={12} />
322
+ Publish
323
+ </button>
324
+ )}
325
+ </div>
326
+ </div>
327
+
328
+ <div style={contentStyle}>
329
+ <div style={diffPanelStyle}>
330
+ <h2 style={{ margin: "0 0 16px", color: "#fff0f2", fontSize: "14px", fontWeight: "600" }}>
331
+ <GitMerge size={16} style={{ marginRight: "8px", verticalAlign: "middle" }} />
332
+ Diff Viewer
333
+ </h2>
334
+ {diff.length === 0 ? (
335
+ <div style={{ color: "#c99aa3", fontSize: "13px" }}>No changes detected</div>
336
+ ) : (
337
+ diff.map((change, index) => (
338
+ <div
339
+ key={index}
340
+ style={{
341
+ ...diffItemStyle,
342
+ border: selectedElement === change.element ? "1px solid rgba(255, 45, 75, 0.4)" : "1px solid transparent",
343
+ }}
344
+ onClick={() => setSelectedElement(change.element)}
345
+ >
346
+ {getChangeIcon(change.type)}
347
+ <div style={{ flex: 1 }}>
348
+ <div style={{ color: "#fff0f2", fontSize: "13px", fontWeight: "600" }}>
349
+ {change.element}
350
+ </div>
351
+ <div style={{ color: "#c99aa3", fontSize: "11px" }}>
352
+ {change.type}
353
+ </div>
354
+ </div>
355
+ </div>
356
+ ))
357
+ )}
358
+
359
+ <div style={{ marginTop: "20px", paddingTop: "16px", borderTop: "1px solid rgba(255, 120, 140, 0.12)" }}>
360
+ <h3 style={{ margin: "0 0 12px", color: "#fff0f2", fontSize: "13px", fontWeight: "600" }}>
361
+ Impact Analysis
362
+ </h3>
363
+ <pre style={{ background: "rgba(6, 3, 5, 0.8)", padding: "12px", borderRadius: "6px", color: "#fff0f2", fontSize: "12px", overflow: "auto" }}>
364
+ {JSON.stringify(proposal.impact_analysis, null, 2)}
365
+ </pre>
366
+ </div>
367
+
368
+ <div style={{ marginTop: "16px" }}>
369
+ <h3 style={{ margin: "0 0 12px", color: "#fff0f2", fontSize: "13px", fontWeight: "600" }}>
370
+ SHACL Validation
371
+ </h3>
372
+ <pre style={{ background: "rgba(6, 3, 5, 0.8)", padding: "12px", borderRadius: "6px", color: "#fff0f2", fontSize: "12px", overflow: "auto" }}>
373
+ {JSON.stringify(proposal.shacl_validation, null, 2)}
374
+ </pre>
375
+ </div>
376
+ </div>
377
+
378
+ <div style={commentsPanelStyle}>
379
+ <h2 style={{ margin: "0 0 16px", color: "#fff0f2", fontSize: "14px", fontWeight: "600" }}>
380
+ <MessageSquare size={16} style={{ marginRight: "8px", verticalAlign: "middle" }} />
381
+ Comments ({proposal.comments.length})
382
+ </h2>
383
+ <div style={{ flex: 1, overflow: "auto", marginBottom: "12px" }}>
384
+ {proposal.comments.length === 0 ? (
385
+ <div style={{ color: "#c99aa3", fontSize: "13px" }}>No comments yet</div>
386
+ ) : (
387
+ proposal.comments.map((comment) => (
388
+ <div
389
+ key={comment.id}
390
+ style={{
391
+ padding: "10px",
392
+ background: "rgba(6, 3, 5, 0.6)",
393
+ borderRadius: "6px",
394
+ marginBottom: "8px",
395
+ }}
396
+ >
397
+ <div style={{ display: "flex", alignItems: "center", gap: "6px", marginBottom: "4px" }}>
398
+ <User size={12} color="#c99aa3" />
399
+ <span style={{ color: "#fff0f2", fontSize: "12px", fontWeight: "600" }}>
400
+ {comment.author}
401
+ </span>
402
+ </div>
403
+ <div style={{ color: "#c99aa3", fontSize: "12px", marginBottom: "4px" }}>{comment.text}</div>
404
+ <div style={{ color: "#8f6d75", fontSize: "10px" }}>
405
+ {new Date(comment.created_at).toLocaleString()}
406
+ </div>
407
+ </div>
408
+ ))
409
+ )}
410
+ </div>
411
+ {selectedElement && (
412
+ <div>
413
+ <textarea
414
+ placeholder="Add a comment..."
415
+ value={commentText}
416
+ onChange={(e) => setCommentText(e.target.value)}
417
+ style={textareaStyle}
418
+ />
419
+ <button style={buttonStyle} onClick={addComment} disabled={!commentText}>
420
+ <Send size={12} />
421
+ Add Comment
422
+ </button>
423
+ </div>
424
+ )}
425
+ </div>
426
+ </div>
427
+ </div>
428
+ );
429
+ }