@semiont/react-ui 0.5.25 → 0.5.26
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-6ZSF6ZTJ.js +677 -0
- package/dist/PdfAnnotationCanvas.client-6ZSF6ZTJ.js.map +1 -0
- package/dist/PdfAnnotationCanvas.client-JCJMXWWJ.css +180 -0
- package/dist/PdfAnnotationCanvas.client-JCJMXWWJ.css.map +1 -0
- package/dist/{ar-CULRHLNG.js → ar-YTUUGK2O.js} +13 -1
- package/dist/ar-YTUUGK2O.js.map +1 -0
- package/dist/{bn-HYR7CEL2.js → bn-ASGNGPMD.js} +13 -1
- package/dist/bn-ASGNGPMD.js.map +1 -0
- package/dist/{chunk-TG2XQEC4.js → chunk-BTUG3VJJ.js} +18 -298
- package/dist/chunk-BTUG3VJJ.js.map +1 -0
- package/dist/{chunk-NH6L2GIL.js → chunk-L3WKRWP3.js} +13 -1
- package/dist/chunk-L3WKRWP3.js.map +1 -0
- package/dist/chunk-OBQOZCJE.js +287 -0
- package/dist/chunk-OBQOZCJE.js.map +1 -0
- package/dist/{cs-YBRWY7NH.js → cs-WMYVAOZY.js} +13 -1
- package/dist/cs-WMYVAOZY.js.map +1 -0
- package/dist/{da-PHDEOBV3.js → da-FSHHN4D6.js} +13 -1
- package/dist/da-FSHHN4D6.js.map +1 -0
- package/dist/{de-O4UCCVLJ.js → de-FJFO3TPT.js} +13 -1
- package/dist/de-FJFO3TPT.js.map +1 -0
- package/dist/{el-MVGO6WZV.js → el-XOH4QIAG.js} +13 -1
- package/dist/el-XOH4QIAG.js.map +1 -0
- package/dist/{en-2KQILFKQ.js → en-3FCBW3YD.js} +4 -2
- package/dist/{es-USSLXQY7.js → es-HDX4QI7E.js} +13 -1
- package/dist/es-HDX4QI7E.js.map +1 -0
- package/dist/{fa-WMAREFWY.js → fa-DJ6WTFTJ.js} +13 -1
- package/dist/fa-DJ6WTFTJ.js.map +1 -0
- package/dist/{fi-B3NBNOTS.js → fi-4S7H7W7U.js} +13 -1
- package/dist/fi-4S7H7W7U.js.map +1 -0
- package/dist/{fr-TXYK5T5W.js → fr-6UJSYHXC.js} +13 -1
- package/dist/fr-6UJSYHXC.js.map +1 -0
- package/dist/{he-JCJACW5D.js → he-ZZJOHJX2.js} +13 -1
- package/dist/he-ZZJOHJX2.js.map +1 -0
- package/dist/{hi-VOQLZNJU.js → hi-HZJTAW7U.js} +13 -1
- package/dist/hi-HZJTAW7U.js.map +1 -0
- package/dist/{id-BFN5RZNZ.js → id-E3I6ILEM.js} +13 -1
- package/dist/id-E3I6ILEM.js.map +1 -0
- package/dist/index.css +140 -58
- package/dist/index.css.map +1 -1
- package/dist/index.js +67 -55
- package/dist/index.js.map +1 -1
- package/dist/{it-TWWDHKKM.js → it-ULAPAD7U.js} +13 -1
- package/dist/it-ULAPAD7U.js.map +1 -0
- package/dist/{ja-YRQLEBFM.js → ja-SAHZQ4CZ.js} +13 -1
- package/dist/ja-SAHZQ4CZ.js.map +1 -0
- package/dist/{ko-PG6B3CRB.js → ko-DFU2ADQ4.js} +13 -1
- package/dist/ko-DFU2ADQ4.js.map +1 -0
- package/dist/{ms-7EZECVKM.js → ms-NWL76XNT.js} +13 -1
- package/dist/ms-NWL76XNT.js.map +1 -0
- package/dist/{nl-HP753YAP.js → nl-ATCM76WN.js} +13 -1
- package/dist/nl-ATCM76WN.js.map +1 -0
- package/dist/{no-JUD5WX3N.js → no-UIAQ5RJF.js} +13 -1
- package/dist/no-UIAQ5RJF.js.map +1 -0
- package/dist/{pl-RVUL4CBM.js → pl-NOECQDXD.js} +13 -1
- package/dist/pl-NOECQDXD.js.map +1 -0
- package/dist/{pt-RPOVD4QM.js → pt-SVWIIUPC.js} +13 -1
- package/dist/pt-SVWIIUPC.js.map +1 -0
- package/dist/{ro-DMYWQTRD.js → ro-NR2DRTZG.js} +13 -1
- package/dist/ro-NR2DRTZG.js.map +1 -0
- package/dist/{sv-DMZ4FGXD.js → sv-STVJ37JP.js} +13 -1
- package/dist/sv-STVJ37JP.js.map +1 -0
- package/dist/test-utils.js +5 -3
- package/dist/test-utils.js.map +1 -1
- package/dist/{th-4HVBJBQ2.js → th-UWQR4K54.js} +13 -1
- package/dist/th-UWQR4K54.js.map +1 -0
- package/dist/{tr-6BDPU5ML.js → tr-WNXZSD27.js} +13 -1
- package/dist/tr-WNXZSD27.js.map +1 -0
- package/dist/{uk-TBZBT2TL.js → uk-SJRMB47Y.js} +13 -1
- package/dist/uk-SJRMB47Y.js.map +1 -0
- package/dist/{vi-UMKZKZL3.js → vi-TMRQFES2.js} +13 -1
- package/dist/vi-TMRQFES2.js.map +1 -0
- package/dist/{zh-YBJWJJYB.js → zh-QOL3LYEM.js} +13 -1
- package/dist/zh-QOL3LYEM.js.map +1 -0
- package/package.json +4 -4
- package/src/components/StatusDisplay.css +35 -35
- package/src/components/annotation/annotations.css +1 -1
- package/src/components/modals/SearchModal.css +5 -5
- package/src/components/pdf-annotation/PdfAnnotationCanvas.css +143 -6
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +622 -231
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +761 -2
- package/src/components/pdf-annotation/__tests__/estimate-slot-height.test.ts +53 -0
- package/src/components/pdf-annotation/estimate-slot-height.ts +31 -0
- package/src/components/resource/__tests__/browse-renderers.dispatch.test.tsx +33 -0
- package/src/components/resource/annotate-renderers.tsx +10 -1
- package/src/components/resource/browse-renderers.tsx +10 -1
- package/src/features/auth/auth.css +10 -10
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +26 -0
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +1 -1
- package/src/styles/core/inputs.css +1 -1
- package/src/styles/features/compose.css +1 -1
- package/src/styles/features/resource-viewer.css +1 -1
- package/src/styles/features/static-pages.css +11 -11
- package/src/styles/motivations/motivation-comment.css +2 -2
- package/src/styles/motivations/motivation-reference.css +2 -2
- package/src/styles/patterns/panels-base.css +4 -4
- package/src/styles/utilities/motion.css +15 -15
- package/src/styles/variables.css +29 -1
- package/translations/ar.json +10 -0
- package/translations/bn.json +10 -0
- package/translations/cs.json +10 -0
- package/translations/da.json +10 -0
- package/translations/de.json +10 -0
- package/translations/el.json +10 -0
- package/translations/en.json +10 -0
- package/translations/es.json +10 -0
- package/translations/fa.json +10 -0
- package/translations/fi.json +10 -0
- package/translations/fr.json +10 -0
- package/translations/he.json +10 -0
- package/translations/hi.json +10 -0
- package/translations/id.json +10 -0
- package/translations/it.json +10 -0
- package/translations/ja.json +10 -0
- package/translations/ko.json +10 -0
- package/translations/ms.json +10 -0
- package/translations/nl.json +10 -0
- package/translations/no.json +10 -0
- package/translations/pl.json +10 -0
- package/translations/pt.json +10 -0
- package/translations/ro.json +10 -0
- package/translations/sv.json +10 -0
- package/translations/th.json +10 -0
- package/translations/tr.json +10 -0
- package/translations/uk.json +10 -0
- package/translations/vi.json +10 -0
- package/translations/zh.json +10 -0
- package/dist/PdfAnnotationCanvas.client-EHQWF6BR.js +0 -462
- package/dist/PdfAnnotationCanvas.client-EHQWF6BR.js.map +0 -1
- package/dist/PdfAnnotationCanvas.client-SU5KNT7G.css +0 -98
- package/dist/PdfAnnotationCanvas.client-SU5KNT7G.css.map +0 -1
- package/dist/ar-CULRHLNG.js.map +0 -1
- package/dist/bn-HYR7CEL2.js.map +0 -1
- package/dist/chunk-NH6L2GIL.js.map +0 -1
- package/dist/chunk-TG2XQEC4.js.map +0 -1
- package/dist/cs-YBRWY7NH.js.map +0 -1
- package/dist/da-PHDEOBV3.js.map +0 -1
- package/dist/de-O4UCCVLJ.js.map +0 -1
- package/dist/el-MVGO6WZV.js.map +0 -1
- package/dist/es-USSLXQY7.js.map +0 -1
- package/dist/fa-WMAREFWY.js.map +0 -1
- package/dist/fi-B3NBNOTS.js.map +0 -1
- package/dist/fr-TXYK5T5W.js.map +0 -1
- package/dist/he-JCJACW5D.js.map +0 -1
- package/dist/hi-VOQLZNJU.js.map +0 -1
- package/dist/id-BFN5RZNZ.js.map +0 -1
- package/dist/it-TWWDHKKM.js.map +0 -1
- package/dist/ja-YRQLEBFM.js.map +0 -1
- package/dist/ko-PG6B3CRB.js.map +0 -1
- package/dist/ms-7EZECVKM.js.map +0 -1
- package/dist/nl-HP753YAP.js.map +0 -1
- package/dist/no-JUD5WX3N.js.map +0 -1
- package/dist/pl-RVUL4CBM.js.map +0 -1
- package/dist/pt-RPOVD4QM.js.map +0 -1
- package/dist/ro-DMYWQTRD.js.map +0 -1
- package/dist/sv-DMZ4FGXD.js.map +0 -1
- package/dist/th-4HVBJBQ2.js.map +0 -1
- package/dist/tr-6BDPU5ML.js.map +0 -1
- package/dist/uk-TBZBT2TL.js.map +0 -1
- package/dist/vi-UMKZKZL3.js.map +0 -1
- package/dist/zh-YBJWJJYB.js.map +0 -1
- /package/dist/{en-2KQILFKQ.js.map → en-3FCBW3YD.js.map} +0 -0
|
@@ -1,462 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
"use client";
|
|
3
|
-
import {
|
|
4
|
-
loadPdfDocument,
|
|
5
|
-
renderPdfPageToDataUrl,
|
|
6
|
-
toViewportAnchorRect
|
|
7
|
-
} from "./chunk-VXASXU4K.js";
|
|
8
|
-
import "./chunk-O23TLLXW.js";
|
|
9
|
-
|
|
10
|
-
// src/components/pdf-annotation/PdfAnnotationCanvas.tsx
|
|
11
|
-
import { useRef, useState, useCallback, useEffect, useMemo } from "react";
|
|
12
|
-
import { resourceId as toResourceId } from "@semiont/core";
|
|
13
|
-
import { createFragmentSelector, anchorRuns, isTextRun, textUnder } from "@semiont/core";
|
|
14
|
-
|
|
15
|
-
// src/components/pdf-annotation/rects-for-page.ts
|
|
16
|
-
import { getTargetSelector, parseFragmentSelector } from "@semiont/core";
|
|
17
|
-
function fragmentSelectors(target) {
|
|
18
|
-
const selector = getTargetSelector(target);
|
|
19
|
-
if (!selector) return [];
|
|
20
|
-
const selectors = Array.isArray(selector) ? selector : [selector];
|
|
21
|
-
return selectors.filter((s) => s.type === "FragmentSelector").map((s) => s);
|
|
22
|
-
}
|
|
23
|
-
function rectsForPage(annotations, pageNumber) {
|
|
24
|
-
const rects = [];
|
|
25
|
-
for (const annotation of annotations) {
|
|
26
|
-
fragmentSelectors(annotation.target).forEach((sel, selectorIndex) => {
|
|
27
|
-
const coord = parseFragmentSelector(sel.value);
|
|
28
|
-
if (coord && coord.page === pageNumber) {
|
|
29
|
-
rects.push({ annId: annotation.id, selectorIndex, coord, annotation });
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
return rects;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// src/components/pdf-annotation/PdfAnnotationCanvas.tsx
|
|
37
|
-
import { createHoverHandlers } from "@semiont/sdk";
|
|
38
|
-
|
|
39
|
-
// src/lib/pdf-coordinates.ts
|
|
40
|
-
function canvasToPdfCoordinates(canvasRect, page, _pageWidth, pageHeight, scale = 1) {
|
|
41
|
-
const x1 = Math.min(canvasRect.startX, canvasRect.endX);
|
|
42
|
-
const y1 = Math.min(canvasRect.startY, canvasRect.endY);
|
|
43
|
-
const x2 = Math.max(canvasRect.startX, canvasRect.endX);
|
|
44
|
-
const y2 = Math.max(canvasRect.startY, canvasRect.endY);
|
|
45
|
-
const pdfX = x1 / scale;
|
|
46
|
-
const pdfWidth = (x2 - x1) / scale;
|
|
47
|
-
const pdfY = pageHeight - y2 / scale;
|
|
48
|
-
const pdfHeight = (y2 - y1) / scale;
|
|
49
|
-
return {
|
|
50
|
-
page,
|
|
51
|
-
x: Math.round(pdfX),
|
|
52
|
-
y: Math.round(pdfY),
|
|
53
|
-
width: Math.round(pdfWidth),
|
|
54
|
-
height: Math.round(pdfHeight)
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
function pdfToCanvasCoordinates(pdfCoord, pageHeight, scale = 1) {
|
|
58
|
-
const canvasX = pdfCoord.x * scale;
|
|
59
|
-
const canvasWidth = pdfCoord.width * scale;
|
|
60
|
-
const canvasY = (pageHeight - pdfCoord.y - pdfCoord.height) * scale;
|
|
61
|
-
const canvasHeight = pdfCoord.height * scale;
|
|
62
|
-
return {
|
|
63
|
-
x: canvasX,
|
|
64
|
-
y: canvasY,
|
|
65
|
-
width: canvasWidth,
|
|
66
|
-
height: canvasHeight
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// src/components/pdf-annotation/PdfAnnotationCanvas.tsx
|
|
71
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
72
|
-
function getMotivationColor(motivation) {
|
|
73
|
-
if (!motivation) {
|
|
74
|
-
return { stroke: "rgb(156, 163, 175)", fill: "rgba(156, 163, 175, 0.2)" };
|
|
75
|
-
}
|
|
76
|
-
switch (motivation) {
|
|
77
|
-
case "highlighting":
|
|
78
|
-
return { stroke: "rgb(250, 204, 21)", fill: "rgba(250, 204, 21, 0.3)" };
|
|
79
|
-
case "linking":
|
|
80
|
-
return { stroke: "rgb(59, 130, 246)", fill: "rgba(59, 130, 246, 0.2)" };
|
|
81
|
-
case "assessing":
|
|
82
|
-
return { stroke: "rgb(239, 68, 68)", fill: "rgba(239, 68, 68, 0.2)" };
|
|
83
|
-
case "commenting":
|
|
84
|
-
return { stroke: "rgb(255, 255, 255)", fill: "rgba(255, 255, 255, 0.2)" };
|
|
85
|
-
default:
|
|
86
|
-
return { stroke: "rgb(156, 163, 175)", fill: "rgba(156, 163, 175, 0.2)" };
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
function PdfAnnotationCanvas({
|
|
90
|
-
pdfUrl,
|
|
91
|
-
resourceUri,
|
|
92
|
-
existingAnnotations = [],
|
|
93
|
-
drawingMode,
|
|
94
|
-
selectedMotivation,
|
|
95
|
-
session,
|
|
96
|
-
hoveredAnnotationId,
|
|
97
|
-
selectedAnnotationId,
|
|
98
|
-
hoverDelayMs = 150
|
|
99
|
-
}) {
|
|
100
|
-
const [pdfDoc, setPdfDoc] = useState(null);
|
|
101
|
-
const [numPages, setNumPages] = useState(0);
|
|
102
|
-
const [pageNumber, setPageNumber] = useState(1);
|
|
103
|
-
const [pageImageUrl, setPageImageUrl] = useState(null);
|
|
104
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
105
|
-
const [error, setError] = useState(null);
|
|
106
|
-
const [pageDimensions, setPageDimensions] = useState(null);
|
|
107
|
-
const [pageAnchored, setPageAnchored] = useState(null);
|
|
108
|
-
const [displayDimensions, setDisplayDimensions] = useState(null);
|
|
109
|
-
const [scale] = useState(1.5);
|
|
110
|
-
const [isDrawing, setIsDrawing] = useState(false);
|
|
111
|
-
const [selection, setSelection] = useState(null);
|
|
112
|
-
const containerRef = useRef(null);
|
|
113
|
-
const imageRef = useRef(null);
|
|
114
|
-
useEffect(() => {
|
|
115
|
-
let cancelled = false;
|
|
116
|
-
async function loadPdf() {
|
|
117
|
-
try {
|
|
118
|
-
setIsLoading(true);
|
|
119
|
-
setError(null);
|
|
120
|
-
const doc = await loadPdfDocument(pdfUrl);
|
|
121
|
-
if (cancelled) return;
|
|
122
|
-
setPdfDoc(doc);
|
|
123
|
-
setNumPages(doc.numPages);
|
|
124
|
-
setIsLoading(false);
|
|
125
|
-
} catch (err) {
|
|
126
|
-
if (cancelled) return;
|
|
127
|
-
console.error("Error loading PDF:", err);
|
|
128
|
-
setError("Failed to load PDF");
|
|
129
|
-
setIsLoading(false);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
loadPdf();
|
|
133
|
-
return () => {
|
|
134
|
-
cancelled = true;
|
|
135
|
-
};
|
|
136
|
-
}, [pdfUrl]);
|
|
137
|
-
const resourceAnchoredRef = useRef(null);
|
|
138
|
-
const fetchResourceAnchored = useCallback(() => {
|
|
139
|
-
if (!session) return Promise.resolve(null);
|
|
140
|
-
const cached = resourceAnchoredRef.current;
|
|
141
|
-
if (cached && cached.uri === resourceUri) return cached.outcome;
|
|
142
|
-
const uri = resourceUri;
|
|
143
|
-
const outcome = session.client.browse.resourceAnchoredText(toResourceId(uri)).then(
|
|
144
|
-
(served) => served && !("declined" in served) ? served : null,
|
|
145
|
-
() => {
|
|
146
|
-
if (resourceAnchoredRef.current?.uri === uri) resourceAnchoredRef.current = null;
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
149
|
-
);
|
|
150
|
-
resourceAnchoredRef.current = { uri, outcome };
|
|
151
|
-
return outcome;
|
|
152
|
-
}, [session, resourceUri]);
|
|
153
|
-
useEffect(() => {
|
|
154
|
-
if (!pdfDoc) return;
|
|
155
|
-
let cancelled = false;
|
|
156
|
-
const doc = pdfDoc;
|
|
157
|
-
setPageAnchored(null);
|
|
158
|
-
async function resolveAnchored(page) {
|
|
159
|
-
try {
|
|
160
|
-
const runs = (await page.getTextContent()).items.filter(isTextRun);
|
|
161
|
-
if (runs.length > 0) return anchorRuns(runs, pageNumber);
|
|
162
|
-
return await fetchResourceAnchored();
|
|
163
|
-
} catch {
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
async function loadPage() {
|
|
168
|
-
try {
|
|
169
|
-
const page = await doc.getPage(pageNumber);
|
|
170
|
-
if (cancelled) return;
|
|
171
|
-
const viewport = page.getViewport({ scale: 1 });
|
|
172
|
-
setPageDimensions({
|
|
173
|
-
width: viewport.width,
|
|
174
|
-
height: viewport.height
|
|
175
|
-
});
|
|
176
|
-
const [anchored, { dataUrl }] = await Promise.all([
|
|
177
|
-
resolveAnchored(page),
|
|
178
|
-
renderPdfPageToDataUrl(page, scale)
|
|
179
|
-
]);
|
|
180
|
-
if (cancelled) return;
|
|
181
|
-
setPageAnchored(anchored);
|
|
182
|
-
setPageImageUrl(dataUrl);
|
|
183
|
-
} catch (err) {
|
|
184
|
-
if (cancelled) return;
|
|
185
|
-
console.error("Error loading page:", err);
|
|
186
|
-
setError("Failed to load page");
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
loadPage();
|
|
190
|
-
return () => {
|
|
191
|
-
cancelled = true;
|
|
192
|
-
};
|
|
193
|
-
}, [pdfDoc, pageNumber, scale, fetchResourceAnchored]);
|
|
194
|
-
useEffect(() => {
|
|
195
|
-
const updateDisplayDimensions = () => {
|
|
196
|
-
if (imageRef.current) {
|
|
197
|
-
setDisplayDimensions({
|
|
198
|
-
width: imageRef.current.clientWidth,
|
|
199
|
-
height: imageRef.current.clientHeight
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
updateDisplayDimensions();
|
|
204
|
-
let resizeObserver = null;
|
|
205
|
-
try {
|
|
206
|
-
resizeObserver = new ResizeObserver(updateDisplayDimensions);
|
|
207
|
-
if (imageRef.current) {
|
|
208
|
-
resizeObserver.observe(imageRef.current);
|
|
209
|
-
}
|
|
210
|
-
} catch (error2) {
|
|
211
|
-
console.warn("ResizeObserver not supported, falling back to window resize listener");
|
|
212
|
-
window.addEventListener("resize", updateDisplayDimensions);
|
|
213
|
-
}
|
|
214
|
-
return () => {
|
|
215
|
-
if (resizeObserver) {
|
|
216
|
-
resizeObserver.disconnect();
|
|
217
|
-
} else {
|
|
218
|
-
window.removeEventListener("resize", updateDisplayDimensions);
|
|
219
|
-
}
|
|
220
|
-
};
|
|
221
|
-
}, [pageImageUrl]);
|
|
222
|
-
const handleMouseDown = useCallback((e) => {
|
|
223
|
-
if (!drawingMode) return;
|
|
224
|
-
if (!imageRef.current) return;
|
|
225
|
-
const rect = imageRef.current.getBoundingClientRect();
|
|
226
|
-
const x = e.clientX - rect.left;
|
|
227
|
-
const y = e.clientY - rect.top;
|
|
228
|
-
setIsDrawing(true);
|
|
229
|
-
setSelection({
|
|
230
|
-
startX: x,
|
|
231
|
-
startY: y,
|
|
232
|
-
endX: x,
|
|
233
|
-
endY: y
|
|
234
|
-
});
|
|
235
|
-
}, [drawingMode]);
|
|
236
|
-
const handleMouseMove = useCallback((e) => {
|
|
237
|
-
if (!isDrawing || !selection || !imageRef.current) return;
|
|
238
|
-
const rect = imageRef.current.getBoundingClientRect();
|
|
239
|
-
setSelection({
|
|
240
|
-
...selection,
|
|
241
|
-
endX: e.clientX - rect.left,
|
|
242
|
-
endY: e.clientY - rect.top
|
|
243
|
-
});
|
|
244
|
-
}, [isDrawing, selection]);
|
|
245
|
-
const handleMouseUp = useCallback(() => {
|
|
246
|
-
if (!isDrawing || !selection || !pageDimensions || !displayDimensions || !session) {
|
|
247
|
-
setIsDrawing(false);
|
|
248
|
-
setSelection(null);
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
const dragDistance = Math.sqrt(
|
|
252
|
-
Math.pow(selection.endX - selection.startX, 2) + Math.pow(selection.endY - selection.startY, 2)
|
|
253
|
-
);
|
|
254
|
-
const MIN_DRAG_DISTANCE = 10;
|
|
255
|
-
if (dragDistance < MIN_DRAG_DISTANCE) {
|
|
256
|
-
if (existingAnnotations.length > 0) {
|
|
257
|
-
let hitRect;
|
|
258
|
-
const hit = rectsForPage(existingAnnotations, pageNumber).find((r) => {
|
|
259
|
-
const rect = pdfToCanvasCoordinates(r.coord, pageDimensions.height, 1);
|
|
260
|
-
const scaleX2 = displayDimensions.width / pageDimensions.width;
|
|
261
|
-
const scaleY2 = displayDimensions.height / pageDimensions.height;
|
|
262
|
-
const displayX = rect.x * scaleX2;
|
|
263
|
-
const displayY = rect.y * scaleY2;
|
|
264
|
-
const displayWidth = rect.width * scaleX2;
|
|
265
|
-
const displayHeight = rect.height * scaleY2;
|
|
266
|
-
const inside = selection.endX >= displayX && selection.endX <= displayX + displayWidth && selection.endY >= displayY && selection.endY <= displayY + displayHeight;
|
|
267
|
-
if (inside && imageRef.current) {
|
|
268
|
-
hitRect = toViewportAnchorRect(imageRef.current.getBoundingClientRect(), displayX, displayY, displayWidth, displayHeight);
|
|
269
|
-
}
|
|
270
|
-
return inside;
|
|
271
|
-
});
|
|
272
|
-
if (hit) {
|
|
273
|
-
session?.client.browse.click(hit.annId, hit.annotation.motivation, hitRect);
|
|
274
|
-
setIsDrawing(false);
|
|
275
|
-
setSelection(null);
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
setIsDrawing(false);
|
|
280
|
-
setSelection(null);
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
const scaleX = pageDimensions.width / displayDimensions.width;
|
|
284
|
-
const scaleY = pageDimensions.height / displayDimensions.height;
|
|
285
|
-
const nativeSelection = {
|
|
286
|
-
startX: selection.startX * scaleX,
|
|
287
|
-
startY: selection.startY * scaleY,
|
|
288
|
-
endX: selection.endX * scaleX,
|
|
289
|
-
endY: selection.endY * scaleY
|
|
290
|
-
};
|
|
291
|
-
const pdfCoord = canvasToPdfCoordinates(
|
|
292
|
-
nativeSelection,
|
|
293
|
-
pageNumber,
|
|
294
|
-
pageDimensions.width,
|
|
295
|
-
pageDimensions.height,
|
|
296
|
-
1
|
|
297
|
-
// Use scale 1.0 since we already scaled to native coords
|
|
298
|
-
);
|
|
299
|
-
const fragmentSelector = createFragmentSelector(pdfCoord);
|
|
300
|
-
const quoted = pageAnchored ? textUnder(pageAnchored, pdfCoord) : "";
|
|
301
|
-
if (selectedMotivation) {
|
|
302
|
-
session.client.mark.request(
|
|
303
|
-
toResourceId(resourceUri),
|
|
304
|
-
[
|
|
305
|
-
{
|
|
306
|
-
type: "FragmentSelector",
|
|
307
|
-
conformsTo: "http://tools.ietf.org/rfc/rfc3778",
|
|
308
|
-
value: fragmentSelector
|
|
309
|
-
},
|
|
310
|
-
...quoted ? [{ type: "TextQuoteSelector", exact: quoted }] : []
|
|
311
|
-
],
|
|
312
|
-
selectedMotivation
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
|
-
setIsDrawing(false);
|
|
316
|
-
}, [isDrawing, selection, pageNumber, pageDimensions, displayDimensions, selectedMotivation, existingAnnotations, session, resourceUri, pageAnchored]);
|
|
317
|
-
const pageRects = rectsForPage(existingAnnotations, pageNumber);
|
|
318
|
-
const { handleMouseEnter, handleMouseLeave } = useMemo(
|
|
319
|
-
() => createHoverHandlers((id) => session?.client.beckon.hover(id), hoverDelayMs),
|
|
320
|
-
[session, hoverDelayMs]
|
|
321
|
-
);
|
|
322
|
-
const { stroke, fill } = getMotivationColor(selectedMotivation ?? null);
|
|
323
|
-
if (error) {
|
|
324
|
-
return /* @__PURE__ */ jsx("div", { className: "semiont-pdf-annotation-canvas__error", children: error });
|
|
325
|
-
}
|
|
326
|
-
return /* @__PURE__ */ jsxs("div", { className: "semiont-pdf-annotation-canvas", children: [
|
|
327
|
-
isLoading && /* @__PURE__ */ jsx("div", { className: "semiont-pdf-annotation-canvas__loading", children: "Loading PDF..." }),
|
|
328
|
-
/* @__PURE__ */ jsxs(
|
|
329
|
-
"div",
|
|
330
|
-
{
|
|
331
|
-
ref: containerRef,
|
|
332
|
-
className: "semiont-pdf-annotation-canvas__container",
|
|
333
|
-
style: { display: isLoading ? "none" : void 0 },
|
|
334
|
-
onMouseDown: handleMouseDown,
|
|
335
|
-
onMouseMove: handleMouseMove,
|
|
336
|
-
onMouseUp: handleMouseUp,
|
|
337
|
-
onMouseLeave: () => {
|
|
338
|
-
if (isDrawing) {
|
|
339
|
-
setIsDrawing(false);
|
|
340
|
-
setSelection(null);
|
|
341
|
-
}
|
|
342
|
-
},
|
|
343
|
-
"data-drawing-mode": drawingMode || "none",
|
|
344
|
-
children: [
|
|
345
|
-
pageImageUrl && /* @__PURE__ */ jsx(
|
|
346
|
-
"img",
|
|
347
|
-
{
|
|
348
|
-
ref: imageRef,
|
|
349
|
-
src: pageImageUrl,
|
|
350
|
-
alt: `PDF page ${pageNumber}`,
|
|
351
|
-
className: "semiont-pdf-annotation-canvas__image",
|
|
352
|
-
draggable: false,
|
|
353
|
-
style: { pointerEvents: "none" },
|
|
354
|
-
onLoad: () => {
|
|
355
|
-
requestAnimationFrame(() => {
|
|
356
|
-
requestAnimationFrame(() => {
|
|
357
|
-
if (imageRef.current) {
|
|
358
|
-
setDisplayDimensions({
|
|
359
|
-
width: imageRef.current.clientWidth,
|
|
360
|
-
height: imageRef.current.clientHeight
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
});
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
),
|
|
368
|
-
displayDimensions && pageDimensions && /* @__PURE__ */ jsx("div", { className: "semiont-pdf-annotation-canvas__overlay-container", children: /* @__PURE__ */ jsx("div", { className: "semiont-pdf-annotation-canvas__overlay", children: /* @__PURE__ */ jsxs(
|
|
369
|
-
"svg",
|
|
370
|
-
{
|
|
371
|
-
className: "semiont-pdf-annotation-canvas__svg",
|
|
372
|
-
width: displayDimensions.width,
|
|
373
|
-
height: displayDimensions.height,
|
|
374
|
-
children: [
|
|
375
|
-
pageRects.map((r) => {
|
|
376
|
-
const rect = pdfToCanvasCoordinates(r.coord, pageDimensions.height, 1);
|
|
377
|
-
const scaleX = displayDimensions.width / pageDimensions.width;
|
|
378
|
-
const scaleY = displayDimensions.height / pageDimensions.height;
|
|
379
|
-
const isHovered = r.annId === hoveredAnnotationId;
|
|
380
|
-
const isSelected = r.annId === selectedAnnotationId;
|
|
381
|
-
const annMotivation = r.annotation.motivation;
|
|
382
|
-
const { stroke: annStroke, fill: annFill } = getMotivationColor(annMotivation);
|
|
383
|
-
return /* @__PURE__ */ jsx(
|
|
384
|
-
"rect",
|
|
385
|
-
{
|
|
386
|
-
x: rect.x * scaleX,
|
|
387
|
-
y: rect.y * scaleY,
|
|
388
|
-
width: rect.width * scaleX,
|
|
389
|
-
height: rect.height * scaleY,
|
|
390
|
-
stroke: annStroke,
|
|
391
|
-
strokeWidth: isSelected ? 4 : isHovered ? 3 : 2,
|
|
392
|
-
fill: annFill,
|
|
393
|
-
style: {
|
|
394
|
-
pointerEvents: "auto",
|
|
395
|
-
cursor: "pointer",
|
|
396
|
-
opacity: isSelected ? 1 : isHovered ? 0.9 : 0.7
|
|
397
|
-
},
|
|
398
|
-
onClick: (e) => session?.client.browse.click(r.annId, r.annotation.motivation, e.currentTarget.getBoundingClientRect()),
|
|
399
|
-
onMouseEnter: () => handleMouseEnter(r.annId),
|
|
400
|
-
onMouseLeave: handleMouseLeave
|
|
401
|
-
},
|
|
402
|
-
`${r.annId}:${r.selectorIndex}`
|
|
403
|
-
);
|
|
404
|
-
}),
|
|
405
|
-
selection && (() => {
|
|
406
|
-
const rectX = Math.min(selection.startX, selection.endX);
|
|
407
|
-
const rectY = Math.min(selection.startY, selection.endY);
|
|
408
|
-
const rectWidth = Math.abs(selection.endX - selection.startX);
|
|
409
|
-
const rectHeight = Math.abs(selection.endY - selection.startY);
|
|
410
|
-
return /* @__PURE__ */ jsx(
|
|
411
|
-
"rect",
|
|
412
|
-
{
|
|
413
|
-
x: rectX,
|
|
414
|
-
y: rectY,
|
|
415
|
-
width: rectWidth,
|
|
416
|
-
height: rectHeight,
|
|
417
|
-
stroke,
|
|
418
|
-
strokeWidth: 2,
|
|
419
|
-
strokeDasharray: "5,5",
|
|
420
|
-
fill,
|
|
421
|
-
pointerEvents: "none"
|
|
422
|
-
}
|
|
423
|
-
);
|
|
424
|
-
})()
|
|
425
|
-
]
|
|
426
|
-
}
|
|
427
|
-
) }) })
|
|
428
|
-
]
|
|
429
|
-
}
|
|
430
|
-
),
|
|
431
|
-
numPages > 0 && /* @__PURE__ */ jsxs("div", { className: "semiont-pdf-annotation-canvas__controls", children: [
|
|
432
|
-
/* @__PURE__ */ jsx(
|
|
433
|
-
"button",
|
|
434
|
-
{
|
|
435
|
-
disabled: pageNumber <= 1,
|
|
436
|
-
onClick: () => setPageNumber(pageNumber - 1),
|
|
437
|
-
className: "semiont-pdf-annotation-canvas__button",
|
|
438
|
-
children: "Previous"
|
|
439
|
-
}
|
|
440
|
-
),
|
|
441
|
-
/* @__PURE__ */ jsxs("span", { className: "semiont-pdf-annotation-canvas__page-info", children: [
|
|
442
|
-
"Page ",
|
|
443
|
-
pageNumber,
|
|
444
|
-
" of ",
|
|
445
|
-
numPages
|
|
446
|
-
] }),
|
|
447
|
-
/* @__PURE__ */ jsx(
|
|
448
|
-
"button",
|
|
449
|
-
{
|
|
450
|
-
disabled: pageNumber >= numPages,
|
|
451
|
-
onClick: () => setPageNumber(pageNumber + 1),
|
|
452
|
-
className: "semiont-pdf-annotation-canvas__button",
|
|
453
|
-
children: "Next"
|
|
454
|
-
}
|
|
455
|
-
)
|
|
456
|
-
] })
|
|
457
|
-
] });
|
|
458
|
-
}
|
|
459
|
-
export {
|
|
460
|
-
PdfAnnotationCanvas
|
|
461
|
-
};
|
|
462
|
-
//# sourceMappingURL=PdfAnnotationCanvas.client-EHQWF6BR.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/pdf-annotation/PdfAnnotationCanvas.tsx","../src/components/pdf-annotation/rects-for-page.ts","../src/lib/pdf-coordinates.ts"],"sourcesContent":["'use client';\n\nimport React, { useRef, useState, useCallback, useEffect, useMemo } from 'react';\nimport type { Annotation, AnchorRect, AnchoredText } from '@semiont/core';\nimport { resourceId as toResourceId } from '@semiont/core';\nimport { toViewportAnchorRect } from '../../lib/anchor-rect';\nimport { createFragmentSelector, anchorRuns, isTextRun, textUnder } from '@semiont/core';\nimport { rectsForPage } from './rects-for-page';\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, TextQuoteSelector?], motivation: SelectionMotivation } — the quote is the text under the rectangle, omitted when the page has no text layer\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 /**\n * The current page's text and per-run geometry, read once when the page\n * loads so a drag can be quoted without a round trip. Null while the page\n * is loading; `items` is empty on a scanned page, which has no text layer\n * for the browser to read.\n */\n const [pageAnchored, setPageAnchored] = useState<AnchoredText | 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 /**\n * The server-derived map, fetched once per document rather than once per\n * page-load effect (PERSIST-ANCHORS P4). The map is WHOLE-RESOURCE — one\n * artifact covering every page — so re-reading it from the per-page effect\n * meant a full refetch and re-decode on every page turn; on a 400-page scan\n * that is one decode per interaction instead of one per document.\n *\n * The cache holds the in-flight promise so concurrent page loads share one\n * fetch. Answers cache — including \"no map\" and a stored decline, which are\n * definitive — but a transport failure clears the entry, so the next page\n * load retries instead of pinning the whole document to geometry-only.\n */\n const resourceAnchoredRef = useRef<{ uri: string; outcome: Promise<AnchoredText | null> } | null>(null);\n const fetchResourceAnchored = useCallback((): Promise<AnchoredText | null> => {\n if (!session) return Promise.resolve(null); // no session yet — don't cache its absence\n const cached = resourceAnchoredRef.current;\n if (cached && cached.uri === resourceUri) return cached.outcome;\n\n const uri = resourceUri;\n const outcome = session.client.browse.resourceAnchoredText(toResourceId(uri)).then(\n (served) => (served && !('declined' in served) ? served : null),\n () => {\n if (resourceAnchoredRef.current?.uri === uri) resourceAnchoredRef.current = null;\n return null;\n },\n );\n resourceAnchoredRef.current = { uri, outcome };\n return outcome;\n }, [session, resourceUri]);\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 // Never quote the previous page's text under this page's rectangles.\n setPageAnchored(null);\n\n /**\n * The map a rectangle on this page quotes from, or null when there is\n * none to be had.\n *\n * Never rejects, which is what lets the render run alongside it: quoting\n * is the optional half of loading a page, so a failure here degrades to\n * geometry-only rather than reaching the caller's error path. It is also\n * the only reason `Promise.all` below is safe — a rejection from either\n * side would leave the other promise dangling, and only the render can\n * reject.\n */\n async function resolveAnchored(page: Awaited<ReturnType<typeof doc.getPage>>): Promise<AnchoredText | null> {\n try {\n // The page's text layer, read once here rather than at drag time —\n // `handleMouseUp` stays synchronous, and a native page costs nothing\n // extra since pdf.js already parsed it to draw the page.\n const runs = (await page.getTextContent()).items.filter(isTextRun);\n if (runs.length > 0) return anchorRuns(runs, pageNumber);\n\n // No runs means a scanned page: the characters exist only as pixels\n // and pdf.js has nothing to give. The server derived a map at ingest,\n // so ask for it rather than leaving the annotation anonymous.\n // Whole-resource — served once per document via the cache above —\n // and `textUnder` filters by page: the same shape the native branch\n // produces, so nothing downstream branches.\n //\n // `null` is the ordinary answer for a document that has no map and\n // never will; a failure is equally non-fatal. Either way the\n // annotation ships with geometry only, which is what shipped before\n // this existed. The served record is the full extraction outcome\n // (PERSIST-ANCHORS D1); a stored decline means extraction ran and\n // found nothing to anchor — for this canvas the same degradation as\n // no map at all. A success outcome IS the anchoring shape, plus\n // provenance this canvas does not read.\n return await fetchResourceAnchored();\n } catch {\n return null;\n }\n }\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 // Anchoring and rendering are independent, and only one of them the\n // reader is waiting on. Sequencing them put a network round-trip in\n // front of the pixels on exactly the documents that need it most: a\n // scanned page fetches its map from the server, and rendering behind\n // that await is how \"failing to quote it must not fail to show it\"\n // became true of errors but not of latency. Started together, the\n // page appears on its own schedule.\n const [anchored, { dataUrl }] = await Promise.all([\n resolveAnchored(page),\n renderPdfPageToDataUrl(page, scale),\n ]);\n\n if (cancelled) return;\n\n setPageAnchored(anchored);\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, fetchResourceAnchored]);\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 hit = rectsForPage(existingAnnotations, pageNumber).find(r => {\n const rect = pdfToCanvasCoordinates(r.coord, 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 inside = (\n selection.endX >= displayX &&\n selection.endX <= displayX + displayWidth &&\n selection.endY >= displayY &&\n selection.endY <= displayY + displayHeight\n );\n if (inside && imageRef.current) {\n hitRect = toViewportAnchorRect(imageRef.current.getBoundingClientRect(), displayX, displayY, displayWidth, displayHeight);\n }\n return inside;\n });\n\n if (hit) {\n session?.client.browse.click(hit.annId, hit.annotation.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 // What the box was drawn around. Without it the annotation is a rectangle\n // with no memory of its own content: the panel entry is blank, search over\n // annotation text misses it, and an export has nothing to print. Empty on a\n // scanned page or over an image — emit no quote at all rather than an empty\n // one, which would assert the box was drawn around nothing.\n const quoted = pageAnchored ? textUnder(pageAnchored, pdfCoord) : '';\n\n // Emit annotation:requested event with FragmentSelector\n if (selectedMotivation) {\n session.client.mark.request(\n toResourceId(resourceUri),\n [\n {\n type: 'FragmentSelector',\n conformsTo: 'http://tools.ietf.org/rfc/rfc3778',\n value: fragmentSelector,\n },\n ...(quoted ? [{ type: 'TextQuoteSelector' as const, exact: quoted }] : []),\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, session, resourceUri, pageAnchored]);\n\n // Every FragmentSelector rect on the current page — one per line for a\n // multi-line (multi-selector) annotation, exactly one for a manual annotation.\n const pageRects = rectsForPage(existingAnnotations, pageNumber);\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 {pageRects.map(r => {\n const rect = pdfToCanvasCoordinates(r.coord, 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 = r.annId === hoveredAnnotationId;\n const isSelected = r.annId === selectedAnnotationId;\n\n // Colour by the annotation's own motivation (not the toolbar's).\n const annMotivation = r.annotation.motivation as SelectionMotivation | null;\n const { stroke: annStroke, fill: annFill } = getMotivationColor(annMotivation);\n\n return (\n <rect\n key={`${r.annId}:${r.selectorIndex}`}\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(r.annId, r.annotation.motivation, e.currentTarget.getBoundingClientRect())}\n onMouseEnter={() => handleMouseEnter(r.annId)}\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","import type { Annotation, PdfCoordinate } from '@semiont/core';\nimport { getTargetSelector, parseFragmentSelector } from '@semiont/core';\n\n/** One FragmentSelector rectangle to paint on a PDF page. */\nexport interface PageRect {\n /** Owning annotation id — shared by every rect of a multi-line annotation (hover/click routing). */\n annId: Annotation['id'];\n /** Index within the annotation's FragmentSelectors — the stable half of the React key. */\n selectorIndex: number;\n /** PDF-point geometry for this rect. */\n coord: PdfCoordinate;\n /** Owning annotation (motivation colour, etc.). */\n annotation: Annotation;\n}\n\n/** Every FragmentSelector on a target, in order (`target.selector` may be one or an array). */\nfunction fragmentSelectors(target: Annotation['target']): { value: string }[] {\n const selector = getTargetSelector(target);\n if (!selector) return [];\n const selectors = Array.isArray(selector) ? selector : [selector];\n return selectors\n .filter(s => s.type === 'FragmentSelector')\n .map(s => s as { type: 'FragmentSelector'; value: string });\n}\n\n/**\n * The rectangles to paint on `pageNumber`: one entry per FragmentSelector whose\n * viewrect page matches. A multi-line (multi-selector) annotation therefore yields\n * one rect per line; a single-selector (manual) annotation yields exactly one.\n *\n * Pure — no React/DOM. `PdfAnnotationCanvas` maps this to `<rect>` keyed\n * `${annId}:${selectorIndex}`, and the rects-for-page axioms exercise it directly.\n * Geometry stays deferred: each `coord` still goes through `pdfToCanvasCoordinates`\n * at paint time (itself covered by the coordinate-transform axioms).\n */\nexport function rectsForPage(annotations: Annotation[], pageNumber: number): PageRect[] {\n const rects: PageRect[] = [];\n for (const annotation of annotations) {\n fragmentSelectors(annotation.target).forEach((sel, selectorIndex) => {\n const coord = parseFragmentSelector(sel.value);\n if (coord && coord.page === pageNumber) {\n rects.push({ annId: annotation.id, selectorIndex, coord, annotation });\n }\n });\n }\n return rects;\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","scaleX","scaleY"]}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
/* src/components/pdf-annotation/PdfAnnotationCanvas.css */
|
|
2
|
-
.semiont-pdf-annotation-canvas {
|
|
3
|
-
position: relative;
|
|
4
|
-
display: inline-block;
|
|
5
|
-
}
|
|
6
|
-
.semiont-pdf-annotation-canvas__loading,
|
|
7
|
-
.semiont-pdf-annotation-canvas__error {
|
|
8
|
-
display: flex;
|
|
9
|
-
align-items: center;
|
|
10
|
-
justify-content: center;
|
|
11
|
-
padding: 2rem;
|
|
12
|
-
}
|
|
13
|
-
.semiont-pdf-annotation-canvas__error {
|
|
14
|
-
color: var(--error-color);
|
|
15
|
-
}
|
|
16
|
-
[data-theme=dark] .semiont-pdf-annotation-canvas__error {
|
|
17
|
-
color: var(--error-color);
|
|
18
|
-
}
|
|
19
|
-
.semiont-pdf-annotation-canvas__container {
|
|
20
|
-
position: relative;
|
|
21
|
-
display: inline-block;
|
|
22
|
-
cursor: crosshair;
|
|
23
|
-
}
|
|
24
|
-
.semiont-pdf-annotation-canvas__container[data-drawing-mode=none] {
|
|
25
|
-
cursor: default;
|
|
26
|
-
}
|
|
27
|
-
.semiont-pdf-annotation-canvas__image {
|
|
28
|
-
display: block;
|
|
29
|
-
max-width: 100%;
|
|
30
|
-
height: auto;
|
|
31
|
-
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
|
32
|
-
-webkit-user-select: none;
|
|
33
|
-
-moz-user-select: none;
|
|
34
|
-
user-select: none;
|
|
35
|
-
pointer-events: none;
|
|
36
|
-
}
|
|
37
|
-
[data-theme=dark] .semiont-pdf-annotation-canvas__image {
|
|
38
|
-
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.3), 0 1px 2px -1px rgb(0 0 0 / 0.3);
|
|
39
|
-
}
|
|
40
|
-
.semiont-pdf-annotation-canvas__overlay-container {
|
|
41
|
-
position: absolute;
|
|
42
|
-
top: 0;
|
|
43
|
-
left: 0;
|
|
44
|
-
width: 100%;
|
|
45
|
-
height: 100%;
|
|
46
|
-
pointer-events: none;
|
|
47
|
-
}
|
|
48
|
-
.semiont-pdf-annotation-canvas__overlay {
|
|
49
|
-
position: absolute;
|
|
50
|
-
top: 0;
|
|
51
|
-
left: 0;
|
|
52
|
-
width: 100%;
|
|
53
|
-
height: 100%;
|
|
54
|
-
pointer-events: none;
|
|
55
|
-
}
|
|
56
|
-
.semiont-pdf-annotation-canvas__svg {
|
|
57
|
-
pointer-events: none;
|
|
58
|
-
}
|
|
59
|
-
.semiont-pdf-annotation-canvas__svg rect {
|
|
60
|
-
pointer-events: auto;
|
|
61
|
-
}
|
|
62
|
-
.semiont-pdf-annotation-canvas__controls {
|
|
63
|
-
display: flex;
|
|
64
|
-
align-items: center;
|
|
65
|
-
justify-content: center;
|
|
66
|
-
gap: 1rem;
|
|
67
|
-
padding: 1rem;
|
|
68
|
-
border-top: 1px solid var(--border-color);
|
|
69
|
-
}
|
|
70
|
-
.semiont-pdf-annotation-canvas__button {
|
|
71
|
-
padding: 0.5rem 1rem;
|
|
72
|
-
border: 1px solid var(--border-color);
|
|
73
|
-
border-radius: 4px;
|
|
74
|
-
background: transparent;
|
|
75
|
-
cursor: pointer;
|
|
76
|
-
}
|
|
77
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
78
|
-
.semiont-pdf-annotation-canvas__button {
|
|
79
|
-
transition: background-color 0.2s;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
.semiont-pdf-annotation-canvas__button:hover:not(:disabled) {
|
|
83
|
-
background-color: var(--hover-color, rgba(0, 0, 0, 0.05));
|
|
84
|
-
}
|
|
85
|
-
.semiont-pdf-annotation-canvas__button:focus-visible {
|
|
86
|
-
outline: 2px solid var(--focus-color, #0066cc);
|
|
87
|
-
outline-offset: 2px;
|
|
88
|
-
}
|
|
89
|
-
.semiont-pdf-annotation-canvas__button:disabled {
|
|
90
|
-
opacity: 0.5;
|
|
91
|
-
cursor: not-allowed;
|
|
92
|
-
}
|
|
93
|
-
.semiont-pdf-annotation-canvas__page-info {
|
|
94
|
-
font-size: 0.875rem;
|
|
95
|
-
min-width: 7rem;
|
|
96
|
-
text-align: center;
|
|
97
|
-
}
|
|
98
|
-
/*# sourceMappingURL=PdfAnnotationCanvas.client-SU5KNT7G.css.map */
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/pdf-annotation/PdfAnnotationCanvas.css"],"sourcesContent":["/**\n * PDF Annotation Canvas Styles\n *\n * Provides consistent styling for PDF annotation interface,\n * following the same pattern as image annotation (SvgDrawingCanvas).\n */\n\n.semiont-pdf-annotation-canvas {\n position: relative;\n display: inline-block;\n}\n\n.semiont-pdf-annotation-canvas__loading,\n.semiont-pdf-annotation-canvas__error {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 2rem;\n}\n\n/* stylelint-disable semiont/accessibility -- Error indicator added by semantic-indicators.css */\n.semiont-pdf-annotation-canvas__error {\n color: var(--error-color);\n}\n\n[data-theme=\"dark\"] .semiont-pdf-annotation-canvas__error {\n color: var(--error-color);\n}\n/* stylelint-enable semiont/accessibility */\n\n.semiont-pdf-annotation-canvas__container {\n position: relative;\n display: inline-block;\n cursor: crosshair;\n}\n\n.semiont-pdf-annotation-canvas__container[data-drawing-mode=\"none\"] {\n cursor: default;\n}\n\n.semiont-pdf-annotation-canvas__image {\n display: block;\n max-width: 100%;\n height: auto;\n box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n pointer-events: none;\n}\n\n[data-theme=\"dark\"] .semiont-pdf-annotation-canvas__image {\n box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.3), 0 1px 2px -1px rgb(0 0 0 / 0.3);\n}\n\n.semiont-pdf-annotation-canvas__overlay-container {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n}\n\n.semiont-pdf-annotation-canvas__overlay {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n}\n\n.semiont-pdf-annotation-canvas__svg {\n pointer-events: none;\n}\n\n.semiont-pdf-annotation-canvas__svg rect {\n pointer-events: auto;\n}\n\n.semiont-pdf-annotation-canvas__controls {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n padding: 1rem;\n border-top: 1px solid var(--border-color);\n}\n\n.semiont-pdf-annotation-canvas__button {\n padding: 0.5rem 1rem;\n border: 1px solid var(--border-color);\n border-radius: 4px;\n background: transparent;\n cursor: pointer;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n .semiont-pdf-annotation-canvas__button {\n transition: background-color 0.2s;\n }\n}\n\n.semiont-pdf-annotation-canvas__button:hover:not(:disabled) {\n background-color: var(--hover-color, rgba(0, 0, 0, 0.05));\n}\n\n.semiont-pdf-annotation-canvas__button:focus-visible {\n outline: 2px solid var(--focus-color, #0066cc);\n outline-offset: 2px;\n}\n\n.semiont-pdf-annotation-canvas__button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.semiont-pdf-annotation-canvas__page-info {\n font-size: 0.875rem;\n min-width: 7rem;\n text-align: center;\n}\n\n/*# sourceMappingURL=src/components/pdf-annotation/PdfAnnotationCanvas.css.map */"],"mappings":";AAOA,CAAC;AACC,YAAU;AACV,WAAS;AACX;AAEA,CAAC;AACD,CAAC;AACC,WAAS;AACT,eAAa;AACb,mBAAiB;AACjB,WAAS;AACX;AAGA,CARC;AASC,SAAO,IAAI;AACb;AAEA,CAAC,iBAAmB,CAZnB;AAaC,SAAO,IAAI;AACb;AAGA,CAAC;AACC,YAAU;AACV,WAAS;AACT,UAAQ;AACV;AAEA,CANC,wCAMwC,CAAC;AACxC,UAAQ;AACV;AAEA,CAAC;AACC,WAAS;AACT,aAAW;AACX,UAAQ;AACR,cAAY,EAAE,IAAI,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,IAAI,KAAK,IAAI,EAAE,EAAE,EAAE,EAAE;AACrE,uBAAqB;AAClB,oBAAkB;AACb,eAAa;AACrB,kBAAgB;AAClB;AAEA,CAAC,iBAAmB,CAXnB;AAYC,cAAY,EAAE,IAAI,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,IAAI,KAAK,IAAI,EAAE,EAAE,EAAE,EAAE;AACvE;AAEA,CAAC;AACC,YAAU;AACV,OAAK;AACL,QAAM;AACN,SAAO;AACP,UAAQ;AACR,kBAAgB;AAClB;AAEA,CAAC;AACC,YAAU;AACV,OAAK;AACL,QAAM;AACN,SAAO;AACP,UAAQ;AACR,kBAAgB;AAClB;AAEA,CAAC;AACC,kBAAgB;AAClB;AAEA,CAJC,mCAImC;AAClC,kBAAgB;AAClB;AAEA,CAAC;AACC,WAAS;AACT,eAAa;AACb,mBAAiB;AACjB,OAAK;AACL,WAAS;AACT,cAAY,IAAI,MAAM,IAAI;AAC5B;AAEA,CAAC;AACC,WAAS,OAAO;AAChB,UAAQ,IAAI,MAAM,IAAI;AACtB,iBAAe;AACf,cAAY;AACZ,UAAQ;AACV;AAEA,QAAO,wBAAyB;AAC9B,GATD;AAUG,gBAAY,iBAAiB;AAC/B;AACF;AAEA,CAdC,qCAcqC,MAAM,KAAK;AAC/C,oBAAkB,IAAI,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AACrD;AAEA,CAlBC,qCAkBqC;AACpC,WAAS,IAAI,MAAM,IAAI,aAAa,EAAE;AACtC,kBAAgB;AAClB;AAEA,CAvBC,qCAuBqC;AACpC,WAAS;AACT,UAAQ;AACV;AAEA,CAAC;AACC,aAAW;AACX,aAAW;AACX,cAAY;AACd;","names":[]}
|