@sandemo/easy-annotator 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +59 -0
- package/dist/components/AnnotatorModal.d.ts +13 -0
- package/dist/components/AnnotatorModal.d.ts.map +1 -0
- package/dist/components/EasyAnnotatorUpload.d.ts +3 -0
- package/dist/components/EasyAnnotatorUpload.d.ts.map +1 -0
- package/dist/components/FilePreviewList.d.ts +10 -0
- package/dist/components/FilePreviewList.d.ts.map +1 -0
- package/dist/components/ImageAnnotator.d.ts +17 -0
- package/dist/components/ImageAnnotator.d.ts.map +1 -0
- package/dist/components/UploadDropzone.d.ts +10 -0
- package/dist/components/UploadDropzone.d.ts.map +1 -0
- package/dist/easy-annotator.cjs +2 -0
- package/dist/easy-annotator.cjs.map +1 -0
- package/dist/easy-annotator.css +1 -0
- package/dist/easy-annotator.d.ts +2 -0
- package/dist/easy-annotator.js +709 -0
- package/dist/easy-annotator.js.map +1 -0
- package/dist/hooks/useEasyAnnotator.d.ts +29 -0
- package/dist/hooks/useEasyAnnotator.d.ts.map +1 -0
- package/dist/hooks/useObjectUrls.d.ts +5 -0
- package/dist/hooks/useObjectUrls.d.ts.map +1 -0
- package/dist/i18n/labels.d.ts +5 -0
- package/dist/i18n/labels.d.ts.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/markerjs3-DI_cywfl.js +2669 -0
- package/dist/markerjs3-DI_cywfl.js.map +1 -0
- package/dist/markerjs3-DiaiYpgs.cjs +57 -0
- package/dist/markerjs3-DiaiYpgs.cjs.map +1 -0
- package/dist/services/defaultPayloadBuilder.d.ts +3 -0
- package/dist/services/defaultPayloadBuilder.d.ts.map +1 -0
- package/dist/styles.cjs +2 -0
- package/dist/styles.cjs.map +1 -0
- package/dist/styles.d.ts +1 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/styles.js +2 -0
- package/dist/styles.js.map +1 -0
- package/dist/types/index.d.ts +95 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/utils/errors.d.ts +8 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/file.d.ts +10 -0
- package/dist/utils/file.d.ts.map +1 -0
- package/dist/utils/id.d.ts +2 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/image.d.ts +3 -0
- package/dist/utils/image.d.ts.map +1 -0
- package/package.json +72 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sandemo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# @sandemo/easy-annotator
|
|
2
|
+
|
|
3
|
+
Reusable React/Next.js component for **file upload with image annotation**. Drop it into any form, point it at your own backend via `uploadHandler`, done.
|
|
4
|
+
|
|
5
|
+
- Image annotation before upload (powered by [marker.js 3](https://markerjs.com/), lazy-loaded)
|
|
6
|
+
- Backend-agnostic — works with S3, MinIO, R2, Laravel, Go Fiber, Next.js API routes, anything
|
|
7
|
+
- TypeScript-first, Next.js-friendly (App Router + Pages Router)
|
|
8
|
+
- PDF / DOC / DOCX passthrough (annotation skipped automatically)
|
|
9
|
+
- ~150 KB gzipped, zero runtime config
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @sandemo/easy-annotator
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Minimal usage
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
'use client'
|
|
21
|
+
|
|
22
|
+
import { EasyAnnotatorUpload, buildFormDataPayload } from '@sandemo/easy-annotator'
|
|
23
|
+
import '@sandemo/easy-annotator/styles.css'
|
|
24
|
+
|
|
25
|
+
const uploadHandler = async (payload) => {
|
|
26
|
+
const formData = buildFormDataPayload(payload)
|
|
27
|
+
const res = await fetch('/api/evidences', { method: 'POST', body: formData })
|
|
28
|
+
if (!res.ok) throw new Error('Upload failed')
|
|
29
|
+
return res.json()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default function MyForm() {
|
|
33
|
+
return (
|
|
34
|
+
<EasyAnnotatorUpload
|
|
35
|
+
title="Upload Lampiran"
|
|
36
|
+
ownerType="surat_rekomendasi"
|
|
37
|
+
ownerId={123}
|
|
38
|
+
fieldKey="hasil_pengamatan"
|
|
39
|
+
uploadHandler={uploadHandler}
|
|
40
|
+
onUploaded={(items) => console.log(items)}
|
|
41
|
+
/>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Documentation
|
|
47
|
+
|
|
48
|
+
- [Usage / quickstart](./docs/usage.md)
|
|
49
|
+
- [Next.js integration](./docs/nextjs.md)
|
|
50
|
+
- [Upload handler contract](./docs/upload-handler.md)
|
|
51
|
+
- [API reference](./docs/api-reference.md)
|
|
52
|
+
|
|
53
|
+
## Contributing
|
|
54
|
+
|
|
55
|
+
Issues and pull requests are welcome at [github.com/sandemoit/easy-annotator](https://github.com/sandemoit/easy-annotator).
|
|
56
|
+
|
|
57
|
+
## License
|
|
58
|
+
|
|
59
|
+
[MIT](./LICENSE) © Sandemo
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { LabelOverrides } from '../types';
|
|
2
|
+
export interface AnnotatorModalProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
src: string | null;
|
|
5
|
+
originalFilename: string;
|
|
6
|
+
initialState?: object;
|
|
7
|
+
labels: LabelOverrides;
|
|
8
|
+
onSave: (annotatedFile: File, annotationState: object) => void;
|
|
9
|
+
onCancel: () => void;
|
|
10
|
+
onError?: (err: unknown) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare function AnnotatorModal({ open, src, originalFilename, initialState, labels, onSave, onCancel, onError, }: AnnotatorModalProps): import("react/jsx-runtime").JSX.Element | null;
|
|
13
|
+
//# sourceMappingURL=AnnotatorModal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnnotatorModal.d.ts","sourceRoot":"","sources":["../../src/components/AnnotatorModal.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAG9C,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAA;IACb,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,gBAAgB,EAAE,MAAM,CAAA;IACxB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,cAAc,CAAA;IACtB,MAAM,EAAE,CAAC,aAAa,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9D,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CACjC;AAED,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,GAAG,EACH,gBAAgB,EAChB,YAAY,EACZ,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAO,GACR,EAAE,mBAAmB,kDA6FrB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EasyAnnotatorUpload.d.ts","sourceRoot":"","sources":["../../src/components/EasyAnnotatorUpload.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAA;AASxD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,2CAqIlE"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { EasyAnnotatorFile, LabelOverrides } from '../types';
|
|
2
|
+
export interface FilePreviewListProps {
|
|
3
|
+
files: EasyAnnotatorFile[];
|
|
4
|
+
labels: LabelOverrides;
|
|
5
|
+
onAnnotate: (id: string) => void;
|
|
6
|
+
onRemove: (id: string) => void;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function FilePreviewList({ files, labels, onAnnotate, onRemove, disabled, }: FilePreviewListProps): import("react/jsx-runtime").JSX.Element | null;
|
|
10
|
+
//# sourceMappingURL=FilePreviewList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FilePreviewList.d.ts","sourceRoot":"","sources":["../../src/components/FilePreviewList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAEjE,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,iBAAiB,EAAE,CAAA;IAC1B,MAAM,EAAE,cAAc,CAAA;IACtB,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAeD,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,MAAM,EACN,UAAU,EACV,QAAQ,EACR,QAAgB,GACjB,EAAE,oBAAoB,kDAsDtB"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { LabelOverrides } from '../types';
|
|
2
|
+
export interface ImageAnnotatorHandle {
|
|
3
|
+
save: () => Promise<{
|
|
4
|
+
file: File;
|
|
5
|
+
state: object;
|
|
6
|
+
}>;
|
|
7
|
+
}
|
|
8
|
+
export interface ImageAnnotatorProps {
|
|
9
|
+
src: string;
|
|
10
|
+
originalFilename: string;
|
|
11
|
+
initialState?: object;
|
|
12
|
+
labels: LabelOverrides;
|
|
13
|
+
onReady?: (handle: ImageAnnotatorHandle) => void;
|
|
14
|
+
onError?: (err: unknown) => void;
|
|
15
|
+
}
|
|
16
|
+
export declare function ImageAnnotator({ src, originalFilename, initialState, labels, onReady, onError, }: ImageAnnotatorProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
//# sourceMappingURL=ImageAnnotator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageAnnotator.d.ts","sourceRoot":"","sources":["../../src/components/ImageAnnotator.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAI9C,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,OAAO,CAAC;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACnD;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,gBAAgB,EAAE,MAAM,CAAA;IACxB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,cAAc,CAAA;IACtB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAA;IAChD,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CACjC;AAmBD,wBAAgB,cAAc,CAAC,EAC7B,GAAG,EACH,gBAAgB,EAChB,YAAY,EACZ,MAAM,EACN,OAAO,EACP,OAAO,GACR,EAAE,mBAAmB,2CAwGrB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { LabelOverrides } from '../types';
|
|
2
|
+
export interface UploadDropzoneProps {
|
|
3
|
+
accept: string[];
|
|
4
|
+
multiple: boolean;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
labels: LabelOverrides;
|
|
7
|
+
onFiles: (files: FileList | File[]) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function UploadDropzone({ accept, multiple, disabled, labels, onFiles, }: UploadDropzoneProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
//# sourceMappingURL=UploadDropzone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UploadDropzone.d.ts","sourceRoot":"","sources":["../../src/components/UploadDropzone.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAE9C,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,cAAc,CAAA;IACtB,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,EAAE,KAAK,IAAI,CAAA;CAC5C;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,QAAQ,EACR,QAAgB,EAChB,MAAM,EACN,OAAO,GACR,EAAE,mBAAmB,2CAgFrB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("react/jsx-runtime"),a=require("react"),B=["image/jpeg","image/jpg","image/png","image/webp","application/pdf","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document"],X=10*1024*1024,K=5,re=new Set(["image/jpeg","image/jpg","image/png","image/webp"]);function se(e){return re.has(e.toLowerCase())}function J(e){const t=e.type.toLowerCase();if(se(t))return"image";if(t==="application/pdf")return"pdf";if(t==="application/msword")return"doc";if(t==="application/vnd.openxmlformats-officedocument.wordprocessingml.document")return"docx";const o=e.name.toLowerCase().split(".").pop()??"";return["jpg","jpeg","png","webp"].includes(o)?"image":o==="pdf"?"pdf":o==="doc"?"doc":o==="docx"?"docx":"doc"}function V(e,t){if(t.length===0)return!0;const o=e.type.toLowerCase();if(o&&t.some(f=>f.toLowerCase()===o))return!0;const i=e.name.toLowerCase().split(".").pop()??"",s={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp",pdf:"application/pdf",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document"}[i];return s?t.includes(s):!1}function G(e){return Math.round(e/(1024*1024)*10)/10}function ie(e){const t=e.lastIndexOf(".");return t<=0?`${e}-annotated.png`:`${e.slice(0,t)}-annotated.png`}const z={title:"Upload",dropzoneText:"Drag & drop files here, or",dropzoneButton:"browse",annotate:"Annotate",remove:"Remove",upload:"Upload",uploading:"Uploading…",uploaded:"Uploaded",retry:"Retry",cancel:"Cancel",save:"Save",errorFileType:e=>`File type not allowed: ${e}`,errorFileTooLarge:(e,t)=>`File too large: ${e} (max ${t} MB)`,errorFileCount:e=>`Maximum ${e} files`,errorAnnotationFailed:"Failed to save annotation.",errorUploadFailed:"Upload failed. Please retry.",modalTitle:"Annotate image",loadingAnnotator:"Loading annotator…",filesSelected:e=>`${e} file${e===1?"":"s"} selected`},le={title:"Unggah",dropzoneText:"Seret & letakkan file di sini, atau",dropzoneButton:"pilih file",annotate:"Anotasi",remove:"Hapus",upload:"Unggah",uploading:"Mengunggah…",uploaded:"Terunggah",retry:"Coba Lagi",cancel:"Batal",save:"Simpan",errorFileType:e=>`Tipe file tidak diizinkan: ${e}`,errorFileTooLarge:(e,t)=>`File terlalu besar: ${e} (maks ${t} MB)`,errorFileCount:e=>`Maksimal ${e} file`,errorAnnotationFailed:"Gagal menyimpan anotasi.",errorUploadFailed:"Unggah gagal. Silakan coba lagi.",modalTitle:"Anotasi gambar",loadingAnnotator:"Memuat anotator…",filesSelected:e=>`${e} file dipilih`};function W(e){return e?{...z,...e}:z}function ce(e,t){return{code:"FILE_TYPE_REJECTED",file:e,reason:t}}function de(e,t){return{code:"FILE_TOO_LARGE",file:e,maxBytes:t}}function ue(e){return{code:"FILE_COUNT_EXCEEDED",maxFiles:e}}function Z(e){return{code:"ANNOTATION_FAILED",cause:e}}function pe(e,t){return{code:"UPLOAD_FAILED",cause:t,file:e}}function H(...e){var o;(typeof process<"u"&&((o=process==null?void 0:process.env)!=null&&o.NODE_ENV)?process.env.NODE_ENV:"development")!=="production"&&console.warn("[easy-annotator]",...e)}function me(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`ea_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,10)}`}function Y(){const e=a.useRef(new Set),t=a.useCallback(i=>{const c=URL.createObjectURL(i);return e.current.add(c),c},[]),o=a.useCallback(i=>{i&&e.current.has(i)&&(URL.revokeObjectURL(i),e.current.delete(i))},[]);return a.useEffect(()=>{const i=e.current;return()=>{i.forEach(c=>URL.revokeObjectURL(c)),i.clear()}},[]),{create:t,revoke:o}}function Q(e){const{accept:t=B,maxFileSize:o=X,maxFiles:i=K,multiple:c=!0,ownerType:s,ownerId:f,fieldKey:d,uploadHandler:_,onError:x,onUploaded:w,onChange:F}=e,[v,n]=a.useState([]),[j,L]=a.useState(!1),{create:u,revoke:p}=Y(),A=a.useRef(F),g=a.useRef(x),U=a.useRef(w);a.useEffect(()=>{A.current=F,g.current=x,U.current=w},[F,x,w]),a.useEffect(()=>{var m;(m=A.current)==null||m.call(A,v)},[v]);const D=a.useCallback(m=>{const b=Array.from(m);n(h=>{var E,N,R;const y=[...h],l=Math.max(0,i-h.length);let k=!1;for(const S of b){if(!c&&y.length>=1)break;if(y.length>=h.length+l){k=!0;break}if(!V(S,t)){(E=g.current)==null||E.call(g,ce(S,`MIME ${S.type||"unknown"}`));continue}if(S.size>o){(N=g.current)==null||N.call(g,de(S,o));continue}const q=J(S),oe=q==="image"?u(S):void 0;y.push({id:me(),originalFile:S,fileType:q,mimeType:S.type||"application/octet-stream",originalFilename:S.name,previewUrl:oe,status:"idle"})}return(k||b.length>l)&&((R=g.current)==null||R.call(g,ue(i))),y})},[t,u,o,i,c]),O=a.useCallback(m=>{n(b=>{const h=b.find(y=>y.id===m);return h&&(p(h.previewUrl),p(h.annotatedPreviewUrl)),b.filter(y=>y.id!==m)})},[p]),I=a.useCallback((m,b,h)=>{n(y=>y.map(l=>l.id!==m?l:(p(l.annotatedPreviewUrl),{...l,annotatedFile:b,annotationState:h,annotatedPreviewUrl:u(b)})))},[u,p]),P=a.useCallback(m=>{n(b=>b.map(h=>{if(h.id!==m)return h;p(h.annotatedPreviewUrl);const{annotatedFile:y,annotationState:l,annotatedPreviewUrl:k,...E}=h;return{...E}}))},[p]),T=a.useCallback((m,b,h)=>{n(y=>y.map(l=>l.id===m?{...l,status:b,errorMessage:h}:l))},[]),M=a.useCallback(m=>({ownerType:s,ownerId:f,fieldKey:d,originalFile:m.originalFile,annotatedFile:m.annotatedFile,annotationState:m.annotationState,fileType:m.fileType,mimeType:m.mimeType,originalFilename:m.originalFilename}),[d,f,s]),C=a.useCallback(async()=>{var h,y;L(!0);const m=await new Promise(l=>{n(k=>{const E=k.filter(N=>N.status==="idle"||N.status==="error");return l(E),k.map(N=>E.find(R=>R.id===N.id)?{...N,status:"uploading",errorMessage:void 0}:N)})}),b=[];for(const l of m)try{const k=await _(M(l));b.push(k),T(l.id,"done")}catch(k){const E=k instanceof Error?k.message:"Upload failed";T(l.id,"error",E),(h=g.current)==null||h.call(g,pe(l,k),l)}return L(!1),b.length>0&&((y=U.current)==null||y.call(U,b)),b},[M,T,_]),$=a.useMemo(()=>({total:v.length,remainingSlots:Math.max(0,i-v.length),maxFileSizeMb:G(o)}),[v.length,o,i]);return{files:v,isUploading:j,stats:$,addFiles:D,removeFile:O,setAnnotation:I,clearAnnotation:P,uploadAll:C}}function ee({accept:e,multiple:t,disabled:o=!1,labels:i,onFiles:c}){const s=a.useRef(null),[f,d]=a.useState(!1),_=a.useCallback(()=>{var n;o||(n=s.current)==null||n.click()},[o]),x=a.useCallback(n=>{n.target.files&&n.target.files.length>0&&c(n.target.files),n.target.value=""},[c]),w=a.useCallback(n=>{n.preventDefault(),d(!1),!o&&n.dataTransfer.files&&n.dataTransfer.files.length>0&&c(n.dataTransfer.files)},[o,c]),F=a.useCallback(n=>{n.preventDefault(),o||d(!0)},[o]),v=a.useCallback(n=>{n.preventDefault(),d(!1)},[]);return r.jsxs("div",{className:`ea-dropzone${f?" ea-dropzone--active":""}${o?" ea-dropzone--disabled":""}`,role:"button",tabIndex:o?-1:0,"aria-disabled":o,onClick:_,onKeyDown:n=>{(n.key==="Enter"||n.key===" ")&&(n.preventDefault(),_())},onDrop:w,onDragOver:F,onDragLeave:v,children:[r.jsxs("p",{className:"ea-dropzone__text",children:[i.dropzoneText," ",r.jsx("span",{className:"ea-dropzone__button",children:i.dropzoneButton})]}),r.jsx("input",{ref:s,type:"file",accept:e.join(","),multiple:t,onChange:x,className:"ea-dropzone__input",disabled:o,"aria-hidden":"true",tabIndex:-1})]})}function fe(e){switch(e){case"pdf":return"PDF";case"doc":return"DOC";case"docx":return"DOCX";default:return"IMG"}}function te({files:e,labels:t,onAnnotate:o,onRemove:i,disabled:c=!1}){return e.length===0?null:r.jsx("ul",{className:"ea-list",role:"list",children:e.map(s=>{const f=s.fileType==="image",d=s.annotatedPreviewUrl??s.previewUrl;return r.jsxs("li",{className:"ea-list__item","data-status":s.status,children:[r.jsx("div",{className:"ea-list__thumb",children:f&&d?r.jsx("img",{src:d,alt:s.originalFilename}):r.jsx("span",{className:"ea-list__icon",children:fe(s.fileType)})}),r.jsxs("div",{className:"ea-list__meta",children:[r.jsx("p",{className:"ea-list__name",title:s.originalFilename,children:s.originalFilename}),r.jsxs("p",{className:"ea-list__status",children:[s.status==="uploading"&&t.uploading,s.status==="done"&&t.uploaded,s.status==="error"&&(s.errorMessage??t.errorUploadFailed),s.status==="idle"&&s.annotatedFile&&"✓ annotated"]})]}),r.jsxs("div",{className:"ea-list__actions",children:[f&&r.jsx("button",{type:"button",className:"ea-btn ea-btn--ghost",onClick:()=>o(s.id),disabled:c||s.status==="uploading",children:t.annotate}),r.jsx("button",{type:"button",className:"ea-btn ea-btn--danger",onClick:()=>i(s.id),disabled:c||s.status==="uploading","aria-label":`${t.remove} ${s.originalFilename}`,children:t.remove})]})]},s.id)})})}function ne({src:e,originalFilename:t,initialState:o,labels:i,onReady:c,onError:s}){const f=a.useRef(null),d=a.useRef(null),_=a.useRef(null),x=a.useRef(null),[w,F]=a.useState(!0),[v,n]=a.useState(null);return a.useEffect(()=>{let j=!1;async function L(){try{const u=await Promise.resolve().then(()=>require("./markerjs3-DiaiYpgs.cjs"));if(j)return;const p=d.current,A=f.current;if(!p||!A||(!p.complete||p.naturalWidth===0)&&(await new Promise((U,D)=>{p.addEventListener("load",()=>U(),{once:!0}),p.addEventListener("error",()=>D(new Error("Image failed to load")),{once:!0})}),j))return;const g=new u.MarkerArea;if(A.appendChild(g),g.targetImage=p,o)try{g.restoreState(o,!1)}catch(U){H("Failed to restore annotation state:",U)}_.current=g,x.current=u.Renderer,F(!1),c==null||c({save:async()=>{const U=_.current,D=x.current,O=d.current;if(!U||!D||!O)throw new Error("Annotator is not ready.");const I=U.getState(),P=new D;P.targetImage=O;const T=await P.rasterize(I);return{file:await ge(T,ie(t)),state:I}}})}catch(u){if(j)return;F(!1),n(i.errorAnnotationFailed),H("Failed to load marker.js:",u),s==null||s(Z(u))}}return L(),()=>{var p;j=!0;const u=_.current;u&&((p=f.current)!=null&&p.contains(u))&&f.current.removeChild(u),_.current=null,x.current=null}},[e]),r.jsxs("div",{className:"ea-annotator",children:[r.jsx("div",{className:"ea-annotator__stage",ref:f,children:r.jsx("img",{ref:d,src:e,alt:"",className:"ea-annotator__img",crossOrigin:"anonymous"})}),w&&r.jsx("div",{className:"ea-annotator__loading",children:i.loadingAnnotator}),v&&r.jsx("div",{className:"ea-annotator__error",children:v})]})}async function ge(e,t){const i=await(await fetch(e)).blob();return new File([i],t,{type:i.type||"image/png"})}function ae({open:e,src:t,originalFilename:o,initialState:i,labels:c,onSave:s,onCancel:f,onError:d}){const _=a.useRef(null),x=a.useRef(null),[w,F]=a.useState(!1),v=a.useCallback(async()=>{if(_.current){F(!0);try{const{file:n,state:j}=await _.current.save();s(n,j)}catch(n){d==null||d(n)}finally{F(!1)}}},[d,s]);return a.useEffect(()=>{var u;if(!e)return;const n=document.activeElement;(u=x.current)==null||u.focus();const j=p=>{p.key==="Escape"&&f()};document.addEventListener("keydown",j);const L=document.body.style.overflow;return document.body.style.overflow="hidden",()=>{var p;document.removeEventListener("keydown",j),document.body.style.overflow=L,(p=n==null?void 0:n.focus)==null||p.call(n)}},[f,e]),!e||!t?null:r.jsx("div",{className:"ea-modal",role:"dialog","aria-modal":"true","aria-label":c.modalTitle,onClick:n=>{n.target===n.currentTarget&&f()},children:r.jsxs("div",{className:"ea-modal__dialog",ref:x,tabIndex:-1,children:[r.jsxs("header",{className:"ea-modal__header",children:[r.jsx("h2",{className:"ea-modal__title",children:c.modalTitle}),r.jsx("button",{type:"button",className:"ea-btn ea-btn--ghost",onClick:f,"aria-label":c.cancel,children:"×"})]}),r.jsx("div",{className:"ea-modal__body",children:r.jsx(ne,{src:t,originalFilename:o,initialState:i,labels:c,onReady:n=>{_.current=n},onError:d})}),r.jsxs("footer",{className:"ea-modal__footer",children:[r.jsx("button",{type:"button",className:"ea-btn ea-btn--ghost",onClick:f,disabled:w,children:c.cancel}),r.jsx("button",{type:"button",className:"ea-btn ea-btn--primary",onClick:v,disabled:w,children:w?c.uploading:c.save})]})]})})}function he(e){const{title:t,ownerType:o,ownerId:i,fieldKey:c,uploadHandler:s,onUploaded:f,onError:d,onChange:_,accept:x=B,maxFileSize:w=X,maxFiles:F=K,multiple:v=!0,labels:n,className:j,disabled:L=!1}=e,u=a.useMemo(()=>W(n),[n]),p=t??u.title,{files:A,isUploading:g,stats:U,addFiles:D,removeFile:O,setAnnotation:I,uploadAll:P}=Q({accept:x,maxFileSize:w,maxFiles:F,multiple:v,ownerType:o,ownerId:i,fieldKey:c,uploadHandler:s,onError:d,onUploaded:f,onChange:_}),[T,M]=a.useState(null),C=a.useMemo(()=>A.find(l=>l.id===T)??null,[T,A]),$=a.useCallback(l=>{M(l)},[]),m=a.useCallback(()=>{M(null)},[]),b=a.useCallback((l,k)=>{T&&I(T,l,k),M(null)},[T,I]),h=a.useCallback(l=>{d==null||d(Z(l))},[d]),y=!L&&!g&&A.some(l=>l.status==="idle"||l.status==="error");return r.jsxs("section",{className:`ea-root${j?` ${j}`:""}`,children:[r.jsxs("header",{className:"ea-header",children:[r.jsx("h2",{className:"ea-title",children:p}),r.jsxs("p",{className:"ea-subtitle",children:[u.filesSelected(U.total)," · max ",G(w)," MB · up to ",F," ","files"]})]}),r.jsx(ee,{accept:x,multiple:v,disabled:L||U.remainingSlots===0,labels:u,onFiles:D}),r.jsx(te,{files:A,labels:u,onAnnotate:$,onRemove:O,disabled:L}),A.length>0&&r.jsx("div",{className:"ea-actions",children:r.jsx("button",{type:"button",className:"ea-btn ea-btn--primary",onClick:()=>{P()},disabled:!y,children:g?u.uploading:u.upload})}),r.jsx(ae,{open:!!C,src:(C==null?void 0:C.previewUrl)??null,originalFilename:(C==null?void 0:C.originalFilename)??"",initialState:C==null?void 0:C.annotationState,labels:u,onSave:b,onCancel:m,onError:h})]})}function be(e){const t=new FormData;return t.append("owner_type",e.ownerType),t.append("owner_id",String(e.ownerId)),t.append("field_key",e.fieldKey),t.append("original_file",e.originalFile,e.originalFile.name),e.annotatedFile&&t.append("annotated_file",e.annotatedFile,e.annotatedFile.name),e.annotationState&&t.append("annotation_state",JSON.stringify(e.annotationState)),t.append("file_type",e.fileType),t.append("mime_type",e.mimeType),t.append("original_filename",e.originalFilename),t}exports.AnnotatorModal=ae;exports.DEFAULT_ACCEPT=B;exports.DEFAULT_MAX_FILES=K;exports.DEFAULT_MAX_FILE_SIZE=X;exports.EasyAnnotatorUpload=he;exports.FilePreviewList=te;exports.ImageAnnotator=ne;exports.UploadDropzone=ee;exports.buildFormDataPayload=be;exports.bytesToMb=G;exports.classifyFile=J;exports.enLabels=z;exports.idLabels=le;exports.isAccepted=V;exports.mergeLabels=W;exports.useEasyAnnotator=Q;exports.useObjectUrls=Y;
|
|
2
|
+
//# sourceMappingURL=easy-annotator.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"easy-annotator.cjs","sources":["../src/utils/file.ts","../src/i18n/labels.ts","../src/utils/errors.ts","../src/utils/id.ts","../src/hooks/useObjectUrls.ts","../src/hooks/useEasyAnnotator.ts","../src/components/UploadDropzone.tsx","../src/components/FilePreviewList.tsx","../src/components/ImageAnnotator.tsx","../src/components/AnnotatorModal.tsx","../src/components/EasyAnnotatorUpload.tsx","../src/services/defaultPayloadBuilder.ts"],"sourcesContent":["import type { EasyAnnotatorFileType } from '../types'\n\nexport const DEFAULT_ACCEPT: string[] = [\n 'image/jpeg',\n 'image/jpg',\n 'image/png',\n 'image/webp',\n 'application/pdf',\n 'application/msword',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n]\n\nexport const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024\nexport const DEFAULT_MAX_FILES = 5\n\nconst IMAGE_MIMES = new Set(['image/jpeg', 'image/jpg', 'image/png', 'image/webp'])\n\nexport function isImageMime(mime: string): boolean {\n return IMAGE_MIMES.has(mime.toLowerCase())\n}\n\nexport function classifyFile(file: File): EasyAnnotatorFileType {\n const mime = file.type.toLowerCase()\n if (isImageMime(mime)) return 'image'\n if (mime === 'application/pdf') return 'pdf'\n if (mime === 'application/msword') return 'doc'\n if (\n mime === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'\n ) {\n return 'docx'\n }\n // Fall back to extension when MIME is empty (common for some browsers/OSes).\n const ext = file.name.toLowerCase().split('.').pop() ?? ''\n if (['jpg', 'jpeg', 'png', 'webp'].includes(ext)) return 'image'\n if (ext === 'pdf') return 'pdf'\n if (ext === 'doc') return 'doc'\n if (ext === 'docx') return 'docx'\n return 'doc'\n}\n\nexport function isAccepted(file: File, accept: string[]): boolean {\n if (accept.length === 0) return true\n const mime = file.type.toLowerCase()\n if (mime && accept.some((a) => a.toLowerCase() === mime)) return true\n const ext = file.name.toLowerCase().split('.').pop() ?? ''\n const extMimeMap: Record<string, string> = {\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n webp: 'image/webp',\n pdf: 'application/pdf',\n doc: 'application/msword',\n docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n }\n const fallback = extMimeMap[ext]\n return fallback ? accept.includes(fallback) : false\n}\n\nexport function bytesToMb(bytes: number): number {\n return Math.round((bytes / (1024 * 1024)) * 10) / 10\n}\n\nexport function withAnnotatedFilename(originalName: string): string {\n const lastDot = originalName.lastIndexOf('.')\n if (lastDot <= 0) return `${originalName}-annotated.png`\n const base = originalName.slice(0, lastDot)\n return `${base}-annotated.png`\n}\n","import type { LabelOverrides } from '../types'\n\nexport const enLabels: LabelOverrides = {\n title: 'Upload',\n dropzoneText: 'Drag & drop files here, or',\n dropzoneButton: 'browse',\n annotate: 'Annotate',\n remove: 'Remove',\n upload: 'Upload',\n uploading: 'Uploading…',\n uploaded: 'Uploaded',\n retry: 'Retry',\n cancel: 'Cancel',\n save: 'Save',\n errorFileType: (filename) => `File type not allowed: ${filename}`,\n errorFileTooLarge: (filename, maxMb) => `File too large: ${filename} (max ${maxMb} MB)`,\n errorFileCount: (max) => `Maximum ${max} files`,\n errorAnnotationFailed: 'Failed to save annotation.',\n errorUploadFailed: 'Upload failed. Please retry.',\n modalTitle: 'Annotate image',\n loadingAnnotator: 'Loading annotator…',\n filesSelected: (count) => `${count} file${count === 1 ? '' : 's'} selected`,\n}\n\nexport const idLabels: LabelOverrides = {\n title: 'Unggah',\n dropzoneText: 'Seret & letakkan file di sini, atau',\n dropzoneButton: 'pilih file',\n annotate: 'Anotasi',\n remove: 'Hapus',\n upload: 'Unggah',\n uploading: 'Mengunggah…',\n uploaded: 'Terunggah',\n retry: 'Coba Lagi',\n cancel: 'Batal',\n save: 'Simpan',\n errorFileType: (filename) => `Tipe file tidak diizinkan: ${filename}`,\n errorFileTooLarge: (filename, maxMb) =>\n `File terlalu besar: ${filename} (maks ${maxMb} MB)`,\n errorFileCount: (max) => `Maksimal ${max} file`,\n errorAnnotationFailed: 'Gagal menyimpan anotasi.',\n errorUploadFailed: 'Unggah gagal. Silakan coba lagi.',\n modalTitle: 'Anotasi gambar',\n loadingAnnotator: 'Memuat anotator…',\n filesSelected: (count) => `${count} file dipilih`,\n}\n\nexport function mergeLabels(overrides?: Partial<LabelOverrides>): LabelOverrides {\n if (!overrides) return enLabels\n return { ...enLabels, ...overrides }\n}\n","import type { EasyAnnotatorError, EasyAnnotatorFile } from '../types'\n\nexport function fileTypeRejected(file: File, reason: string): EasyAnnotatorError {\n return { code: 'FILE_TYPE_REJECTED', file, reason }\n}\n\nexport function fileTooLarge(file: File, maxBytes: number): EasyAnnotatorError {\n return { code: 'FILE_TOO_LARGE', file, maxBytes }\n}\n\nexport function fileCountExceeded(maxFiles: number): EasyAnnotatorError {\n return { code: 'FILE_COUNT_EXCEEDED', maxFiles }\n}\n\nexport function annotationFailed(cause?: unknown): EasyAnnotatorError {\n return { code: 'ANNOTATION_FAILED', cause }\n}\n\nexport function uploadFailed(file: EasyAnnotatorFile, cause?: unknown): EasyAnnotatorError {\n return { code: 'UPLOAD_FAILED', cause, file }\n}\n\ndeclare const process: { env?: { NODE_ENV?: string } } | undefined\n\nexport function devWarn(...args: unknown[]): void {\n const env =\n typeof process !== 'undefined' && process?.env?.NODE_ENV\n ? process.env.NODE_ENV\n : 'development'\n if (env !== 'production') {\n // eslint-disable-next-line no-console\n console.warn('[easy-annotator]', ...args)\n }\n}\n","export function createId(): string {\n if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {\n return crypto.randomUUID()\n }\n return `ea_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`\n}\n","import { useCallback, useEffect, useRef } from 'react'\n\nexport function useObjectUrls() {\n const urlsRef = useRef<Set<string>>(new Set())\n\n const create = useCallback((blob: Blob): string => {\n const url = URL.createObjectURL(blob)\n urlsRef.current.add(url)\n return url\n }, [])\n\n const revoke = useCallback((url: string | undefined): void => {\n if (!url) return\n if (urlsRef.current.has(url)) {\n URL.revokeObjectURL(url)\n urlsRef.current.delete(url)\n }\n }, [])\n\n useEffect(() => {\n const urls = urlsRef.current\n return () => {\n urls.forEach((url) => URL.revokeObjectURL(url))\n urls.clear()\n }\n }, [])\n\n return { create, revoke }\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type {\n EasyAnnotatorError,\n EasyAnnotatorFile,\n EasyAnnotatorUploadHandler,\n EasyAnnotatorUploadPayload,\n EasyAnnotatorUploadedResult,\n} from '../types'\nimport {\n DEFAULT_ACCEPT,\n DEFAULT_MAX_FILES,\n DEFAULT_MAX_FILE_SIZE,\n bytesToMb,\n classifyFile,\n isAccepted,\n} from '../utils/file'\nimport {\n fileCountExceeded,\n fileTooLarge,\n fileTypeRejected,\n uploadFailed,\n} from '../utils/errors'\nimport { createId } from '../utils/id'\nimport { useObjectUrls } from './useObjectUrls'\n\nexport interface UseEasyAnnotatorOptions {\n accept?: string[]\n maxFileSize?: number\n maxFiles?: number\n multiple?: boolean\n ownerType: string\n ownerId: string | number\n fieldKey: string\n uploadHandler: EasyAnnotatorUploadHandler\n onError?: (err: EasyAnnotatorError, file?: EasyAnnotatorFile) => void\n onUploaded?: (items: EasyAnnotatorUploadedResult[]) => void\n onChange?: (files: EasyAnnotatorFile[]) => void\n}\n\nexport function useEasyAnnotator(options: UseEasyAnnotatorOptions) {\n const {\n accept = DEFAULT_ACCEPT,\n maxFileSize = DEFAULT_MAX_FILE_SIZE,\n maxFiles = DEFAULT_MAX_FILES,\n multiple = true,\n ownerType,\n ownerId,\n fieldKey,\n uploadHandler,\n onError,\n onUploaded,\n onChange,\n } = options\n\n const [files, setFiles] = useState<EasyAnnotatorFile[]>([])\n const [isUploading, setIsUploading] = useState(false)\n const { create: createObjectUrl, revoke: revokeObjectUrl } = useObjectUrls()\n\n // Keep latest callbacks in refs so they don't force effect re-runs.\n const onChangeRef = useRef(onChange)\n const onErrorRef = useRef(onError)\n const onUploadedRef = useRef(onUploaded)\n useEffect(() => {\n onChangeRef.current = onChange\n onErrorRef.current = onError\n onUploadedRef.current = onUploaded\n }, [onChange, onError, onUploaded])\n\n useEffect(() => {\n onChangeRef.current?.(files)\n }, [files])\n\n const addFiles = useCallback(\n (incoming: FileList | File[]) => {\n const arr = Array.from(incoming)\n setFiles((prev) => {\n const next = [...prev]\n const remainingSlots = Math.max(0, maxFiles - prev.length)\n let countExceeded = false\n\n for (const raw of arr) {\n if (!multiple && next.length >= 1) break\n if (next.length >= prev.length + remainingSlots) {\n countExceeded = true\n break\n }\n if (!isAccepted(raw, accept)) {\n onErrorRef.current?.(fileTypeRejected(raw, `MIME ${raw.type || 'unknown'}`))\n continue\n }\n if (raw.size > maxFileSize) {\n onErrorRef.current?.(fileTooLarge(raw, maxFileSize))\n continue\n }\n const fileType = classifyFile(raw)\n const previewUrl = fileType === 'image' ? createObjectUrl(raw) : undefined\n next.push({\n id: createId(),\n originalFile: raw,\n fileType,\n mimeType: raw.type || 'application/octet-stream',\n originalFilename: raw.name,\n previewUrl,\n status: 'idle',\n })\n }\n\n if (countExceeded || arr.length > remainingSlots) {\n onErrorRef.current?.(fileCountExceeded(maxFiles))\n }\n return next\n })\n },\n [accept, createObjectUrl, maxFileSize, maxFiles, multiple],\n )\n\n const removeFile = useCallback(\n (id: string) => {\n setFiles((prev) => {\n const target = prev.find((f) => f.id === id)\n if (target) {\n revokeObjectUrl(target.previewUrl)\n revokeObjectUrl(target.annotatedPreviewUrl)\n }\n return prev.filter((f) => f.id !== id)\n })\n },\n [revokeObjectUrl],\n )\n\n const setAnnotation = useCallback(\n (id: string, annotatedFile: File, annotationState: object) => {\n setFiles((prev) =>\n prev.map((f) => {\n if (f.id !== id) return f\n revokeObjectUrl(f.annotatedPreviewUrl)\n return {\n ...f,\n annotatedFile,\n annotationState,\n annotatedPreviewUrl: createObjectUrl(annotatedFile),\n }\n }),\n )\n },\n [createObjectUrl, revokeObjectUrl],\n )\n\n const clearAnnotation = useCallback(\n (id: string) => {\n setFiles((prev) =>\n prev.map((f) => {\n if (f.id !== id) return f\n revokeObjectUrl(f.annotatedPreviewUrl)\n const { annotatedFile: _af, annotationState: _as, annotatedPreviewUrl: _ap, ...rest } = f\n void _af\n void _as\n void _ap\n return { ...rest }\n }),\n )\n },\n [revokeObjectUrl],\n )\n\n const setStatus = useCallback(\n (id: string, status: EasyAnnotatorFile['status'], errorMessage?: string) => {\n setFiles((prev) =>\n prev.map((f) => (f.id === id ? { ...f, status, errorMessage } : f)),\n )\n },\n [],\n )\n\n const buildPayload = useCallback(\n (file: EasyAnnotatorFile): EasyAnnotatorUploadPayload => ({\n ownerType,\n ownerId,\n fieldKey,\n originalFile: file.originalFile,\n annotatedFile: file.annotatedFile,\n annotationState: file.annotationState,\n fileType: file.fileType,\n mimeType: file.mimeType,\n originalFilename: file.originalFilename,\n }),\n [fieldKey, ownerId, ownerType],\n )\n\n const uploadAll = useCallback(async () => {\n setIsUploading(true)\n const targets = (await new Promise<EasyAnnotatorFile[]>((resolve) => {\n setFiles((prev) => {\n const toUpload = prev.filter((f) => f.status === 'idle' || f.status === 'error')\n resolve(toUpload)\n return prev.map((f) =>\n toUpload.find((t) => t.id === f.id)\n ? { ...f, status: 'uploading', errorMessage: undefined }\n : f,\n )\n })\n })) as EasyAnnotatorFile[]\n\n const results: EasyAnnotatorUploadedResult[] = []\n\n for (const file of targets) {\n try {\n const result = await uploadHandler(buildPayload(file))\n results.push(result)\n setStatus(file.id, 'done')\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Upload failed'\n setStatus(file.id, 'error', message)\n onErrorRef.current?.(uploadFailed(file, err), file)\n }\n }\n\n setIsUploading(false)\n if (results.length > 0) onUploadedRef.current?.(results)\n return results\n }, [buildPayload, setStatus, uploadHandler])\n\n const stats = useMemo(\n () => ({\n total: files.length,\n remainingSlots: Math.max(0, maxFiles - files.length),\n maxFileSizeMb: bytesToMb(maxFileSize),\n }),\n [files.length, maxFileSize, maxFiles],\n )\n\n return {\n files,\n isUploading,\n stats,\n addFiles,\n removeFile,\n setAnnotation,\n clearAnnotation,\n uploadAll,\n }\n}\n","import { useCallback, useRef, useState, type DragEvent, type ChangeEvent } from 'react'\nimport type { LabelOverrides } from '../types'\n\nexport interface UploadDropzoneProps {\n accept: string[]\n multiple: boolean\n disabled?: boolean\n labels: LabelOverrides\n onFiles: (files: FileList | File[]) => void\n}\n\nexport function UploadDropzone({\n accept,\n multiple,\n disabled = false,\n labels,\n onFiles,\n}: UploadDropzoneProps) {\n const inputRef = useRef<HTMLInputElement>(null)\n const [isDragging, setIsDragging] = useState(false)\n\n const openPicker = useCallback(() => {\n if (disabled) return\n inputRef.current?.click()\n }, [disabled])\n\n const handleChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n if (e.target.files && e.target.files.length > 0) {\n onFiles(e.target.files)\n }\n e.target.value = ''\n },\n [onFiles],\n )\n\n const handleDrop = useCallback(\n (e: DragEvent<HTMLDivElement>) => {\n e.preventDefault()\n setIsDragging(false)\n if (disabled) return\n if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {\n onFiles(e.dataTransfer.files)\n }\n },\n [disabled, onFiles],\n )\n\n const handleDragOver = useCallback(\n (e: DragEvent<HTMLDivElement>) => {\n e.preventDefault()\n if (!disabled) setIsDragging(true)\n },\n [disabled],\n )\n\n const handleDragLeave = useCallback((e: DragEvent<HTMLDivElement>) => {\n e.preventDefault()\n setIsDragging(false)\n }, [])\n\n return (\n <div\n className={`ea-dropzone${isDragging ? ' ea-dropzone--active' : ''}${\n disabled ? ' ea-dropzone--disabled' : ''\n }`}\n role=\"button\"\n tabIndex={disabled ? -1 : 0}\n aria-disabled={disabled}\n onClick={openPicker}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n openPicker()\n }\n }}\n onDrop={handleDrop}\n onDragOver={handleDragOver}\n onDragLeave={handleDragLeave}\n >\n <p className=\"ea-dropzone__text\">\n {labels.dropzoneText}{' '}\n <span className=\"ea-dropzone__button\">{labels.dropzoneButton}</span>\n </p>\n <input\n ref={inputRef}\n type=\"file\"\n accept={accept.join(',')}\n multiple={multiple}\n onChange={handleChange}\n className=\"ea-dropzone__input\"\n disabled={disabled}\n aria-hidden=\"true\"\n tabIndex={-1}\n />\n </div>\n )\n}\n","import type { EasyAnnotatorFile, LabelOverrides } from '../types'\n\nexport interface FilePreviewListProps {\n files: EasyAnnotatorFile[]\n labels: LabelOverrides\n onAnnotate: (id: string) => void\n onRemove: (id: string) => void\n disabled?: boolean\n}\n\nfunction fileIcon(type: EasyAnnotatorFile['fileType']): string {\n switch (type) {\n case 'pdf':\n return 'PDF'\n case 'doc':\n return 'DOC'\n case 'docx':\n return 'DOCX'\n default:\n return 'IMG'\n }\n}\n\nexport function FilePreviewList({\n files,\n labels,\n onAnnotate,\n onRemove,\n disabled = false,\n}: FilePreviewListProps) {\n if (files.length === 0) return null\n\n return (\n <ul className=\"ea-list\" role=\"list\">\n {files.map((file) => {\n const isImage = file.fileType === 'image'\n const thumb = file.annotatedPreviewUrl ?? file.previewUrl\n return (\n <li key={file.id} className=\"ea-list__item\" data-status={file.status}>\n <div className=\"ea-list__thumb\">\n {isImage && thumb ? (\n <img src={thumb} alt={file.originalFilename} />\n ) : (\n <span className=\"ea-list__icon\">{fileIcon(file.fileType)}</span>\n )}\n </div>\n <div className=\"ea-list__meta\">\n <p className=\"ea-list__name\" title={file.originalFilename}>\n {file.originalFilename}\n </p>\n <p className=\"ea-list__status\">\n {file.status === 'uploading' && labels.uploading}\n {file.status === 'done' && labels.uploaded}\n {file.status === 'error' && (file.errorMessage ?? labels.errorUploadFailed)}\n {file.status === 'idle' && file.annotatedFile && '✓ annotated'}\n </p>\n </div>\n <div className=\"ea-list__actions\">\n {isImage && (\n <button\n type=\"button\"\n className=\"ea-btn ea-btn--ghost\"\n onClick={() => onAnnotate(file.id)}\n disabled={disabled || file.status === 'uploading'}\n >\n {labels.annotate}\n </button>\n )}\n <button\n type=\"button\"\n className=\"ea-btn ea-btn--danger\"\n onClick={() => onRemove(file.id)}\n disabled={disabled || file.status === 'uploading'}\n aria-label={`${labels.remove} ${file.originalFilename}`}\n >\n {labels.remove}\n </button>\n </div>\n </li>\n )\n })}\n </ul>\n )\n}\n","import { useEffect, useRef, useState } from 'react'\nimport type { LabelOverrides } from '../types'\nimport { withAnnotatedFilename } from '../utils/file'\nimport { annotationFailed, devWarn } from '../utils/errors'\n\nexport interface ImageAnnotatorHandle {\n save: () => Promise<{ file: File; state: object }>\n}\n\nexport interface ImageAnnotatorProps {\n src: string\n originalFilename: string\n initialState?: object\n labels: LabelOverrides\n onReady?: (handle: ImageAnnotatorHandle) => void\n onError?: (err: unknown) => void\n}\n\n// Local minimal interfaces so we don't leak marker.js types through our public .d.ts.\ntype MarkerAreaLike = HTMLElement & {\n targetImage: HTMLImageElement | undefined\n getState: () => object\n restoreState: (state: object, addUndoStep?: boolean) => void\n}\n\ntype RendererLike = {\n targetImage: HTMLImageElement | undefined\n rasterize: (state: object, targetCanvas?: HTMLCanvasElement) => Promise<string>\n}\n\ntype MarkerJsModule = {\n MarkerArea: new () => MarkerAreaLike\n Renderer: new () => RendererLike\n}\n\nexport function ImageAnnotator({\n src,\n originalFilename,\n initialState,\n labels,\n onReady,\n onError,\n}: ImageAnnotatorProps) {\n const containerRef = useRef<HTMLDivElement>(null)\n const imgRef = useRef<HTMLImageElement>(null)\n const markerAreaRef = useRef<MarkerAreaLike | null>(null)\n const rendererCtorRef = useRef<(new () => RendererLike) | null>(null)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => {\n let disposed = false\n\n async function init() {\n try {\n const mod = (await import('@markerjs/markerjs3')) as unknown as MarkerJsModule\n if (disposed) return\n const img = imgRef.current\n const container = containerRef.current\n if (!img || !container) return\n\n // Wait for the <img> to have natural dimensions before handing it to MarkerArea.\n if (!img.complete || img.naturalWidth === 0) {\n await new Promise<void>((resolve, reject) => {\n img.addEventListener('load', () => resolve(), { once: true })\n img.addEventListener('error', () => reject(new Error('Image failed to load')), {\n once: true,\n })\n })\n if (disposed) return\n }\n\n const markerArea = new mod.MarkerArea()\n // marker.js 3's MarkerArea is a custom element — append, then set the target image.\n container.appendChild(markerArea)\n markerArea.targetImage = img\n\n if (initialState) {\n try {\n markerArea.restoreState(initialState, false)\n } catch (restoreErr) {\n devWarn('Failed to restore annotation state:', restoreErr)\n }\n }\n\n markerAreaRef.current = markerArea\n rendererCtorRef.current = mod.Renderer\n setLoading(false)\n\n onReady?.({\n save: async () => {\n const area = markerAreaRef.current\n const RendererCtor = rendererCtorRef.current\n const targetImg = imgRef.current\n if (!area || !RendererCtor || !targetImg) {\n throw new Error('Annotator is not ready.')\n }\n const state = area.getState()\n const renderer = new RendererCtor()\n renderer.targetImage = targetImg\n const dataUrl = await renderer.rasterize(state)\n const file = await dataUrlToFile(\n dataUrl,\n withAnnotatedFilename(originalFilename),\n )\n return { file, state }\n },\n })\n } catch (err) {\n if (disposed) return\n setLoading(false)\n setError(labels.errorAnnotationFailed)\n devWarn('Failed to load marker.js:', err)\n onError?.(annotationFailed(err))\n }\n }\n\n void init()\n\n return () => {\n disposed = true\n const area = markerAreaRef.current\n if (area && containerRef.current?.contains(area)) {\n containerRef.current.removeChild(area)\n }\n markerAreaRef.current = null\n rendererCtorRef.current = null\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [src])\n\n return (\n <div className=\"ea-annotator\">\n <div className=\"ea-annotator__stage\" ref={containerRef}>\n <img\n ref={imgRef}\n src={src}\n alt=\"\"\n className=\"ea-annotator__img\"\n crossOrigin=\"anonymous\"\n />\n </div>\n {loading && <div className=\"ea-annotator__loading\">{labels.loadingAnnotator}</div>}\n {error && <div className=\"ea-annotator__error\">{error}</div>}\n </div>\n )\n}\n\nasync function dataUrlToFile(dataUrl: string, filename: string): Promise<File> {\n const res = await fetch(dataUrl)\n const blob = await res.blob()\n return new File([blob], filename, { type: blob.type || 'image/png' })\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\nimport type { LabelOverrides } from '../types'\nimport { ImageAnnotator, type ImageAnnotatorHandle } from './ImageAnnotator'\n\nexport interface AnnotatorModalProps {\n open: boolean\n src: string | null\n originalFilename: string\n initialState?: object\n labels: LabelOverrides\n onSave: (annotatedFile: File, annotationState: object) => void\n onCancel: () => void\n onError?: (err: unknown) => void\n}\n\nexport function AnnotatorModal({\n open,\n src,\n originalFilename,\n initialState,\n labels,\n onSave,\n onCancel,\n onError,\n}: AnnotatorModalProps) {\n const handleRef = useRef<ImageAnnotatorHandle | null>(null)\n const dialogRef = useRef<HTMLDivElement>(null)\n const [saving, setSaving] = useState(false)\n\n const handleSave = useCallback(async () => {\n if (!handleRef.current) return\n setSaving(true)\n try {\n const { file, state } = await handleRef.current.save()\n onSave(file, state)\n } catch (err) {\n onError?.(err)\n } finally {\n setSaving(false)\n }\n }, [onError, onSave])\n\n useEffect(() => {\n if (!open) return\n const previousActive = document.activeElement as HTMLElement | null\n dialogRef.current?.focus()\n\n const handleKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') onCancel()\n }\n document.addEventListener('keydown', handleKey)\n const previousOverflow = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n return () => {\n document.removeEventListener('keydown', handleKey)\n document.body.style.overflow = previousOverflow\n previousActive?.focus?.()\n }\n }, [onCancel, open])\n\n if (!open || !src) return null\n\n return (\n <div\n className=\"ea-modal\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={labels.modalTitle}\n onClick={(e) => {\n if (e.target === e.currentTarget) onCancel()\n }}\n >\n <div className=\"ea-modal__dialog\" ref={dialogRef} tabIndex={-1}>\n <header className=\"ea-modal__header\">\n <h2 className=\"ea-modal__title\">{labels.modalTitle}</h2>\n <button\n type=\"button\"\n className=\"ea-btn ea-btn--ghost\"\n onClick={onCancel}\n aria-label={labels.cancel}\n >\n ×\n </button>\n </header>\n <div className=\"ea-modal__body\">\n <ImageAnnotator\n src={src}\n originalFilename={originalFilename}\n initialState={initialState}\n labels={labels}\n onReady={(h) => {\n handleRef.current = h\n }}\n onError={onError}\n />\n </div>\n <footer className=\"ea-modal__footer\">\n <button\n type=\"button\"\n className=\"ea-btn ea-btn--ghost\"\n onClick={onCancel}\n disabled={saving}\n >\n {labels.cancel}\n </button>\n <button\n type=\"button\"\n className=\"ea-btn ea-btn--primary\"\n onClick={handleSave}\n disabled={saving}\n >\n {saving ? labels.uploading : labels.save}\n </button>\n </footer>\n </div>\n </div>\n )\n}\n","import { useCallback, useMemo, useState } from 'react'\nimport type { EasyAnnotatorUploadProps } from '../types'\nimport { DEFAULT_ACCEPT, DEFAULT_MAX_FILES, DEFAULT_MAX_FILE_SIZE, bytesToMb } from '../utils/file'\nimport { mergeLabels } from '../i18n/labels'\nimport { useEasyAnnotator } from '../hooks/useEasyAnnotator'\nimport { UploadDropzone } from './UploadDropzone'\nimport { FilePreviewList } from './FilePreviewList'\nimport { AnnotatorModal } from './AnnotatorModal'\nimport { annotationFailed } from '../utils/errors'\n\nexport function EasyAnnotatorUpload(props: EasyAnnotatorUploadProps) {\n const {\n title,\n ownerType,\n ownerId,\n fieldKey,\n uploadHandler,\n onUploaded,\n onError,\n onChange,\n accept = DEFAULT_ACCEPT,\n maxFileSize = DEFAULT_MAX_FILE_SIZE,\n maxFiles = DEFAULT_MAX_FILES,\n multiple = true,\n labels: labelOverrides,\n className,\n disabled = false,\n } = props\n\n const labels = useMemo(() => mergeLabels(labelOverrides), [labelOverrides])\n const resolvedTitle = title ?? labels.title\n\n const {\n files,\n isUploading,\n stats,\n addFiles,\n removeFile,\n setAnnotation,\n uploadAll,\n } = useEasyAnnotator({\n accept,\n maxFileSize,\n maxFiles,\n multiple,\n ownerType,\n ownerId,\n fieldKey,\n uploadHandler,\n onError,\n onUploaded,\n onChange,\n })\n\n const [activeAnnotateId, setActiveAnnotateId] = useState<string | null>(null)\n const activeFile = useMemo(\n () => files.find((f) => f.id === activeAnnotateId) ?? null,\n [activeAnnotateId, files],\n )\n\n const handleAnnotate = useCallback((id: string) => {\n setActiveAnnotateId(id)\n }, [])\n\n const handleCancelAnnotate = useCallback(() => {\n setActiveAnnotateId(null)\n }, [])\n\n const handleSaveAnnotation = useCallback(\n (annotatedFile: File, annotationState: object) => {\n if (activeAnnotateId) {\n setAnnotation(activeAnnotateId, annotatedFile, annotationState)\n }\n setActiveAnnotateId(null)\n },\n [activeAnnotateId, setAnnotation],\n )\n\n const handleAnnotatorError = useCallback(\n (err: unknown) => {\n onError?.(annotationFailed(err))\n },\n [onError],\n )\n\n const canUpload =\n !disabled &&\n !isUploading &&\n files.some((f) => f.status === 'idle' || f.status === 'error')\n\n return (\n <section className={`ea-root${className ? ` ${className}` : ''}`}>\n <header className=\"ea-header\">\n <h2 className=\"ea-title\">{resolvedTitle}</h2>\n <p className=\"ea-subtitle\">\n {labels.filesSelected(stats.total)} · max {bytesToMb(maxFileSize)} MB · up to {maxFiles}{' '}\n files\n </p>\n </header>\n\n <UploadDropzone\n accept={accept}\n multiple={multiple}\n disabled={disabled || stats.remainingSlots === 0}\n labels={labels}\n onFiles={addFiles}\n />\n\n <FilePreviewList\n files={files}\n labels={labels}\n onAnnotate={handleAnnotate}\n onRemove={removeFile}\n disabled={disabled}\n />\n\n {files.length > 0 && (\n <div className=\"ea-actions\">\n <button\n type=\"button\"\n className=\"ea-btn ea-btn--primary\"\n onClick={() => {\n void uploadAll()\n }}\n disabled={!canUpload}\n >\n {isUploading ? labels.uploading : labels.upload}\n </button>\n </div>\n )}\n\n <AnnotatorModal\n open={Boolean(activeFile)}\n src={activeFile?.previewUrl ?? null}\n originalFilename={activeFile?.originalFilename ?? ''}\n initialState={activeFile?.annotationState}\n labels={labels}\n onSave={handleSaveAnnotation}\n onCancel={handleCancelAnnotate}\n onError={handleAnnotatorError}\n />\n </section>\n )\n}\n","import type { EasyAnnotatorUploadPayload } from '../types'\n\nexport function buildFormDataPayload(payload: EasyAnnotatorUploadPayload): FormData {\n const fd = new FormData()\n fd.append('owner_type', payload.ownerType)\n fd.append('owner_id', String(payload.ownerId))\n fd.append('field_key', payload.fieldKey)\n fd.append('original_file', payload.originalFile, payload.originalFile.name)\n if (payload.annotatedFile) {\n fd.append('annotated_file', payload.annotatedFile, payload.annotatedFile.name)\n }\n if (payload.annotationState) {\n fd.append('annotation_state', JSON.stringify(payload.annotationState))\n }\n fd.append('file_type', payload.fileType)\n fd.append('mime_type', payload.mimeType)\n fd.append('original_filename', payload.originalFilename)\n return fd\n}\n"],"names":["DEFAULT_ACCEPT","DEFAULT_MAX_FILE_SIZE","DEFAULT_MAX_FILES","IMAGE_MIMES","isImageMime","mime","classifyFile","file","ext","isAccepted","accept","a","fallback","bytesToMb","bytes","withAnnotatedFilename","originalName","lastDot","enLabels","filename","maxMb","max","count","idLabels","mergeLabels","overrides","fileTypeRejected","reason","fileTooLarge","maxBytes","fileCountExceeded","maxFiles","annotationFailed","cause","uploadFailed","devWarn","args","_a","createId","useObjectUrls","urlsRef","useRef","create","useCallback","blob","url","revoke","useEffect","urls","useEasyAnnotator","options","maxFileSize","multiple","ownerType","ownerId","fieldKey","uploadHandler","onError","onUploaded","onChange","files","setFiles","useState","isUploading","setIsUploading","createObjectUrl","revokeObjectUrl","onChangeRef","onErrorRef","onUploadedRef","addFiles","incoming","arr","prev","next","remainingSlots","countExceeded","raw","_b","fileType","previewUrl","_c","removeFile","id","target","f","setAnnotation","annotatedFile","annotationState","clearAnnotation","_af","_as","_ap","rest","setStatus","status","errorMessage","buildPayload","uploadAll","targets","resolve","toUpload","t","results","result","err","message","stats","useMemo","UploadDropzone","disabled","labels","onFiles","inputRef","isDragging","setIsDragging","openPicker","handleChange","e","handleDrop","handleDragOver","handleDragLeave","jsxs","jsx","fileIcon","type","FilePreviewList","onAnnotate","onRemove","isImage","thumb","ImageAnnotator","src","originalFilename","initialState","onReady","containerRef","imgRef","markerAreaRef","rendererCtorRef","loading","setLoading","error","setError","disposed","init","mod","img","container","reject","markerArea","restoreErr","area","RendererCtor","targetImg","state","renderer","dataUrl","dataUrlToFile","AnnotatorModal","open","onSave","onCancel","handleRef","dialogRef","saving","setSaving","handleSave","previousActive","handleKey","previousOverflow","h","EasyAnnotatorUpload","props","title","labelOverrides","className","resolvedTitle","activeAnnotateId","setActiveAnnotateId","activeFile","handleAnnotate","handleCancelAnnotate","handleSaveAnnotation","handleAnnotatorError","canUpload","buildFormDataPayload","payload","fd"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.ea-root{display:flex;flex-direction:column;gap:12px;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;color:#1f2937}.ea-header{display:flex;flex-direction:column;gap:2px}.ea-title{margin:0;font-size:1rem;font-weight:600}.ea-subtitle{margin:0;font-size:.8rem;color:#6b7280}.ea-dropzone{position:relative;display:flex;align-items:center;justify-content:center;padding:24px;border:2px dashed #d1d5db;border-radius:8px;background:#f9fafb;cursor:pointer;transition:border-color .12s ease,background-color .12s ease;outline:none}.ea-dropzone:focus-visible{border-color:#2563eb;box-shadow:0 0 0 3px #2563eb40}.ea-dropzone--active{border-color:#2563eb;background:#eff6ff}.ea-dropzone--disabled{opacity:.6;cursor:not-allowed}.ea-dropzone__text{margin:0;font-size:.875rem;color:#4b5563;text-align:center}.ea-dropzone__button{color:#2563eb;font-weight:600;text-decoration:underline}.ea-dropzone__input{position:absolute;top:0;right:0;bottom:0;left:0;opacity:0;pointer-events:none}.ea-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.ea-list__item{display:flex;align-items:center;gap:12px;padding:10px 12px;border:1px solid #e5e7eb;border-radius:6px;background:#fff}.ea-list__item[data-status=error]{border-color:#fca5a5;background:#fef2f2}.ea-list__item[data-status=done]{border-color:#86efac;background:#f0fdf4}.ea-list__thumb{flex:0 0 48px;width:48px;height:48px;border-radius:4px;background:#f3f4f6;display:flex;align-items:center;justify-content:center;overflow:hidden}.ea-list__thumb img{width:100%;height:100%;object-fit:cover}.ea-list__icon{font-size:.7rem;font-weight:700;color:#6b7280;letter-spacing:.05em}.ea-list__meta{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}.ea-list__name{margin:0;font-size:.875rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ea-list__status{margin:0;font-size:.75rem;color:#6b7280}.ea-list__actions{display:flex;gap:6px;flex-shrink:0}.ea-btn{-webkit-appearance:none;-moz-appearance:none;appearance:none;border:1px solid transparent;border-radius:6px;padding:6px 12px;font-size:.8125rem;font-weight:500;cursor:pointer;transition:background-color .12s ease,border-color .12s ease}.ea-btn:disabled{opacity:.55;cursor:not-allowed}.ea-btn--primary{background:#2563eb;color:#fff}.ea-btn--primary:hover:not(:disabled){background:#1d4ed8}.ea-btn--ghost{background:transparent;border-color:#d1d5db;color:#1f2937}.ea-btn--ghost:hover:not(:disabled){background:#f3f4f6}.ea-btn--danger{background:transparent;border-color:#fca5a5;color:#b91c1c}.ea-btn--danger:hover:not(:disabled){background:#fef2f2}.ea-actions{display:flex;justify-content:flex-end}.ea-modal{position:fixed;top:0;right:0;bottom:0;left:0;background:#0f172a8c;display:flex;align-items:center;justify-content:center;z-index:9999;padding:16px}.ea-modal__dialog{background:#fff;border-radius:10px;width:min(960px,100%);max-height:90vh;display:flex;flex-direction:column;box-shadow:0 20px 40px #00000040;outline:none}.ea-modal__header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid #e5e7eb}.ea-modal__title{margin:0;font-size:1rem;font-weight:600}.ea-modal__body{flex:1 1 auto;min-height:0;padding:12px;background:#f3f4f6;overflow:hidden}.ea-modal__footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 16px;border-top:1px solid #e5e7eb}.ea-annotator{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center}.ea-annotator__stage{position:relative;max-width:100%;max-height:100%;display:flex;align-items:center;justify-content:center}.ea-annotator__img{max-width:100%;max-height:70vh;display:block;object-fit:contain}.ea-annotator__loading,.ea-annotator__error{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;background:#fffc;font-size:.875rem;color:#374151}.ea-annotator__error{color:#b91c1c;background:#fef2f2f2}
|