@semiont/react-ui 0.5.11 → 0.5.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{PdfAnnotationCanvas.client-KWQ7XDWT.js → PdfAnnotationCanvas.client-75GY2EDR.js} +12 -6
- package/dist/PdfAnnotationCanvas.client-75GY2EDR.js.map +1 -0
- package/dist/{chunk-O2MD7TGE.js → chunk-VXASXU4K.js} +10 -2
- package/dist/chunk-VXASXU4K.js.map +1 -0
- package/dist/index.d.ts +54 -16
- package/dist/index.js +395 -343
- package/dist/index.js.map +1 -1
- package/dist/test-utils.d.ts +5 -1
- package/dist/test-utils.js +3 -2
- package/dist/test-utils.js.map +1 -1
- package/package.json +5 -5
- package/src/components/image-annotation/AnnotationOverlay.tsx +6 -6
- package/src/components/image-annotation/SvgDrawingCanvas.tsx +12 -4
- package/src/components/image-annotation/__tests__/AnnotationOverlay.click.test.tsx +137 -0
- package/src/components/image-annotation/__tests__/SvgDrawingCanvas.click.test.tsx +108 -0
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +12 -4
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +137 -1
- package/src/components/resource/BrowseView.tsx +14 -6
- package/src/components/resource/ResourceViewer.tsx +6 -4
- package/src/components/resource/__tests__/BrowseView.test.tsx +98 -31
- package/src/components/resource/__tests__/ResourceViewer.embeddable.test.tsx +40 -1
- package/src/components/resource/__tests__/browse-renderers.dispatch.test.tsx +113 -0
- package/src/components/resource/browse-renderers.tsx +26 -4
- package/src/components/resource/panels/AssessmentEntry.tsx +5 -4
- package/src/components/resource/panels/AssessmentPanel.tsx +8 -5
- package/src/components/resource/panels/AssistSection.tsx +4 -3
- package/src/components/resource/panels/CommentEntry.tsx +5 -4
- package/src/components/resource/panels/CommentsPanel.tsx +8 -5
- package/src/components/resource/panels/HighlightEntry.tsx +5 -4
- package/src/components/resource/panels/HighlightPanel.tsx +8 -5
- package/src/components/resource/panels/ReferenceEntry.tsx +9 -10
- package/src/components/resource/panels/ReferencesPanel.tsx +11 -6
- package/src/components/resource/panels/ResourceInfoPanel.tsx +4 -3
- package/src/components/resource/panels/TagEntry.tsx +5 -3
- package/src/components/resource/panels/TaggingPanel.tsx +7 -4
- package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +10 -0
- package/src/components/resource/panels/__tests__/AssessmentEntry.test.tsx +28 -20
- package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +38 -28
- package/src/components/resource/panels/__tests__/AssistSection.test.tsx +46 -17
- package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +65 -57
- package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +53 -24
- package/src/components/resource/panels/__tests__/HighlightEntry.test.tsx +27 -17
- package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +23 -17
- package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +48 -77
- package/src/components/resource/panels/__tests__/ReferencesPanel.headless.test.tsx +87 -0
- package/src/components/resource/panels/__tests__/ReferencesPanel.observable-flow.test.tsx +15 -9
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +77 -59
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +11 -4
- package/src/components/resource/panels/__tests__/TagEntry.test.tsx +33 -19
- package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +15 -7
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +3 -0
- package/dist/PdfAnnotationCanvas.client-KWQ7XDWT.js.map +0 -1
- package/dist/chunk-O2MD7TGE.js.map +0 -1
package/dist/{PdfAnnotationCanvas.client-KWQ7XDWT.js → PdfAnnotationCanvas.client-75GY2EDR.js}
RENAMED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
"use client";
|
|
3
3
|
import {
|
|
4
4
|
loadPdfDocument,
|
|
5
|
-
renderPdfPageToDataUrl
|
|
6
|
-
|
|
5
|
+
renderPdfPageToDataUrl,
|
|
6
|
+
toViewportAnchorRect
|
|
7
|
+
} from "./chunk-VXASXU4K.js";
|
|
7
8
|
import "./chunk-O23TLLXW.js";
|
|
8
9
|
|
|
9
10
|
// src/components/pdf-annotation/PdfAnnotationCanvas.tsx
|
|
@@ -204,6 +205,7 @@ function PdfAnnotationCanvas({
|
|
|
204
205
|
const MIN_DRAG_DISTANCE = 10;
|
|
205
206
|
if (dragDistance < MIN_DRAG_DISTANCE) {
|
|
206
207
|
if (existingAnnotations.length > 0) {
|
|
208
|
+
let hitRect;
|
|
207
209
|
const clickedAnnotation = pageAnnotations.find((ann) => {
|
|
208
210
|
const fragmentSel = getFragmentSelector(ann.target);
|
|
209
211
|
if (!fragmentSel) return false;
|
|
@@ -216,10 +218,14 @@ function PdfAnnotationCanvas({
|
|
|
216
218
|
const displayY = rect.y * scaleY2;
|
|
217
219
|
const displayWidth = rect.width * scaleX2;
|
|
218
220
|
const displayHeight = rect.height * scaleY2;
|
|
219
|
-
|
|
221
|
+
const hit = selection.endX >= displayX && selection.endX <= displayX + displayWidth && selection.endY >= displayY && selection.endY <= displayY + displayHeight;
|
|
222
|
+
if (hit && imageRef.current) {
|
|
223
|
+
hitRect = toViewportAnchorRect(imageRef.current.getBoundingClientRect(), displayX, displayY, displayWidth, displayHeight);
|
|
224
|
+
}
|
|
225
|
+
return hit;
|
|
220
226
|
});
|
|
221
227
|
if (clickedAnnotation) {
|
|
222
|
-
session?.client.browse.click(clickedAnnotation.id, clickedAnnotation.motivation);
|
|
228
|
+
session?.client.browse.click(clickedAnnotation.id, clickedAnnotation.motivation, hitRect);
|
|
223
229
|
setIsDrawing(false);
|
|
224
230
|
setSelection(null);
|
|
225
231
|
return;
|
|
@@ -357,7 +363,7 @@ function PdfAnnotationCanvas({
|
|
|
357
363
|
cursor: "pointer",
|
|
358
364
|
opacity: isSelected ? 1 : isHovered ? 0.9 : 0.7
|
|
359
365
|
},
|
|
360
|
-
onClick: () => session?.client.browse.click(ann.id, ann.motivation),
|
|
366
|
+
onClick: (e) => session?.client.browse.click(ann.id, ann.motivation, e.currentTarget.getBoundingClientRect()),
|
|
361
367
|
onMouseEnter: () => handleMouseEnter(ann.id),
|
|
362
368
|
onMouseLeave: handleMouseLeave
|
|
363
369
|
},
|
|
@@ -421,4 +427,4 @@ function PdfAnnotationCanvas({
|
|
|
421
427
|
export {
|
|
422
428
|
PdfAnnotationCanvas
|
|
423
429
|
};
|
|
424
|
-
//# sourceMappingURL=PdfAnnotationCanvas.client-
|
|
430
|
+
//# sourceMappingURL=PdfAnnotationCanvas.client-75GY2EDR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/pdf-annotation/PdfAnnotationCanvas.tsx","../src/lib/pdf-coordinates.ts"],"sourcesContent":["'use client';\n\nimport React, { useRef, useState, useCallback, useEffect, useMemo } from 'react';\nimport type { Annotation, AnchorRect } from '@semiont/core';\nimport { resourceId as toResourceId } from '@semiont/core';\nimport { toViewportAnchorRect } from '../../lib/anchor-rect';\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 /** The '@id' of the annotated resource — stamped as `source` on mark:requested (multi-viewer routing). */\n resourceUri: 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 resourceUri,\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 // The hit-test owns the coordinate transform — capture the hit\n // annotation's viewport rect for the emission below (A1 anchor).\n let hitRect: AnchorRect | undefined;\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 const hit = (\n selection.endX >= displayX &&\n selection.endX <= displayX + displayWidth &&\n selection.endY >= displayY &&\n selection.endY <= displayY + displayHeight\n );\n if (hit && imageRef.current) {\n hitRect = toViewportAnchorRect(imageRef.current.getBoundingClientRect(), displayX, displayY, displayWidth, displayHeight);\n }\n return hit;\n });\n\n if (clickedAnnotation) {\n session?.client.browse.click(clickedAnnotation.id, clickedAnnotation.motivation, hitRect);\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 toResourceId(resourceUri),\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={(e) => session?.client.browse.click(ann.id, ann.motivation, e.currentTarget.getBoundingClientRect())}\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"],"mappings":"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","names":["error","pdfCoord","scaleX","scaleY"]}
|
|
@@ -34,9 +34,17 @@ async function renderPdfPageToDataUrl(page, scale = 1) {
|
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
// src/lib/anchor-rect.ts
|
|
38
|
+
function toViewportAnchorRect(origin, x, y, width, height) {
|
|
39
|
+
const left = origin.left + x;
|
|
40
|
+
const top = origin.top + y;
|
|
41
|
+
return { x: left, y: top, width, height, top, right: left + width, bottom: top + height, left };
|
|
42
|
+
}
|
|
43
|
+
|
|
37
44
|
export {
|
|
38
45
|
setPdfWorkerSrc,
|
|
39
46
|
loadPdfDocument,
|
|
40
|
-
renderPdfPageToDataUrl
|
|
47
|
+
renderPdfPageToDataUrl,
|
|
48
|
+
toViewportAnchorRect
|
|
41
49
|
};
|
|
42
|
-
//# sourceMappingURL=chunk-
|
|
50
|
+
//# sourceMappingURL=chunk-VXASXU4K.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/browser-pdfjs.ts","../src/lib/anchor-rect.ts"],"sourcesContent":["/**\n * Browser PDF.js — bundled from the npm `pdfjs-dist` (v6) package, loaded\n * lazily.\n *\n * pdf.js (the ~300 kB display layer) is pulled in via a dynamic `import()` the\n * first time a PDF is actually opened, so it is code-split out of the main app\n * bundle — restoring the lazy-load behaviour the old CDN loader had. Only the\n * pdf.js *types* are imported statically (erased at build time, zero runtime\n * cost).\n *\n * The worker can't be resolved inside this tsup-built library (Vite's `?url`\n * lives in the app), so the host hands us the worker URL via `setPdfWorkerSrc`\n * once at startup; it is applied to `GlobalWorkerOptions` when pdf.js loads.\n * See `apps/frontend/src/main.tsx`.\n */\nimport type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist';\n\nexport type { PDFDocumentProxy };\n\nlet workerSrc: string | undefined;\n\n/**\n * Supply the (Vite-resolved) pdf.js worker URL. Call once at app startup,\n * before any PDF is opened.\n */\nexport function setPdfWorkerSrc(src: string): void {\n workerSrc = src;\n}\n\nlet pdfjsPromise: Promise<typeof import('pdfjs-dist')> | undefined;\n\n/** Lazy-load pdf.js once, applying the worker URL on first load. */\nasync function getPdfjs(): Promise<typeof import('pdfjs-dist')> {\n if (!pdfjsPromise) {\n pdfjsPromise = import('pdfjs-dist').then((pdfjsLib) => {\n if (workerSrc) {\n pdfjsLib.GlobalWorkerOptions.workerSrc = workerSrc;\n }\n return pdfjsLib;\n });\n }\n return pdfjsPromise;\n}\n\n/**\n * Load a PDF document from a URL. The URL must carry auth (e.g. `?token=…`);\n * pdf.js streams the document directly.\n */\nexport async function loadPdfDocument(url: string): Promise<PDFDocumentProxy> {\n const pdfjsLib = await getPdfjs();\n return pdfjsLib.getDocument({ url }).promise;\n}\n\n/**\n * Render a PDF page to a PNG data URL. The `page` is already loaded (its owning\n * document came from `loadPdfDocument`), so no pdf.js import is needed here.\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 const canvas = document.createElement('canvas');\n canvas.width = viewport.width;\n canvas.height = viewport.height;\n\n // pdf.js 6 requires the `canvas` parameter; `canvasContext` is deprecated.\n await page.render({ canvas, viewport }).promise;\n\n return {\n dataUrl: canvas.toDataURL('image/png'),\n width: viewport.width,\n height: viewport.height,\n };\n}\n","import type { AnchorRect } from '@semiont/core';\n\n/**\n * Convert a rect in element-local display coordinates to a viewport-space\n * AnchorRect, offset by the element's own viewport position. Used by the\n * canvas hit-tests (image / PDF), whose annotation geometry lives in display\n * coordinates rather than on a DOM element.\n */\nexport function toViewportAnchorRect(\n origin: { left: number; top: number },\n x: number,\n y: number,\n width: number,\n height: number,\n): AnchorRect {\n const left = origin.left + x;\n const top = origin.top + y;\n return { x: left, y: top, width, height, top, right: left + width, bottom: top + height, left };\n}\n"],"mappings":";;;AAmBA,IAAI;AAMG,SAAS,gBAAgB,KAAmB;AACjD,cAAY;AACd;AAEA,IAAI;AAGJ,eAAe,WAAiD;AAC9D,MAAI,CAAC,cAAc;AACjB,mBAAe,OAAO,YAAY,EAAE,KAAK,CAAC,aAAa;AACrD,UAAI,WAAW;AACb,iBAAS,oBAAoB,YAAY;AAAA,MAC3C;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAMA,eAAsB,gBAAgB,KAAwC;AAC5E,QAAM,WAAW,MAAM,SAAS;AAChC,SAAO,SAAS,YAAY,EAAE,IAAI,CAAC,EAAE;AACvC;AAMA,eAAsB,uBACpB,MACA,QAAQ,GACqD;AAC7D,QAAM,WAAW,KAAK,YAAY,EAAE,MAAM,CAAC;AAE3C,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,QAAQ,SAAS;AACxB,SAAO,SAAS,SAAS;AAGzB,QAAM,KAAK,OAAO,EAAE,QAAQ,SAAS,CAAC,EAAE;AAExC,SAAO;AAAA,IACL,SAAS,OAAO,UAAU,WAAW;AAAA,IACrC,OAAO,SAAS;AAAA,IAChB,QAAQ,SAAS;AAAA,EACnB;AACF;;;ACnEO,SAAS,qBACd,QACA,GACA,GACA,OACA,QACY;AACZ,QAAM,OAAO,OAAO,OAAO;AAC3B,QAAM,MAAM,OAAO,MAAM;AACzB,SAAO,EAAE,GAAG,MAAM,GAAG,KAAK,OAAO,QAAQ,KAAK,OAAO,OAAO,OAAO,QAAQ,MAAM,QAAQ,KAAK;AAChG;","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1656,12 +1656,19 @@ interface MediaRendererProps {
|
|
|
1656
1656
|
mimeType: string;
|
|
1657
1657
|
resourceUri: string;
|
|
1658
1658
|
annotations: Annotation[];
|
|
1659
|
+
/**
|
|
1660
|
+
* Session for interaction routing inside annotation-bearing renderers — the
|
|
1661
|
+
* canvases emit clicks/hover via `session.client.browse/beckon`. Absent/null
|
|
1662
|
+
* → the annotations still PAINT, but are inert (paint-only). Threaded by
|
|
1663
|
+
* BrowseView's dispatch; custom renderers may ignore it.
|
|
1664
|
+
*/
|
|
1665
|
+
session?: SemiontSession | null;
|
|
1659
1666
|
}
|
|
1660
1667
|
/** Read-only media dispatch, keyed by the registry render mode. */
|
|
1661
1668
|
type BrowseMediaRenderers = Partial<Record<string, ComponentType<MediaRendererProps>>>;
|
|
1662
1669
|
declare function TextBrowseRenderer({ content }: MediaRendererProps): React$1.JSX.Element;
|
|
1663
|
-
declare function ImageBrowseRenderer({ content,
|
|
1664
|
-
declare function PdfBrowseRenderer({ content, resourceUri, annotations }: MediaRendererProps): React$1.JSX.Element;
|
|
1670
|
+
declare function ImageBrowseRenderer({ content, resourceUri, annotations, session }: MediaRendererProps): React$1.JSX.Element;
|
|
1671
|
+
declare function PdfBrowseRenderer({ content, resourceUri, annotations, session }: MediaRendererProps): React$1.JSX.Element;
|
|
1665
1672
|
/**
|
|
1666
1673
|
* Default read-only media renderers. `BrowseView` merges a caller's `renderers`
|
|
1667
1674
|
* override on top of these, so a host can swap one renderer (e.g. its own PDF
|
|
@@ -1822,12 +1829,14 @@ interface Props$4 {
|
|
|
1822
1829
|
declare function ResourceViewer({ resource, annotations, session, onOpenResource, onOpenPanel, onLinkClick, onReferenceHover, inline, newAnnotationIds, generatingReferenceId, showLineNumbers, hoverDelayMs, hoveredAnnotationId: hoveredAnnotationIdProp, annotateMode: annotateModeProp, onAnnotateModeChange, clickAction: clickActionProp, onClickActionChange, selectionMotivation: selectionMotivationProp, onSelectionMotivationChange, shape: shapeProp, onShapeChange, showToolbar, }: Props$4): React__default.JSX.Element;
|
|
1823
1830
|
|
|
1824
1831
|
interface AssessmentEntryProps {
|
|
1832
|
+
/** Session for interaction routing (browse.click etc.); the panel threads it. */
|
|
1833
|
+
session: SemiontSession | null;
|
|
1825
1834
|
assessment: Annotation;
|
|
1826
1835
|
isFocused: boolean;
|
|
1827
1836
|
isHovered?: boolean;
|
|
1828
1837
|
ref?: Ref<HTMLDivElement>;
|
|
1829
1838
|
}
|
|
1830
|
-
declare function AssessmentEntry({ assessment, isFocused, isHovered, ref, }: AssessmentEntryProps): React$1.JSX.Element;
|
|
1839
|
+
declare function AssessmentEntry({ session, assessment, isFocused, isHovered, ref, }: AssessmentEntryProps): React$1.JSX.Element;
|
|
1831
1840
|
|
|
1832
1841
|
type Motivation$5 = components['schemas']['Motivation'];
|
|
1833
1842
|
type JobProgress$6 = components['schemas']['JobProgress'];
|
|
@@ -1836,6 +1845,8 @@ interface PendingAnnotation$5 {
|
|
|
1836
1845
|
motivation: Motivation$5;
|
|
1837
1846
|
}
|
|
1838
1847
|
interface AssessmentPanelProps {
|
|
1848
|
+
/** Session carrying the client and event bus; null renders inert. */
|
|
1849
|
+
session: SemiontSession | null;
|
|
1839
1850
|
/** The '@id' of the panel's resource — stamped as `source` on mark:submit (multi-viewer routing). */
|
|
1840
1851
|
resourceId: string;
|
|
1841
1852
|
annotations: Annotation[];
|
|
@@ -1857,7 +1868,7 @@ interface AssessmentPanelProps {
|
|
|
1857
1868
|
* @emits mark:cancel-pending - Cancel pending assessment annotation. Payload: undefined
|
|
1858
1869
|
* @subscribes browse:click - Annotation clicked. Payload: { annotationId: string }
|
|
1859
1870
|
*/
|
|
1860
|
-
declare function AssessmentPanel({ resourceId, annotations, pendingAnnotation, isAssisting, progress, locale, sourceLanguage, annotateMode, scrollToAnnotationId, onScrollCompleted, hoveredAnnotationId, }: AssessmentPanelProps): React$1.JSX.Element;
|
|
1871
|
+
declare function AssessmentPanel({ session, resourceId, annotations, pendingAnnotation, isAssisting, progress, locale, sourceLanguage, annotateMode, scrollToAnnotationId, onScrollCompleted, hoveredAnnotationId, }: AssessmentPanelProps): React$1.JSX.Element;
|
|
1861
1872
|
|
|
1862
1873
|
interface Props$3 {
|
|
1863
1874
|
/**
|
|
@@ -1885,13 +1896,15 @@ interface Props$3 {
|
|
|
1885
1896
|
declare function CollaborationPanel({ state, eventCount, lastEventTimestamp, knowledgeBaseName }: Props$3): React$1.JSX.Element;
|
|
1886
1897
|
|
|
1887
1898
|
interface CommentEntryProps {
|
|
1899
|
+
/** Session for interaction routing (browse.click etc.); the panel threads it. */
|
|
1900
|
+
session: SemiontSession | null;
|
|
1888
1901
|
comment: Annotation;
|
|
1889
1902
|
isFocused: boolean;
|
|
1890
1903
|
isHovered?: boolean;
|
|
1891
1904
|
annotateMode?: boolean;
|
|
1892
1905
|
ref?: Ref<HTMLDivElement>;
|
|
1893
1906
|
}
|
|
1894
|
-
declare function CommentEntry({ comment, isFocused, isHovered, annotateMode, ref, }: CommentEntryProps): React$1.JSX.Element;
|
|
1907
|
+
declare function CommentEntry({ session, comment, isFocused, isHovered, annotateMode, ref, }: CommentEntryProps): React$1.JSX.Element;
|
|
1895
1908
|
|
|
1896
1909
|
type Motivation$4 = components['schemas']['Motivation'];
|
|
1897
1910
|
type JobProgress$5 = components['schemas']['JobProgress'];
|
|
@@ -1900,6 +1913,8 @@ interface PendingAnnotation$4 {
|
|
|
1900
1913
|
motivation: Motivation$4;
|
|
1901
1914
|
}
|
|
1902
1915
|
interface CommentsPanelProps {
|
|
1916
|
+
/** Session carrying the client and event bus; null renders inert. */
|
|
1917
|
+
session: SemiontSession | null;
|
|
1903
1918
|
/** The '@id' of the panel's resource — stamped as `source` on mark:submit (multi-viewer routing). */
|
|
1904
1919
|
resourceId: string;
|
|
1905
1920
|
annotations: Annotation[];
|
|
@@ -1921,10 +1936,12 @@ interface CommentsPanelProps {
|
|
|
1921
1936
|
* @emits mark:cancel-pending - Cancel pending comment annotation. Payload: undefined
|
|
1922
1937
|
* @subscribes browse:click - Annotation clicked. Payload: { annotationId: string }
|
|
1923
1938
|
*/
|
|
1924
|
-
declare function CommentsPanel({ resourceId, annotations, pendingAnnotation, annotateMode, isAssisting, progress, locale, sourceLanguage, scrollToAnnotationId, onScrollCompleted, hoveredAnnotationId, }: CommentsPanelProps): React$1.JSX.Element;
|
|
1939
|
+
declare function CommentsPanel({ session, resourceId, annotations, pendingAnnotation, annotateMode, isAssisting, progress, locale, sourceLanguage, scrollToAnnotationId, onScrollCompleted, hoveredAnnotationId, }: CommentsPanelProps): React$1.JSX.Element;
|
|
1925
1940
|
|
|
1926
1941
|
type JobProgress$4 = components['schemas']['JobProgress'];
|
|
1927
1942
|
interface AssistSectionProps {
|
|
1943
|
+
/** Session carrying the client and event bus; null renders inert. */
|
|
1944
|
+
session: SemiontSession | null;
|
|
1928
1945
|
annotationType: 'highlight' | 'assessment' | 'comment';
|
|
1929
1946
|
isAssisting: boolean;
|
|
1930
1947
|
/** User UI locale — written into the annotation body's `language` field for comment/assessment. */
|
|
@@ -1945,15 +1962,17 @@ interface AssistSectionProps {
|
|
|
1945
1962
|
* @emits mark:assist-request - Start assist for annotation type. Payload: { motivation: Motivation, options: { instructions?: string, tone?: string, density?: number } }
|
|
1946
1963
|
* @emits mark:progress-dismiss - Dismiss the annotation progress display
|
|
1947
1964
|
*/
|
|
1948
|
-
declare function AssistSection({ annotationType, isAssisting, locale, sourceLanguage, progress, }: AssistSectionProps): React$1.JSX.Element;
|
|
1965
|
+
declare function AssistSection({ session, annotationType, isAssisting, locale, sourceLanguage, progress, }: AssistSectionProps): React$1.JSX.Element;
|
|
1949
1966
|
|
|
1950
1967
|
interface HighlightEntryProps {
|
|
1968
|
+
/** Session for interaction routing (browse.click etc.); the panel threads it. */
|
|
1969
|
+
session: SemiontSession | null;
|
|
1951
1970
|
highlight: Annotation;
|
|
1952
1971
|
isFocused: boolean;
|
|
1953
1972
|
isHovered?: boolean;
|
|
1954
1973
|
ref?: Ref<HTMLDivElement>;
|
|
1955
1974
|
}
|
|
1956
|
-
declare function HighlightEntry({ highlight, isFocused, isHovered, ref, }: HighlightEntryProps): React$1.JSX.Element;
|
|
1975
|
+
declare function HighlightEntry({ session, highlight, isFocused, isHovered, ref, }: HighlightEntryProps): React$1.JSX.Element;
|
|
1957
1976
|
|
|
1958
1977
|
type Motivation$3 = components['schemas']['Motivation'];
|
|
1959
1978
|
type JobProgress$3 = components['schemas']['JobProgress'];
|
|
@@ -1962,6 +1981,8 @@ interface PendingAnnotation$3 {
|
|
|
1962
1981
|
motivation: Motivation$3;
|
|
1963
1982
|
}
|
|
1964
1983
|
interface HighlightPanelProps {
|
|
1984
|
+
/** Session carrying the client and event bus; null renders inert. */
|
|
1985
|
+
session: SemiontSession | null;
|
|
1965
1986
|
/** The '@id' of the panel's resource — stamped as `source` on mark:submit (multi-viewer routing). */
|
|
1966
1987
|
resourceId: string;
|
|
1967
1988
|
annotations: Annotation[];
|
|
@@ -1981,7 +2002,7 @@ interface HighlightPanelProps {
|
|
|
1981
2002
|
* @emits mark:create - Create new highlight annotation (auto-triggered). Payload: { motivation: 'highlighting', selector: Selector | Selector[], body: Body[] }
|
|
1982
2003
|
* @subscribes browse:click - Annotation clicked. Payload: { annotationId: string }
|
|
1983
2004
|
*/
|
|
1984
|
-
declare function HighlightPanel({ resourceId, annotations, pendingAnnotation, isAssisting, progress, annotateMode, scrollToAnnotationId, onScrollCompleted, hoveredAnnotationId, sourceLanguage, }: HighlightPanelProps): React$1.JSX.Element;
|
|
2005
|
+
declare function HighlightPanel({ session, resourceId, annotations, pendingAnnotation, isAssisting, progress, annotateMode, scrollToAnnotationId, onScrollCompleted, hoveredAnnotationId, sourceLanguage, }: HighlightPanelProps): React$1.JSX.Element;
|
|
1985
2006
|
|
|
1986
2007
|
interface Props$2 {
|
|
1987
2008
|
resourceId: ResourceId;
|
|
@@ -2009,15 +2030,18 @@ interface PanelHeaderProps {
|
|
|
2009
2030
|
declare function PanelHeader({ count, title }: PanelHeaderProps): React$1.JSX.Element;
|
|
2010
2031
|
|
|
2011
2032
|
interface ReferenceEntryProps {
|
|
2033
|
+
/** Session for interaction routing (browse.click etc.); the panel threads it. */
|
|
2034
|
+
session: SemiontSession | null;
|
|
2012
2035
|
reference: Annotation;
|
|
2013
2036
|
isFocused: boolean;
|
|
2014
2037
|
isHovered?: boolean;
|
|
2015
|
-
|
|
2038
|
+
/** Host-owned navigation: called with the resolved resource id when the entry's link opens. */
|
|
2039
|
+
onOpenResource?: (resourceId: string) => void;
|
|
2016
2040
|
annotateMode?: boolean;
|
|
2017
2041
|
isGenerating?: boolean;
|
|
2018
2042
|
ref?: Ref<HTMLDivElement>;
|
|
2019
2043
|
}
|
|
2020
|
-
declare function ReferenceEntry({ reference, isFocused, isHovered,
|
|
2044
|
+
declare function ReferenceEntry({ session, reference, isFocused, isHovered, onOpenResource, annotateMode, isGenerating, ref, }: ReferenceEntryProps): React$1.JSX.Element;
|
|
2021
2045
|
|
|
2022
2046
|
type JobProgress$2 = components['schemas']['JobProgress'];
|
|
2023
2047
|
|
|
@@ -2028,6 +2052,10 @@ interface PendingAnnotation$2 {
|
|
|
2028
2052
|
motivation: Motivation$2;
|
|
2029
2053
|
}
|
|
2030
2054
|
interface Props$1 {
|
|
2055
|
+
/** Session carrying the client and event bus; null renders inert. */
|
|
2056
|
+
session: SemiontSession | null;
|
|
2057
|
+
/** Host-owned navigation: called with the resolved resource id when a reference entry opens. */
|
|
2058
|
+
onOpenResource?: (resourceId: string) => void;
|
|
2031
2059
|
/** The '@id' of the panel's resource — stamped as `source` on mark:submit (multi-viewer routing). */
|
|
2032
2060
|
resourceId: string;
|
|
2033
2061
|
annotations?: Annotation[];
|
|
@@ -2057,10 +2085,12 @@ interface Props$1 {
|
|
|
2057
2085
|
* @emits mark:cancel-pending - Cancel pending reference annotation. Payload: undefined
|
|
2058
2086
|
* @subscribes browse:click - Annotation clicked. Payload: { annotationId: string }
|
|
2059
2087
|
*/
|
|
2060
|
-
declare function ReferencesPanel({ resourceId, annotations, isAssisting, progress, annotateMode, Link, routes, allEntityTypes, generatingReferenceId, referencedBy, referencedByLoading, pendingAnnotation, scrollToAnnotationId, onScrollCompleted, hoveredAnnotationId, locale, sourceLanguage, }: Props$1): React__default.JSX.Element;
|
|
2088
|
+
declare function ReferencesPanel({ session, onOpenResource, resourceId, annotations, isAssisting, progress, annotateMode, Link, routes, allEntityTypes, generatingReferenceId, referencedBy, referencedByLoading, pendingAnnotation, scrollToAnnotationId, onScrollCompleted, hoveredAnnotationId, locale, sourceLanguage, }: Props$1): React__default.JSX.Element;
|
|
2061
2089
|
|
|
2062
2090
|
type Agent = components['schemas']['Agent'];
|
|
2063
2091
|
interface Props {
|
|
2092
|
+
/** Session carrying the client and event bus; null renders inert. */
|
|
2093
|
+
session: SemiontSession | null;
|
|
2064
2094
|
resourceId: string;
|
|
2065
2095
|
documentEntityTypes: string[];
|
|
2066
2096
|
documentLocale?: string | undefined;
|
|
@@ -2087,7 +2117,7 @@ interface Props {
|
|
|
2087
2117
|
* @emits mark:unarchive - Unarchive this resource
|
|
2088
2118
|
* @emits mark:archive - Archive this resource
|
|
2089
2119
|
*/
|
|
2090
|
-
declare function ResourceInfoPanel({ resourceId, documentEntityTypes, documentLocale, primaryMediaType, primaryByteSize, storageUri, isArchived, dateCreated, dateModified, wasAttributedTo, wasDerivedFrom, generator, onGenerate, }: Props): React$1.JSX.Element;
|
|
2120
|
+
declare function ResourceInfoPanel({ session, resourceId, documentEntityTypes, documentLocale, primaryMediaType, primaryByteSize, storageUri, isArchived, dateCreated, dateModified, wasAttributedTo, wasDerivedFrom, generator, onGenerate, }: Props): React$1.JSX.Element;
|
|
2091
2121
|
|
|
2092
2122
|
interface StatisticsPanelProps {
|
|
2093
2123
|
highlights: Annotation[];
|
|
@@ -2099,12 +2129,14 @@ interface StatisticsPanelProps {
|
|
|
2099
2129
|
declare function StatisticsPanel({ highlights, comments, assessments, references, tags }: StatisticsPanelProps): React$1.JSX.Element;
|
|
2100
2130
|
|
|
2101
2131
|
interface TagEntryProps {
|
|
2132
|
+
/** Session for interaction routing (browse.click, tag schemas); the panel threads it. */
|
|
2133
|
+
session: SemiontSession | null;
|
|
2102
2134
|
tag: Annotation;
|
|
2103
2135
|
isFocused: boolean;
|
|
2104
2136
|
isHovered?: boolean;
|
|
2105
2137
|
ref?: Ref<HTMLDivElement>;
|
|
2106
2138
|
}
|
|
2107
|
-
declare function TagEntry({ tag, isFocused, isHovered, ref, }: TagEntryProps): React$1.JSX.Element;
|
|
2139
|
+
declare function TagEntry({ session, tag, isFocused, isHovered, ref, }: TagEntryProps): React$1.JSX.Element;
|
|
2108
2140
|
|
|
2109
2141
|
type Motivation$1 = components['schemas']['Motivation'];
|
|
2110
2142
|
type JobProgress$1 = components['schemas']['JobProgress'];
|
|
@@ -2113,6 +2145,8 @@ interface PendingAnnotation$1 {
|
|
|
2113
2145
|
motivation: Motivation$1;
|
|
2114
2146
|
}
|
|
2115
2147
|
interface TaggingPanelProps {
|
|
2148
|
+
/** Session carrying the client and event bus; null renders inert. */
|
|
2149
|
+
session: SemiontSession | null;
|
|
2116
2150
|
/** The '@id' of the panel's resource — stamped as `source` on mark:submit (multi-viewer routing). */
|
|
2117
2151
|
resourceId: string;
|
|
2118
2152
|
annotations: Annotation[];
|
|
@@ -2136,7 +2170,7 @@ interface TaggingPanelProps {
|
|
|
2136
2170
|
* @emits mark:create - Create new tag annotation. Payload: { motivation: 'tagging', selector: Selector | Selector[], body: Body[] }
|
|
2137
2171
|
* @subscribes browse:click - Annotation clicked. Payload: { annotationId: string }
|
|
2138
2172
|
*/
|
|
2139
|
-
declare function TaggingPanel({ resourceId, annotations, annotateMode, isAssisting, progress, pendingAnnotation, scrollToAnnotationId, onScrollCompleted, hoveredAnnotationId, locale, sourceLanguage, }: TaggingPanelProps): React$1.JSX.Element;
|
|
2173
|
+
declare function TaggingPanel({ session, resourceId, annotations, annotateMode, isAssisting, progress, pendingAnnotation, scrollToAnnotationId, onScrollCompleted, hoveredAnnotationId, locale, sourceLanguage, }: TaggingPanelProps): React$1.JSX.Element;
|
|
2140
2174
|
|
|
2141
2175
|
type JobProgress = components['schemas']['JobProgress'];
|
|
2142
2176
|
|
|
@@ -2156,6 +2190,10 @@ interface PendingAnnotation {
|
|
|
2156
2190
|
* - All operations managed via event bus (no callback props)
|
|
2157
2191
|
*/
|
|
2158
2192
|
interface UnifiedAnnotationsPanelProps {
|
|
2193
|
+
/** Session carrying the client and event bus; null renders inert. */
|
|
2194
|
+
session: SemiontSession | null;
|
|
2195
|
+
/** Host-owned navigation: called with the resolved resource id when a reference entry opens. */
|
|
2196
|
+
onOpenResource?: (resourceId: string) => void;
|
|
2159
2197
|
annotations: Annotation[];
|
|
2160
2198
|
annotators: Record<string, Annotator>;
|
|
2161
2199
|
annotateMode?: boolean;
|
|
@@ -3946,7 +3984,7 @@ interface HoverEmitterProps {
|
|
|
3946
3984
|
onMouseEnter: () => void;
|
|
3947
3985
|
onMouseLeave: () => void;
|
|
3948
3986
|
}
|
|
3949
|
-
declare function useHoverEmitter(annotationId: AnnotationId, hoverDelayMs?: number): HoverEmitterProps;
|
|
3987
|
+
declare function useHoverEmitter(session: SemiontSession | null, annotationId: AnnotationId, hoverDelayMs?: number): HoverEmitterProps;
|
|
3950
3988
|
|
|
3951
3989
|
interface SessionStateUnit extends StateUnit {
|
|
3952
3990
|
isLoggingOut$: Observable<boolean>;
|