@semiont/react-ui 0.5.5 → 0.5.6

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 (87) hide show
  1. package/README.md +59 -55
  2. package/dist/{PdfAnnotationCanvas.client-CN3C3S55.js → PdfAnnotationCanvas.client-NIMALXNZ.js} +7 -27
  3. package/dist/PdfAnnotationCanvas.client-NIMALXNZ.js.map +1 -0
  4. package/dist/index.css +6 -0
  5. package/dist/index.css.map +1 -1
  6. package/dist/index.d.ts +243 -99
  7. package/dist/index.js +539 -405
  8. package/dist/index.js.map +1 -1
  9. package/package.json +16 -12
  10. package/src/components/Button/__tests__/Button.test.tsx +0 -2
  11. package/src/components/CodeMirrorRenderer.tsx +2 -0
  12. package/src/components/ErrorBoundary.tsx +0 -9
  13. package/src/components/ProtectedErrorBoundary.tsx +6 -2
  14. package/src/components/__tests__/AnnotateReferencesProgressWidget.test.tsx +0 -1
  15. package/src/components/__tests__/ErrorBoundary.test.tsx +20 -13
  16. package/src/components/__tests__/LiveRegion.hooks.test.tsx +1 -1
  17. package/src/components/__tests__/ProtectedErrorBoundary.test.tsx +2 -1
  18. package/src/components/__tests__/ResizeHandle.test.tsx +0 -1
  19. package/src/components/__tests__/SessionExpiryBanner.test.tsx +0 -1
  20. package/src/components/__tests__/StatusDisplay.test.tsx +0 -1
  21. package/src/components/__tests__/Toast.test.tsx +2 -3
  22. package/src/components/__tests__/Toolbar.test.tsx +0 -1
  23. package/src/components/annotation/annotations.css +14 -0
  24. package/src/components/annotation-popups/__tests__/JsonLdView.test.tsx +3 -5
  25. package/src/components/annotation-popups/__tests__/SharedPopupElements.test.tsx +0 -1
  26. package/src/components/branding/__tests__/SemiontBranding.test.tsx +1 -2
  27. package/src/components/layout/__tests__/LeftSidebar.test.tsx +5 -6
  28. package/src/components/layout/__tests__/PageLayout.test.tsx +1 -3
  29. package/src/components/layout/__tests__/SkipLinks.a11y.test.tsx +8 -8
  30. package/src/components/layout/__tests__/UnifiedHeader.test.tsx +12 -1
  31. package/src/components/modals/__tests__/KeyboardShortcutsHelpModal.test.tsx +0 -1
  32. package/src/components/modals/__tests__/PermissionDeniedModal.test.tsx +3 -4
  33. package/src/components/modals/__tests__/ResourceSearchModal.test.tsx +0 -1
  34. package/src/components/modals/__tests__/SearchModal.basic.test.tsx +1 -1
  35. package/src/components/modals/__tests__/SearchModal.keyboard.test.tsx +0 -5
  36. package/src/components/modals/__tests__/SearchModal.search-wiring.test.tsx +0 -1
  37. package/src/components/modals/__tests__/SearchModal.visual.test.tsx +2 -2
  38. package/src/components/modals/__tests__/SessionExpiredModal.test.tsx +0 -1
  39. package/src/components/navigation/NavigationMenu.tsx +1 -1
  40. package/src/components/navigation/__tests__/Footer.a11y.test.tsx +4 -0
  41. package/src/components/navigation/__tests__/Footer.test.tsx +3 -6
  42. package/src/components/navigation/__tests__/NavigationMenu.a11y.test.tsx +1 -1
  43. package/src/components/navigation/__tests__/NavigationMenu.test.tsx +7 -9
  44. package/src/components/navigation/__tests__/ObservableLink.test.tsx +0 -1
  45. package/src/components/navigation/__tests__/SimpleNavigation.test.tsx +1 -2
  46. package/src/components/navigation/__tests__/SortableResourceTab.test.tsx +0 -1
  47. package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +6 -4
  48. package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +10 -19
  49. package/src/components/resource/__tests__/BrowseView.test.tsx +8 -6
  50. package/src/components/resource/__tests__/HistoryEvent.test.tsx +0 -4
  51. package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +4 -6
  52. package/src/components/resource/panels/ReferencesPanel.tsx +1 -1
  53. package/src/components/resource/panels/__tests__/AssessmentEntry.test.tsx +3 -4
  54. package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +7 -6
  55. package/src/components/resource/panels/__tests__/AssistSection.test.tsx +14 -10
  56. package/src/components/resource/panels/__tests__/CollaborationPanel.test.tsx +0 -1
  57. package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +30 -17
  58. package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +6 -5
  59. package/src/components/resource/panels/__tests__/HighlightEntry.test.tsx +4 -5
  60. package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +19 -13
  61. package/src/components/resource/panels/__tests__/JsonLdPanel.test.tsx +1 -3
  62. package/src/components/resource/panels/__tests__/PanelHeader.test.tsx +0 -1
  63. package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +5 -5
  64. package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +40 -7
  65. package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +3 -3
  66. package/src/components/resource/panels/__tests__/StatisticsPanel.test.tsx +30 -32
  67. package/src/components/resource/panels/__tests__/TagEntry.test.tsx +5 -5
  68. package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +6 -5
  69. package/src/components/settings/__tests__/SettingsPanel.test.tsx +0 -1
  70. package/src/components/viewers/__tests__/ImageViewer.test.tsx +0 -1
  71. package/src/features/auth/__tests__/SignInForm.a11y.test.tsx +2 -0
  72. package/src/features/auth/__tests__/SignUpForm.a11y.test.tsx +11 -12
  73. package/src/features/auth/__tests__/SignUpForm.test.tsx +3 -3
  74. package/src/features/moderate-tag-schemas/components/TagSchemasPage.tsx +1 -0
  75. package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +2 -1
  76. package/src/features/resource-discovery/__tests__/ResourceCard.test.tsx +0 -1
  77. package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +33 -35
  78. package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +12 -11
  79. package/src/features/resource-discovery/state/__tests__/discover-state-unit.test.ts +204 -11
  80. package/src/features/resource-discovery/state/discover-state-unit.ts +70 -11
  81. package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +2 -2
  82. package/src/features/resource-viewer/components/ResourceViewerPage.tsx +3 -2
  83. package/src/features/resource-viewer/state/__tests__/resource-viewer-page-state-unit.test.ts +0 -1
  84. package/src/features/resource-viewer/state/resource-viewer-page-state-unit.ts +5 -2
  85. package/src/integrations/__tests__/css-modules-helper.test.tsx +2 -3
  86. package/src/integrations/__tests__/styled-components-theme.test.ts +1 -3
  87. package/dist/PdfAnnotationCanvas.client-CN3C3S55.js.map +0 -1
package/README.md CHANGED
@@ -18,8 +18,8 @@ Framework-agnostic React component library for building Semiont knowledge manage
18
18
 
19
19
  - **Framework Independence** - Zero dependencies on Next.js, next-auth, or next-intl
20
20
  - **Component Composition** - Components accept framework-specific implementations (Link, routing) as props
21
- - **Provider Pattern** - Consistent approach for session, translations, API client, and routing
22
- - **Type-Safe API Hooks** - React Query wrappers for all Semiont API operations
21
+ - **Provider Pattern** - Consistent approach for session, translations, and routing
22
+ - **SDK Live Queries** - Read data with `useObservable()` over the SDK's read-through cache; no bespoke fetching layer
23
23
  - **Authentication Components** - Sign-in, sign-up, and error display components
24
24
  - **Navigation Components** - Collapsible sidebar with drag & drop resource tabs
25
25
  - **Modal Components** - Global search and resource selection modals
@@ -33,17 +33,19 @@ Framework-agnostic React component library for building Semiont knowledge manage
33
33
  ## Installation
34
34
 
35
35
  ```bash
36
- npm install @semiont/react-ui @semiont/api-client @tanstack/react-query
36
+ npm install @semiont/react-ui @semiont/sdk @semiont/core
37
37
  ```
38
38
 
39
+ `@semiont/sdk` is the primary dependency — it provides the `SemiontBrowser`,
40
+ `SemiontClient`, the read-through cache, and the state machinery that
41
+ `@semiont/react-ui` renders. `@semiont/core` provides the shared API types.
42
+
39
43
  ### Peer Dependencies
40
44
 
41
45
  ```json
42
46
  {
43
- "react": "^18.0.0",
44
- "react-dom": "^18.0.0",
45
- "@tanstack/react-query": "^5.0.0",
46
- "@semiont/api-client": "*"
47
+ "react": "^19.0.0",
48
+ "react-dom": "^19.0.0"
47
49
  }
48
50
  ```
49
51
 
@@ -72,33 +74,28 @@ Import the styles in your app's main CSS file:
72
74
 
73
75
  ```tsx
74
76
  import {
77
+ SemiontProvider,
75
78
  TranslationProvider,
76
- ApiClientProvider,
77
- KnowledgeBaseSessionProvider,
78
79
  ProtectedErrorBoundary,
79
80
  SessionExpiredModal,
80
81
  PermissionDeniedModal,
81
82
  } from '@semiont/react-ui';
82
- import { QueryClientProvider } from '@tanstack/react-query';
83
83
 
84
84
  function App({ children }) {
85
85
  const translationManager = useTranslationManager(); // Your implementation
86
- const queryClient = new QueryClient();
87
86
 
88
87
  return (
89
88
  <TranslationProvider translationManager={translationManager}>
90
- <QueryClientProvider client={queryClient}>
91
- <ApiClientProvider baseUrl="https://api.example.com">
92
- {/* Mount KnowledgeBaseSessionProvider only on protected routes */}
93
- <KnowledgeBaseSessionProvider>
94
- <ProtectedErrorBoundary>
95
- <SessionExpiredModal />
96
- <PermissionDeniedModal />
97
- {children}
98
- </ProtectedErrorBoundary>
99
- </KnowledgeBaseSessionProvider>
100
- </ApiClientProvider>
101
- </QueryClientProvider>
89
+ {/* SemiontProvider puts the SemiontBrowser singleton into context.
90
+ useSemiont() hands it back; the active SemiontSession (and its
91
+ SemiontClient) flow from there. */}
92
+ <SemiontProvider>
93
+ <ProtectedErrorBoundary>
94
+ <SessionExpiredModal />
95
+ <PermissionDeniedModal />
96
+ {children}
97
+ </ProtectedErrorBoundary>
98
+ </SemiontProvider>
102
99
  </TranslationProvider>
103
100
  );
104
101
  }
@@ -107,15 +104,16 @@ function App({ children }) {
107
104
  ### 2. Use Components
108
105
 
109
106
  ```tsx
110
- import { ResourceViewer, useResources } from '@semiont/react-ui';
107
+ import { ResourceViewer, useSemiont, useObservable } from '@semiont/react-ui';
111
108
 
112
109
  function MyComponent() {
113
- const resources = useResources();
114
- const { data, isLoading } = resources.list.useQuery();
110
+ const semiont = useObservable(useSemiont().activeSession$)?.client;
111
+ // browse.resources() is an SDK live query backed by the read-through cache.
112
+ const resources = useObservable(semiont?.browse.resources({ limit: 20 }));
115
113
 
116
- if (isLoading) return <div>Loading...</div>;
114
+ if (!resources) return <div>Loading...</div>;
117
115
 
118
- return <ResourceViewer resource={data[0]} />;
116
+ return <ResourceViewer resource={resources[0]} />;
119
117
  }
120
118
  ```
121
119
 
@@ -191,12 +189,10 @@ See [docs/STYLES.md](docs/STYLES.md) for detailed CSS documentation.
191
189
 
192
190
  ### Providers
193
191
 
194
- All cross-cutting concerns use the Provider Pattern:
192
+ Cross-cutting concerns use the Provider Pattern:
195
193
 
196
- - **KnowledgeBaseSessionProvider** - KB list, active KB, validated session, modal state one merged provider that's the single source of truth for "which KB and what's the session against it"
194
+ - **SemiontProvider** - The single React provider for session state. Puts the module-scoped `SemiontBrowser` singleton into context; `useSemiont()` returns it. The browser owns the KB list, active KB, and validated session lifecycleit's the single source of truth for "which KB and what's the session against it." The active `SemiontSession` (and its `SemiontClient`) flow from `browser.activeSession$`.
197
195
  - **TranslationProvider** - Internationalization
198
- - **ApiClientProvider** - Authenticated API client
199
- - **OpenResourcesProvider** - Recently opened resources
200
196
  - **RoutingContext** - Framework-agnostic navigation
201
197
 
202
198
  See [docs/SESSION.md](docs/SESSION.md) for details.
@@ -216,18 +212,24 @@ Use them via `import { createComposePageStateUnit } from '@semiont/react-ui'`. U
216
212
 
217
213
  ### API Integration
218
214
 
219
- React Query hooks for all API operations:
215
+ Data fetching and caching are an SDK concern. The SDK exposes an RxJS
216
+ read-through cache (stale-while-revalidate); react-ui consumes it as live
217
+ queries via `useObservable()`. Reads come from `client.browse.*(...)`
218
+ observables; writes go through the typed namespaces (`client.mark.*`,
219
+ `client.bind.*`, etc.).
220
220
 
221
221
  ```tsx
222
- import { useResources, useAnnotations, useEntityTypes } from '@semiont/react-ui';
222
+ import { useSemiont, useObservable } from '@semiont/react-ui';
223
+
224
+ function Example() {
225
+ const client = useObservable(useSemiont().activeSession$)?.client;
223
226
 
224
- // List resources
225
- const resources = useResources();
226
- const { data } = resources.list.useQuery({ limit: 10 });
227
+ // Read: live query over the read-through cache
228
+ const resources = useObservable(client?.browse.resources({ limit: 10 }));
227
229
 
228
- // Create annotation
229
- const annotations = useAnnotations();
230
- const { mutate } = annotations.create.useMutation();
230
+ // Write: typed namespace call (cache invalidation is handled by the SDK)
231
+ const archive = (rUri) => client?.mark.archive(rUri);
232
+ }
231
233
  ```
232
234
 
233
235
  See [docs/API-INTEGRATION.md](docs/API-INTEGRATION.md) for details.
@@ -271,10 +273,8 @@ See [docs/TESTING.md](docs/TESTING.md) for details.
271
273
  // app/providers.tsx
272
274
  'use client';
273
275
 
274
- import { useSession } from 'next-auth/react';
275
276
  import { useLocale } from 'next-intl';
276
- import { SemiontApiClient } from '@semiont/api-client';
277
- import { TranslationProvider, ApiClientProvider } from '@semiont/react-ui';
277
+ import { TranslationProvider, SemiontProvider } from '@semiont/react-ui';
278
278
 
279
279
  export function useTranslationManager() {
280
280
  const locale = useLocale();
@@ -285,17 +285,19 @@ export function useTranslationManager() {
285
285
  };
286
286
  }
287
287
 
288
- export function useApiClientManager() {
289
- const { data: session } = useSession();
290
- const [token$] = useState(() => new BehaviorSubject<AccessToken | null>(null));
291
-
292
- useEffect(() => {
293
- token$.next(session?.backendToken ? accessToken(session.backendToken) : null);
294
- }, [session?.backendToken, token$]);
288
+ export function Providers({ children }) {
289
+ const translationManager = useTranslationManager();
295
290
 
296
- return {
297
- client: new SemiontApiClient({ baseUrl, eventBus, token$ }),
298
- };
291
+ // SemiontProvider defaults to the canonical web setup (WebBrowserStorage +
292
+ // an HTTP session factory). It reads the module-scoped SemiontBrowser
293
+ // singleton via getBrowser() — no client construction here. Sign-in flows
294
+ // adopt the OAuth session onto the browser; browser.activeSession$ then
295
+ // carries the live SemiontClient.
296
+ return (
297
+ <TranslationProvider translationManager={translationManager}>
298
+ <SemiontProvider>{children}</SemiontProvider>
299
+ </TranslationProvider>
300
+ );
299
301
  }
300
302
  ```
301
303
 
@@ -350,7 +352,7 @@ This library follows strict quality standards:
350
352
  - **No cruft** - Delete dead code immediately, no "legacy" patterns
351
353
  - **Direct fixes** - If something is wrong, fix it directly (no aliases/wrappers)
352
354
 
353
- See [CLAUDE.md](../../CLAUDE.md) in the repository root for full guidelines.
355
+ See [CONTRIBUTING.md](../../CONTRIBUTING.md) in the repository root for full guidelines.
354
356
 
355
357
  ## License
356
358
 
@@ -358,5 +360,7 @@ See [CLAUDE.md](../../CLAUDE.md) in the repository root for full guidelines.
358
360
 
359
361
  ## Related Packages
360
362
 
361
- - [@semiont/api-client](../api-client) - TypeScript client for Semiont API
363
+ - [@semiont/sdk](../sdk) - `SemiontBrowser`, `SemiontClient`, the read-through cache, and the state machinery
364
+ - [@semiont/core](../core) - Shared API types (`components`) generated from the OpenAPI spec
365
+ - [@semiont/api-client](../api-client) - HTTP transport (`HttpTransport`, `HttpContentTransport`, `APIError`)
362
366
  - [semiont-frontend](../../apps/frontend) - Reference Next.js implementation
@@ -4,7 +4,12 @@ import "./chunk-K6BJDL2I.js";
4
4
 
5
5
  // src/components/pdf-annotation/PdfAnnotationCanvas.tsx
6
6
  import { useRef, useState, useCallback, useEffect, useMemo } from "react";
7
- import { getTargetSelector } from "@semiont/core";
7
+ import {
8
+ getTargetSelector,
9
+ createFragmentSelector,
10
+ parseFragmentSelector,
11
+ getPageFromFragment
12
+ } from "@semiont/core";
8
13
  import { createHoverHandlers } from "@semiont/sdk";
9
14
 
10
15
  // src/lib/pdf-coordinates.ts
@@ -37,31 +42,6 @@ function pdfToCanvasCoordinates(pdfCoord, pageHeight, scale = 1) {
37
42
  height: canvasHeight
38
43
  };
39
44
  }
40
- function createFragmentSelector(coord) {
41
- return `page=${coord.page}&viewrect=${coord.x},${coord.y},${coord.width},${coord.height}`;
42
- }
43
- function parseFragmentSelector(fragment) {
44
- try {
45
- const pageMatch = fragment.match(/page=(\d+)/);
46
- if (!pageMatch) return null;
47
- const page = parseInt(pageMatch[1], 10);
48
- const viewrectMatch = fragment.match(/viewrect=([\d.]+),([\d.]+),([\d.]+),([\d.]+)/);
49
- if (!viewrectMatch) return null;
50
- return {
51
- page,
52
- x: parseFloat(viewrectMatch[1]),
53
- y: parseFloat(viewrectMatch[2]),
54
- width: parseFloat(viewrectMatch[3]),
55
- height: parseFloat(viewrectMatch[4])
56
- };
57
- } catch {
58
- return null;
59
- }
60
- }
61
- function getPageFromFragment(fragment) {
62
- const match = fragment.match(/page=(\d+)/);
63
- return match ? parseInt(match[1], 10) : null;
64
- }
65
45
 
66
46
  // src/lib/browser-pdfjs.ts
67
47
  async function ensurePdfJs() {
@@ -484,4 +464,4 @@ function PdfAnnotationCanvas({
484
464
  export {
485
465
  PdfAnnotationCanvas
486
466
  };
487
- //# sourceMappingURL=PdfAnnotationCanvas.client-CN3C3S55.js.map
467
+ //# sourceMappingURL=PdfAnnotationCanvas.client-NIMALXNZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/pdf-annotation/PdfAnnotationCanvas.tsx","../src/lib/pdf-coordinates.ts","../src/lib/browser-pdfjs.ts"],"sourcesContent":["'use client';\n\nimport React, { useRef, useState, useCallback, useEffect, useMemo } from 'react';\nimport type { Annotation } from '@semiont/core';\nimport {\n getTargetSelector,\n createFragmentSelector,\n parseFragmentSelector,\n getPageFromFragment,\n} from '@semiont/core';\nimport { createHoverHandlers, type SemiontSession } from '@semiont/sdk';\nimport type { SelectionMotivation } from '../annotation/AnnotateToolbar';\nimport {\n canvasToPdfCoordinates,\n pdfToCanvasCoordinates,\n type CanvasRectangle\n} from '../../lib/pdf-coordinates';\nimport {\n loadPdfDocument,\n renderPdfPageToDataUrl,\n type PDFDocumentProxy\n} from '../../lib/browser-pdfjs';\nimport './PdfAnnotationCanvas.css';\n\nexport type DrawingMode = 'rectangle' | 'circle' | 'polygon' | null;\n\n/**\n * Get color for annotation based on motivation\n */\nfunction getMotivationColor(motivation: SelectionMotivation | null): { stroke: string; fill: string } {\n if (!motivation) {\n return { stroke: 'rgb(156, 163, 175)', fill: 'rgba(156, 163, 175, 0.2)' };\n }\n\n switch (motivation) {\n case 'highlighting':\n return { stroke: 'rgb(250, 204, 21)', fill: 'rgba(250, 204, 21, 0.3)' };\n case 'linking':\n return { stroke: 'rgb(59, 130, 246)', fill: 'rgba(59, 130, 246, 0.2)' };\n case 'assessing':\n return { stroke: 'rgb(239, 68, 68)', fill: 'rgba(239, 68, 68, 0.2)' };\n case 'commenting':\n return { stroke: 'rgb(255, 255, 255)', fill: 'rgba(255, 255, 255, 0.2)' };\n default:\n return { stroke: 'rgb(156, 163, 175)', fill: 'rgba(156, 163, 175, 0.2)' };\n }\n}\n\ninterface PdfAnnotationCanvasProps {\n pdfUrl: string;\n existingAnnotations?: Annotation[];\n drawingMode: DrawingMode;\n selectedMotivation?: SelectionMotivation | null;\n session?: SemiontSession | null | undefined;\n hoveredAnnotationId?: string | null;\n selectedAnnotationId?: string | null;\n hoverDelayMs?: number;\n}\n\n/**\n * PDF annotation canvas with page navigation and rectangle drawing\n *\n * @emits browse:click - Annotation clicked on PDF. Payload: { annotationId: string, motivation: Motivation }\n * @emits mark:requested - New annotation drawn on PDF. Payload: { selector: FragmentSelector, motivation: SelectionMotivation }\n * @emits beckon:hover - Annotation hovered or unhovered. Payload: { annotationId: string | null }\n */\nexport function PdfAnnotationCanvas({\n pdfUrl,\n existingAnnotations = [],\n drawingMode,\n selectedMotivation,\n session,\n hoveredAnnotationId,\n selectedAnnotationId,\n hoverDelayMs = 150\n}: PdfAnnotationCanvasProps) {\n // PDF state\n const [pdfDoc, setPdfDoc] = useState<PDFDocumentProxy | null>(null);\n const [numPages, setNumPages] = useState<number>(0);\n const [pageNumber, setPageNumber] = useState(1);\n const [pageImageUrl, setPageImageUrl] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [pageDimensions, setPageDimensions] = useState<{ width: number; height: number } | null>(null);\n const [displayDimensions, setDisplayDimensions] = useState<{ width: number; height: number } | null>(null);\n const [scale] = useState(1.5); // Fixed scale for better quality\n\n // Drawing state\n const [isDrawing, setIsDrawing] = useState(false);\n const [selection, setSelection] = useState<CanvasRectangle | null>(null);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const imageRef = useRef<HTMLImageElement>(null);\n\n // Load PDF document on mount\n useEffect(() => {\n let cancelled = false;\n\n async function loadPdf() {\n try {\n setIsLoading(true);\n setError(null);\n\n const doc = await loadPdfDocument(pdfUrl);\n\n if (cancelled) return;\n\n setPdfDoc(doc);\n setNumPages(doc.numPages);\n setIsLoading(false);\n } catch (err) {\n if (cancelled) return;\n\n console.error('Error loading PDF:', err);\n setError('Failed to load PDF');\n setIsLoading(false);\n }\n }\n\n loadPdf();\n\n return () => {\n cancelled = true;\n };\n }, [pdfUrl]);\n\n // Load current page when page number changes\n useEffect(() => {\n if (!pdfDoc) return;\n\n let cancelled = false;\n const doc = pdfDoc;\n\n async function loadPage() {\n try {\n const page = await doc.getPage(pageNumber);\n\n if (cancelled) return;\n\n // Get page dimensions (at scale 1.0)\n const viewport = page.getViewport({ scale: 1.0 });\n setPageDimensions({\n width: viewport.width,\n height: viewport.height\n });\n\n // Render page to image\n const { dataUrl } = await renderPdfPageToDataUrl(page, scale);\n\n if (cancelled) return;\n\n setPageImageUrl(dataUrl);\n } catch (err) {\n if (cancelled) return;\n\n console.error('Error loading page:', err);\n setError('Failed to load page');\n }\n }\n\n loadPage();\n\n return () => {\n cancelled = true;\n };\n }, [pdfDoc, pageNumber, scale]);\n\n // Update display dimensions on resize\n useEffect(() => {\n const updateDisplayDimensions = () => {\n if (imageRef.current) {\n setDisplayDimensions({\n width: imageRef.current.clientWidth,\n height: imageRef.current.clientHeight\n });\n }\n };\n\n updateDisplayDimensions();\n\n // Use ResizeObserver to detect image element size changes\n // This catches: sidebar open/close, window resize, font size changes, etc.\n let resizeObserver: ResizeObserver | null = null;\n\n try {\n resizeObserver = new ResizeObserver(updateDisplayDimensions);\n if (imageRef.current) {\n resizeObserver.observe(imageRef.current);\n }\n } catch (error) {\n // Fallback for browsers without ResizeObserver support\n console.warn('ResizeObserver not supported, falling back to window resize listener');\n window.addEventListener('resize', updateDisplayDimensions);\n }\n\n return () => {\n if (resizeObserver) {\n resizeObserver.disconnect();\n } else {\n window.removeEventListener('resize', updateDisplayDimensions);\n }\n };\n }, [pageImageUrl]);\n\n // Mouse event handlers for drawing\n const handleMouseDown = useCallback((e: React.MouseEvent) => {\n if (!drawingMode) return;\n if (!imageRef.current) return;\n\n const rect = imageRef.current.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n\n // Clear any previous selection when starting new drawing\n setIsDrawing(true);\n setSelection({\n startX: x,\n startY: y,\n endX: x,\n endY: y\n });\n }, [drawingMode]);\n\n const handleMouseMove = useCallback((e: React.MouseEvent) => {\n if (!isDrawing || !selection || !imageRef.current) return;\n\n const rect = imageRef.current.getBoundingClientRect();\n\n setSelection({\n ...selection,\n endX: e.clientX - rect.left,\n endY: e.clientY - rect.top\n });\n }, [isDrawing, selection]);\n\n const handleMouseUp = useCallback(() => {\n if (!isDrawing || !selection || !pageDimensions || !displayDimensions || !session) {\n setIsDrawing(false);\n setSelection(null);\n return;\n }\n\n // Calculate drag distance\n const dragDistance = Math.sqrt(\n Math.pow(selection.endX - selection.startX, 2) +\n Math.pow(selection.endY - selection.startY, 2)\n );\n\n // Minimum drag threshold in pixels (10px)\n const MIN_DRAG_DISTANCE = 10;\n\n if (dragDistance < MIN_DRAG_DISTANCE) {\n // This was a click, not a drag - check if we clicked an existing annotation\n if (existingAnnotations.length > 0) {\n const clickedAnnotation = pageAnnotations.find(ann => {\n const fragmentSel = getFragmentSelector(ann.target);\n if (!fragmentSel) return false;\n\n const pdfCoord = parseFragmentSelector(fragmentSel.value);\n if (!pdfCoord) return false;\n\n const rect = pdfToCanvasCoordinates(pdfCoord, pageDimensions.height, 1.0);\n\n // Scale to display coordinates\n const scaleX = displayDimensions.width / pageDimensions.width;\n const scaleY = displayDimensions.height / pageDimensions.height;\n\n const displayX = rect.x * scaleX;\n const displayY = rect.y * scaleY;\n const displayWidth = rect.width * scaleX;\n const displayHeight = rect.height * scaleY;\n\n return (\n selection.endX >= displayX &&\n selection.endX <= displayX + displayWidth &&\n selection.endY >= displayY &&\n selection.endY <= displayY + displayHeight\n );\n });\n\n if (clickedAnnotation) {\n session?.client.browse.click(clickedAnnotation.id, clickedAnnotation.motivation);\n setIsDrawing(false);\n setSelection(null);\n return;\n }\n }\n\n // Click on empty space - do nothing\n setIsDrawing(false);\n setSelection(null);\n return;\n }\n\n // This was a drag - create new annotation\n // Scale selection from display coordinates to native page coordinates\n const scaleX = pageDimensions.width / displayDimensions.width;\n const scaleY = pageDimensions.height / displayDimensions.height;\n\n const nativeSelection: CanvasRectangle = {\n startX: selection.startX * scaleX,\n startY: selection.startY * scaleY,\n endX: selection.endX * scaleX,\n endY: selection.endY * scaleY\n };\n\n // Convert canvas coordinates to PDF coordinates\n const pdfCoord = canvasToPdfCoordinates(\n nativeSelection,\n pageNumber,\n pageDimensions.width,\n pageDimensions.height,\n 1.0 // Use scale 1.0 since we already scaled to native coords\n );\n\n // Create FragmentSelector\n const fragmentSelector = createFragmentSelector(pdfCoord);\n\n // Emit annotation:requested event with FragmentSelector\n if (selectedMotivation) {\n session.client.mark.request(\n {\n type: 'FragmentSelector',\n conformsTo: 'http://tools.ietf.org/rfc/rfc3778',\n value: fragmentSelector,\n },\n selectedMotivation,\n );\n }\n\n // Keep drawing state active to show preview until annotation is persisted\n // The parent component should clear this by changing drawingMode after save\n setIsDrawing(false);\n // Note: We keep selection so the preview remains visible\n // It will be cleared when drawingMode changes or user starts new selection\n }, [isDrawing, selection, pageNumber, pageDimensions, displayDimensions, selectedMotivation, existingAnnotations]);\n\n // Helper to get FragmentSelector from annotation target\n const getFragmentSelector = (target: Annotation['target']) => {\n const selector = getTargetSelector(target);\n if (!selector) return null;\n const selectors = Array.isArray(selector) ? selector : [selector];\n\n const found = selectors.find(s => s.type === 'FragmentSelector');\n if (!found || found.type !== 'FragmentSelector') return null;\n return found as { type: 'FragmentSelector'; value: string; conformsTo?: string };\n };\n\n // Filter annotations for current page\n const pageAnnotations = existingAnnotations.filter(ann => {\n const fragmentSel = getFragmentSelector(ann.target);\n if (!fragmentSel) return false;\n const page = getPageFromFragment(fragmentSel.value);\n return page === pageNumber;\n });\n\n // Hover handlers with currentHover guard and dwell delay\n const { handleMouseEnter, handleMouseLeave } = useMemo(\n () => createHoverHandlers((id) => session?.client.beckon.hover(id), hoverDelayMs),\n [session, hoverDelayMs]\n );\n\n // Calculate motivation color\n const { stroke, fill } = getMotivationColor(selectedMotivation ?? null);\n\n if (error) {\n return <div className=\"semiont-pdf-annotation-canvas__error\">{error}</div>;\n }\n\n return (\n <div className=\"semiont-pdf-annotation-canvas\">\n {isLoading && <div className=\"semiont-pdf-annotation-canvas__loading\">Loading PDF...</div>}\n\n <div\n ref={containerRef}\n className=\"semiont-pdf-annotation-canvas__container\"\n style={{ display: isLoading ? 'none' : undefined }}\n onMouseDown={handleMouseDown}\n onMouseMove={handleMouseMove}\n onMouseUp={handleMouseUp}\n onMouseLeave={() => {\n if (isDrawing) {\n setIsDrawing(false);\n setSelection(null);\n }\n }}\n data-drawing-mode={drawingMode || 'none'}\n >\n {/* PDF page rendered as image */}\n {pageImageUrl && (\n <img\n ref={imageRef}\n src={pageImageUrl}\n alt={`PDF page ${pageNumber}`}\n className=\"semiont-pdf-annotation-canvas__image\"\n draggable={false}\n style={{ pointerEvents: 'none' }}\n onLoad={() => {\n // Use double RAF to ensure layout is complete even in onLoad\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n if (imageRef.current) {\n setDisplayDimensions({\n width: imageRef.current.clientWidth,\n height: imageRef.current.clientHeight\n });\n }\n });\n });\n }}\n />\n )}\n\n {/* SVG overlay for annotations */}\n {displayDimensions && pageDimensions && (\n <div className=\"semiont-pdf-annotation-canvas__overlay-container\">\n <div className=\"semiont-pdf-annotation-canvas__overlay\">\n <svg\n className=\"semiont-pdf-annotation-canvas__svg\"\n width={displayDimensions.width}\n height={displayDimensions.height}\n >\n {/* Render existing annotations for this page */}\n {pageAnnotations.map(ann => {\n const fragmentSel = getFragmentSelector(ann.target);\n if (!fragmentSel) return null;\n\n const pdfCoord = parseFragmentSelector(fragmentSel.value);\n if (!pdfCoord) return null;\n\n const rect = pdfToCanvasCoordinates(pdfCoord, pageDimensions.height, 1.0);\n\n // Scale to display coordinates\n const scaleX = displayDimensions.width / pageDimensions.width;\n const scaleY = displayDimensions.height / pageDimensions.height;\n\n const isHovered = ann.id === hoveredAnnotationId;\n const isSelected = ann.id === selectedAnnotationId;\n\n // Get color for this annotation's motivation (not the selected motivation)\n const annMotivation = ann.motivation as SelectionMotivation | null;\n const { stroke: annStroke, fill: annFill } = getMotivationColor(annMotivation);\n\n return (\n <rect\n key={ann.id}\n x={rect.x * scaleX}\n y={rect.y * scaleY}\n width={rect.width * scaleX}\n height={rect.height * scaleY}\n stroke={annStroke}\n strokeWidth={isSelected ? 4 : isHovered ? 3 : 2}\n fill={annFill}\n style={{\n pointerEvents: 'auto',\n cursor: 'pointer',\n opacity: isSelected ? 1 : isHovered ? 0.9 : 0.7\n }}\n onClick={() => session?.client.browse.click(ann.id, ann.motivation)}\n onMouseEnter={() => handleMouseEnter(ann.id)}\n onMouseLeave={handleMouseLeave}\n />\n );\n })}\n\n {/* Render current selection while drawing or awaiting save */}\n {selection && (() => {\n const rectX = Math.min(selection.startX, selection.endX);\n const rectY = Math.min(selection.startY, selection.endY);\n const rectWidth = Math.abs(selection.endX - selection.startX);\n const rectHeight = Math.abs(selection.endY - selection.startY);\n\n // PDF only supports rectangle shapes (FragmentSelector with viewrect)\n // Circle/polygon are disabled in the UI for PDF media types\n return (\n <rect\n x={rectX}\n y={rectY}\n width={rectWidth}\n height={rectHeight}\n stroke={stroke}\n strokeWidth={2}\n strokeDasharray=\"5,5\"\n fill={fill}\n pointerEvents=\"none\"\n />\n );\n })()}\n </svg>\n </div>\n </div>\n )}\n </div>\n\n {/* Page navigation controls */}\n {numPages > 0 && (\n <div className=\"semiont-pdf-annotation-canvas__controls\">\n <button\n disabled={pageNumber <= 1}\n onClick={() => setPageNumber(pageNumber - 1)}\n className=\"semiont-pdf-annotation-canvas__button\"\n >\n Previous\n </button>\n <span className=\"semiont-pdf-annotation-canvas__page-info\">\n Page {pageNumber} of {numPages}\n </span>\n <button\n disabled={pageNumber >= numPages}\n onClick={() => setPageNumber(pageNumber + 1)}\n className=\"semiont-pdf-annotation-canvas__button\"\n >\n Next\n </button>\n </div>\n )}\n </div>\n );\n}\n","/**\n * PDF Canvas Coordinate Transforms\n *\n * Converts between canvas space (pixels, top-left origin, Y increases downward)\n * and PDF space (points, bottom-left origin, Y increases upward) — the Y-flip and\n * scale. UI-only: the server has no canvas.\n *\n * `PdfCoordinate` and the viewrect FragmentSelector codec live in `@semiont/core`.\n *\n * Based on RFC 3778 PDF Fragment Identifiers:\n * https://tools.ietf.org/html/rfc3778\n */\n\nimport type { PdfCoordinate } from '@semiont/core';\n\nexport interface Rectangle {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface CanvasRectangle {\n startX: number;\n startY: number;\n endX: number;\n endY: number;\n}\n\n/**\n * Convert canvas coordinates to PDF coordinates\n *\n * Canvas: Origin at top-left, Y increases downward\n * PDF: Origin at bottom-left, Y increases upward\n *\n * @param canvasRect - Rectangle in canvas pixel coordinates\n * @param page - PDF page number (1-indexed)\n * @param pageWidth - PDF page width in points\n * @param pageHeight - PDF page height in points\n * @param scale - Current canvas scale factor\n */\nexport function canvasToPdfCoordinates(\n canvasRect: CanvasRectangle,\n page: number,\n _pageWidth: number,\n pageHeight: number,\n scale: number = 1\n): PdfCoordinate {\n // Normalize rectangle (handle drag in any direction)\n const x1 = Math.min(canvasRect.startX, canvasRect.endX);\n const y1 = Math.min(canvasRect.startY, canvasRect.endY);\n const x2 = Math.max(canvasRect.startX, canvasRect.endX);\n const y2 = Math.max(canvasRect.startY, canvasRect.endY);\n\n // Convert from canvas pixels to PDF points\n const pdfX = x1 / scale;\n const pdfWidth = (x2 - x1) / scale;\n\n // Flip Y coordinate (canvas top-left to PDF bottom-left)\n const pdfY = pageHeight - (y2 / scale);\n const pdfHeight = (y2 - y1) / scale;\n\n return {\n page,\n x: Math.round(pdfX),\n y: Math.round(pdfY),\n width: Math.round(pdfWidth),\n height: Math.round(pdfHeight)\n };\n}\n\n/**\n * Convert PDF coordinates to canvas coordinates\n *\n * @param pdfCoord - Coordinates in PDF space\n * @param pageHeight - PDF page height in points\n * @param scale - Current canvas scale factor\n */\nexport function pdfToCanvasCoordinates(\n pdfCoord: PdfCoordinate,\n pageHeight: number,\n scale: number = 1\n): Rectangle {\n // Convert from PDF points to canvas pixels\n const canvasX = pdfCoord.x * scale;\n const canvasWidth = pdfCoord.width * scale;\n\n // Flip Y coordinate (PDF bottom-left to canvas top-left)\n const canvasY = (pageHeight - pdfCoord.y - pdfCoord.height) * scale;\n const canvasHeight = pdfCoord.height * scale;\n\n return {\n x: canvasX,\n y: canvasY,\n width: canvasWidth,\n height: canvasHeight\n };\n}\n","/**\n * Browser PDF.js utilities\n *\n * Uses native browser PDF.js when available, falls back to CDN.\n * Zero npm dependencies - no webpack bundling issues.\n */\n\n// Type definitions for PDF.js API\nexport interface PDFDocumentProxy {\n numPages: number;\n getPage(pageNumber: number): Promise<PDFPageProxy>;\n}\n\nexport interface PDFPageProxy {\n getViewport(params: { scale: number; rotation?: number }): PDFViewport;\n render(params: PDFRenderParams): PDFRenderTask;\n getTextContent(): Promise<TextContent>;\n}\n\nexport interface PDFViewport {\n width: number;\n height: number;\n scale: number;\n rotation: number;\n}\n\nexport interface PDFRenderParams {\n canvasContext: CanvasRenderingContext2D;\n viewport: PDFViewport;\n}\n\nexport interface PDFRenderTask {\n promise: Promise<void>;\n cancel(): void;\n}\n\nexport interface PDFLib {\n getDocument(params: { url: string }): PDFLoadingTask;\n GlobalWorkerOptions: {\n workerSrc: string;\n };\n version: string;\n}\n\nexport interface PDFLoadingTask {\n promise: Promise<PDFDocumentProxy>;\n destroy(): void;\n}\n\n/**\n * Text content types (for Phase 2)\n */\nexport interface TextItem {\n str: string;\n dir: string;\n transform: number[]; // [scaleX, skewX, skewY, scaleY, x, y]\n width: number;\n height: number;\n fontName: string;\n hasEOL: boolean;\n}\n\nexport interface TextContent {\n items: TextItem[];\n styles: Record<string, any>;\n}\n\n/**\n * Ensure PDF.js is available, loading from local public folder if needed\n */\nexport async function ensurePdfJs(): Promise<PDFLib> {\n // Check if already available (browser native or already loaded)\n if (typeof window !== 'undefined' && (window as any).pdfjsLib) {\n return (window as any).pdfjsLib as PDFLib;\n }\n\n // Load from local public folder (staged during build)\n return new Promise((resolve, reject) => {\n const script = document.createElement('script');\n script.src = '/pdfjs/pdf.min.mjs';\n script.type = 'module';\n\n script.onload = () => {\n const pdfjsLib = (window as any).pdfjsLib as PDFLib;\n\n if (!pdfjsLib) {\n reject(new Error('PDF.js loaded but pdfjsLib not available'));\n return;\n }\n\n // Configure worker (also served from local public folder)\n pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdfjs/pdf.worker.min.mjs';\n\n resolve(pdfjsLib);\n };\n\n script.onerror = () => {\n reject(new Error('Failed to load PDF.js from /pdfjs/pdf.min.mjs'));\n };\n\n document.head.appendChild(script);\n });\n}\n\n/**\n * Load PDF document from a URL.\n *\n * The URL must include authentication (e.g. ?token=<media-token>).\n * PDF.js streams the document directly — no ArrayBuffer buffering in JS.\n */\nexport async function loadPdfDocument(url: string): Promise<PDFDocumentProxy> {\n const pdfjsLib = await ensurePdfJs();\n const loadingTask = pdfjsLib.getDocument({ url });\n return loadingTask.promise;\n}\n\n/**\n * Render PDF page to canvas and return as data URL\n */\nexport async function renderPdfPageToDataUrl(\n page: PDFPageProxy,\n scale = 1.0\n): Promise<{ dataUrl: string; width: number; height: number }> {\n const viewport = page.getViewport({ scale });\n\n // Create canvas\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d');\n if (!context) {\n throw new Error('Failed to get 2D context');\n }\n\n canvas.width = viewport.width;\n canvas.height = viewport.height;\n\n // Render PDF page to canvas\n const renderTask = page.render({\n canvasContext: context,\n viewport: viewport\n });\n\n await renderTask.promise;\n\n // Convert to data URL\n return {\n dataUrl: canvas.toDataURL('image/png'),\n width: viewport.width,\n height: viewport.height\n };\n}\n\n/**\n * Render PDF page with text content extraction (Phase 2)\n *\n * This function extracts text in parallel with rendering for future\n * text layer support. Currently the text content is available but not used.\n */\nexport async function renderPdfPageWithText(\n page: PDFPageProxy,\n scale = 1.0\n): Promise<{\n dataUrl: string;\n width: number;\n height: number;\n textContent: TextContent;\n}> {\n const viewport = page.getViewport({ scale });\n\n // Create canvas for rendering\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d');\n if (!context) {\n throw new Error('Failed to get 2D context');\n }\n\n canvas.width = viewport.width;\n canvas.height = viewport.height;\n\n // Render PDF page to canvas\n const renderTask = page.render({\n canvasContext: context,\n viewport: viewport\n });\n\n // Extract text content in parallel (for future text layer support)\n const [, textContent] = await Promise.all([\n renderTask.promise,\n page.getTextContent()\n ]);\n\n // Convert to data URL\n return {\n dataUrl: canvas.toDataURL('image/png'),\n width: viewport.width,\n height: viewport.height,\n textContent\n };\n}\n"],"mappings":"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","names":["error","pdfCoord","scaleX","scaleY"]}
package/dist/index.css CHANGED
@@ -447,6 +447,12 @@
447
447
  z-index: 10;
448
448
  position: relative;
449
449
  }
450
+ .annotation-low-confidence {
451
+ -webkit-text-decoration: underline dotted var(--semiont-color-warning-500, #d97706);
452
+ text-decoration: underline dotted var(--semiont-color-warning-500, #d97706);
453
+ text-decoration-thickness: 2px;
454
+ text-underline-offset: 2px;
455
+ }
450
456
  @media print {
451
457
  .annotation-highlight,
452
458
  .annotation-comment,