@xom11/whiteboard 0.7.0 → 0.9.1
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/README.md +51 -1
- package/dist/chunk-74VEEZBV.mjs +619 -0
- package/dist/chunk-74VEEZBV.mjs.map +1 -0
- package/dist/chunk-DU2NFHRR.mjs +103 -0
- package/dist/chunk-DU2NFHRR.mjs.map +1 -0
- package/dist/{chunk-SHFOGORM.mjs → chunk-DU3RHKT5.mjs} +4 -4
- package/dist/{chunk-SHFOGORM.mjs.map → chunk-DU3RHKT5.mjs.map} +1 -1
- package/dist/{chunk-HYXFHEDJ.mjs → chunk-IUVV52HO.mjs} +22 -7
- package/dist/chunk-IUVV52HO.mjs.map +1 -0
- package/dist/{chunk-BJX4YNA5.mjs → chunk-KEYZ5EZT.mjs} +26 -9
- package/dist/chunk-KEYZ5EZT.mjs.map +1 -0
- package/dist/{chunk-LPM4MM45.mjs → chunk-SBDMF4NQ.mjs} +3 -2
- package/dist/chunk-SBDMF4NQ.mjs.map +1 -0
- package/dist/{chunk-3SSQKRRO.mjs → chunk-ZVN356JZ.mjs} +4 -4
- package/dist/{chunk-3SSQKRRO.mjs.map → chunk-ZVN356JZ.mjs.map} +1 -1
- package/dist/geometry-2d.js +250 -218
- package/dist/geometry-2d.js.map +1 -1
- package/dist/geometry-2d.mjs +2 -2
- package/dist/geometry-3d.d.mts +1 -1
- package/dist/geometry-3d.d.ts +1 -1
- package/dist/geometry-3d.js +3276 -1201
- package/dist/geometry-3d.js.map +1 -1
- package/dist/geometry-3d.mjs +3 -2
- package/dist/graph-2d.js +360 -66
- package/dist/graph-2d.js.map +1 -1
- package/dist/graph-2d.mjs +2 -2
- package/dist/{host-2QGKMGCT.mjs → host-LZH2FZ2N.mjs} +3 -3
- package/dist/{host-2QGKMGCT.mjs.map → host-LZH2FZ2N.mjs.map} +1 -1
- package/dist/host-PIIDSMVE.mjs +3187 -0
- package/dist/host-PIIDSMVE.mjs.map +1 -0
- package/dist/{host-T2W6R6SO.mjs → host-VDNAJMLC.mjs} +221 -216
- package/dist/host-VDNAJMLC.mjs.map +1 -0
- package/dist/index.d.mts +6 -5
- package/dist/index.d.ts +6 -5
- package/dist/index.js +4365 -1821
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +246 -102
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
- package/dist/chunk-BJX4YNA5.mjs.map +0 -1
- package/dist/chunk-DJTBZEAR.mjs +0 -25
- package/dist/chunk-DJTBZEAR.mjs.map +0 -1
- package/dist/chunk-HM7RIXJE.mjs +0 -331
- package/dist/chunk-HM7RIXJE.mjs.map +0 -1
- package/dist/chunk-HYXFHEDJ.mjs.map +0 -1
- package/dist/chunk-LPM4MM45.mjs.map +0 -1
- package/dist/host-T2W6R6SO.mjs.map +0 -1
- package/dist/host-XUFON6CQ.mjs +0 -1422
- package/dist/host-XUFON6CQ.mjs.map +0 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/serialize.ts","../src/stamps/shared/registry.ts","../src/stamps/shared/ToolbarInjector.tsx","../src/stamps/shared/useShortcuts.ts","../src/stamps/shared/useStampDoubleClick.ts","../src/stamps/shared/useStampShortcutBlocker.ts","../src/stamps/shared/useStampClickOutside.ts","../src/stamps/shared/restoreStampFiles.ts","../src/core/persistence/sceneStore.ts","../src/core/persistence/fileStore.ts","../src/Whiteboard.tsx"],"names":["useEffect","useRef","jsx","useState","useMemo","useCallback","jsxs"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAGO,SAAS,qBAAqB,CAAA,EAA+B;AAClE,EAAA,OAAO;AAAA,IACL,qBAAqB,CAAA,CAAE,mBAAA;AAAA,IACvB,MAAM,CAAA,CAAE,IAAA;AAAA,IACR,SAAS,CAAA,CAAE,OAAA;AAAA,IACX,SAAS,CAAA,CAAE,OAAA;AAAA,IACX,QAAA,EAAU,EAAE,QAAA,IAAY,IAAA;AAAA,IACxB,OAAO,CAAA,CAAE;AAAA,GACX;AACF;;;ACKO,IAAM,aAAA,GAA0C,OAAO,MAAA,CAAO;AAAA,EACnE,aAAA;AAAA,EACA;AACF,CAAC;AAGM,IAAM,mBAAA,GAAgD,OAAO,MAAA,CAAO;AAAA,EACzE,eAAA;AAAA,EACA;AACF,CAAC;AAGM,IAAM,UAAA,GAAuC,OAAO,MAAA,CAAO;AAAA,EAChE,GAAG,aAAA;AAAA,EACH,GAAG;AACL,CAAC;AAaM,IAAM,cAAA,GAA2C;AAGjD,SAAS,sBAAA,CACd,IAAA,EACA,MAAA,GAAmC,cAAA,EACjB;AAClB,EAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACtB,IAAA,IAAI,CAAA,CAAE,iBAAA,CAAkB,IAAI,CAAA,EAAG,OAAO,CAAA;AAAA,EACxC;AACA,EAAA,OAAO,IAAA;AACT;AAGO,SAAS,cAAA,CACd,OAAA,EACA,MAAA,GAAmC,cAAA,EAC1B;AACT,EAAA,OAAO,sBAAA,CAAuB,OAAA,CAAQ,UAAA,EAAY,MAAM,CAAA,KAAM,IAAA;AAChE;AC9CA,IAAM,eAAA,GAAkB,2BAAA;AAMxB,IAAM,gBAAA,GACJ,6DAAA;AAaK,SAAS,eAAA,CAAgB;AAAA,EAC9B,OAAA;AAAA,EACA,eAAA;AAAA,EACA,QAAA;AAAA,EACA,MAAA,GAAS;AACX,CAAA,EAAU;AACR,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAA6B,IAAI,CAAA;AACnE,EAAA,MAAM,YAAA,GAAe,OAA2B,IAAI,CAAA;AAEpD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,IAAI,YAAA,CAAa,YAAY,IAAA,EAAM;AACjC,QAAA,YAAA,CAAa,OAAA,GAAU,IAAA;AACvB,QAAA,YAAA,CAAa,IAAI,CAAA;AAAA,MACnB;AACA,MAAA,QAAA,CAAS,cAAA,CAAe,eAAe,CAAA,EAAG,MAAA,EAAO;AACjD,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,SAAA,GAAY,KAAA;AAChB,IAAA,IAAI,QAAA,GAAoC,IAAA;AACxC,IAAA,IAAI,KAAA,GAAuB,IAAA;AAE3B,IAAA,MAAM,KAAA,GAAQ,CAAC,IAAA,KAA6B;AAC1C,MAAA,IAAI,SAAA,IAAa,YAAA,CAAa,OAAA,KAAY,IAAA,EAAM;AAChD,MAAA,YAAA,CAAa,OAAA,GAAU,IAAA;AACvB,MAAA,cAAA,CAAe,MAAM;AACnB,QAAA,IAAI,CAAC,SAAA,EAAW,YAAA,CAAa,IAAI,CAAA;AAAA,MACnC,CAAC,CAAA;AAAA,IACH,CAAA;AAEA,IAAA,MAAM,WAAW,MAAM;AACrB,MAAA,IAAI,SAAA,EAAW;AACf,MAAA,MAAM,SAAA,GAAY,QAAA,CAAS,aAAA,CAA2B,gBAAgB,CAAA;AACtE,MAAA,IAAI,CAAC,SAAA,EAAW;AACd,QAAA,KAAA,CAAM,IAAI,CAAA;AACV,QAAA;AAAA,MACF;AACA,MAAA,IAAI,OAAA,GAAU,SAAA,CAAU,aAAA,CAA8B,GAAA,GAAM,eAAe,CAAA;AAC3E,MAAA,IAAI,CAAC,OAAA,EAAS;AACZ,QAAA,OAAA,GAAU,QAAA,CAAS,cAAc,KAAK,CAAA;AACtC,QAAA,OAAA,CAAQ,EAAA,GAAK,eAAA;AACb,QAAA,OAAA,CAAQ,YAAA,CAAa,mBAAmB,MAAM,CAAA;AAC9C,QAAA,OAAA,CAAQ,YAAA,CAAa,mBAAmB,MAAM,CAAA;AAC9C,QAAA,OAAA,CAAQ,MAAM,OAAA,GAAU,UAAA;AACxB,QAAA,SAAA,CAAU,YAAA,CAAa,OAAA,EAAS,SAAA,CAAU,UAAU,CAAA;AAAA,MACtD;AACA,MAAA,KAAA,CAAM,OAAO,CAAA;AAAA,IACf,CAAA;AAEA,IAAA,MAAM,WAAW,MAAM;AACrB,MAAA,IAAI,SAAS,IAAA,EAAM;AACnB,MAAA,KAAA,GAAQ,sBAAsB,MAAM;AAClC,QAAA,KAAA,GAAQ,IAAA;AACR,QAAA,QAAA,EAAS;AAAA,MACX,CAAC,CAAA;AAAA,IACH,CAAA;AAEA,IAAA,QAAA,EAAS;AAET,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,aAAA,CAAc,aAAa,KAAK,QAAA,CAAS,IAAA;AAC/D,IAAA,QAAA,GAAW,IAAI,iBAAiB,QAAQ,CAAA;AACxC,IAAA,QAAA,CAAS,QAAQ,IAAA,EAAM,EAAE,WAAW,IAAA,EAAM,OAAA,EAAS,MAAM,CAAA;AAEzD,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,IAAI,KAAA,IAAS,IAAA,EAAM,oBAAA,CAAqB,KAAK,CAAA;AAC7C,MAAA,QAAA,EAAU,UAAA,EAAW;AACrB,MAAA,QAAA,CAAS,cAAA,CAAe,eAAe,CAAA,EAAG,MAAA,EAAO;AAAA,IACnD,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,IAAI,CAAC,OAAA,IAAW,CAAC,SAAA,EAAW,OAAO,IAAA;AAEnC,EAAA,MAAM,eAAe,MAAM;AACzB,IAAA,MAAM,UAAU,QAAA,CAAS,aAAA;AAAA,MACvB;AAAA,KACF;AACA,IAAA,OAAA,EAAS,KAAA,EAAM;AAAA,EACjB,CAAA;AAEA,EAAA,OAAO,YAAA;AAAA,oBACL,IAAA,CAAA,QAAA,EAAA,EACG,QAAA,EAAA;AAAA,MAAA,MAAA,CAAO,GAAA,CAAI,CAAC,KAAA,qBACX,GAAA;AAAA,QAAC,aAAA;AAAA,QAAA;AAAA,UAEC,MAAM,KAAA,CAAM,WAAA;AAAA,UACZ,OAAO,KAAA,CAAM,YAAA;AAAA,UACb,MAAA,EAAQ,oBAAoB,KAAA,CAAM,IAAA;AAAA,UAClC,SAAS,MAAM;AACb,YAAA,QAAA,CAAS,MAAM,IAAI,CAAA;AACnB,YAAA,YAAA,EAAa;AAAA,UACf,CAAA;AAAA,UACA,YAAY,KAAA,CAAM;AAAA,SAAA;AAAA,QARb,KAAA,CAAM;AAAA,OAUd,CAAA;AAAA,sBACD,GAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,aAAA,EAAY,MAAA;AAAA,UACZ,KAAA,EAAO;AAAA,YACL,MAAA,EAAQ,CAAA;AAAA,YACR,UAAA,EAAY,+CAAA;AAAA,YACZ,MAAA,EAAQ;AAAA;AACV;AAAA;AACF,KAAA,EACF,CAAA;AAAA,IACA;AAAA,GACF;AACF;AAUA,SAAS,cAAc,EAAE,IAAA,EAAM,OAAO,MAAA,EAAQ,OAAA,EAAS,YAAW,EAAuB;AACvF,EAAA,MAAM,SAAA,GAAY;AAAA,IAChB,oBAAA;AAAA,IACA,yBAAA;AAAA,IACA,SAAS,8BAAA,GAAiC;AAAA,GAC5C,CACG,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AACX,EAAA,uBACE,IAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAK,QAAA;AAAA,MACL,OAAA;AAAA,MACA,YAAA,EAAY,KAAA;AAAA,MACZ,cAAA,EAAc,MAAA;AAAA,MACd,aAAA,EAAa,UAAA;AAAA,MACb,SAAA;AAAA,MACA,KAAA,EAAO;AAAA,QACL,OAAA,EAAS,MAAA;AAAA,QACT,UAAA,EAAY,QAAA;AAAA,QACZ,SAAA,EAAW,UAAA;AAAA,QACX,KAAA,EAAO,MAAA;AAAA,QACP,SAAA,EAAW,YAAA;AAAA,QACX,UAAA,EAAY,aAAA;AAAA,QACZ,MAAA,EAAQ,uBAAA;AAAA,QACR,MAAA,EAAQ,SAAA;AAAA,QACR,UAAA,EAAY,SAAA;AAAA,QACZ,QAAA,EAAU,UAAA;AAAA,QACV,KAAA,EAAO;AAAA,OACT;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,aAAA,EAAY,MAAA;AAAA,YACZ,KAAA,EAAO;AAAA,cACL,OAAA,EAAS,aAAA;AAAA,cACT,UAAA,EAAY,QAAA;AAAA,cACZ,cAAA,EAAgB,QAAA;AAAA,cAChB,KAAA,EAAO,MAAA;AAAA,cACP,MAAA,EAAQ;AAAA,aACV;AAAA,YAEC,QAAA,EAAA;AAAA;AAAA,SACH;AAAA,wBACA,GAAA,CAAC,UAAM,QAAA,EAAA,KAAA,EAAM;AAAA;AAAA;AAAA,GACf;AAEJ;AC5LA,SAAS,iBAAiB,CAAA,EAAgC;AACxD,EAAA,IAAI,CAAC,CAAA,IAAK,EAAE,CAAA,YAAa,cAAc,OAAO,KAAA;AAC9C,EAAA,IAAI,CAAA,CAAE,mBAAmB,OAAO,IAAA;AAChC,EAAA,MAAM,MAAM,CAAA,CAAE,OAAA;AACd,EAAA,OAAO,GAAA,KAAQ,OAAA,IAAW,GAAA,KAAQ,UAAA,IAAc,GAAA,KAAQ,QAAA;AAC1D;AAQO,SAAS,YAAA,CAAa;AAAA,EAC3B,OAAA;AAAA,EACA,QAAA;AAAA,EACA,MAAA,GAAS;AACX,CAAA,EAAkB;AAChB,EAAAA,UAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,EAAS;AACd,IAAA,MAAM,SAAA,uBAAgB,GAAA,EAAoB;AAC1C,IAAA,KAAA,MAAW,KAAK,MAAA,EAAQ,SAAA,CAAU,IAAI,CAAA,CAAE,WAAA,EAAa,EAAE,IAAI,CAAA;AAE3D,IAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KAAqB;AACpC,MAAA,IAAI,CAAA,CAAE,OAAA,IAAW,CAAA,CAAE,OAAA,IAAW,EAAE,MAAA,EAAQ;AACxC,MAAA,IAAI,gBAAA,CAAiB,CAAA,CAAE,MAAM,CAAA,EAAG;AAChC,MAAA,MAAM,GAAA,GAAM,CAAA,CAAE,GAAA,CAAI,WAAA,EAAY;AAC9B,MAAA,MAAM,IAAA,GAAO,SAAA,CAAU,GAAA,CAAI,GAAG,CAAA;AAC9B,MAAA,IAAI,CAAC,IAAA,EAAM;AACX,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,MAAA,QAAA,CAAS,IAAI,CAAA;AAAA,IACf,CAAA;AACA,IAAA,MAAA,CAAO,iBAAiB,SAAA,EAAW,OAAA,EAAS,EAAE,OAAA,EAAS,MAAM,CAAA;AAC7D,IAAA,OAAO,MAAM,OAAO,mBAAA,CAAoB,SAAA,EAAW,SAAS,EAAE,OAAA,EAAS,MAAM,CAAA;AAAA,EAC/E,CAAA,EAAG,CAAC,OAAA,EAAS,QAAA,EAAU,MAAM,CAAC,CAAA;AAChC;AC5CA,IAAM,eAAA,GAAkB,GAAA;AAejB,SAAS,mBAAA,CAAoB,EAAE,OAAA,EAAS,MAAA,EAAQ,QAAO,EAAS;AACrE,EAAA,MAAM,eAAeC,MAAAA,CAAmD;AAAA,IACtE,IAAA,EAAM,CAAA;AAAA,IACN,SAAA,EAAW;AAAA,GACZ,CAAA;AAED,EAAA,OAAO,WAAA;AAAA;AAAA,IAEL,CAAC,aAAkB,gBAAA,KAA0B;AAC3C,MAAA,IAAI,CAAC,OAAA,EAAS;AACd,MAAA,MAAM,UAAA,GAAa,kBAAkB,GAAA,EAAK,OAAA;AAC1C,MAAA,IAAI,CAAC,UAAA,IAAc,UAAA,CAAW,IAAA,KAAS,OAAA,EAAS;AAChD,MAAA,MAAM,KAAA,GAAQ,sBAAA,CAAuB,UAAA,CAAW,UAAA,EAAY,MAAM,CAAA;AAClE,MAAA,IAAI,CAAC,KAAA,EAAO;AACZ,MAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,MAAA,MAAM,QAAA,GACJ,aAAa,OAAA,CAAQ,SAAA,KAAc,WAAW,EAAA,IAC9C,GAAA,GAAM,YAAA,CAAa,OAAA,CAAQ,IAAA,GAAO,eAAA;AACpC,MAAA,YAAA,CAAa,UAAU,EAAE,IAAA,EAAM,GAAA,EAAK,SAAA,EAAW,WAAW,EAAA,EAAG;AAC7D,MAAA,IAAI,CAAC,QAAA,EAAU;AACf,MAAA,MAAA,CAAO,MAAM,IAAA,EAAM;AAAA,QACjB,IAAI,UAAA,CAAW,EAAA;AAAA,QACf,YAAY,UAAA,CAAW;AAAA,OACxB,CAAA;AAAA,IACH,CAAA;AAAA,IACA,CAAC,OAAA,EAAS,MAAA,EAAQ,MAAM;AAAA,GAC1B;AACF;AC3CA,IAAM,YAAA,uBAAmB,GAAA,CAAI;AAAA,EAC3B,KAAA;AAAA,EAAO,SAAA;AAAA,EAAW,WAAA;AAAA,EAAa,WAAA;AAAA,EAAa,YAAA;AAAA,EAC5C,OAAA;AAAA,EAAS,SAAA;AAAA,EAAW,KAAA;AAAA,EAAO,MAAA;AAAA,EAAQ,UAAA;AAAA,EACnC,MAAA;AAAA,EAAQ,KAAA;AAAA,EAAO,QAAA;AAAA,EAAU;AAC3B,CAAC,CAAA;AAED,SAAS,WAAW,EAAA,EAAiC;AACnD,EAAA,IAAI,EAAE,EAAA,YAAc,WAAA,CAAA,EAAc,OAAO,KAAA;AACzC,EAAA,IAAI,EAAA,CAAG,mBAAmB,OAAO,IAAA;AACjC,EAAA,MAAM,MAAM,EAAA,CAAG,OAAA;AACf,EAAA,OAAO,GAAA,KAAQ,OAAA,IAAW,GAAA,KAAQ,UAAA,IAAc,GAAA,KAAQ,QAAA;AAC1D;AAcO,SAAS,uBAAA,CAAwB,EAAE,WAAA,EAAa,MAAA,EAAO,EAAS;AACrE,EAAA,MAAM,YAAA,GAAe,OAAA;AAAA,IACnB,MAAM,IAAI,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,WAAA,CAAY,WAAA,EAAa,CAAC,CAAA;AAAA,IAC5D,CAAC,MAAM;AAAA,GACT;AAEA,EAAAD,UAAU,MAAM;AACd,IAAA,IAAI,CAAC,WAAA,EAAa;AAElB,IAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KAAqB;AACpC,MAAA,IAAI,UAAA,CAAW,CAAA,CAAE,MAAM,CAAA,EAAG;AAC1B,MAAA,IAAI,CAAA,CAAE,OAAA,IAAW,CAAA,CAAE,OAAA,EAAS;AAC5B,MAAA,IAAI,YAAA,CAAa,GAAA,CAAI,CAAA,CAAE,GAAG,CAAA,EAAG;AAC7B,MAAA,IAAI,CAAA,CAAE,QAAQ,QAAA,EAAU;AACxB,MAAA,IAAI,aAAa,GAAA,CAAI,CAAA,CAAE,GAAA,CAAI,WAAA,EAAa,CAAA,EAAG;AAC3C,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,CAAA,CAAE,eAAA,EAAgB;AAAA,IACpB,CAAA;AACA,IAAA,MAAA,CAAO,iBAAiB,SAAA,EAAW,OAAA,EAAS,EAAE,OAAA,EAAS,MAAM,CAAA;AAC7D,IAAA,OAAO,MAAM,OAAO,mBAAA,CAAoB,SAAA,EAAW,SAAS,EAAE,OAAA,EAAS,MAAM,CAAA;AAAA,EAC/E,CAAA,EAAG,CAAC,WAAA,EAAa,YAAY,CAAC,CAAA;AAChC;AChCO,SAAS,oBAAA,CAAqB,EAAE,WAAA,EAAa,OAAA,EAAS,SAAQ,EAAS;AAC5E,EAAAA,UAAU,MAAM;AACd,IAAA,IAAI,CAAC,WAAA,EAAa;AAClB,IAAA,IAAI,QAAA,GAAW,CAAA;AACf,IAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KAAkB;AACjC,MAAA,MAAM,SAAS,CAAA,CAAE,MAAA;AACjB,MAAA,IAAI,CAAC,MAAA,EAAQ;AACb,MAAA,IAAI,MAAA,CAAO,OAAA,CAAQ,0BAA0B,CAAA,EAAG;AAChD,MAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,MAAA,IAAI,GAAA,GAAM,WAAW,EAAA,EAAI;AACzB,MAAA,QAAA,GAAW,GAAA;AACX,MAAA,OAAA,CAAQ,SAAS,SAAA,EAAU;AAC3B,MAAA,OAAA,EAAQ;AAAA,IACV,CAAA;AACA,IAAA,MAAA,CAAO,iBAAiB,aAAA,EAAe,OAAA,EAAS,EAAE,OAAA,EAAS,MAAM,CAAA;AACjE,IAAA,MAAA,CAAO,iBAAiB,WAAA,EAAa,OAAA,EAAS,EAAE,OAAA,EAAS,MAAM,CAAA;AAC/D,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,CAAO,oBAAoB,aAAA,EAAe,OAAA,EAAS,EAAE,OAAA,EAAS,MAAM,CAAA;AACpE,MAAA,MAAA,CAAO,oBAAoB,WAAA,EAAa,OAAA,EAAS,EAAE,OAAA,EAAS,MAAM,CAAA;AAAA,IACpE,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,WAAA,EAAa,OAAA,EAAS,OAAO,CAAC,CAAA;AACpC;;;ACDA,SAAS,aAAa,GAAA,EAAqB;AACzC,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,kBAAA,CAAmB,GAAG,CAAC,CAAA;AAC7C,EAAA,OAAO,4BAAA,GAA+B,KAAK,IAAI,CAAA;AACjD;AAEA,eAAe,iBAAA,CACb,MAAA,EACA,UAAA,EACA,KAAA,EAC+B;AAC/B,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,uBAAA,CAAwB,UAAU,CAAA;AAC1D,IAAA,OAAO,EAAE,EAAA,EAAI,MAAA,EAAQ,OAAA,EAAS,YAAA,CAAa,GAAG,CAAA,EAAG,QAAA,EAAU,eAAA,EAAiB,OAAA,EAAS,IAAA,CAAK,GAAA,EAAI,EAAE;AAAA,EAClG,SAAS,GAAA,EAAK;AACZ,IAAA,OAAA,CAAQ,KAAK,0BAAA,EAA4B,MAAA,EAAQ,MAAM,KAAA,CAAM,IAAA,GAAO,KAAK,GAAG,CAAA;AAC5E,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAgBA,eAAsB,wBAAA,CAEpB,GAAA,EACA,QAAA,EACA,MAAA,GAAmC,cAAA,EACpB;AACf,EAAA,IAAI,CAAC,GAAA,EAAK;AAEV,EAAA,MAAM,aAA8B,EAAC;AAGrC,EAAA,MAAM,cAAA,uBAAqB,GAAA,EAAY;AACvC,EAAA,KAAA,MAAW,MAAM,QAAA,EAAU;AACzB,IAAA,MAAM,KAAA,GAAQ,sBAAA,CAAuB,EAAA,CAAG,UAAA,EAAY,MAAM,CAAA;AAC1D,IAAA,IAAI,CAAC,OAAO,yBAAA,EAA2B;AAEvC,IAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,yBAAA,CAA0B,EAAS,CAAA;AAChE,IAAA,IAAI,CAAC,QAAA,EAAU;AACf,IAAA,cAAA,CAAe,GAAA,CAAI,GAAG,EAAE,CAAA;AACxB,IAAA,UAAA,CAAW,IAAA,CAAK;AAAA,MACd,IAAI,QAAA,CAAS,MAAA;AAAA,MACb,SAAS,QAAA,CAAS,OAAA;AAAA,MAClB,UAAU,QAAA,CAAS,QAAA;AAAA,MACnB,OAAA,EAAS,KAAK,GAAA;AAAI,KACnB,CAAA;AAAA,EACH;AAGA,EAAA,MAAM,QAAA,GAAY,OAAO,GAAA,CAAI,QAAA,KAAa,aAAc,GAAA,CAAI,QAAA,KAAa,EAAC;AAC1E,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAC7B,EAAA,KAAA,MAAW,MAAM,QAAA,EAAU;AACzB,IAAA,IAAI,cAAA,CAAe,GAAA,CAAI,EAAA,CAAG,EAAE,CAAA,EAAG;AAC/B,IAAA,IAAI,EAAA,CAAG,SAAS,OAAA,EAAS;AACzB,IAAA,IAAI,CAAC,GAAG,MAAA,EAAQ;AAChB,IAAA,IAAI,QAAA,IAAY,QAAA,CAAS,EAAA,CAAG,MAAM,CAAA,EAAG;AACrC,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,EAAA,CAAG,MAAM,CAAA,EAAG;AACzB,IAAA,MAAM,KAAA,GAAQ,sBAAA,CAAuB,EAAA,CAAG,UAAA,EAAY,MAAM,CAAA;AAC1D,IAAA,IAAI,CAAC,KAAA,EAAO;AACZ,IAAA,IAAA,CAAK,GAAA,CAAI,GAAG,MAAM,CAAA;AAClB,IAAA,MAAM,QAAQ,MAAM,iBAAA,CAAkB,GAAG,MAAA,EAAQ,EAAA,CAAG,YAAY,KAAK,CAAA;AACrE,IAAA,IAAI,KAAA,EAAO,UAAA,CAAW,IAAA,CAAK,KAAK,CAAA;AAAA,EAClC;AAEA,EAAA,IAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AACzB,IAAA,IAAI;AAAE,MAAA,GAAA,CAAI,SAAS,UAAU,CAAA;AAAA,IAAG,SAAS,GAAA,EAAK;AAAE,MAAA,OAAA,CAAQ,IAAA,CAAK,oBAAoB,GAAG,CAAA;AAAA,IAAG;AAAA,EACzF;AACF;;;AClHA,IAAM,MAAA,GAAS,mBAAA;AACf,IAAM,cAAA,GAAiB,CAAA;AASvB,SAAS,QAAQ,GAAA,EAAqB;AACpC,EAAA,OAAO,MAAA,GAAS,GAAA;AAClB;AAEO,SAAS,UAAU,GAAA,EAAiC;AACzD,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,IAAA;AAC1C,EAAA,MAAM,MAAM,MAAA,CAAO,YAAA,CAAa,OAAA,CAAQ,OAAA,CAAQ,GAAG,CAAC,CAAA;AACpD,EAAA,IAAI,CAAC,KAAK,OAAO,IAAA;AACjB,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAC7B,IAAA,IAAI,CAAC,MAAA,IAAU,OAAO,MAAA,KAAW,UAAU,OAAO,IAAA;AAClD,IAAA,IAAI,MAAA,CAAO,YAAY,cAAA,EAAgB;AAGrC,MAAA,OAAA,CAAQ,IAAA;AAAA,QACN,CAAA,2BAAA,EAA8B,MAAA,CAAO,OAAO,CAAA,oBAAA,EAAe,cAAc,CAAA,cAAA;AAAA,OAC3E;AACA,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,MAAA,CAAO,QAAQ,GAAG,OAAO,IAAA;AAC5C,IAAA,OAAO;AAAA,MACL,OAAA,EAAS,cAAA;AAAA,MACT,UAAU,MAAA,CAAO,QAAA;AAAA,MACjB,QAAA,EAAW,MAAA,CAAO,QAAA,IAAY,EAAC;AAAA,MAC/B,OAAA,EAAS,OAAO,MAAA,CAAO,OAAA,KAAY,WAAW,MAAA,CAAO,OAAA,GAAU,KAAK,GAAA;AAAI,KAC1E;AAAA,EACF,SAAS,GAAA,EAAK;AACZ,IAAA,OAAA,CAAQ,IAAA,CAAK,0CAA0C,GAAG,CAAA;AAC1D,IAAA,IAAI;AACF,MAAA,MAAA,CAAO,YAAA,CAAa,UAAA,CAAW,OAAA,CAAQ,GAAG,CAAC,CAAA;AAAA,IAC7C,CAAA,CAAA,MAAQ;AAAA,IAAe;AACvB,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAEO,SAAS,UAAA,CACd,KACA,OAAA,EACM;AACN,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACnC,EAAA,MAAM,MAAA,GAAsB;AAAA,IAC1B,OAAA,EAAS,cAAA;AAAA,IACT,UAAU,OAAA,CAAQ,QAAA;AAAA,IAClB,UAAU,OAAA,CAAQ,QAAA;AAAA,IAClB,OAAA,EAAS,KAAK,GAAA;AAAI,GACpB;AACA,EAAA,IAAI;AACF,IAAA,MAAA,CAAO,YAAA,CAAa,QAAQ,OAAA,CAAQ,GAAG,GAAG,IAAA,CAAK,SAAA,CAAU,MAAM,CAAC,CAAA;AAAA,EAClE,SAAS,GAAA,EAAK;AACZ,IAAA,OAAA,CAAQ,IAAA,CAAK,oCAAoC,GAAG,CAAA;AAAA,EACtD;AACF;;;AC7DA,IAAM,OAAA,GAAU,kBAAA;AAChB,IAAM,UAAA,GAAa,CAAA;AACnB,IAAM,KAAA,GAAQ,OAAA;AAWd,IAAI,SAAA,GAAyC,IAAA;AAC7C,IAAI,WAAA,GAAc,KAAA;AAElB,SAAS,MAAA,GAA+B;AACtC,EAAA,IAAI,aAAa,OAAO,OAAA,CAAQ,OAAO,IAAI,KAAA,CAAM,oBAAoB,CAAC,CAAA;AACtE,EAAA,IAAI,WAAW,OAAO,SAAA;AACtB,EAAA,SAAA,GAAY,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AAC3C,IAAA,IAAI,OAAO,cAAc,WAAA,EAAa;AACpC,MAAA,WAAA,GAAc,IAAA;AACd,MAAA,MAAA,CAAO,IAAI,KAAA,CAAM,qBAAqB,CAAC,CAAA;AACvC,MAAA;AAAA,IACF;AACA,IAAA,MAAM,GAAA,GAAM,SAAA,CAAU,IAAA,CAAK,OAAA,EAAS,UAAU,CAAA;AAC9C,IAAA,GAAA,CAAI,kBAAkB,MAAM;AAC1B,MAAA,MAAM,KAAK,GAAA,CAAI,MAAA;AACf,MAAA,IAAI,CAAC,EAAA,CAAG,gBAAA,CAAiB,QAAA,CAAS,KAAK,CAAA,EAAG;AACxC,QAAA,MAAM,QAAQ,EAAA,CAAG,iBAAA,CAAkB,OAAO,EAAE,OAAA,EAAS,MAAM,CAAA;AAC3D,QAAA,KAAA,CAAM,YAAY,YAAA,EAAc,YAAA,EAAc,EAAE,MAAA,EAAQ,OAAO,CAAA;AAAA,MACjE;AAAA,IACF,CAAA;AACA,IAAA,GAAA,CAAI,SAAA,GAAY,MAAM,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAA;AACxC,IAAA,GAAA,CAAI,UAAU,MAAM;AAClB,MAAA,WAAA,GAAc,IAAA;AACd,MAAA,MAAA,CAAO,GAAA,CAAI,KAAA,IAAS,IAAI,KAAA,CAAM,iBAAiB,CAAC,CAAA;AAAA,IAClD,CAAA;AAAA,EACF,CAAC,CAAA;AACD,EAAA,OAAO,SAAA;AACT;AAEA,eAAe,SAAA,CACb,IAAA,EACA,EAAA,EAKA,QAAA,EACY;AACZ,EAAA,IAAI,EAAA;AACJ,EAAA,IAAI;AACF,IAAA,EAAA,GAAK,MAAM,MAAA,EAAO;AAAA,EACpB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,QAAA;AAAA,EACT;AACA,EAAA,OAAO,IAAI,OAAA,CAAW,CAAC,OAAA,EAAS,MAAA,KAAW;AACzC,IAAA,MAAM,EAAA,GAAK,EAAA,CAAG,WAAA,CAAY,KAAA,EAAO,IAAI,CAAA;AACrC,IAAA,MAAM,KAAA,GAAQ,EAAA,CAAG,WAAA,CAAY,KAAK,CAAA;AAClC,IAAA,IAAI,MAAA,GAAS,QAAA;AACb,IAAA,IAAI;AACF,MAAA,EAAA;AAAA,QACE,KAAA;AAAA,QACA,CAAC,KAAA,KAAU;AACT,UAAA,MAAA,GAAS,KAAA;AAAA,QACX,CAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF,SAAS,GAAA,EAAK;AACZ,MAAA,MAAA,CAAO,GAAG,CAAA;AACV,MAAA;AAAA,IACF;AACA,IAAA,EAAA,CAAG,UAAA,GAAa,MAAM,OAAA,CAAQ,MAAM,CAAA;AACpC,IAAA,EAAA,CAAG,UAAU,MAAM;AACjB,MAAA,OAAA,CAAQ,IAAA,CAAK,4BAAA,EAA8B,EAAA,CAAG,KAAK,CAAA;AACnD,MAAA,MAAA,CAAO,EAAA,CAAG,KAAA,IAAS,IAAI,KAAA,CAAM,cAAc,CAAC,CAAA;AAAA,IAC9C,CAAA;AACA,IAAA,EAAA,CAAG,OAAA,GAAU,MAAM,MAAA,CAAO,EAAA,CAAG,SAAS,IAAI,KAAA,CAAM,gBAAgB,CAAC,CAAA;AAAA,EACnE,CAAC,CAAA;AACH;AAEA,eAAsB,UAAU,UAAA,EAA0C;AACxE,EAAA,IAAI;AACF,IAAA,OAAO,MAAM,SAAA;AAAA,MACX,UAAA;AAAA,MACA,CAAC,KAAA,EAAO,SAAA,EAAW,IAAA,KAAS;AAC1B,QAAA,MAAM,MAAmB,EAAC;AAC1B,QAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,YAAY,EAAE,UAAA,CAAW,WAAA,CAAY,IAAA,CAAK,UAAU,CAAC,CAAA;AAC7E,QAAA,GAAA,CAAI,YAAY,MAAM;AACpB,UAAA,MAAM,SAAS,GAAA,CAAI,MAAA;AACnB,UAAA,IAAI,CAAC,MAAA,EAAQ;AACX,YAAA,SAAA,CAAU,GAAG,CAAA;AACb,YAAA;AAAA,UACF;AACA,UAAA,MAAM,SAAS,MAAA,CAAO,KAAA;AAEtB,UAAC,GAAA,CAAY,MAAA,CAAO,EAAE,CAAA,GAAI;AAAA,YACxB,SAAS,MAAA,CAAO,OAAA;AAAA,YAChB,UAAU,MAAA,CAAO,QAAA;AAAA,YACjB,SAAS,MAAA,CAAO;AAAA,WAClB;AACA,UAAA,MAAA,CAAO,QAAA,EAAS;AAAA,QAClB,CAAA;AACA,QAAA,GAAA,CAAI,OAAA,GAAU,MAAM,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA;AAAA,MACpC,CAAA;AAAA,MACA;AAAC,KACH;AAAA,EACF,SAAS,GAAA,EAAK;AACZ,IAAA,OAAA,CAAQ,IAAA,CAAK,kCAAkC,GAAG,CAAA;AAClD,IAAA,OAAO,EAAC;AAAA,EACV;AACF;AAEA,eAAsB,UAAA,CAAW,YAAoB,KAAA,EAAmC;AACtF,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA;AACpC,EAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AAC1B,EAAA,IAAI;AACF,IAAA,MAAM,SAAA;AAAA,MACJ,WAAA;AAAA,MACA,CAAC,KAAA,EAAO,SAAA,EAAW,IAAA,KAAS;AAC1B,QAAA,IAAI,UAAU,OAAA,CAAQ,MAAA;AACtB,QAAA,MAAM,YAAY,MAAM;AACtB,UAAA,OAAA,IAAW,CAAA;AACX,UAAA,IAAI,OAAA,KAAY,CAAA,EAAG,SAAA,CAAU,KAAA,CAAS,CAAA;AAAA,QACxC,CAAA;AAEA,QAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,QAAA,KAAA,MAAW,CAAC,EAAA,EAAI,CAAC,CAAA,IAAK,OAAA,EAAS;AAE7B,UAAA,MAAM,EAAA,GAAK,CAAA;AACX,UAAA,MAAM,MAAA,GAAS,KAAA,CAAM,GAAA,CAAI,EAAE,CAAA;AAC3B,UAAA,MAAA,CAAO,YAAY,MAAM;AACvB,YAAA,IAAI,OAAO,MAAA,EAAQ;AACjB,cAAA,SAAA,EAAU;AACV,cAAA;AAAA,YACF;AACA,YAAA,MAAM,GAAA,GAAkB;AAAA,cACtB,EAAA;AAAA,cACA,UAAA;AAAA,cACA,SAAS,EAAA,CAAG,OAAA;AAAA,cACZ,UAAU,EAAA,CAAG,QAAA;AAAA,cACb,OAAA,EAAS,GAAG,OAAA,IAAW,GAAA;AAAA,cACvB,OAAA,EAAS;AAAA,aACX;AACA,YAAA,MAAM,MAAA,GAAS,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAC5B,YAAA,MAAA,CAAO,SAAA,GAAY,SAAA;AACnB,YAAA,MAAA,CAAO,OAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA;AAAA,UAC1C,CAAA;AACA,UAAA,MAAA,CAAO,OAAA,GAAU,MAAM,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA;AAAA,QAC1C;AAAC,QAAA;AAAA,MACH,CAAA;AAAA,MACA,KAAA;AAAA,KACF;AAAA,EACF,SAAS,GAAA,EAAK;AACZ,IAAA,OAAA,CAAQ,IAAA,CAAK,mCAAmC,GAAG,CAAA;AAAA,EACrD;AACF;AAEA,eAAsB,UAAA,CACpB,YACA,OAAA,EACe;AACf,EAAA,IAAI;AACF,IAAA,MAAM,SAAA;AAAA,MACJ,WAAA;AAAA,MACA,CAAC,KAAA,EAAO,SAAA,EAAW,IAAA,KAAS;AAC1B,QAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,YAAY,EAAE,UAAA,CAAW,WAAA,CAAY,IAAA,CAAK,UAAU,CAAC,CAAA;AAC7E,QAAA,GAAA,CAAI,YAAY,MAAM;AACpB,UAAA,MAAM,SAAS,GAAA,CAAI,MAAA;AACnB,UAAA,IAAI,CAAC,MAAA,EAAQ;AACX,YAAA,SAAA,CAAU,KAAA,CAAS,CAAA;AACnB,YAAA;AAAA,UACF;AACA,UAAA,MAAM,SAAS,MAAA,CAAO,KAAA;AACtB,UAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,MAAA,CAAO,EAAE,CAAA,EAAG;AAC1B,YAAA,MAAA,CAAO,QAAA,EAAS;AAChB,YAAA;AAAA,UACF;AACA,UAAA,MAAM,SAAA,GAAY,OAAO,MAAA,EAAO;AAChC,UAAA,SAAA,CAAU,SAAA,GAAY,MAAM,MAAA,CAAO,QAAA,EAAS;AAC5C,UAAA,SAAA,CAAU,OAAA,GAAU,MAAM,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA;AAAA,QAChD,CAAA;AACA,QAAA,GAAA,CAAI,OAAA,GAAU,MAAM,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA;AAAA,MACpC,CAAA;AAAA,MACA,KAAA;AAAA,KACF;AAAA,EACF,SAAS,GAAA,EAAK;AACZ,IAAA,OAAA,CAAQ,IAAA,CAAK,mCAAmC,GAAG,CAAA;AAAA,EACrD;AACF;ACpKA,IAAM,UAAA,GAAa,IAAA;AAAA,EAAK,MACtB,OAAO,oCAAuB,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,MAAO,EAAE,OAAA,EAAS,CAAA,CAAE,mBAAA,EAAoB,CAAE;AAClF,CAAA;AAEA,IAAM,4BAA4B,sBAChCE,IAAC,KAAA,EAAA,EAAI,SAAA,EAAU,iEAAgE,QAAA,EAAA,oCAAA,EAE/E,CAAA;AAMF,IAAM,gBAAA,GAAmB,GAAA;AAwClB,SAAS,UAAA,CAAW;AAAA,EACzB,UAAA,GAAa,SAAA;AAAA,EACb,QAAA,GAAW,KAAA;AAAA,EACX,aAAA;AAAA,EACA,aAAA;AAAA,EACA,KAAA;AAAA,EACA,QAAA,GAAW,OAAA;AAAA,EACX,MAAA,GAAS;AACX,CAAA,EAAoB;AAClB,EAAA,MAAM,CAAC,GAAA,EAAK,MAAM,CAAA,GAAIC,SAAuB,IAAI,CAAA;AACjD,EAAA,MAAM,MAAA,GAASF,OAAqB,IAAI,CAAA;AACxC,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAIE,SAAS,KAAK,CAAA;AACpD,EAAA,MAAM,cAAA,GAAiBF,OAAO,KAAK,CAAA;AACnC,EAAA,MAAM,eAAA,GAAkBA,MAAAA,iBAAoB,IAAI,GAAA,EAAK,CAAA;AACrD,EAAA,MAAM,gBAAA,GAAmBA,OAAe,EAAE,CAAA;AAC1C,EAAA,MAAM,gBAAA,GAAmBA,OAA6C,IAAI,CAAA;AAC1E,EAAA,MAAM,eAAA,GAAkBA,OAA6C,IAAI,CAAA;AACzE,EAAA,MAAM,gBAAA,GAAmBA,OAA6C,IAAI,CAAA;AAC1E,EAAA,MAAM,cAAA,GAAiBA,OAIb,IAAI,CAAA;AACd,EAAA,MAAM,eAAA,GAAkBA,MAAAA,CAAoB,EAAE,CAAA;AAC9C,EAAA,MAAM,cAAA,GAAiB,OAAO,UAAA,KAAe,QAAA,IAAY,WAAW,MAAA,GAAS,CAAA;AAC7E,EAAA,MAAM,aAAA,GAAgBA,OAAO,UAAU,CAAA;AACvC,EAAA,aAAA,CAAc,OAAA,GAAU,UAAA;AAExB,EAAA,MAAM,gBAAA,GAAmBG,OAAAA;AAAA,IACvB,MAAO,cAAA,GAAiB,SAAA,CAAU,UAAoB,CAAA,GAAI,IAAA;AAAA,IAC1D,CAAC,gBAAgB,UAAU;AAAA,GAC7B;AACA,EAAA,MAAM,wBAAwD,gBAAA,GAC1D;AAAA,IACE,UAAU,gBAAA,CAAiB,QAAA;AAAA,IAC3B,UAAU,gBAAA,CAAiB;AAAA,GAC7B,GACA,IAAA;AAGJ,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAID,SAAwB,IAAI,CAAA;AAClE,EAAA,MAAM,cAAA,GAAiBF,OAAO,WAAW,CAAA;AACzC,EAAA,cAAA,CAAe,OAAA,GAAU,WAAA;AACzB,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAIE,SAAgC,IAAI,CAAA;AAChF,EAAA,MAAM,OAAA,GAAUF,OAA+B,IAAI,CAAA;AAEnD,EAAA,MAAM,gBAAA,GAAmBA,OAAsB,IAAI,CAAA;AACnD,EAAA,MAAM,qBAAA,GAAwBA,OAAe,WAAW,CAAA;AAExD,EAAA,MAAM,WAAA,GAAcG,QAAQ,MAAM;AAChC,IAAA,MAAM,CAAA,uBAAQ,GAAA,EAAuB;AACrC,IAAA,KAAA,MAAW,KAAK,MAAA,EAAQ,CAAA,CAAE,GAAA,CAAI,CAAA,CAAE,MAAM,CAAC,CAAA;AACvC,IAAA,OAAO,CAAA;AAAA,EACT,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAEX,EAAA,MAAM,iBAAiB,WAAA,GAAc,WAAA,CAAY,GAAA,CAAI,WAAW,KAAK,IAAA,GAAO,IAAA;AAC5E,EAAA,MAAM,aAAA,GAAgB,gBAAgB,IAAA,IAAQ,IAAA;AAG9C,EAAA,MAAM,SAAA,GAAYC,WAAAA;AAAA,IAChB,CAAC,IAAA,EAAc,OAAA,GAAiC,IAAA,KAAS;AACvD,MAAA,IAAI,QAAA,EAAU;AACd,MAAA,IAAI,CAAC,WAAA,CAAY,GAAA,CAAI,IAAI,CAAA,EAAG;AAC5B,MAAA,iBAAA,CAAkB,OAAO,CAAA;AACzB,MAAA,cAAA,CAAe,IAAI,CAAA;AAAA,IACrB,CAAA;AAAA,IACA,CAAC,UAAU,WAAW;AAAA,GACxB;AAEA,EAAA,MAAM,UAAA,GAAaA,YAAY,MAAM;AACnC,IAAA,cAAA,CAAe,IAAI,CAAA;AACnB,IAAA,iBAAA,CAAkB,IAAI,CAAA;AAAA,EACxB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,iBAAA,GAAoBA,WAAAA;AAAA,IACxB,CAAC,IAAA,KAAiB;AAChB,MAAA,IAAI,WAAA,KAAgB,MAAM,UAAA,EAAW;AAAA,qBACtB,IAAI,CAAA;AAAA,IACrB,CAAA;AAAA,IACA,CAAC,WAAA,EAAa,SAAA,EAAW,UAAU;AAAA,GACrC;AAGA,EAAA,MAAM,YAAA,GAAeA,WAAAA;AAAA;AAAA,IAEnB,CAAC,QAAA,EAAwC,QAAA,EAAe,KAAA,KAAuB;AAM7E,MAAA,MAAM,QAAA,GAAW,UAAU,KAAA,KAAU,MAAA;AACrC,MAAA,IAAI,cAAA,CAAe,YAAY,QAAA,EAAU;AACvC,QAAA,cAAA,CAAe,OAAA,GAAU,QAAA;AACzB,QAAA,cAAA,CAAe,MAAM,cAAA,CAAe,QAAQ,CAAC,CAAA;AAAA,MAC/C;AAEA,MAAA,IAAI,QAAA,EAAU;AACd,MAAA,cAAA,CAAe,OAAA,GAAU,EAAE,QAAA,EAAU,QAAA,EAAS;AAM9C,MAAA,MAAM,SAAS,QAAA,EAAU,iBAAA;AACzB,MAAA,IAAI,MAAA,IAAU,MAAA,KAAW,gBAAA,CAAiB,OAAA,IAAW,GAAA,EAAK;AACxD,QAAA,MAAM,KAAK,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAyB,CAAA,CAAE,OAAO,MAAM,CAAA;AAClE,QAAA,IAAI,EAAA,EAAI;AACN,UAAA,MAAM,KAAA,GAAQ,sBAAA,CAAwB,EAAA,CAAgC,UAAA,EAAY,MAAM,CAAA;AACxF,UAAA,IAAI,KAAA,EAAO;AACT,YAAA,gBAAA,CAAiB,OAAA,GAAU,MAAA;AAK3B,YAAA,MAAM,OAAO,EAAA,CAAG,EAAA;AAChB,YAAA,MAAM,WAAY,EAAA,CAAgC,UAAA;AAClD,YAAA,MAAM,YAAY,KAAA,CAAM,IAAA;AACxB,YAAA,cAAA,CAAe,MAAM;AACnB,cAAA,IAAI;AACF,gBAAA,GAAA,CAAI,WAAA,CAAY;AAAA,kBACd,UAAU,EAAE,iBAAA,EAAmB,IAAA,EAAM,kBAAA,EAAoB,EAAC;AAAE,iBAC7D,CAAA;AAAA,cACH,CAAA,CAAA,MAAQ;AAAA,cAAe;AACvB,cAAA,SAAA,CAAU,WAAW,EAAE,EAAA,EAAI,IAAA,EAAM,UAAA,EAAY,UAAU,CAAA;AAAA,YACzD,CAAC,CAAA;AACD,YAAA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,MAAA,IAAI,CAAC,MAAA,EAAQ;AACX,QAAA,gBAAA,CAAiB,OAAA,GAAU,IAAA;AAAA,MAC7B;AAEA,MAAA,MAAM,OAAA,GAAU,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA;AACjC,MAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,MAAA,CAAO,CAAC,EAAA,KAAO,CAAC,eAAA,CAAgB,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAC,CAAA;AACtE,MAAA,IAAI,MAAA,CAAO,SAAS,CAAA,EAAG;AACrB,QAAA,MAAA,CAAO,QAAQ,CAAC,EAAA,KAAO,gBAAgB,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAC,CAAA;AACtD,QAAA,aAAA,GAAgB,OAAO,MAAM,CAAA;AAAA,MAC/B;AAEA,MAAA,IAAI,CAAC,iBAAiB,OAAA,EAAS;AAC7B,QAAA,gBAAA,CAAiB,OAAA,GAAU,WAAW,YAAY;AAChD,UAAA,gBAAA,CAAiB,OAAA,GAAU,IAAA;AAC3B,UAAA,MAAM,GAAA,GAAM,MAAM,OAAO,wBAAwB,CAAA;AACjD,UAAA,MAAM,WAAA,GAAc,cAAA,CAAe,OAAA,IAAW,EAAE,UAAU,QAAA,EAAS;AACnE,UAAA,MAAM,YAAA,GAAe,YAAY,QAAA,CAAS,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,EAAE,SAAS,CAAA;AACpE,UAAA,MAAM,YAAA,GAAe,oBAAA,CAAqB,WAAA,CAAY,QAAQ,CAAA;AAE9D,UAAA,MAAM,WAAA,GAAe,GAAA,CAAY,mBAAA,CAAoB,YAAY,CAAA;AACjE,UAAA,MAAM,YAAY,CAAA,EAAG,WAAW,IAAI,IAAA,CAAK,SAAA,CAAU,YAAY,CAAC,CAAA,CAAA;AAChE,UAAA,IAAI,SAAA,KAAc,iBAAiB,OAAA,EAAS;AAC5C,UAAA,gBAAA,CAAiB,OAAA,GAAU,SAAA;AAC3B,UAAA,aAAA,GAAgB,EAAE,QAAA,EAAU,YAAA,EAAc,QAAA,EAAU,cAAc,CAAA;AAElE,UAAA,IAAI,cAAA,EAAgB;AAClB,YAAA,UAAA,CAAW,UAAA,EAAsB;AAAA,cAC/B,QAAA,EAAU,YAAA;AAAA,cACV,QAAA,EAAU;AAAA,aACX,CAAA;AAAA,UACH;AAAA,QACF,GAAG,gBAAgB,CAAA;AAAA,MACrB;AAGA,MAAA,IAAI,cAAA,IAAkB,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG;AACvC,QAAA,KAAA,MAAW,MAAM,MAAA,EAAQ;AACvB,UAAA,IAAI,KAAA,CAAM,EAAE,CAAA,EAAG,eAAA,CAAgB,QAAQ,EAAE,CAAA,GAAI,MAAM,EAAE,CAAA;AAAA,QACvD;AACA,QAAA,IAAI,CAAC,gBAAgB,OAAA,EAAS;AAC5B,UAAA,eAAA,CAAgB,OAAA,GAAU,WAAW,MAAM;AACzC,YAAA,eAAA,CAAgB,OAAA,GAAU,IAAA;AAC1B,YAAA,MAAM,UAAU,eAAA,CAAgB,OAAA;AAChC,YAAA,eAAA,CAAgB,UAAU,EAAC;AAC3B,YAAA,MAAM,eAAA,GAAmB,GAAA,EAAK,gBAAA,IAAmB,IAAK,QAAA;AACtD,YAAA,MAAM,QAAA,uBAAe,GAAA,EAAY;AACjC,YAAA,KAAA,MAAW,MAAM,eAAA,EAAiB;AAEhC,cAAA,MAAM,MAAO,EAAA,CAAW,MAAA;AACxB,cAAA,IAAI,OAAO,cAAA,CAAe,EAAE,CAAA,EAAG,QAAA,CAAS,IAAI,GAAG,CAAA;AAAA,YACjD;AACA,YAAA,MAAM,SAAsB,EAAC;AAC7B,YAAA,KAAA,MAAW,CAAC,EAAA,EAAI,CAAC,KAAK,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAA,EAAG;AAC7C,cAAA,IAAI,CAAC,QAAA,CAAS,GAAA,CAAI,EAAE,CAAA,EAAG,MAAA,CAAO,EAAE,CAAA,GAAI,CAAA;AAAA,YACtC;AACA,YAAA,IAAI,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,CAAE,SAAS,CAAA,EAAG;AAClC,cAAA,KAAK,UAAA,CAAW,aAAA,CAAc,OAAA,EAAmB,MAAM,CAAA;AAAA,YACzD;AAAA,UACF,GAAG,GAAI,CAAA;AAAA,QACT;AAAA,MACF;AAGA,MAAA,IAAI,cAAA,IAAkB,CAAC,gBAAA,CAAiB,OAAA,EAAS;AAC/C,QAAA,gBAAA,CAAiB,OAAA,GAAU,WAAW,MAAM;AAC1C,UAAA,gBAAA,CAAiB,OAAA,GAAU,IAAA;AAC3B,UAAA,MAAM,eAAA,GAAmB,GAAA,EAAK,gBAAA,IAAmB,IAAK,QAAA;AACtD,UAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAC7B,UAAA,KAAA,MAAW,MAAM,eAAA,EAAiB;AAEhC,YAAA,MAAM,MAAO,EAAA,CAAW,MAAA;AACxB,YAAA,IAAI,OAAO,CAAC,cAAA,CAAe,EAAE,CAAA,EAAG,IAAA,CAAK,IAAI,GAAG,CAAA;AAAA,UAC9C;AACA,UAAA,KAAK,UAAA,CAAW,aAAA,CAAc,OAAA,EAAmB,IAAI,CAAA;AAAA,QACvD,GAAG,GAAI,CAAA;AAAA,MACT;AAAA,IACF,CAAA;AAAA,IACA,CAAC,UAAU,GAAA,EAAK,aAAA,EAAe,eAAe,cAAA,EAAgB,UAAA,EAAY,QAAQ,SAAS;AAAA,GAC7F;AAGA,EAAAL,UAAU,MAAM;AACd,IAAA,IAAI,CAAC,GAAA,IAAO,CAAC,cAAA,EAAgB;AAC7B,IAAA,IAAI,SAAA,GAAY,KAAA;AAChB,IAAA,KAAK,SAAA,CAAU,UAAoB,CAAA,CAAE,IAAA,CAAK,CAAC,KAAA,KAAU;AACnD,MAAA,IAAI,SAAA,EAAW;AACf,MAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA;AACpC,MAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AAC1B,MAAA,IAAI;AACF,QAAA,GAAA,CAAI,QAAA;AAAA,UACF,QAAQ,GAAA,CAAI,CAAC,CAAC,EAAA,EAAI,CAAC,CAAA,MAAO;AAAA,YACxB,EAAA;AAAA;AAAA,YAEA,SAAU,CAAA,CAAU,OAAA;AAAA;AAAA,YAEpB,UAAW,CAAA,CAAU,QAAA;AAAA;AAAA,YAErB,OAAA,EAAU,CAAA,CAAU,OAAA,IAAW,IAAA,CAAK,GAAA;AAAI,WAC1C,CAAE;AAAA,SACJ;AACA,QAAA,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAC,EAAE,MAAM,eAAA,CAAgB,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA,MAC3D,SAAS,GAAA,EAAK;AACZ,QAAA,OAAA,CAAQ,IAAA,CAAK,yDAA0C,GAAG,CAAA;AAAA,MAC5D;AAAA,IACF,CAAC,CAAA;AACD,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AAAA,IACd,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,GAAA,EAAK,cAAA,EAAgB,UAAU,CAAC,CAAA;AAEpC,EAAAA,UAAU,MAAM;AACd,IAAA,IAAI,CAAC,GAAA,EAAK;AACV,IAAA,IAAI,SAAA,GAAY,KAAA;AAChB,IAAA,MAAM,MAAM,YAAY;AACtB,MAAA,IAAI;AACF,QAAA,MAAM,QAAA,GAAW,IAAI,gBAAA,EAAiB;AACtC,QAAA,IAAI,CAAC,QAAA,IAAY,QAAA,CAAS,MAAA,KAAW,CAAA,EAAG;AACxC,QAAA,IAAI,SAAA,EAAW;AACf,QAAA,MAAM,wBAAA,CAAyB,GAAA,EAAK,QAAA,EAAU,MAAM,CAAA;AAAA,MACtD,SAAS,GAAA,EAAK;AACZ,QAAA,OAAA,CAAQ,IAAA,CAAK,mCAAmC,GAAG,CAAA;AAAA,MACrD;AAAA,IACF,CAAA;AACA,IAAA,GAAA,EAAI;AACJ,IAAA,MAAM,CAAA,GAAI,UAAA,CAAW,GAAA,EAAK,GAAG,CAAA;AAC7B,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,YAAA,CAAa,CAAC,CAAA;AAAA,IAChB,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,GAAA,EAAK,gBAAA,EAAkB,MAAM,CAAC,CAAA;AAElC,EAAAA,SAAAA;AAAA,IACE,MAAM,MAAM;AACV,MAAA,IAAI,gBAAA,CAAiB,OAAA,EAAS,YAAA,CAAa,gBAAA,CAAiB,OAAO,CAAA;AACnE,MAAA,IAAI,eAAA,CAAgB,OAAA,EAAS,YAAA,CAAa,eAAA,CAAgB,OAAO,CAAA;AACjE,MAAA,IAAI,gBAAA,CAAiB,OAAA,EAAS,YAAA,CAAa,gBAAA,CAAiB,OAAO,CAAA;AAAA,IACrE,CAAA;AAAA,IACA;AAAC,GACH;AAGA,EAAA,MAAM,oBAAoB,mBAAA,CAAoB;AAAA,IAC5C,SAAS,CAAC,QAAA;AAAA,IACV,MAAA;AAAA,IACA,MAAA,EAAQ;AAAA,GACT,CAAA;AAGD,EAAA,YAAA,CAAa;AAAA,IACX,SAAS,CAAC,QAAA;AAAA,IACV,QAAA,EAAU,iBAAA;AAAA,IACV;AAAA,GACD,CAAA;AAGD,EAAAA,UAAU,MAAM;AACd,IAAA,IAAI,CAAC,GAAA,EAAK;AACV,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,IAAI;AACF,QAAA,MAAM,GAAA,GAAM,GAAA,CAAI,WAAA,IAAc,EAAG,YAAY,IAAA,IAAQ,WAAA;AACrD,QAAA,IAAI,GAAA,IAAO,GAAA,KAAQ,MAAA,EAAQ,qBAAA,CAAsB,OAAA,GAAU,GAAA;AAC3D,QAAA,GAAA,CAAI,aAAA,GAAgB,EAAE,IAAA,EAAM,MAAA,EAAQ,CAAA;AAAA,MACtC,CAAA,CAAA,MAAQ;AAAA,MAAe;AAAA,IACzB,CAAA,MAAO;AACL,MAAA,IAAI;AAEF,QAAA,GAAA,CAAI,aAAA,GAAgB,EAAE,IAAA,EAAM,qBAAA,CAAsB,SAAgB,CAAA;AAAA,MACpE,CAAA,CAAA,MAAQ;AAAA,MAAe;AAAA,IACzB;AAAA,EACF,CAAA,EAAG,CAAC,WAAA,EAAa,GAAG,CAAC,CAAA;AAGrB,EAAA,uBAAA,CAAwB,EAAE,WAAA,EAAa,MAAA,EAAQ,CAAA;AAG/C,EAAAA,UAAU,MAAM;AACd,IAAA,IAAI,CAAC,WAAA,EAAa;AAClB,IAAA,MAAM,KAAA,GAAQ,CAAC,CAAA,KAAqB;AAClC,MAAA,IAAI,CAAA,CAAE,QAAQ,QAAA,EAAU;AACxB,MAAA,MAAM,KAAK,QAAA,CAAS,aAAA;AACpB,MAAA,IAAI,EAAA,KAAO,EAAA,CAAG,OAAA,KAAY,UAAA,IAAc,GAAG,iBAAA,CAAA,EAAoB;AAC7D,QAAA;AAAA,MACF;AACA,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,MAAA,UAAA,EAAW;AAAA,IACb,CAAA;AACA,IAAA,MAAA,CAAO,iBAAiB,SAAA,EAAW,KAAA,EAAO,EAAE,OAAA,EAAS,MAAM,CAAA;AAC3D,IAAA,OAAO,MAAM,OAAO,mBAAA,CAAoB,SAAA,EAAW,OAAO,EAAE,OAAA,EAAS,MAAM,CAAA;AAAA,EAC7E,CAAA,EAAG,CAAC,WAAA,EAAa,UAAU,CAAC,CAAA;AAE5B,EAAA,oBAAA,CAAqB,EAAE,WAAA,EAAa,OAAA,EAAS,OAAA,EAAS,YAAY,CAAA;AAElE,EAAA,uBACEM,KAAC,KAAA,EAAA,EAAI,SAAA,EAAW,yBAAyB,WAAA,GAAc,cAAA,GAAiB,EAAE,CAAA,CAAA,EACxE,QAAA,EAAA;AAAA,oBAAAJ,IAAC,QAAA,EAAA,EAAS,QAAA,kBAAUA,GAAAA,CAAC,yBAAA,EAAA,EAA0B,GAC7C,QAAA,kBAAAA,GAAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,aAAA,EAAe,CAAC,CAAA,KAAa;AAI3B,UAAA,IAAI,MAAA,CAAO,YAAY,CAAA,EAAG;AAC1B,UAAA,MAAA,CAAO,OAAA,GAAU,CAAA;AACjB,UAAA,cAAA,CAAe,MAAM;AACnB,YAAA,MAAA,CAAO,CAAC,CAAA;AACR,YAAA,KAAA,GAAQ,CAAC,CAAA;AAAA,UACX,CAAC,CAAA;AAAA,QACH,CAAA;AAAA,QACA,QAAA;AAAA,QACA,eAAA,EAAiB,QAAA;AAAA,QACjB,aACE,qBAAA,GACI;AAAA,UACE,UAAU,qBAAA,CAAsB,QAAA;AAAA,UAChC,QAAA,EAAU;AAAA,YACR,GAAG,qBAAA,CAAsB,QAAA;AAAA,YACzB,QAAA,EAAU,qBAAA,CAAsB,QAAA,CAAS,QAAA,IAAY;AAAA;AACvD,YAEF,EAAE,QAAA,EAAU,EAAE,mBAAA,EAAqB,WAAU,EAAE;AAAA,QAErD,QAAA,EAAU,YAAA;AAAA,QACV,aAAA,EAAe;AAAA;AAAA,KACjB,EACF,CAAA;AAAA,oBAEAA,GAAAA;AAAA,MAAC,eAAA;AAAA,MAAA;AAAA,QACC,SAAS,CAAC,QAAA;AAAA,QACV,eAAA,EAAiB,WAAA;AAAA,QACjB,QAAA,EAAU,iBAAA;AAAA,QACV;AAAA;AAAA,KACF;AAAA,IAEC,iCACCA,GAAAA;AAAA,MAAC,aAAA;AAAA,MAAA;AAAA,QACC,GAAA,EAAK,OAAA;AAAA,QACL,GAAA;AAAA,QACA,cAAA;AAAA,QACA,OAAA,EAAS,UAAA;AAAA,QACT,MAAA,EAAQ;AAAA;AAAA;AACV,GAAA,EAEJ,CAAA;AAEJ","file":"index.mjs","sourcesContent":["import type { AppState } from '@excalidraw/excalidraw/types';\nimport type { SyncableAppState } from './types';\n\nexport function pickSyncableAppState(s: AppState): SyncableAppState {\n return {\n viewBackgroundColor: s.viewBackgroundColor,\n zoom: s.zoom,\n scrollX: s.scrollX,\n scrollY: s.scrollY,\n gridSize: s.gridSize ?? null,\n theme: s.theme,\n };\n}\n","import { geometryStamp } from '../geometry-2d';\nimport { latexStamp } from '../latex';\nimport { geometry3dStamp } from '../geometry-3d';\nimport { graph2dStamp } from '../graph-2d';\nimport type { StampType } from './types';\n\nexport { geometryStamp, type GeometryCustomData, isGeometryCustomData } from '../geometry-2d';\nexport { latexStamp, type LatexCustomData, isLatexCustomData } from '../latex';\nexport {\n geometry3dStamp,\n type Geometry3DCustomData,\n isGeometry3DCustomData,\n} from '../geometry-3d';\nexport { graph2dStamp, type Graph2DCustomData, isGraph2DCustomData } from '../graph-2d';\nexport type { StampType, BaseStampCustomData } from './types';\n\n/** Stamp ổn định, sẵn sàng production. */\nexport const STABLE_STAMPS: ReadonlyArray<StampType> = Object.freeze([\n geometryStamp,\n latexStamp,\n]);\n\n/** Stamp experimental — chưa ổn định cho production. Consumer phải opt-in. */\nexport const EXPERIMENTAL_STAMPS: ReadonlyArray<StampType> = Object.freeze([\n geometry3dStamp,\n graph2dStamp,\n]);\n\n/** Tất cả stamp (stable + experimental). Dùng khi consumer muốn full feature. */\nexport const ALL_STAMPS: ReadonlyArray<StampType> = Object.freeze([\n ...STABLE_STAMPS,\n ...EXPERIMENTAL_STAMPS,\n]);\n\n/**\n * Set stamp mặc định cho Whiteboard. v0.7.0 trở đi = STABLE_STAMPS.\n * Consumer muốn experimental: `<Whiteboard stamps={ALL_STAMPS} />` hoặc\n * `[...DEFAULT_STAMPS, geometry3dStamp]`.\n *\n * Để thêm 1 stamp mới (vd chart):\n * 1. Tạo `src/stamps/chart/index.tsx` export `chartStamp: StampType`.\n * 2. Import + add vào STABLE_STAMPS (production-ready) hoặc\n * EXPERIMENTAL_STAMPS (chưa ổn định) ở file này.\n * 3. Re-export `chartStamp` từ `src/stamps/index.ts` + `src/index.ts`.\n */\nexport const DEFAULT_STAMPS: ReadonlyArray<StampType> = STABLE_STAMPS;\n\n/** Tìm stamp tương ứng với customData của element. null nếu không match. */\nexport function findStampForCustomData(\n data: unknown,\n stamps: ReadonlyArray<StampType> = DEFAULT_STAMPS,\n): StampType | null {\n for (const s of stamps) {\n if (s.matchesCustomData(data)) return s;\n }\n return null;\n}\n\n/** isMathStamp version dựa trên registry. */\nexport function isStampElement<T extends { customData?: unknown }>(\n element: T,\n stamps: ReadonlyArray<StampType> = DEFAULT_STAMPS,\n): boolean {\n return findStampForCustomData(element.customData, stamps) !== null;\n}\n","'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { DEFAULT_STAMPS } from './registry';\nimport type { StampType } from './types';\n\ninterface Props {\n /** Bật/tắt theo role. Khi disabled → không mount portal. */\n enabled: boolean;\n /** Kind stamp đang active, hoặc null nếu không có stamp nào mở. */\n activeStampKind: string | null;\n /** Toggle stamp theo kind. */\n onToggle: (kind: string) => void;\n /** Danh sách stamp đăng ký. Mặc định DEFAULT_STAMPS. */\n stamps?: ReadonlyArray<StampType>;\n}\n\nconst MENU_WRAPPER_ID = 'stamp-menu-portal-wrapper';\n/**\n * Excalidraw 0.18 áp dụng class `App-toolbar__extra-tools-dropdown` lên\n * popover wrapper của More tools cho cả desktop lẫn mobile. Mobile có\n * thêm modifier `dropdown-menu--mobile`. Selector này cover cả hai mode.\n */\nconst POPOVER_SELECTOR =\n '.App-toolbar__extra-tools-dropdown .dropdown-menu-container';\n\n/**\n * Inject stamp buttons vào popover \"More tools\" của Excalidraw.\n *\n * v0.7.0: thay vì inject inline vào main toolbar (desktop) hoặc dropdown\n * riêng cho mobile, ta chỉ inject vào dropdown-menu-container bên trong\n * popover của Excalidraw. Selector cover cả desktop lẫn mobile vì hai\n * mode dùng cùng cấu trúc DOM (DropdownMenu.Content).\n *\n * Popover mount/unmount theo trigger click → MutationObserver dò DOM,\n * mount lại wrapper khi cần.\n */\nexport function ToolbarInjector({\n enabled,\n activeStampKind,\n onToggle,\n stamps = DEFAULT_STAMPS,\n}: Props) {\n const [menuMount, setMenuMount] = useState<HTMLElement | null>(null);\n const menuMountRef = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n if (!enabled) {\n if (menuMountRef.current !== null) {\n menuMountRef.current = null;\n setMenuMount(null);\n }\n document.getElementById(MENU_WRAPPER_ID)?.remove();\n return;\n }\n\n let cancelled = false;\n let observer: MutationObserver | null = null;\n let rafId: number | null = null;\n\n const apply = (next: HTMLElement | null) => {\n if (cancelled || menuMountRef.current === next) return;\n menuMountRef.current = next;\n queueMicrotask(() => {\n if (!cancelled) setMenuMount(next);\n });\n };\n\n const findMenu = () => {\n if (cancelled) return;\n const container = document.querySelector<HTMLElement>(POPOVER_SELECTOR);\n if (!container) {\n apply(null);\n return;\n }\n let wrapper = container.querySelector<HTMLDivElement>('#' + MENU_WRAPPER_ID);\n if (!wrapper) {\n wrapper = document.createElement('div');\n wrapper.id = MENU_WRAPPER_ID;\n wrapper.setAttribute('data-stamp-menu', 'true');\n wrapper.setAttribute('data-stamp-area', 'true');\n wrapper.style.display = 'contents';\n container.insertBefore(wrapper, container.firstChild);\n }\n apply(wrapper);\n };\n\n const schedule = () => {\n if (rafId != null) return;\n rafId = requestAnimationFrame(() => {\n rafId = null;\n findMenu();\n });\n };\n\n findMenu();\n\n const root = document.querySelector('.excalidraw') ?? document.body;\n observer = new MutationObserver(schedule);\n observer.observe(root, { childList: true, subtree: true });\n\n return () => {\n cancelled = true;\n if (rafId != null) cancelAnimationFrame(rafId);\n observer?.disconnect();\n document.getElementById(MENU_WRAPPER_ID)?.remove();\n };\n }, [enabled]);\n\n if (!enabled || !menuMount) return null;\n\n const closePopover = () => {\n const trigger = document.querySelector<HTMLButtonElement>(\n '.App-toolbar__extra-tools-trigger',\n );\n trigger?.click();\n };\n\n return createPortal(\n <>\n {stamps.map((stamp) => (\n <StampMenuItem\n key={stamp.kind}\n icon={stamp.toolbarIcon}\n label={stamp.toolbarTitle}\n active={activeStampKind === stamp.kind}\n onClick={() => {\n onToggle(stamp.kind);\n closePopover();\n }}\n dataTestId={stamp.toolbarTestId}\n />\n ))}\n <div\n aria-hidden=\"true\"\n style={{\n height: 1,\n background: 'var(--default-border-color, rgba(0,0,0,0.08))',\n margin: '6px 4px',\n }}\n />\n </>,\n menuMount,\n );\n}\n\ninterface StampMenuItemProps {\n icon: React.ReactNode;\n label: string;\n active: boolean;\n onClick: () => void;\n dataTestId?: string;\n}\n\nfunction StampMenuItem({ icon, label, active, onClick, dataTestId }: StampMenuItemProps) {\n const className = [\n 'dropdown-menu-item',\n 'dropdown-menu-item-base',\n active ? 'dropdown-menu-item--selected' : '',\n ]\n .filter(Boolean)\n .join(' ');\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-label={label}\n aria-pressed={active}\n data-testid={dataTestId}\n className={className}\n style={{\n display: 'flex',\n alignItems: 'center',\n columnGap: '0.625rem',\n width: '100%',\n boxSizing: 'border-box',\n background: 'transparent',\n border: '1px solid transparent',\n cursor: 'pointer',\n fontFamily: 'inherit',\n fontSize: '0.875rem',\n color: 'var(--color-on-surface)',\n }}\n >\n <span\n aria-hidden=\"true\"\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '1rem',\n height: '1rem',\n }}\n >\n {icon}\n </span>\n <span>{label}</span>\n </button>\n );\n}\n","import { useEffect } from 'react';\nimport { DEFAULT_STAMPS } from './registry';\nimport type { StampType } from './types';\n\ninterface Options {\n enabled: boolean;\n /** Toggle stamp theo kind khi user bấm shortcut tương ứng. */\n onToggle: (kind: string) => void;\n /** Registry. Mặc định DEFAULT_STAMPS. */\n stamps?: ReadonlyArray<StampType>;\n}\n\nfunction isEditableTarget(t: EventTarget | null): boolean {\n if (!t || !(t instanceof HTMLElement)) return false;\n if (t.isContentEditable) return true;\n const tag = t.tagName;\n return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT';\n}\n\n/**\n * Bind keyboard shortcut cho mỗi stamp trong registry. Capture phase +\n * stopPropagation để chặn trước Excalidraw's bubble-phase handlers (Excalidraw\n * dùng `L` cho Line tool, nếu không chặn → bấm L lại chuyển tool thay vì\n * toggle LaTeX panel).\n */\nexport function useShortcuts({\n enabled,\n onToggle,\n stamps = DEFAULT_STAMPS,\n}: Options): void {\n useEffect(() => {\n if (!enabled) return;\n const keyToKind = new Map<string, string>();\n for (const s of stamps) keyToKind.set(s.shortcutKey, s.kind);\n\n const handler = (e: KeyboardEvent) => {\n if (e.metaKey || e.ctrlKey || e.altKey) return;\n if (isEditableTarget(e.target)) return;\n const key = e.key.toLowerCase();\n const kind = keyToKind.get(key);\n if (!kind) return;\n e.preventDefault();\n e.stopPropagation();\n onToggle(kind);\n };\n window.addEventListener('keydown', handler, { capture: true });\n return () => window.removeEventListener('keydown', handler, { capture: true });\n }, [enabled, onToggle, stamps]);\n}\n","import { useCallback, useRef } from 'react';\nimport { findStampForCustomData } from './registry';\nimport type { StampType } from './types';\n\nconst DOUBLE_CLICK_MS = 400;\n\ninterface Opts {\n enabled: boolean;\n stamps: ReadonlyArray<StampType>;\n onOpen: (\n kind: string,\n editingElement: { id: string; customData: unknown },\n ) => void;\n}\n\n/**\n * Trả về handler cho Excalidraw `onPointerDown`. Phát hiện double-click vào\n * image element thuộc một stamp đã đăng ký → gọi `onOpen(kind, element)`.\n */\nexport function useStampDoubleClick({ enabled, stamps, onOpen }: Opts) {\n const lastClickRef = useRef<{ time: number; elementId: string | null }>({\n time: 0,\n elementId: null,\n });\n\n return useCallback(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (_activeTool: any, pointerDownState: any) => {\n if (!enabled) return;\n const hitElement = pointerDownState?.hit?.element;\n if (!hitElement || hitElement.type !== 'image') return;\n const stamp = findStampForCustomData(hitElement.customData, stamps);\n if (!stamp) return;\n const now = Date.now();\n const isDouble =\n lastClickRef.current.elementId === hitElement.id &&\n now - lastClickRef.current.time < DOUBLE_CLICK_MS;\n lastClickRef.current = { time: now, elementId: hitElement.id };\n if (!isDouble) return;\n onOpen(stamp.kind, {\n id: hitElement.id,\n customData: hitElement.customData,\n });\n },\n [enabled, stamps, onOpen],\n );\n}\n","import { useEffect, useMemo } from 'react';\nimport type { StampType } from './types';\n\nconst ALLOWED_KEYS = new Set([\n 'Tab', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight',\n 'Shift', 'Control', 'Alt', 'Meta', 'CapsLock',\n 'Home', 'End', 'PageUp', 'PageDown',\n]);\n\nfunction isEditable(el: EventTarget | null): boolean {\n if (!(el instanceof HTMLElement)) return false;\n if (el.isContentEditable) return true;\n const tag = el.tagName;\n return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT';\n}\n\ninterface Opts {\n /** Active stamp kind. Null = no blocker. */\n activeStamp: string | null;\n /** Stamps đăng ký để allow shortcut keys của chúng đi qua. */\n stamps: ReadonlyArray<StampType>;\n}\n\n/**\n * Khi 1 stamp panel đang mở, block hoàn toàn các phím tắt Excalidraw\n * (1-9, V, R, D...) bằng cách prevent + stop tại capture phase. Cho phép qua:\n * Tab/Arrow/Modifier, Escape, và phím tắt đã đăng ký bởi stamps (V/G/L/D...).\n */\nexport function useStampShortcutBlocker({ activeStamp, stamps }: Opts) {\n const shortcutKeys = useMemo(\n () => new Set(stamps.map((s) => s.shortcutKey.toLowerCase())),\n [stamps],\n );\n\n useEffect(() => {\n if (!activeStamp) return;\n\n const blocker = (e: KeyboardEvent) => {\n if (isEditable(e.target)) return;\n if (e.ctrlKey || e.metaKey) return;\n if (ALLOWED_KEYS.has(e.key)) return;\n if (e.key === 'Escape') return;\n if (shortcutKeys.has(e.key.toLowerCase())) return;\n e.preventDefault();\n e.stopPropagation();\n };\n window.addEventListener('keydown', blocker, { capture: true });\n return () => window.removeEventListener('keydown', blocker, { capture: true });\n }, [activeStamp, shortcutKeys]);\n}\n","import { useEffect, type RefObject } from 'react';\nimport type { StampHostHandle } from './types';\n\ninterface Opts {\n /** Active stamp. Null = no listener. */\n activeStamp: string | null;\n /** Ref tới Host imperative API (tryInsert / hasContent). */\n hostRef: RefObject<StampHostHandle | null>;\n /** Callback đóng panel. */\n onClose: () => void;\n}\n\n/**\n * Khi 1 stamp panel mở, lắng nghe pointer/mouse-down toàn document. Nếu\n * điểm click không nằm trong vùng `[data-stamp-area=\"true\"]`, gọi\n * `hostRef.tryInsert()` để auto-commit (nếu có nội dung) rồi `onClose()`.\n */\nexport function useStampClickOutside({ activeStamp, hostRef, onClose }: Opts) {\n useEffect(() => {\n if (!activeStamp) return;\n let lastFire = 0;\n const handler = (e: MouseEvent) => {\n const target = e.target as HTMLElement | null;\n if (!target) return;\n if (target.closest('[data-stamp-area=\"true\"]')) return;\n const now = Date.now();\n if (now - lastFire < 50) return;\n lastFire = now;\n hostRef.current?.tryInsert();\n onClose();\n };\n window.addEventListener('pointerdown', handler, { capture: true });\n window.addEventListener('mousedown', handler, { capture: true });\n return () => {\n window.removeEventListener('pointerdown', handler, { capture: true });\n window.removeEventListener('mousedown', handler, { capture: true });\n };\n }, [activeStamp, hostRef, onClose]);\n}\n","// Regenerate Excalidraw BinaryFiles for stamp elements after page reload.\n//\n// Why this exists: VideoRoom only persists the Excalidraw scene (elements +\n// appState) to sessionStorage; binary files (the SVG dataURLs for stamp\n// images) are NOT persisted. After reload, elements still reference a fileId\n// via customData, but the file payload is missing — Excalidraw renders the\n// image area as an empty placeholder.\n//\n// Strategy: tra registry để tìm StampType khớp customData.kind. Nếu stamp có\n// `restoreFileFromCustomData`, gọi trực tiếp với full element (stamp tự lấy\n// fileId + render). Ngược lại, fallback sang `renderSvgFromCustomData` (path\n// cũ: filter type=image + fileId + kiểm tra existing files).\n//\n// Excalidraw key trên fileId chứ không phải hash dataURL nên nếu kết quả\n// render có sai khác nhỏ (vd thứ tự element) cũng không ảnh hưởng.\n//\n// Theme: stamps được render với LIGHT palette (nét đậm). Excalidraw áp dụng\n// `filter: invert(...)` lên canvas trong dark mode → nét tự đảo sáng. KHÔNG\n// cần force regenerate khi user toggle theme.\n\nimport { DEFAULT_STAMPS, findStampForCustomData } from './registry';\nimport type { StampType } from './types';\n\ninterface ElementLike {\n id: string;\n type?: string;\n fileId?: string | null;\n customData?: unknown;\n}\n\ninterface AddFileRecord {\n id: string;\n dataURL: string;\n mimeType: string;\n created: number;\n}\n\nfunction svgToDataURL(svg: string): string {\n const utf8 = unescape(encodeURIComponent(svg));\n return 'data:image/svg+xml;base64,' + btoa(utf8);\n}\n\nasync function buildFileForStamp(\n fileId: string,\n customData: unknown,\n stamp: StampType,\n): Promise<AddFileRecord | null> {\n try {\n const svg = await stamp.renderSvgFromCustomData(customData);\n return { id: fileId, dataURL: svgToDataURL(svg), mimeType: 'image/svg+xml', created: Date.now() };\n } catch (err) {\n console.warn('Stamp restore failed for', fileId, '(' + stamp.kind + ')', err);\n return null;\n }\n}\n\n/**\n * Find stamp elements whose binary file is missing from Excalidraw, then\n * regenerate via registry dispatch. Idempotent: safe to call on every scene\n * update.\n *\n * Stamps that implement `restoreFileFromCustomData` are handled via the new\n * registry-driven path (stamp receives the full element and returns the file\n * record). Stamps that only implement `renderSvgFromCustomData` use the legacy\n * path (filter type=image + fileId, skip already-present files).\n *\n * @param api Excalidraw imperative API.\n * @param elements Tất cả elements trong scene.\n * @param stamps Registry. Default = DEFAULT_STAMPS.\n */\nexport async function restoreMissingStampFiles(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n api: any,\n elements: readonly ElementLike[],\n stamps: ReadonlyArray<StampType> = DEFAULT_STAMPS,\n): Promise<void> {\n if (!api) return;\n\n const filesToAdd: AddFileRecord[] = [];\n\n // --- New registry-driven path: stamp.restoreFileFromCustomData ---\n const newPathHandled = new Set<string>();\n for (const el of elements) {\n const stamp = findStampForCustomData(el.customData, stamps);\n if (!stamp?.restoreFileFromCustomData) continue;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const restored = await stamp.restoreFileFromCustomData(el as any);\n if (!restored) continue;\n newPathHandled.add(el.id);\n filesToAdd.push({\n id: restored.fileId,\n dataURL: restored.dataURL,\n mimeType: restored.mimeType,\n created: Date.now(),\n });\n }\n\n // --- Legacy path: stamp.renderSvgFromCustomData (type=image + fileId filter) ---\n const existing = (typeof api.getFiles === 'function') ? api.getFiles() : {};\n const seen = new Set<string>();\n for (const el of elements) {\n if (newPathHandled.has(el.id)) continue;\n if (el.type !== 'image') continue;\n if (!el.fileId) continue;\n if (existing && existing[el.fileId]) continue;\n if (seen.has(el.fileId)) continue;\n const stamp = findStampForCustomData(el.customData, stamps);\n if (!stamp) continue;\n seen.add(el.fileId);\n const built = await buildFileForStamp(el.fileId, el.customData, stamp);\n if (built) filesToAdd.push(built);\n }\n\n if (filesToAdd.length > 0) {\n try { api.addFiles(filesToAdd); } catch (err) { console.warn('addFiles failed:', err); }\n }\n}\n","import type { ExcalidrawElement, SyncableAppState } from '../../types';\n\nconst PREFIX = 'whiteboard:scene:';\nconst SCHEMA_VERSION = 1 as const;\n\nexport interface StoredScene {\n version: typeof SCHEMA_VERSION;\n elements: readonly ExcalidrawElement[];\n appState: Partial<SyncableAppState>;\n savedAt: number;\n}\n\nfunction fullKey(key: string): string {\n return PREFIX + key;\n}\n\nexport function readScene(key: string): StoredScene | null {\n if (typeof window === 'undefined') return null;\n const raw = window.localStorage.getItem(fullKey(key));\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as Partial<StoredScene>;\n if (!parsed || typeof parsed !== 'object') return null;\n if (parsed.version !== SCHEMA_VERSION) {\n // Cố ý KHÔNG xoá entry — version mismatch có thể là dữ liệu của client\n // mới hơn (user vừa downgrade). Giữ lại để client tương ứng đọc được sau.\n console.warn(\n `[whiteboard] scene version ${parsed.version} không khớp ${SCHEMA_VERSION}, bỏ qua.`,\n );\n return null;\n }\n if (!Array.isArray(parsed.elements)) return null;\n return {\n version: SCHEMA_VERSION,\n elements: parsed.elements,\n appState: (parsed.appState ?? {}) as Partial<SyncableAppState>,\n savedAt: typeof parsed.savedAt === 'number' ? parsed.savedAt : Date.now(),\n };\n } catch (err) {\n console.warn('[whiteboard] scene parse error, clear:', err);\n try {\n window.localStorage.removeItem(fullKey(key));\n } catch { /* ignore */ }\n return null;\n }\n}\n\nexport function writeScene(\n key: string,\n payload: { elements: readonly ExcalidrawElement[]; appState: Partial<SyncableAppState> },\n): void {\n if (typeof window === 'undefined') return;\n const record: StoredScene = {\n version: SCHEMA_VERSION,\n elements: payload.elements,\n appState: payload.appState,\n savedAt: Date.now(),\n };\n try {\n window.localStorage.setItem(fullKey(key), JSON.stringify(record));\n } catch (err) {\n console.warn('[whiteboard] scene write failed:', err);\n }\n}\n\nexport function clearScene(key: string): void {\n if (typeof window === 'undefined') return;\n try {\n window.localStorage.removeItem(fullKey(key));\n } catch { /* ignore */ }\n}\n","import type { BinaryFiles } from '../../types';\n\nconst DB_NAME = 'whiteboard-files';\nconst DB_VERSION = 1;\nconst STORE = 'files';\n\ninterface FileRecord {\n id: string;\n storageKey: string;\n dataURL: string;\n mimeType: string;\n created: number;\n savedAt: number;\n}\n\nlet dbPromise: Promise<IDBDatabase> | null = null;\nlet idbDisabled = false;\n\nfunction openDb(): Promise<IDBDatabase> {\n if (idbDisabled) return Promise.reject(new Error('IndexedDB disabled'));\n if (dbPromise) return dbPromise;\n dbPromise = new Promise((resolve, reject) => {\n if (typeof indexedDB === 'undefined') {\n idbDisabled = true;\n reject(new Error('indexedDB undefined'));\n return;\n }\n const req = indexedDB.open(DB_NAME, DB_VERSION);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains(STORE)) {\n const store = db.createObjectStore(STORE, { keyPath: 'id' });\n store.createIndex('storageKey', 'storageKey', { unique: false });\n }\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => {\n idbDisabled = true;\n reject(req.error ?? new Error('IDB open failed'));\n };\n });\n return dbPromise;\n}\n\nasync function withStore<T>(\n mode: IDBTransactionMode,\n fn: (\n store: IDBObjectStore,\n setResult: (value: T) => void,\n fail: (error: unknown) => void,\n ) => void,\n fallback: T,\n): Promise<T> {\n let db: IDBDatabase;\n try {\n db = await openDb();\n } catch {\n return fallback;\n }\n return new Promise<T>((resolve, reject) => {\n const tx = db.transaction(STORE, mode);\n const store = tx.objectStore(STORE);\n let result = fallback;\n try {\n fn(\n store,\n (value) => {\n result = value;\n },\n reject,\n );\n } catch (err) {\n reject(err);\n return;\n }\n tx.oncomplete = () => resolve(result);\n tx.onerror = () => {\n console.warn('[whiteboard] IDB tx error:', tx.error);\n reject(tx.error ?? new Error('IDB tx error'));\n };\n tx.onabort = () => reject(tx.error ?? new Error('IDB tx aborted'));\n });\n}\n\nexport async function readFiles(storageKey: string): Promise<BinaryFiles> {\n try {\n return await withStore(\n 'readonly',\n (store, setResult, fail) => {\n const out: BinaryFiles = {};\n const req = store.index('storageKey').openCursor(IDBKeyRange.only(storageKey));\n req.onsuccess = () => {\n const cursor = req.result;\n if (!cursor) {\n setResult(out);\n return;\n }\n const record = cursor.value as FileRecord;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (out as any)[record.id] = {\n dataURL: record.dataURL,\n mimeType: record.mimeType,\n created: record.created,\n };\n cursor.continue();\n };\n req.onerror = () => fail(req.error);\n },\n {},\n );\n } catch (err) {\n console.warn('[whiteboard] readFiles failed:', err);\n return {};\n }\n}\n\nexport async function writeFiles(storageKey: string, files: BinaryFiles): Promise<void> {\n const entries = Object.entries(files);\n if (entries.length === 0) return;\n try {\n await withStore<void>(\n 'readwrite',\n (store, setResult, fail) => {\n let pending = entries.length;\n const finishOne = () => {\n pending -= 1;\n if (pending === 0) setResult(undefined);\n };\n\n const now = Date.now();\n for (const [id, f] of entries) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ff = f as any;\n const getReq = store.get(id);\n getReq.onsuccess = () => {\n if (getReq.result) {\n finishOne();\n return;\n }\n const rec: FileRecord = {\n id,\n storageKey,\n dataURL: ff.dataURL,\n mimeType: ff.mimeType,\n created: ff.created ?? now,\n savedAt: now,\n };\n const putReq = store.put(rec);\n putReq.onsuccess = finishOne;\n putReq.onerror = () => fail(putReq.error);\n };\n getReq.onerror = () => fail(getReq.error);\n };\n },\n undefined,\n );\n } catch (err) {\n console.warn('[whiteboard] writeFiles failed:', err);\n }\n}\n\nexport async function pruneFiles(\n storageKey: string,\n keepIds: ReadonlySet<string>,\n): Promise<void> {\n try {\n await withStore<void>(\n 'readwrite',\n (store, setResult, fail) => {\n const req = store.index('storageKey').openCursor(IDBKeyRange.only(storageKey));\n req.onsuccess = () => {\n const cursor = req.result;\n if (!cursor) {\n setResult(undefined);\n return;\n }\n const record = cursor.value as FileRecord;\n if (keepIds.has(record.id)) {\n cursor.continue();\n return;\n }\n const deleteReq = cursor.delete();\n deleteReq.onsuccess = () => cursor.continue();\n deleteReq.onerror = () => fail(deleteReq.error);\n };\n req.onerror = () => fail(req.error);\n },\n undefined,\n );\n } catch (err) {\n console.warn('[whiteboard] pruneFiles failed:', err);\n }\n}\n\nexport async function clearAll(storageKey: string): Promise<void> {\n try {\n await withStore<void>(\n 'readwrite',\n (store, setResult, fail) => {\n const req = store.index('storageKey').openCursor(IDBKeyRange.only(storageKey));\n req.onsuccess = () => {\n const cursor = req.result;\n if (!cursor) {\n setResult(undefined);\n return;\n }\n const deleteReq = cursor.delete();\n deleteReq.onsuccess = () => cursor.continue();\n deleteReq.onerror = () => fail(deleteReq.error);\n };\n req.onerror = () => fail(req.error);\n },\n undefined,\n );\n } catch (err) {\n console.warn('[whiteboard] clearAll failed:', err);\n }\n}\n","'use client';\n\nimport { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type {\n ExcalidrawElement,\n BinaryFiles,\n ExcalidrawSceneSnapshot,\n SyncableAppState,\n} from './types';\nimport { pickSyncableAppState } from './serialize';\nimport {\n isStampElement,\n DEFAULT_STAMPS,\n findStampForCustomData,\n type StampType,\n} from './stamps/shared/registry';\nimport { ToolbarInjector } from './stamps/shared/ToolbarInjector';\nimport { useShortcuts } from './stamps/shared/useShortcuts';\nimport { useStampDoubleClick } from './stamps/shared/useStampDoubleClick';\nimport { useStampShortcutBlocker } from './stamps/shared/useStampShortcutBlocker';\nimport { useStampClickOutside } from './stamps/shared/useStampClickOutside';\nimport { restoreMissingStampFiles } from './stamps/shared/restoreStampFiles';\nimport type { StampHostHandle } from './stamps/shared/types';\nimport { readScene, writeScene } from './core/persistence/sceneStore';\nimport { readFiles, writeFiles, pruneFiles } from './core/persistence/fileStore';\nimport '@excalidraw/excalidraw/index.css';\nimport './stamps/shared/stamp.css';\n\nconst Excalidraw = lazy(() =>\n import('./ExcalidrawWithMenus').then((m) => ({ default: m.ExcalidrawWithMenus })),\n);\n\nconst ExcalidrawLoadingFallback = () => (\n <div className=\"flex h-full items-center justify-center text-sm text-gray-500\">\n Đang tải bảng…\n </div>\n);\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype ExApi = any;\n\nconst SYNC_THROTTLE_MS = 200;\n\n/** Element đang re-edit (double-click) — đủ tối thiểu để Host parse customData. */\ninterface EditingElement {\n id: string;\n customData: unknown;\n}\n\nexport interface WhiteboardProps {\n /**\n * Storage key cho persist client-side.\n * - Scene -> localStorage['whiteboard:scene:'+storageKey]\n * - Files raster -> IndexedDB 'whiteboard-files' index theo storageKey\n * - Default: 'default'\n * - Truyen `null` de tat persist (consumer drive state qua onApi).\n */\n storageKey?: string | null;\n\n /** View-only (Excalidraw viewModeEnabled). Default false. */\n readOnly?: boolean;\n\n /** Local edits -> consumer broadcast. Optional. */\n onSceneChange?: (snapshot: ExcalidrawSceneSnapshot) => void;\n onFilesChange?: (files: BinaryFiles, newFileIds: string[]) => void;\n\n /** Excalidraw imperative API. Consumer dung inject remote scene khi can. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onApi?: (api: any) => void;\n\n /** Excalidraw UI language. Defaults to 'vi-VN'. See @excalidraw/excalidraw locales. */\n langCode?: string;\n\n /**\n * Danh sách stamp đăng ký. Mỗi stamp khai báo phím tắt + toolbar button +\n * Host component (UI editing). Mặc định DEFAULT_STAMPS (geometry + latex).\n * Truyền `[...DEFAULT_STAMPS, customStamp]` để thêm stamp mới.\n */\n stamps?: ReadonlyArray<StampType>;\n}\n\nexport function Whiteboard({\n storageKey = 'default',\n readOnly = false,\n onSceneChange,\n onFilesChange,\n onApi,\n langCode = 'vi-VN',\n stamps = DEFAULT_STAMPS,\n}: WhiteboardProps) {\n const [api, setApi] = useState<ExApi | null>(null);\n const apiRef = useRef<ExApi | null>(null);\n const [isDarkTheme, setIsDarkTheme] = useState(false);\n const isDarkThemeRef = useRef(false);\n const knownFileIdsRef = useRef<Set<string>>(new Set());\n const lastSceneHashRef = useRef<string>('');\n const sceneThrottleRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const fileThrottleRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pruneThrottleRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const latestSceneRef = useRef<{\n elements: readonly ExcalidrawElement[];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n appState: any;\n } | null>(null);\n const pendingFilesRef = useRef<BinaryFiles>({});\n const persistEnabled = typeof storageKey === 'string' && storageKey.length > 0;\n const persistKeyRef = useRef(storageKey);\n persistKeyRef.current = storageKey;\n\n const persistedInitial = useMemo(\n () => (persistEnabled ? readScene(storageKey as string) : null),\n [persistEnabled, storageKey],\n );\n const effectiveInitialScene: ExcalidrawSceneSnapshot | null = persistedInitial\n ? {\n elements: persistedInitial.elements,\n appState: persistedInitial.appState as SyncableAppState,\n }\n : null;\n\n // ---- Stamp state (registry-driven) ----\n const [activeStamp, setActiveStamp] = useState<string | null>(null);\n const activeStampRef = useRef(activeStamp);\n activeStampRef.current = activeStamp;\n const [editingElement, setEditingElement] = useState<EditingElement | null>(null);\n const hostRef = useRef<StampHostHandle | null>(null);\n\n const handledCropIdRef = useRef<string | null>(null);\n const prevExcalidrawToolRef = useRef<string>('selection');\n\n const stampByKind = useMemo(() => {\n const m = new Map<string, StampType>();\n for (const s of stamps) m.set(s.kind, s);\n return m;\n }, [stamps]);\n\n const activeStampDef = activeStamp ? stampByKind.get(activeStamp) ?? null : null;\n const HostComponent = activeStampDef?.Host ?? null;\n\n // ---- Open / close helpers ----\n const openStamp = useCallback(\n (kind: string, element: EditingElement | null = null) => {\n if (readOnly) return;\n if (!stampByKind.has(kind)) return;\n setEditingElement(element);\n setActiveStamp(kind);\n },\n [readOnly, stampByKind],\n );\n\n const closeStamp = useCallback(() => {\n setActiveStamp(null);\n setEditingElement(null);\n }, []);\n\n const toggleStampByKind = useCallback(\n (kind: string) => {\n if (activeStamp === kind) closeStamp();\n else openStamp(kind);\n },\n [activeStamp, openStamp, closeStamp],\n );\n\n // ---- Capture local changes ----\n const handleChange = useCallback(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (elements: readonly ExcalidrawElement[], appState: any, files: BinaryFiles) => {\n // Sync theme từ Excalidraw appState -> React state.\n // Excalidraw 0.18 gọi onChange đồng bộ trong state-updater của họ\n // (React 19 / Next.js 16 sẽ warn: \"scheduled from inside an update\n // function\"). Bail-out qua ref + defer setState bằng queueMicrotask để\n // setState chạy SAU commit của Excalidraw, không nằm trong updater.\n const nextDark = appState?.theme === 'dark';\n if (isDarkThemeRef.current !== nextDark) {\n isDarkThemeRef.current = nextDark;\n queueMicrotask(() => setIsDarkTheme(nextDark));\n }\n\n if (readOnly) return;\n latestSceneRef.current = { elements, appState };\n\n // Intercept Excalidraw crop-image flow cho math stamps: khi user double-click\n // 1 stamp, Excalidraw set appState.croppingElementId. Ta dismiss crop mode +\n // mở Host editor tương ứng. handlePointerDown phát hiện double-click sớm\n // hơn — đây là fallback (đặc biệt khi click rơi vào selection handle).\n const cropId = appState?.croppingElementId as string | null | undefined;\n if (cropId && cropId !== handledCropIdRef.current && api) {\n const el = elements.find((e: ExcalidrawElement) => e.id === cropId);\n if (el) {\n const stamp = findStampForCustomData((el as { customData?: unknown }).customData, stamps);\n if (stamp) {\n handledCropIdRef.current = cropId;\n // Defer cả updateScene + openStamp ra khỏi commit-phase của\n // Excalidraw — nếu chạy đồng bộ, React 19 warn \"update scheduled\n // from inside an update function\" (handleChange chạy trong updater\n // của Excalidraw).\n const elId = el.id;\n const elCustom = (el as { customData?: unknown }).customData;\n const stampKind = stamp.kind;\n queueMicrotask(() => {\n try {\n api.updateScene({\n appState: { croppingElementId: null, selectedElementIds: {} },\n });\n } catch { /* ignore */ }\n openStamp(stampKind, { id: elId, customData: elCustom });\n });\n return;\n }\n }\n }\n if (!cropId) {\n handledCropIdRef.current = null;\n }\n\n const fileIds = Object.keys(files);\n const newIds = fileIds.filter((id) => !knownFileIdsRef.current.has(id));\n if (newIds.length > 0) {\n newIds.forEach((id) => knownFileIdsRef.current.add(id));\n onFilesChange?.(files, newIds);\n }\n\n if (!sceneThrottleRef.current) {\n sceneThrottleRef.current = setTimeout(async () => {\n sceneThrottleRef.current = null;\n const mod = await import('@excalidraw/excalidraw');\n const latestScene = latestSceneRef.current ?? { elements, appState };\n const liveElements = latestScene.elements.filter((e) => !e.isDeleted) as readonly ExcalidrawElement[];\n const liveAppState = pickSyncableAppState(latestScene.appState);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const elementHash = (mod as any).hashElementsVersion(liveElements);\n const sceneHash = `${elementHash}:${JSON.stringify(liveAppState)}`;\n if (sceneHash === lastSceneHashRef.current) return;\n lastSceneHashRef.current = sceneHash;\n onSceneChange?.({ elements: liveElements, appState: liveAppState });\n\n if (persistEnabled) {\n writeScene(storageKey as string, {\n elements: liveElements,\n appState: liveAppState,\n });\n }\n }, SYNC_THROTTLE_MS);\n }\n\n // File throttle (1s): lưu raster vào IDB, bỏ math-stamp files.\n if (persistEnabled && newIds.length > 0) {\n for (const id of newIds) {\n if (files[id]) pendingFilesRef.current[id] = files[id];\n }\n if (!fileThrottleRef.current) {\n fileThrottleRef.current = setTimeout(() => {\n fileThrottleRef.current = null;\n const pending = pendingFilesRef.current;\n pendingFilesRef.current = {};\n const currentElements = (api?.getSceneElements?.() ?? elements) as readonly ExcalidrawElement[];\n const stampIds = new Set<string>();\n for (const el of currentElements) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const fid = (el as any).fileId as string | undefined;\n if (fid && isStampElement(el)) stampIds.add(fid);\n }\n const raster: BinaryFiles = {};\n for (const [id, f] of Object.entries(pending)) {\n if (!stampIds.has(id)) raster[id] = f;\n }\n if (Object.keys(raster).length > 0) {\n void writeFiles(persistKeyRef.current as string, raster);\n }\n }, 1000);\n }\n }\n\n // Prune throttle (2s): dọn orphan raster sau khi xoá element.\n if (persistEnabled && !pruneThrottleRef.current) {\n pruneThrottleRef.current = setTimeout(() => {\n pruneThrottleRef.current = null;\n const currentElements = (api?.getSceneElements?.() ?? elements) as readonly ExcalidrawElement[];\n const keep = new Set<string>();\n for (const el of currentElements) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const fid = (el as any).fileId as string | undefined;\n if (fid && !isStampElement(el)) keep.add(fid);\n }\n void pruneFiles(persistKeyRef.current as string, keep);\n }, 2000);\n }\n },\n [readOnly, api, onSceneChange, onFilesChange, persistEnabled, storageKey, stamps, openStamp],\n );\n\n // ---- Mount: load persisted raster files từ IDB -> addFiles ----\n useEffect(() => {\n if (!api || !persistEnabled) return;\n let cancelled = false;\n void readFiles(storageKey as string).then((files) => {\n if (cancelled) return;\n const entries = Object.entries(files);\n if (entries.length === 0) return;\n try {\n api.addFiles(\n entries.map(([id, f]) => ({\n id,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n dataURL: (f as any).dataURL,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mimeType: (f as any).mimeType,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n created: (f as any).created ?? Date.now(),\n })),\n );\n entries.forEach(([id]) => knownFileIdsRef.current.add(id));\n } catch (err) {\n console.warn('[whiteboard] addFiles từ IDB thất bại:', err);\n }\n });\n return () => {\n cancelled = true;\n };\n }, [api, persistEnabled, storageKey]);\n\n useEffect(() => {\n if (!api) return;\n let cancelled = false;\n const run = async () => {\n try {\n const elements = api.getSceneElements();\n if (!elements || elements.length === 0) return;\n if (cancelled) return;\n await restoreMissingStampFiles(api, elements, stamps);\n } catch (err) {\n console.warn('Math stamp restore pass failed:', err);\n }\n };\n run();\n const t = setTimeout(run, 400);\n return () => {\n cancelled = true;\n clearTimeout(t);\n };\n }, [api, persistedInitial, stamps]);\n\n useEffect(\n () => () => {\n if (sceneThrottleRef.current) clearTimeout(sceneThrottleRef.current);\n if (fileThrottleRef.current) clearTimeout(fileThrottleRef.current);\n if (pruneThrottleRef.current) clearTimeout(pruneThrottleRef.current);\n },\n [],\n );\n\n // ---- Double-click detection for re-edit ----\n const handlePointerDown = useStampDoubleClick({\n enabled: !readOnly,\n stamps,\n onOpen: openStamp,\n });\n\n // ---- Keyboard shortcuts: đọc registry, mỗi stamp tự khai báo phím tắt ----\n useShortcuts({\n enabled: !readOnly,\n onToggle: toggleStampByKind,\n stamps,\n });\n\n // ---- Sync Excalidraw activeTool với activeStamp ----\n useEffect(() => {\n if (!api) return;\n if (activeStamp) {\n try {\n const cur = api.getAppState?.()?.activeTool?.type ?? 'selection';\n if (cur && cur !== 'hand') prevExcalidrawToolRef.current = cur;\n api.setActiveTool?.({ type: 'hand' });\n } catch { /* ignore */ }\n } else {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n api.setActiveTool?.({ type: prevExcalidrawToolRef.current as any });\n } catch { /* ignore */ }\n }\n }, [activeStamp, api]);\n\n // ---- Block Excalidraw shortcuts khi stamp panel đang mở ----\n useStampShortcutBlocker({ activeStamp, stamps });\n\n // ---- Esc đóng panel (capture phase để chạy TRƯỚC Excalidraw) ----\n useEffect(() => {\n if (!activeStamp) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== 'Escape') return;\n const ae = document.activeElement as HTMLElement | null;\n if (ae && (ae.tagName === 'TEXTAREA' || ae.isContentEditable)) {\n return;\n }\n e.preventDefault();\n e.stopPropagation();\n closeStamp();\n };\n window.addEventListener('keydown', onKey, { capture: true });\n return () => window.removeEventListener('keydown', onKey, { capture: true });\n }, [activeStamp, closeStamp]);\n\n useStampClickOutside({ activeStamp, hostRef, onClose: closeStamp });\n\n return (\n <div className={`relative h-full w-full${isDarkTheme ? ' theme--dark' : ''}`}>\n <Suspense fallback={<ExcalidrawLoadingFallback />}>\n <Excalidraw\n excalidrawAPI={(a: ExApi) => {\n // Excalidraw có thể gọi callback này đồng bộ trong commit-phase của\n // họ. Bail-out qua ref + defer setApi để tránh \"update scheduled\n // from inside an update function\" trên React 19 / Next.js 16.\n if (apiRef.current === a) return;\n apiRef.current = a;\n queueMicrotask(() => {\n setApi(a);\n onApi?.(a);\n });\n }}\n langCode={langCode}\n viewModeEnabled={readOnly}\n initialData={\n effectiveInitialScene\n ? {\n elements: effectiveInitialScene.elements,\n appState: {\n ...effectiveInitialScene.appState,\n gridSize: effectiveInitialScene.appState.gridSize ?? undefined,\n },\n }\n : { appState: { viewBackgroundColor: '#ffffff' } }\n }\n onChange={handleChange}\n onPointerDown={handlePointerDown}\n />\n </Suspense>\n\n <ToolbarInjector\n enabled={!readOnly}\n activeStampKind={activeStamp}\n onToggle={toggleStampByKind}\n stamps={stamps}\n />\n\n {HostComponent && (\n <HostComponent\n ref={hostRef}\n api={api}\n editingElement={editingElement}\n onClose={closeStamp}\n isDark={isDarkTheme}\n />\n )}\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/serialize.ts","../src/stamps/shared/registry.ts","../src/stamps/shared/ToolbarInjector.tsx","../src/stamps/shared/useShortcuts.ts","../src/stamps/shared/useStampDoubleClick.ts","../src/stamps/shared/useStampShortcutBlocker.ts","../src/stamps/shared/useStampClickOutside.ts","../src/stamps/shared/restoreStampFiles.ts","../src/core/persistence/validation.ts","../src/core/persistence/sceneStore.ts","../src/core/persistence/fileStore.ts","../src/Whiteboard.tsx"],"names":["useEffect","useRef","jsx","useState","useMemo","useCallback","jsxs"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { AppState } from '@excalidraw/excalidraw/types';\nimport type { SyncableAppState } from './types';\n\nexport function pickSyncableAppState(s: AppState): SyncableAppState {\n return {\n viewBackgroundColor: s.viewBackgroundColor,\n zoom: s.zoom,\n scrollX: s.scrollX,\n scrollY: s.scrollY,\n gridSize: s.gridSize ?? null,\n theme: s.theme,\n };\n}\n","import { geometryStamp } from '../geometry-2d';\nimport { latexStamp } from '../latex';\nimport { geometry3dStamp } from '../geometry-3d';\nimport { graph2dStamp } from '../graph-2d';\nimport type { StampType } from './types';\n\nexport { geometryStamp, type GeometryCustomData, isGeometryCustomData } from '../geometry-2d';\nexport { latexStamp, type LatexCustomData, isLatexCustomData } from '../latex';\nexport {\n geometry3dStamp,\n type Geometry3DCustomData,\n isGeometry3DCustomData,\n} from '../geometry-3d';\nexport { graph2dStamp, type Graph2DCustomData, isGraph2DCustomData } from '../graph-2d';\nexport type { StampType, BaseStampCustomData } from './types';\n\n/** Stamp ổn định, sẵn sàng production. */\nexport const STABLE_STAMPS: ReadonlyArray<StampType> = Object.freeze([\n geometryStamp,\n latexStamp,\n]);\n\n/** Stamp experimental — chưa ổn định cho production. Consumer phải opt-in. */\nexport const EXPERIMENTAL_STAMPS: ReadonlyArray<StampType> = Object.freeze([\n geometry3dStamp,\n graph2dStamp,\n]);\n\n/** Tất cả stamp (stable + experimental). Dùng khi consumer muốn full feature. */\nexport const ALL_STAMPS: ReadonlyArray<StampType> = Object.freeze([\n ...STABLE_STAMPS,\n ...EXPERIMENTAL_STAMPS,\n]);\n\n/**\n * Set stamp mặc định cho Whiteboard = ALL_STAMPS (bật tất cả tool).\n * Consumer muốn ẩn experimental: `<Whiteboard stamps={STABLE_STAMPS} />`.\n *\n * Để thêm 1 stamp mới (vd chart):\n * 1. Tạo `src/stamps/chart/index.tsx` export `chartStamp: StampType`.\n * 2. Import + add vào STABLE_STAMPS (production-ready) hoặc\n * EXPERIMENTAL_STAMPS (chưa ổn định) ở file này.\n * 3. Re-export `chartStamp` từ `src/stamps/index.ts` + `src/index.ts`.\n */\nexport const DEFAULT_STAMPS: ReadonlyArray<StampType> = ALL_STAMPS;\n\n/** Tìm stamp tương ứng với customData của element. null nếu không match. */\nexport function findStampForCustomData(\n data: unknown,\n stamps: ReadonlyArray<StampType> = DEFAULT_STAMPS,\n): StampType | null {\n for (const s of stamps) {\n if (s.matchesCustomData(data)) return s;\n }\n return null;\n}\n\n/** isMathStamp version dựa trên registry. */\nexport function isStampElement<T extends { customData?: unknown }>(\n element: T,\n stamps: ReadonlyArray<StampType> = DEFAULT_STAMPS,\n): boolean {\n return findStampForCustomData(element.customData, stamps) !== null;\n}\n","'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { DEFAULT_STAMPS } from './registry';\nimport type { StampType } from './types';\n\ninterface Props {\n /** Bật/tắt theo role. Khi disabled → không mount portal. */\n enabled: boolean;\n /** Kind stamp đang active, hoặc null nếu không có stamp nào mở. */\n activeStampKind: string | null;\n /** Toggle stamp theo kind. */\n onToggle: (kind: string) => void;\n /** Danh sách stamp đăng ký. Mặc định DEFAULT_STAMPS. */\n stamps?: ReadonlyArray<StampType>;\n}\n\nconst MENU_WRAPPER_ID = 'stamp-menu-portal-wrapper';\n/**\n * Excalidraw 0.18 áp dụng class `App-toolbar__extra-tools-dropdown` lên\n * popover wrapper của More tools cho cả desktop lẫn mobile. Mobile có\n * thêm modifier `dropdown-menu--mobile`. Selector này cover cả hai mode.\n */\nconst POPOVER_SELECTOR =\n '.App-toolbar__extra-tools-dropdown .dropdown-menu-container';\n\n/**\n * Inject stamp buttons vào popover \"More tools\" của Excalidraw.\n *\n * v0.7.0: thay vì inject inline vào main toolbar (desktop) hoặc dropdown\n * riêng cho mobile, ta chỉ inject vào dropdown-menu-container bên trong\n * popover của Excalidraw. Selector cover cả desktop lẫn mobile vì hai\n * mode dùng cùng cấu trúc DOM (DropdownMenu.Content).\n *\n * Popover mount/unmount theo trigger click → MutationObserver dò DOM,\n * mount lại wrapper khi cần.\n */\nexport function ToolbarInjector({\n enabled,\n activeStampKind,\n onToggle,\n stamps = DEFAULT_STAMPS,\n}: Props) {\n const [menuMount, setMenuMount] = useState<HTMLElement | null>(null);\n const menuMountRef = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n if (!enabled) {\n if (menuMountRef.current !== null) {\n menuMountRef.current = null;\n setMenuMount(null);\n }\n document.getElementById(MENU_WRAPPER_ID)?.remove();\n return;\n }\n\n let cancelled = false;\n let observer: MutationObserver | null = null;\n let rafId: number | null = null;\n let observedRoot: Element | null = null;\n\n const apply = (next: HTMLElement | null) => {\n if (cancelled || menuMountRef.current === next) return;\n menuMountRef.current = next;\n queueMicrotask(() => {\n if (!cancelled) setMenuMount(next);\n });\n };\n\n const findMenu = () => {\n if (cancelled) return;\n const container = document.querySelector<HTMLElement>(POPOVER_SELECTOR);\n if (!container) {\n apply(null);\n return;\n }\n let wrapper = container.querySelector<HTMLDivElement>('#' + MENU_WRAPPER_ID);\n if (!wrapper) {\n wrapper = document.createElement('div');\n wrapper.id = MENU_WRAPPER_ID;\n wrapper.setAttribute('data-stamp-menu', 'true');\n wrapper.setAttribute('data-stamp-area', 'true');\n wrapper.style.display = 'contents';\n container.insertBefore(wrapper, container.firstChild);\n }\n apply(wrapper);\n };\n\n /**\n * Scope observer xuống `.excalidraw` để giảm tần suất callback trigger\n * (mutation ở các node ngoài Excalidraw không liên quan tới popover).\n * Nếu `.excalidraw` chưa mount → tạm observe `document.body` để bắt\n * lúc nó xuất hiện, rồi switch sang root nhỏ hơn.\n */\n const attachObserver = () => {\n if (cancelled) return;\n const excalidraw = document.querySelector<HTMLElement>('.excalidraw');\n const nextRoot: Element = excalidraw ?? document.body;\n if (observedRoot === nextRoot) return;\n observer?.disconnect();\n observedRoot = nextRoot;\n observer = new MutationObserver(onMutation);\n observer.observe(nextRoot, { childList: true, subtree: true });\n };\n\n /**\n * rAF debounce: gộp nhiều mutation cùng tick thành 1 lần xử lý.\n * Excalidraw có thể trigger hàng chục mutation khi mở/đóng popover —\n * mỗi pass chỉ tốn 1 lần querySelector + DOM insert.\n */\n const onMutation = () => {\n if (rafId != null) return;\n rafId = requestAnimationFrame(() => {\n rafId = null;\n if (cancelled) return;\n // Nếu đang observe body mà giờ `.excalidraw` đã mount → switch sang nó.\n if (observedRoot !== document.querySelector('.excalidraw')) {\n attachObserver();\n }\n findMenu();\n });\n };\n\n findMenu();\n attachObserver();\n\n return () => {\n cancelled = true;\n if (rafId != null) {\n cancelAnimationFrame(rafId);\n rafId = null;\n }\n observer?.disconnect();\n observer = null;\n observedRoot = null;\n document.getElementById(MENU_WRAPPER_ID)?.remove();\n };\n }, [enabled]);\n\n if (!enabled || !menuMount) return null;\n\n const closePopover = () => {\n const trigger = document.querySelector<HTMLButtonElement>(\n '.App-toolbar__extra-tools-trigger',\n );\n trigger?.click();\n };\n\n return createPortal(\n <>\n {stamps.map((stamp) => (\n <StampMenuItem\n key={stamp.kind}\n icon={stamp.toolbarIcon}\n label={stamp.toolbarTitle}\n active={activeStampKind === stamp.kind}\n onClick={() => {\n onToggle(stamp.kind);\n closePopover();\n }}\n dataTestId={stamp.toolbarTestId}\n />\n ))}\n <div\n aria-hidden=\"true\"\n style={{\n height: 1,\n background: 'var(--default-border-color, rgba(0,0,0,0.08))',\n margin: '6px 4px',\n }}\n />\n </>,\n menuMount,\n );\n}\n\ninterface StampMenuItemProps {\n icon: React.ReactNode;\n label: string;\n active: boolean;\n onClick: () => void;\n dataTestId?: string;\n}\n\nfunction StampMenuItem({ icon, label, active, onClick, dataTestId }: StampMenuItemProps) {\n const className = [\n 'dropdown-menu-item',\n 'dropdown-menu-item-base',\n active ? 'dropdown-menu-item--selected' : '',\n ]\n .filter(Boolean)\n .join(' ');\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-label={label}\n aria-pressed={active}\n data-testid={dataTestId}\n className={className}\n style={{\n display: 'flex',\n alignItems: 'center',\n columnGap: '0.625rem',\n width: '100%',\n boxSizing: 'border-box',\n background: 'transparent',\n border: '1px solid transparent',\n cursor: 'pointer',\n fontFamily: 'inherit',\n fontSize: '0.875rem',\n color: 'var(--color-on-surface)',\n }}\n >\n <span\n aria-hidden=\"true\"\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '1rem',\n height: '1rem',\n }}\n >\n {icon}\n </span>\n <span>{label}</span>\n </button>\n );\n}\n","import { useEffect } from 'react';\nimport { DEFAULT_STAMPS } from './registry';\nimport type { StampType } from './types';\n\ninterface Options {\n enabled: boolean;\n /** Toggle stamp theo kind khi user bấm shortcut tương ứng. */\n onToggle: (kind: string) => void;\n /** Registry. Mặc định DEFAULT_STAMPS. */\n stamps?: ReadonlyArray<StampType>;\n}\n\nfunction isEditableTarget(t: EventTarget | null): boolean {\n if (!t || !(t instanceof HTMLElement)) return false;\n if (t.isContentEditable) return true;\n const tag = t.tagName;\n return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT';\n}\n\n/**\n * Bind keyboard shortcut cho mỗi stamp trong registry. Capture phase +\n * stopPropagation để chặn trước Excalidraw's bubble-phase handlers (Excalidraw\n * dùng `L` cho Line tool, nếu không chặn → bấm L lại chuyển tool thay vì\n * toggle LaTeX panel).\n */\nexport function useShortcuts({\n enabled,\n onToggle,\n stamps = DEFAULT_STAMPS,\n}: Options): void {\n useEffect(() => {\n if (!enabled) return;\n const keyToKind = new Map<string, string>();\n for (const s of stamps) keyToKind.set(s.shortcutKey, s.kind);\n\n const handler = (e: KeyboardEvent) => {\n if (e.metaKey || e.ctrlKey || e.altKey) return;\n if (isEditableTarget(e.target)) return;\n const key = e.key.toLowerCase();\n const kind = keyToKind.get(key);\n if (!kind) return;\n e.preventDefault();\n e.stopPropagation();\n onToggle(kind);\n };\n window.addEventListener('keydown', handler, { capture: true });\n return () => window.removeEventListener('keydown', handler, { capture: true });\n }, [enabled, onToggle, stamps]);\n}\n","import { useCallback, useRef } from 'react';\nimport { findStampForCustomData } from './registry';\nimport type { StampType } from './types';\n\nconst DOUBLE_CLICK_MS = 400;\n\ninterface Opts {\n enabled: boolean;\n stamps: ReadonlyArray<StampType>;\n onOpen: (\n kind: string,\n editingElement: { id: string; customData: unknown },\n ) => void;\n}\n\n/**\n * Trả về handler cho Excalidraw `onPointerDown`. Phát hiện double-click vào\n * image element thuộc một stamp đã đăng ký → gọi `onOpen(kind, element)`.\n */\nexport function useStampDoubleClick({ enabled, stamps, onOpen }: Opts) {\n const lastClickRef = useRef<{ time: number; elementId: string | null }>({\n time: 0,\n elementId: null,\n });\n\n return useCallback(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (_activeTool: any, pointerDownState: any) => {\n if (!enabled) return;\n const hitElement = pointerDownState?.hit?.element;\n if (!hitElement || hitElement.type !== 'image') return;\n const stamp = findStampForCustomData(hitElement.customData, stamps);\n if (!stamp) return;\n const now = Date.now();\n const isDouble =\n lastClickRef.current.elementId === hitElement.id &&\n now - lastClickRef.current.time < DOUBLE_CLICK_MS;\n lastClickRef.current = { time: now, elementId: hitElement.id };\n if (!isDouble) return;\n onOpen(stamp.kind, {\n id: hitElement.id,\n customData: hitElement.customData,\n });\n },\n [enabled, stamps, onOpen],\n );\n}\n","import { useEffect, useMemo } from 'react';\nimport type { StampType } from './types';\n\nconst ALLOWED_KEYS = new Set([\n 'Tab', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight',\n 'Shift', 'Control', 'Alt', 'Meta', 'CapsLock',\n 'Home', 'End', 'PageUp', 'PageDown',\n]);\n\nfunction isEditable(el: EventTarget | null): boolean {\n if (!(el instanceof HTMLElement)) return false;\n if (el.isContentEditable) return true;\n const tag = el.tagName;\n return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT';\n}\n\ninterface Opts {\n /** Active stamp kind. Null = no blocker. */\n activeStamp: string | null;\n /** Stamps đăng ký để allow shortcut keys của chúng đi qua. */\n stamps: ReadonlyArray<StampType>;\n}\n\n/**\n * Khi 1 stamp panel đang mở, block hoàn toàn các phím tắt Excalidraw\n * (1-9, V, R, D...) bằng cách prevent + stop tại capture phase. Cho phép qua:\n * Tab/Arrow/Modifier, Escape, và phím tắt đã đăng ký bởi stamps (V/G/L/D...).\n */\nexport function useStampShortcutBlocker({ activeStamp, stamps }: Opts) {\n const shortcutKeys = useMemo(\n () => new Set(stamps.map((s) => s.shortcutKey.toLowerCase())),\n [stamps],\n );\n\n useEffect(() => {\n if (!activeStamp) return;\n\n const blocker = (e: KeyboardEvent) => {\n if (isEditable(e.target)) return;\n if (e.ctrlKey || e.metaKey) return;\n if (ALLOWED_KEYS.has(e.key)) return;\n if (e.key === 'Escape') return;\n if (shortcutKeys.has(e.key.toLowerCase())) return;\n e.preventDefault();\n e.stopPropagation();\n };\n window.addEventListener('keydown', blocker, { capture: true });\n return () => window.removeEventListener('keydown', blocker, { capture: true });\n }, [activeStamp, shortcutKeys]);\n}\n","import { useEffect, type RefObject } from 'react';\nimport type { StampHostHandle } from './types';\n\ninterface Opts {\n /** Active stamp. Null = no listener. */\n activeStamp: string | null;\n /** Ref tới Host imperative API (tryInsert / hasContent). */\n hostRef: RefObject<StampHostHandle | null>;\n /** Callback đóng panel. */\n onClose: () => void;\n}\n\n/**\n * Khi 1 stamp panel mở, lắng nghe pointer/mouse-down toàn document. Nếu\n * điểm click không nằm trong vùng `[data-stamp-area=\"true\"]`, gọi\n * `hostRef.tryInsert()` để auto-commit (nếu có nội dung) rồi `onClose()`.\n */\nexport function useStampClickOutside({ activeStamp, hostRef, onClose }: Opts) {\n useEffect(() => {\n if (!activeStamp) return;\n let lastFire = 0;\n const handler = (e: MouseEvent) => {\n const target = e.target as HTMLElement | null;\n if (!target) return;\n if (target.closest('[data-stamp-area=\"true\"]')) return;\n const now = Date.now();\n if (now - lastFire < 50) return;\n lastFire = now;\n hostRef.current?.tryInsert();\n onClose();\n };\n window.addEventListener('pointerdown', handler, { capture: true });\n window.addEventListener('mousedown', handler, { capture: true });\n return () => {\n window.removeEventListener('pointerdown', handler, { capture: true });\n window.removeEventListener('mousedown', handler, { capture: true });\n };\n }, [activeStamp, hostRef, onClose]);\n}\n","// Regenerate Excalidraw BinaryFiles for stamp elements after page reload.\n//\n// Why this exists: VideoRoom only persists the Excalidraw scene (elements +\n// appState) to sessionStorage; binary files (the SVG dataURLs for stamp\n// images) are NOT persisted. After reload, elements still reference a fileId\n// via customData, but the file payload is missing — Excalidraw renders the\n// image area as an empty placeholder.\n//\n// Strategy: tra registry để tìm StampType khớp customData.kind. Nếu stamp có\n// `restoreFileFromCustomData`, gọi trực tiếp với full element (stamp tự lấy\n// fileId + render). Ngược lại, fallback sang `renderSvgFromCustomData` (path\n// cũ: filter type=image + fileId + kiểm tra existing files).\n//\n// Excalidraw key trên fileId chứ không phải hash dataURL nên nếu kết quả\n// render có sai khác nhỏ (vd thứ tự element) cũng không ảnh hưởng.\n//\n// Theme: stamps được render với LIGHT palette (nét đậm). Excalidraw áp dụng\n// `filter: invert(...)` lên canvas trong dark mode → nét tự đảo sáng. KHÔNG\n// cần force regenerate khi user toggle theme.\n\nimport { DEFAULT_STAMPS, findStampForCustomData } from './registry';\nimport type { StampType } from './types';\n\ninterface ElementLike {\n id: string;\n type?: string;\n fileId?: string | null;\n customData?: unknown;\n}\n\ninterface AddFileRecord {\n id: string;\n dataURL: string;\n mimeType: string;\n created: number;\n}\n\nfunction svgToDataURL(svg: string): string {\n const utf8 = unescape(encodeURIComponent(svg));\n return 'data:image/svg+xml;base64,' + btoa(utf8);\n}\n\nasync function buildFileForStamp(\n fileId: string,\n customData: unknown,\n stamp: StampType,\n): Promise<AddFileRecord | null> {\n try {\n const svg = await stamp.renderSvgFromCustomData(customData);\n return { id: fileId, dataURL: svgToDataURL(svg), mimeType: 'image/svg+xml', created: Date.now() };\n } catch (err) {\n console.warn('Stamp restore failed for', fileId, '(' + stamp.kind + ')', err);\n return null;\n }\n}\n\n/**\n * Find stamp elements whose binary file is missing from Excalidraw, then\n * regenerate via registry dispatch. Idempotent: safe to call on every scene\n * update.\n *\n * Stamps that implement `restoreFileFromCustomData` are handled via the new\n * registry-driven path (stamp receives the full element and returns the file\n * record). Stamps that only implement `renderSvgFromCustomData` use the legacy\n * path (filter type=image + fileId, skip already-present files).\n *\n * @param api Excalidraw imperative API.\n * @param elements Tất cả elements trong scene.\n * @param stamps Registry. Default = DEFAULT_STAMPS.\n */\nexport async function restoreMissingStampFiles(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n api: any,\n elements: readonly ElementLike[],\n stamps: ReadonlyArray<StampType> = DEFAULT_STAMPS,\n): Promise<void> {\n if (!api) return;\n\n const filesToAdd: AddFileRecord[] = [];\n\n // --- New registry-driven path: stamp.restoreFileFromCustomData ---\n const newPathHandled = new Set<string>();\n for (const el of elements) {\n const stamp = findStampForCustomData(el.customData, stamps);\n if (!stamp?.restoreFileFromCustomData) continue;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const restored = await stamp.restoreFileFromCustomData(el as any);\n if (!restored) continue;\n newPathHandled.add(el.id);\n filesToAdd.push({\n id: restored.fileId,\n dataURL: restored.dataURL,\n mimeType: restored.mimeType,\n created: Date.now(),\n });\n }\n\n // --- Legacy path: stamp.renderSvgFromCustomData (type=image + fileId filter) ---\n const existing = (typeof api.getFiles === 'function') ? api.getFiles() : {};\n const seen = new Set<string>();\n for (const el of elements) {\n if (newPathHandled.has(el.id)) continue;\n if (el.type !== 'image') continue;\n if (!el.fileId) continue;\n if (existing && existing[el.fileId]) continue;\n if (seen.has(el.fileId)) continue;\n const stamp = findStampForCustomData(el.customData, stamps);\n if (!stamp) continue;\n seen.add(el.fileId);\n const built = await buildFileForStamp(el.fileId, el.customData, stamp);\n if (built) filesToAdd.push(built);\n }\n\n if (filesToAdd.length > 0) {\n try { api.addFiles(filesToAdd); } catch (err) { console.warn('addFiles failed:', err); }\n }\n}\n","/**\n * Hardening helpers cho persistence layer:\n *\n * - validateStorageKey: chặn ký tự lạ trong storageKey (consumer-supplied) trước khi\n * ghép vào key prefix (`whiteboard:scene:<key>`) hoặc dùng làm index trong IndexedDB.\n * - safeParseScene: parse JSON với reviver loại bỏ `__proto__/constructor/prototype`\n * để chặn prototype pollution, check max nesting depth, whitelist top-level keys.\n *\n * Các helper này không phụ thuộc DOM nên test được trong jsdom hoặc node thuần.\n */\n\nconst STORAGE_KEY_RE = /^[a-zA-Z0-9_-]{1,128}$/;\n\n/**\n * Validate storageKey từ consumer.\n *\n * Format cho phép: chữ + số + underscore + dash, 1..128 ký tự.\n * Throw nếu không hợp lệ — caller bị buộc handle hoặc crash sớm.\n */\nexport function validateStorageKey(key: unknown): string {\n if (typeof key !== 'string' || !STORAGE_KEY_RE.test(key)) {\n const sample = key === undefined ? 'undefined' : String(key).slice(0, 32);\n throw new Error(\n `[whiteboard] Invalid storageKey: must match ${STORAGE_KEY_RE} (got: ${sample})`,\n );\n }\n return key;\n}\n\nconst DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\n/**\n * Reviver dùng cho `JSON.parse`: drop bất kỳ key nào trong DANGEROUS_KEYS,\n * tránh prototype pollution khi parse dữ liệu không tin cậy từ localStorage.\n */\nfunction sanitizingReviver(_key: string, value: unknown): unknown {\n if (DANGEROUS_KEYS.has(_key)) return undefined;\n return value;\n}\n\n/** Max nesting depth cho scene tree (object/array). */\nexport const MAX_NESTED_DEPTH = 64;\n\nfunction depthExceeds(v: unknown, max: number, depth = 0): boolean {\n if (depth > max) return true;\n if (v === null || typeof v !== 'object') return false;\n const children = Array.isArray(v)\n ? v\n : Object.values(v as Record<string, unknown>);\n for (const child of children) {\n if (depthExceeds(child, max, depth + 1)) return true;\n }\n return false;\n}\n\nconst ALLOWED_TOP_LEVEL_KEYS = new Set(['version', 'elements', 'appState', 'savedAt']);\n\n/** Shape của scene đã được validate (chưa check version + element shape — caller lo). */\nexport interface ParsedScene {\n version?: unknown;\n elements: unknown[];\n appState: Record<string, unknown>;\n savedAt?: unknown;\n}\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n/**\n * Parse + sanitize raw JSON string từ localStorage.\n *\n * 1. JSON.parse với reviver strip dangerous keys.\n * 2. Reject nếu nested depth vượt MAX_NESTED_DEPTH.\n * 3. Whitelist top-level keys (drop extras).\n * 4. `elements` phải là array, mỗi item là object có `id: string` + `type: string`.\n *\n * Trả về `null` nếu bất kỳ check nào fail (caller handle như \"no data\").\n */\nexport function safeParseScene(raw: string): ParsedScene | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw, sanitizingReviver);\n } catch {\n return null;\n }\n if (!isPlainObject(parsed)) return null;\n if (depthExceeds(parsed, MAX_NESTED_DEPTH)) return null;\n\n // Whitelist top-level keys.\n const safe: Record<string, unknown> = {};\n for (const k of Object.keys(parsed)) {\n if (ALLOWED_TOP_LEVEL_KEYS.has(k)) safe[k] = parsed[k];\n }\n\n if (!Array.isArray(safe.elements)) return null;\n for (const el of safe.elements as unknown[]) {\n if (!isPlainObject(el)) return null;\n if (typeof el.id !== 'string' || typeof el.type !== 'string') return null;\n }\n\n const appState = isPlainObject(safe.appState) ? safe.appState : {};\n\n return {\n version: safe.version,\n elements: safe.elements as unknown[],\n appState,\n savedAt: safe.savedAt,\n };\n}\n","import type { ExcalidrawElement, SyncableAppState } from '../../types';\nimport { safeParseScene, validateStorageKey } from './validation';\n\nconst PREFIX = 'whiteboard:scene:';\nconst SCHEMA_VERSION = 1 as const;\n\nexport interface StoredScene {\n version: typeof SCHEMA_VERSION;\n elements: readonly ExcalidrawElement[];\n appState: Partial<SyncableAppState>;\n savedAt: number;\n}\n\nfunction fullKey(key: string): string {\n return PREFIX + key;\n}\n\nexport function readScene(key: string): StoredScene | null {\n const validKey = validateStorageKey(key);\n if (typeof window === 'undefined') return null;\n const raw = window.localStorage.getItem(fullKey(validKey));\n if (!raw) return null;\n const parsed = safeParseScene(raw);\n if (!parsed) {\n console.warn('[whiteboard] scene parse/validation failed, clear:', validKey);\n try {\n window.localStorage.removeItem(fullKey(validKey));\n } catch { /* ignore */ }\n return null;\n }\n if (parsed.version !== SCHEMA_VERSION) {\n // Cố ý KHÔNG xoá entry — version mismatch có thể là dữ liệu của client\n // mới hơn (user vừa downgrade). Giữ lại để client tương ứng đọc được sau.\n console.warn(\n `[whiteboard] scene version ${parsed.version} không khớp ${SCHEMA_VERSION}, bỏ qua.`,\n );\n return null;\n }\n return {\n version: SCHEMA_VERSION,\n elements: parsed.elements as readonly ExcalidrawElement[],\n appState: parsed.appState as Partial<SyncableAppState>,\n savedAt: typeof parsed.savedAt === 'number' ? parsed.savedAt : Date.now(),\n };\n}\n\nexport function writeScene(\n key: string,\n payload: { elements: readonly ExcalidrawElement[]; appState: Partial<SyncableAppState> },\n): void {\n const validKey = validateStorageKey(key);\n if (typeof window === 'undefined') return;\n const record: StoredScene = {\n version: SCHEMA_VERSION,\n elements: payload.elements,\n appState: payload.appState,\n savedAt: Date.now(),\n };\n try {\n window.localStorage.setItem(fullKey(validKey), JSON.stringify(record));\n } catch (err) {\n console.warn('[whiteboard] scene write failed:', err);\n }\n}\n\nexport function clearScene(key: string): void {\n const validKey = validateStorageKey(key);\n if (typeof window === 'undefined') return;\n try {\n window.localStorage.removeItem(fullKey(validKey));\n } catch { /* ignore */ }\n}\n","import type { BinaryFiles } from '../../types';\nimport { validateStorageKey } from './validation';\n\nconst DB_NAME = 'whiteboard-files';\nconst DB_VERSION = 1;\nconst STORE = 'files';\n\ninterface FileRecord {\n id: string;\n storageKey: string;\n dataURL: string;\n mimeType: string;\n created: number;\n savedAt: number;\n}\n\nlet dbPromise: Promise<IDBDatabase> | null = null;\nlet idbDisabled = false;\n\nfunction openDb(): Promise<IDBDatabase> {\n if (idbDisabled) return Promise.reject(new Error('IndexedDB disabled'));\n if (dbPromise) return dbPromise;\n dbPromise = new Promise((resolve, reject) => {\n if (typeof indexedDB === 'undefined') {\n idbDisabled = true;\n reject(new Error('indexedDB undefined'));\n return;\n }\n const req = indexedDB.open(DB_NAME, DB_VERSION);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains(STORE)) {\n const store = db.createObjectStore(STORE, { keyPath: 'id' });\n store.createIndex('storageKey', 'storageKey', { unique: false });\n }\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => {\n idbDisabled = true;\n reject(req.error ?? new Error('IDB open failed'));\n };\n });\n return dbPromise;\n}\n\nasync function withStore<T>(\n mode: IDBTransactionMode,\n fn: (\n store: IDBObjectStore,\n setResult: (value: T) => void,\n fail: (error: unknown) => void,\n ) => void,\n fallback: T,\n): Promise<T> {\n let db: IDBDatabase;\n try {\n db = await openDb();\n } catch {\n return fallback;\n }\n return new Promise<T>((resolve, reject) => {\n const tx = db.transaction(STORE, mode);\n const store = tx.objectStore(STORE);\n let result = fallback;\n try {\n fn(\n store,\n (value) => {\n result = value;\n },\n reject,\n );\n } catch (err) {\n reject(err);\n return;\n }\n tx.oncomplete = () => resolve(result);\n tx.onerror = () => {\n console.warn('[whiteboard] IDB tx error:', tx.error);\n reject(tx.error ?? new Error('IDB tx error'));\n };\n tx.onabort = () => reject(tx.error ?? new Error('IDB tx aborted'));\n });\n}\n\nexport async function readFiles(storageKey: string): Promise<BinaryFiles> {\n const validKey = validateStorageKey(storageKey);\n try {\n return await withStore(\n 'readonly',\n (store, setResult, fail) => {\n const out: BinaryFiles = {};\n const req = store.index('storageKey').openCursor(IDBKeyRange.only(validKey));\n req.onsuccess = () => {\n const cursor = req.result;\n if (!cursor) {\n setResult(out);\n return;\n }\n const record = cursor.value as FileRecord;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (out as any)[record.id] = {\n dataURL: record.dataURL,\n mimeType: record.mimeType,\n created: record.created,\n };\n cursor.continue();\n };\n req.onerror = () => fail(req.error);\n },\n {},\n );\n } catch (err) {\n console.warn('[whiteboard] readFiles failed:', err);\n return {};\n }\n}\n\nexport async function writeFiles(storageKey: string, files: BinaryFiles): Promise<void> {\n const validKey = validateStorageKey(storageKey);\n const entries = Object.entries(files);\n if (entries.length === 0) return;\n try {\n await withStore<void>(\n 'readwrite',\n (store, setResult, fail) => {\n let pending = entries.length;\n const finishOne = () => {\n pending -= 1;\n if (pending === 0) setResult(undefined);\n };\n\n const now = Date.now();\n for (const [id, f] of entries) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ff = f as any;\n const getReq = store.get(id);\n getReq.onsuccess = () => {\n if (getReq.result) {\n finishOne();\n return;\n }\n const rec: FileRecord = {\n id,\n storageKey: validKey,\n dataURL: ff.dataURL,\n mimeType: ff.mimeType,\n created: ff.created ?? now,\n savedAt: now,\n };\n const putReq = store.put(rec);\n putReq.onsuccess = finishOne;\n putReq.onerror = () => fail(putReq.error);\n };\n getReq.onerror = () => fail(getReq.error);\n };\n },\n undefined,\n );\n } catch (err) {\n console.warn('[whiteboard] writeFiles failed:', err);\n }\n}\n\nexport async function pruneFiles(\n storageKey: string,\n keepIds: ReadonlySet<string>,\n): Promise<void> {\n const validKey = validateStorageKey(storageKey);\n try {\n await withStore<void>(\n 'readwrite',\n (store, setResult, fail) => {\n const req = store.index('storageKey').openCursor(IDBKeyRange.only(validKey));\n req.onsuccess = () => {\n const cursor = req.result;\n if (!cursor) {\n setResult(undefined);\n return;\n }\n const record = cursor.value as FileRecord;\n if (keepIds.has(record.id)) {\n cursor.continue();\n return;\n }\n const deleteReq = cursor.delete();\n deleteReq.onsuccess = () => cursor.continue();\n deleteReq.onerror = () => fail(deleteReq.error);\n };\n req.onerror = () => fail(req.error);\n },\n undefined,\n );\n } catch (err) {\n console.warn('[whiteboard] pruneFiles failed:', err);\n }\n}\n\nexport async function clearAll(storageKey: string): Promise<void> {\n const validKey = validateStorageKey(storageKey);\n try {\n await withStore<void>(\n 'readwrite',\n (store, setResult, fail) => {\n const req = store.index('storageKey').openCursor(IDBKeyRange.only(validKey));\n req.onsuccess = () => {\n const cursor = req.result;\n if (!cursor) {\n setResult(undefined);\n return;\n }\n const deleteReq = cursor.delete();\n deleteReq.onsuccess = () => cursor.continue();\n deleteReq.onerror = () => fail(deleteReq.error);\n };\n req.onerror = () => fail(req.error);\n },\n undefined,\n );\n } catch (err) {\n console.warn('[whiteboard] clearAll failed:', err);\n }\n}\n","'use client';\n\nimport { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type {\n ExcalidrawElement,\n BinaryFiles,\n ExcalidrawSceneSnapshot,\n SyncableAppState,\n} from './types';\nimport { pickSyncableAppState } from './serialize';\nimport {\n isStampElement,\n DEFAULT_STAMPS,\n findStampForCustomData,\n type StampType,\n} from './stamps/shared/registry';\nimport { ToolbarInjector } from './stamps/shared/ToolbarInjector';\nimport { useShortcuts } from './stamps/shared/useShortcuts';\nimport { useStampDoubleClick } from './stamps/shared/useStampDoubleClick';\nimport { useStampShortcutBlocker } from './stamps/shared/useStampShortcutBlocker';\nimport { useStampClickOutside } from './stamps/shared/useStampClickOutside';\nimport { restoreMissingStampFiles } from './stamps/shared/restoreStampFiles';\nimport type { StampHostHandle } from './stamps/shared/types';\nimport { readScene, writeScene } from './core/persistence/sceneStore';\nimport { readFiles, writeFiles, pruneFiles } from './core/persistence/fileStore';\nimport '@excalidraw/excalidraw/index.css';\nimport './stamps/shared/stamp.css';\n\nconst Excalidraw = lazy(() =>\n import('./ExcalidrawWithMenus').then((m) => ({ default: m.ExcalidrawWithMenus })),\n);\n\nconst ExcalidrawLoadingFallback = () => (\n <div className=\"flex h-full items-center justify-center text-sm text-gray-500\">\n Đang tải bảng…\n </div>\n);\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype ExApi = any;\n\nconst SYNC_THROTTLE_MS = 200;\n\n/** Element đang re-edit (double-click) — đủ tối thiểu để Host parse customData. */\ninterface EditingElement {\n id: string;\n customData: unknown;\n}\n\nexport interface WhiteboardProps {\n /**\n * Storage key cho persist client-side.\n * - Scene -> localStorage['whiteboard:scene:'+storageKey]\n * - Files raster -> IndexedDB 'whiteboard-files' index theo storageKey\n * - Default: 'default'\n * - Truyen `null` de tat persist (consumer drive state qua onApi).\n */\n storageKey?: string | null;\n\n /** View-only (Excalidraw viewModeEnabled). Default false. */\n readOnly?: boolean;\n\n /** Local edits -> consumer broadcast. Optional. */\n onSceneChange?: (snapshot: ExcalidrawSceneSnapshot) => void;\n onFilesChange?: (files: BinaryFiles, newFileIds: string[]) => void;\n\n /** Excalidraw imperative API. Consumer dung inject remote scene khi can. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onApi?: (api: any) => void;\n\n /** Excalidraw UI language. Defaults to 'vi-VN'. See @excalidraw/excalidraw locales. */\n langCode?: string;\n\n /**\n * Danh sách stamp đăng ký. Mỗi stamp khai báo phím tắt + toolbar button +\n * Host component (UI editing). Mặc định DEFAULT_STAMPS (= ALL_STAMPS,\n * gồm geometry + latex + geometry3d + graph2d).\n * Truyền `[...DEFAULT_STAMPS, customStamp]` để thêm stamp mới hoặc\n * `STABLE_STAMPS` để chỉ bật stamp ổn định.\n */\n stamps?: ReadonlyArray<StampType>;\n}\n\nexport function Whiteboard({\n storageKey = 'default',\n readOnly = false,\n onSceneChange,\n onFilesChange,\n onApi,\n langCode = 'vi-VN',\n stamps = DEFAULT_STAMPS,\n}: WhiteboardProps) {\n const [api, setApi] = useState<ExApi | null>(null);\n const apiRef = useRef<ExApi | null>(null);\n const [isDarkTheme, setIsDarkTheme] = useState(false);\n const isDarkThemeRef = useRef(false);\n const knownFileIdsRef = useRef<Set<string>>(new Set());\n const lastSceneHashRef = useRef<string>('');\n const sceneThrottleRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const fileThrottleRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pruneThrottleRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const latestSceneRef = useRef<{\n elements: readonly ExcalidrawElement[];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n appState: any;\n } | null>(null);\n const pendingFilesRef = useRef<BinaryFiles>({});\n // Cached hashElementsVersion để flushScene chạy sync trong unmount cleanup\n // (cleanup không thể await dynamic import).\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const hashElementsVersionRef = useRef<((elements: readonly ExcalidrawElement[]) => any) | null>(null);\n // Stamps prop có thể thay đổi — capture vào ref để callback trong setTimeout\n // không bị stale closure.\n const stampsRef = useRef(stamps);\n stampsRef.current = stamps;\n const persistEnabled = typeof storageKey === 'string' && storageKey.length > 0;\n const persistKeyRef = useRef(storageKey);\n persistKeyRef.current = storageKey;\n // Lưu callback refs để cleanup unmount truy cập được mà không bị stale closure\n // (props onSceneChange/onFilesChange có thể thay đổi).\n const onSceneChangeRef = useRef(onSceneChange);\n onSceneChangeRef.current = onSceneChange;\n const onFilesChangeRef = useRef(onFilesChange);\n onFilesChangeRef.current = onFilesChange;\n const persistEnabledRef = useRef(persistEnabled);\n persistEnabledRef.current = persistEnabled;\n\n const persistedInitial = useMemo(\n () => (persistEnabled ? readScene(storageKey as string) : null),\n [persistEnabled, storageKey],\n );\n const effectiveInitialScene: ExcalidrawSceneSnapshot | null = persistedInitial\n ? {\n elements: persistedInitial.elements,\n appState: persistedInitial.appState as SyncableAppState,\n }\n : null;\n\n // ---- Stamp state (registry-driven) ----\n const [activeStamp, setActiveStamp] = useState<string | null>(null);\n const activeStampRef = useRef(activeStamp);\n activeStampRef.current = activeStamp;\n const [editingElement, setEditingElement] = useState<EditingElement | null>(null);\n const hostRef = useRef<StampHostHandle | null>(null);\n\n const handledCropIdRef = useRef<string | null>(null);\n const prevExcalidrawToolRef = useRef<string>('selection');\n\n const stampByKind = useMemo(() => {\n const m = new Map<string, StampType>();\n for (const s of stamps) m.set(s.kind, s);\n return m;\n }, [stamps]);\n\n const activeStampDef = activeStamp ? stampByKind.get(activeStamp) ?? null : null;\n const HostComponent = activeStampDef?.Host ?? null;\n\n // ---- Open / close helpers ----\n const openStamp = useCallback(\n (kind: string, element: EditingElement | null = null) => {\n if (readOnly) return;\n if (!stampByKind.has(kind)) return;\n setEditingElement(element);\n setActiveStamp(kind);\n },\n [readOnly, stampByKind],\n );\n\n const closeStamp = useCallback(() => {\n setActiveStamp(null);\n setEditingElement(null);\n }, []);\n\n const toggleStampByKind = useCallback(\n (kind: string) => {\n if (activeStamp === kind) closeStamp();\n else openStamp(kind);\n },\n [activeStamp, openStamp, closeStamp],\n );\n\n // ---- Capture local changes ----\n const handleChange = useCallback(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (elements: readonly ExcalidrawElement[], appState: any, files: BinaryFiles) => {\n // Sync theme từ Excalidraw appState -> React state.\n // Excalidraw 0.18 gọi onChange đồng bộ trong state-updater của họ\n // (React 19 / Next.js 16 sẽ warn: \"scheduled from inside an update\n // function\"). Bail-out qua ref + defer setState bằng queueMicrotask để\n // setState chạy SAU commit của Excalidraw, không nằm trong updater.\n const nextDark = appState?.theme === 'dark';\n if (isDarkThemeRef.current !== nextDark) {\n isDarkThemeRef.current = nextDark;\n queueMicrotask(() => setIsDarkTheme(nextDark));\n }\n\n if (readOnly) return;\n latestSceneRef.current = { elements, appState };\n\n // Intercept Excalidraw crop-image flow cho math stamps: khi user double-click\n // 1 stamp, Excalidraw set appState.croppingElementId. Ta dismiss crop mode +\n // mở Host editor tương ứng. handlePointerDown phát hiện double-click sớm\n // hơn — đây là fallback (đặc biệt khi click rơi vào selection handle).\n const cropId = appState?.croppingElementId as string | null | undefined;\n if (cropId && cropId !== handledCropIdRef.current && api) {\n const el = elements.find((e: ExcalidrawElement) => e.id === cropId);\n if (el) {\n const stamp = findStampForCustomData((el as { customData?: unknown }).customData, stamps);\n if (stamp) {\n handledCropIdRef.current = cropId;\n // Defer cả updateScene + openStamp ra khỏi commit-phase của\n // Excalidraw — nếu chạy đồng bộ, React 19 warn \"update scheduled\n // from inside an update function\" (handleChange chạy trong updater\n // của Excalidraw).\n const elId = el.id;\n const elCustom = (el as { customData?: unknown }).customData;\n const stampKind = stamp.kind;\n queueMicrotask(() => {\n try {\n api.updateScene({\n appState: { croppingElementId: null, selectedElementIds: {} },\n });\n } catch { /* ignore */ }\n openStamp(stampKind, { id: elId, customData: elCustom });\n });\n return;\n }\n }\n }\n if (!cropId) {\n handledCropIdRef.current = null;\n }\n\n const fileIds = Object.keys(files);\n const newIds = fileIds.filter((id) => !knownFileIdsRef.current.has(id));\n if (newIds.length > 0) {\n newIds.forEach((id) => knownFileIdsRef.current.add(id));\n onFilesChange?.(files, newIds);\n }\n\n if (!sceneThrottleRef.current) {\n sceneThrottleRef.current = setTimeout(async () => {\n sceneThrottleRef.current = null;\n try {\n const mod = await import('@excalidraw/excalidraw');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n hashElementsVersionRef.current = (mod as any).hashElementsVersion;\n } catch (err) {\n console.warn('[whiteboard] import excalidraw để flush scene thất bại:', err);\n return;\n }\n flushSceneRef.current();\n }, SYNC_THROTTLE_MS);\n }\n\n // File throttle (1s): lưu raster vào IDB, bỏ math-stamp files.\n if (persistEnabled && newIds.length > 0) {\n for (const id of newIds) {\n if (files[id]) pendingFilesRef.current[id] = files[id];\n }\n if (!fileThrottleRef.current) {\n fileThrottleRef.current = setTimeout(() => {\n fileThrottleRef.current = null;\n flushFilesRef.current();\n }, 1000);\n }\n }\n\n // Prune throttle (2s): dọn orphan raster sau khi xoá element.\n if (persistEnabled && !pruneThrottleRef.current) {\n pruneThrottleRef.current = setTimeout(() => {\n pruneThrottleRef.current = null;\n flushPruneRef.current();\n }, 2000);\n }\n },\n [readOnly, api, onSceneChange, onFilesChange, persistEnabled, storageKey, stamps, openStamp],\n );\n\n // ---- Flush helpers (gọi từ setTimeout VÀ unmount cleanup) ----\n // Lưu vào ref để cleanup luôn dùng version mới nhất (closure-stable).\n const flushSceneRef = useRef<() => void>(() => undefined);\n flushSceneRef.current = () => {\n try {\n const latestScene = latestSceneRef.current;\n if (!latestScene) return;\n const liveElements = latestScene.elements.filter((e) => !e.isDeleted) as readonly ExcalidrawElement[];\n const liveAppState = pickSyncableAppState(latestScene.appState);\n const hashFn = hashElementsVersionRef.current;\n // Nếu chưa load module → bỏ qua hash dedupe, cứ ghi (correctness > perf trong unmount path).\n const elementHash = hashFn ? hashFn(liveElements) : liveElements.map((e) => e.id).join('|');\n const sceneHash = `${elementHash}:${JSON.stringify(liveAppState)}`;\n if (sceneHash === lastSceneHashRef.current) return;\n lastSceneHashRef.current = sceneHash;\n onSceneChangeRef.current?.({ elements: liveElements, appState: liveAppState });\n if (persistEnabledRef.current) {\n writeScene(persistKeyRef.current as string, {\n elements: liveElements,\n appState: liveAppState,\n });\n }\n } catch (err) {\n console.warn('[whiteboard] flushScene thất bại:', err);\n }\n };\n\n const flushFilesRef = useRef<() => void>(() => undefined);\n flushFilesRef.current = () => {\n try {\n const pending = pendingFilesRef.current;\n pendingFilesRef.current = {};\n if (Object.keys(pending).length === 0) return;\n const currentElements = (apiRef.current?.getSceneElements?.()\n ?? latestSceneRef.current?.elements\n ?? []) as readonly ExcalidrawElement[];\n const stampIds = new Set<string>();\n for (const el of currentElements) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const fid = (el as any).fileId as string | undefined;\n if (fid && isStampElement(el)) stampIds.add(fid);\n }\n const raster: BinaryFiles = {};\n for (const [id, f] of Object.entries(pending)) {\n if (!stampIds.has(id)) raster[id] = f;\n }\n if (Object.keys(raster).length > 0) {\n void writeFiles(persistKeyRef.current as string, raster);\n }\n } catch (err) {\n console.warn('[whiteboard] flushFiles thất bại:', err);\n }\n };\n\n const flushPruneRef = useRef<() => void>(() => undefined);\n flushPruneRef.current = () => {\n try {\n const currentElements = (apiRef.current?.getSceneElements?.()\n ?? latestSceneRef.current?.elements\n ?? []) as readonly ExcalidrawElement[];\n const keep = new Set<string>();\n for (const el of currentElements) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const fid = (el as any).fileId as string | undefined;\n if (fid && !isStampElement(el)) keep.add(fid);\n }\n void pruneFiles(persistKeyRef.current as string, keep);\n } catch (err) {\n console.warn('[whiteboard] flushPrune thất bại:', err);\n }\n };\n\n // ---- Mount: load persisted raster files từ IDB -> addFiles ----\n useEffect(() => {\n if (!api || !persistEnabled) return;\n let cancelled = false;\n void readFiles(storageKey as string).then(\n (files) => {\n // Recheck cancelled NGAY khi promise resolve — IDB onsuccess có thể\n // fire sau component unmount (tx microtask).\n if (cancelled) return;\n const entries = Object.entries(files);\n if (entries.length === 0) return;\n // Recheck một lần nữa trước khi gọi api.addFiles để chắc chắn\n // không invoke API trên instance đã teardown.\n if (cancelled) return;\n try {\n api.addFiles(\n entries.map(([id, f]) => ({\n id,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n dataURL: (f as any).dataURL,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mimeType: (f as any).mimeType,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n created: (f as any).created ?? Date.now(),\n })),\n );\n if (cancelled) return;\n entries.forEach(([id]) => knownFileIdsRef.current.add(id));\n } catch (err) {\n if (cancelled) return;\n console.warn('[whiteboard] addFiles từ IDB thất bại:', err);\n }\n },\n (err) => {\n if (cancelled) return;\n console.warn('[whiteboard] readFiles thất bại:', err);\n },\n );\n return () => {\n cancelled = true;\n };\n }, [api, persistEnabled, storageKey]);\n\n useEffect(() => {\n if (!api) return;\n let cancelled = false;\n const run = async () => {\n if (cancelled) return;\n try {\n const elements = api.getSceneElements();\n if (!elements || elements.length === 0) return;\n if (cancelled) return;\n // Đọc stamps từ ref — props có thể đã thay đổi sau setTimeout 400ms,\n // closure-captured `stamps` sẽ stale.\n await restoreMissingStampFiles(api, elements, stampsRef.current);\n } catch (err) {\n if (cancelled) return;\n console.warn('Math stamp restore pass failed:', err);\n }\n };\n void run();\n const t = setTimeout(() => {\n void run();\n }, 400);\n return () => {\n cancelled = true;\n clearTimeout(t);\n };\n }, [api, persistedInitial]);\n\n // Unmount cleanup: flush pending writes TRƯỚC khi clearTimeout để không\n // mất scene/file write cuối khi user navigate giữa throttle window.\n useEffect(\n () => () => {\n if (sceneThrottleRef.current) {\n clearTimeout(sceneThrottleRef.current);\n sceneThrottleRef.current = null;\n flushSceneRef.current();\n }\n if (fileThrottleRef.current) {\n clearTimeout(fileThrottleRef.current);\n fileThrottleRef.current = null;\n flushFilesRef.current();\n }\n if (pruneThrottleRef.current) {\n clearTimeout(pruneThrottleRef.current);\n pruneThrottleRef.current = null;\n flushPruneRef.current();\n }\n },\n [],\n );\n\n // ---- Double-click detection for re-edit ----\n const handlePointerDown = useStampDoubleClick({\n enabled: !readOnly,\n stamps,\n onOpen: openStamp,\n });\n\n // ---- Keyboard shortcuts: đọc registry, mỗi stamp tự khai báo phím tắt ----\n useShortcuts({\n enabled: !readOnly,\n onToggle: toggleStampByKind,\n stamps,\n });\n\n // ---- Sync Excalidraw activeTool với activeStamp ----\n useEffect(() => {\n if (!api) return;\n if (activeStamp) {\n try {\n const cur = api.getAppState?.()?.activeTool?.type ?? 'selection';\n if (cur && cur !== 'hand') prevExcalidrawToolRef.current = cur;\n api.setActiveTool?.({ type: 'hand' });\n } catch { /* ignore */ }\n } else {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n api.setActiveTool?.({ type: prevExcalidrawToolRef.current as any });\n } catch { /* ignore */ }\n }\n }, [activeStamp, api]);\n\n // ---- Block Excalidraw shortcuts khi stamp panel đang mở ----\n useStampShortcutBlocker({ activeStamp, stamps });\n\n // ---- Esc đóng panel (capture phase để chạy TRƯỚC Excalidraw) ----\n useEffect(() => {\n if (!activeStamp) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== 'Escape') return;\n const ae = document.activeElement as HTMLElement | null;\n if (ae && (ae.tagName === 'TEXTAREA' || ae.isContentEditable)) {\n return;\n }\n e.preventDefault();\n e.stopPropagation();\n closeStamp();\n };\n window.addEventListener('keydown', onKey, { capture: true });\n return () => window.removeEventListener('keydown', onKey, { capture: true });\n }, [activeStamp, closeStamp]);\n\n useStampClickOutside({ activeStamp, hostRef, onClose: closeStamp });\n\n return (\n <div className={`relative h-full w-full${isDarkTheme ? ' theme--dark' : ''}`}>\n <Suspense fallback={<ExcalidrawLoadingFallback />}>\n <Excalidraw\n excalidrawAPI={(a: ExApi) => {\n // Excalidraw có thể gọi callback này đồng bộ trong commit-phase của\n // họ. Bail-out qua ref + defer setApi để tránh \"update scheduled\n // from inside an update function\" trên React 19 / Next.js 16.\n if (apiRef.current === a) return;\n apiRef.current = a;\n queueMicrotask(() => {\n setApi(a);\n onApi?.(a);\n });\n }}\n langCode={langCode}\n viewModeEnabled={readOnly}\n initialData={\n effectiveInitialScene\n ? {\n elements: effectiveInitialScene.elements,\n appState: {\n ...effectiveInitialScene.appState,\n gridSize: effectiveInitialScene.appState.gridSize ?? undefined,\n },\n }\n : { appState: { viewBackgroundColor: '#ffffff' } }\n }\n onChange={handleChange}\n onPointerDown={handlePointerDown}\n />\n </Suspense>\n\n <ToolbarInjector\n enabled={!readOnly}\n activeStampKind={activeStamp}\n onToggle={toggleStampByKind}\n stamps={stamps}\n />\n\n {HostComponent && (\n <HostComponent\n ref={hostRef}\n api={api}\n editingElement={editingElement}\n onClose={closeStamp}\n isDark={isDarkTheme}\n />\n )}\n </div>\n );\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xom11/whiteboard",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Excalidraw + JSXGraph + KaTeX whiteboard component (drawing, geometry stamps, LaTeX stamps).",
|
|
6
6
|
"license": "MIT",
|
|
@@ -50,20 +50,20 @@
|
|
|
50
50
|
"build": "tsup && node scripts/inject-use-client.mjs",
|
|
51
51
|
"dev": "tsup --watch --onSuccess \"node scripts/inject-use-client.mjs\"",
|
|
52
52
|
"test": "jest",
|
|
53
|
+
"test:e2e": "playwright test",
|
|
53
54
|
"typecheck": "tsc --noEmit",
|
|
54
55
|
"clean": "rm -rf dist .yalc yalc.lock",
|
|
55
56
|
"demo": "vite --config scripts/demo/vite.config.ts"
|
|
56
57
|
},
|
|
57
58
|
"peerDependencies": {
|
|
58
|
-
"react": ">=18.0.0",
|
|
59
|
-
"react-dom": ">=18.0.0"
|
|
60
|
-
},
|
|
61
|
-
"dependencies": {
|
|
62
59
|
"@excalidraw/excalidraw": "^0.18.1",
|
|
63
60
|
"jsxgraph": "^1.12.2",
|
|
64
|
-
"katex": "^0.16.45"
|
|
61
|
+
"katex": "^0.16.45",
|
|
62
|
+
"react": ">=18.0.0",
|
|
63
|
+
"react-dom": ">=18.0.0"
|
|
65
64
|
},
|
|
66
65
|
"devDependencies": {
|
|
66
|
+
"@playwright/test": "^1.60.0",
|
|
67
67
|
"@tailwindcss/vite": "^4.3.0",
|
|
68
68
|
"@testing-library/jest-dom": "^6.9.1",
|
|
69
69
|
"@testing-library/react": "^16.3.2",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stamps/geometry-2d/renderInline.ts","../src/stamps/geometry-2d/serialize.ts","../src/stamps/geometry-2d/render.ts","../src/stamps/geometry-2d/types.ts"],"names":[],"mappings":"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","file":"chunk-BJX4YNA5.mjs","sourcesContent":["export function renderGeometryToSvg(boardContainer: HTMLElement): string {\n const svgEl = boardContainer.querySelector('svg');\n if (!svgEl) throw new Error('renderGeometryToSvg: no SVG found in board container');\n const clone = svgEl.cloneNode(true) as SVGElement;\n if (!clone.getAttribute('xmlns')) {\n clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');\n }\n return new XMLSerializer().serializeToString(clone);\n}\n","// JSXGraph không có built-in getJSON. Component giữ MỘT LOG riêng của các create() call\n// do user trigger, pass log đó vào serializeBoard. Replay = gọi board.create() theo thứ tự log.\n//\n// type === 'transform': args là [refs đến điểm/đường/scalar], attrs là { type: 'translate'|'rotate'|'reflect'|'scale', ... }.\n// Object trả về (kết quả board.create('transform', ...)) được đăng ký vào idMap như mọi element khác\n// để point/line phụ thuộc reference được bằng id ('j5' → JSXGraph transform object).\n//\n// Log lưu màu dưới dạng sentinel ('@stroke', '@axis', '@grid', '@label') để\n// theme-neutral. Khi replay, palette resolve thành màu thực theo `isDark` hiện\n// tại (truyền qua options.palette).\n\nimport { paletteFor, resolveAttrColors, type GeomPalette } from './editor/theme';\n\nexport interface SerializedElement {\n type: string;\n args: unknown[];\n attrs: Record<string, unknown>;\n id: string;\n}\n\nexport interface SerializedBoard {\n bbox: [number, number, number, number];\n elements: SerializedElement[];\n showAxis?: boolean;\n showGrid?: boolean;\n}\n\ninterface BoardLike {\n getBoundingBox(): [number, number, number, number];\n create(type: string, args: unknown[], attrs: Record<string, unknown>): unknown;\n}\n\nexport function serializeBoard(\n board: BoardLike,\n log: SerializedElement[],\n options: { showAxis?: boolean; showGrid?: boolean } = {},\n): SerializedBoard {\n return {\n bbox: board.getBoundingBox(),\n elements: log.map(e => ({ type: e.type, args: e.args, attrs: e.attrs, id: e.id })),\n showAxis: !!options.showAxis,\n showGrid: !!options.showGrid,\n };\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction createValueLabel(board: any, target: any): unknown {\n if (!board || !target) return null;\n const e = (target.elType ?? target.type ?? '').toString().toLowerCase();\n if (e === 'segment' || e === 'line' || e === 'arrow') {\n const p1 = target.point1, p2 = target.point2;\n if (!p1 || !p2) return null;\n return board.create('text', [\n () => (p1.X() + p2.X()) / 2 + 0.15,\n () => (p1.Y() + p2.Y()) / 2 + 0.25,\n () => {\n const len = Math.hypot(p2.X() - p1.X(), p2.Y() - p1.Y());\n const name = typeof target.name === 'string' && target.name ? target.name : 'd';\n return `${name} = ${len.toFixed(2)}`;\n },\n ], { fontSize: 12, color: '#dc2626', fixed: true, highlight: false });\n }\n if (e === 'circle' || e === 'circumcircle') {\n const center = target.center ?? target.midpoint ?? target.point1;\n if (!center) return null;\n return board.create('text', [\n () => center.X() + 0.3,\n () => center.Y() + 0.3,\n () => {\n const r = typeof target.Radius === 'function' ? target.Radius() : 0;\n const name = typeof target.name === 'string' && target.name ? target.name : 'r';\n return `${name} = ${r.toFixed(2)}`;\n },\n ], { fontSize: 12, color: '#dc2626', fixed: true, highlight: false });\n }\n return null;\n}\n\nexport interface DeserializeOptions {\n /** Theme-aware palette để resolve sentinel attrs. Mặc định = light. */\n palette?: GeomPalette;\n}\n\nexport function deserializeIntoBoard(\n board: BoardLike,\n serialized: SerializedBoard,\n options: DeserializeOptions = {},\n): void {\n // Replay: args may contain references to earlier elements by our serialized id (\"j0\", \"j1\"…).\n // We resolve those to actual JSXGraph objects via a local id→object map. Nested\n // arrays are also resolved recursively — needed for dilate, which logs the\n // transform parent of a transformed point as [\"j2\",\"j3\",\"j4\"] (a chain of 3\n // transforms passed to `board.create('point', [src, [t1,t2,t3]])`).\n const palette = options.palette ?? paletteFor(false);\n const idMap = new Map<string, unknown>();\n const resolve = (a: unknown): unknown => {\n if (typeof a === 'string' && idMap.has(a)) return idMap.get(a);\n if (Array.isArray(a)) return a.map(resolve);\n return a;\n };\n for (const el of serialized.elements) {\n const resolvedArgs = el.args.map(resolve);\n if (el.type === 'valueLabel') {\n const target = resolvedArgs[0];\n const txt = createValueLabel(board, target);\n if (txt) idMap.set(el.id, txt);\n continue;\n }\n const themedAttrs = resolveAttrColors({ ...el.attrs }, palette);\n const created = board.create(el.type, resolvedArgs, themedAttrs);\n idMap.set(el.id, created);\n }\n}\n","import { renderGeometryToSvg } from './renderInline';\nimport { deserializeIntoBoard, type SerializedBoard } from './serialize';\nimport { paletteFor } from './editor/theme';\n\n/**\n * Re-render geometry SVG từ jsonState đã serialize. Dùng cho:\n * 1. Restore math-stamp file sau khi reload page (Excalidraw mất binary files).\n * 2. Generate SVG lúc INSERT (thay vì clone DOM với màu theo theme editor).\n *\n * LƯU Ý quan trọng — luôn dùng LIGHT palette (nét đậm). Excalidraw apply CSS\n * `filter: invert(93%) hue-rotate(180deg)` lên canvas trong dark mode → nét\n * đậm tự đảo thành sáng. Nếu ta bake nét sáng vào SVG cho dark mode, filter\n * sẽ đảo thành đậm → chìm vào nền tối. Giải pháp: luôn dùng nét đậm + để\n * Excalidraw tự lo invert.\n *\n * Implementation: tạo 1 div ẩn (off-screen, real dimensions để JSXGraph render\n * chuẩn), initBoard, replay creation log từ jsonState, dump SVG, dọn dẹp.\n *\n * Lý do JXG.Options.text.display = 'internal': JSXGraph mặc định render\n * label bằng HTML <div> overlay → clone SVG export sẽ thiếu label.\n */\nexport async function renderGeometrySvgFromState(jsonState: string): Promise<string> {\n const parsed = JSON.parse(jsonState) as SerializedBoard;\n // Stamps inserted vào Excalidraw canvas → luôn dùng light palette.\n // Excalidraw's THEME_FILTER tự đảo nét trong dark mode.\n const palette = paletteFor(false);\n const JXG = (await import('jsxgraph')).default;\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const opts = (JXG as any).Options;\n if (opts) {\n opts.text = opts.text || {};\n opts.text.display = 'internal';\n opts.text.useASCIIMathML = false;\n opts.text.useMathJax = false;\n opts.text.useKatex = false;\n opts.text.strokeColor = palette.label;\n opts.label = opts.label || {};\n opts.label.display = 'internal';\n opts.label.strokeColor = palette.label;\n opts.axis = opts.axis || {};\n opts.axis.strokeColor = palette.axis;\n opts.grid = opts.grid || {};\n opts.grid.strokeColor = palette.grid;\n }\n } catch { /* ignore */ }\n const container = document.createElement('div');\n const containerId = 'jxg_offscreen_' + Date.now() + '_' + Math.random().toString(36).slice(2, 8);\n container.id = containerId;\n container.style.cssText = 'position:absolute;top:-99999px;left:-99999px;width:400px;height:300px;visibility:hidden;pointer-events:none;';\n document.body.appendChild(container);\n let board: unknown = null;\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n board = (JXG as any).JSXGraph.initBoard(containerId, {\n boundingbox: parsed.bbox,\n axis: !!parsed.showAxis,\n grid: !!parsed.showGrid,\n showCopyright: false,\n showNavigation: false,\n keepAspectRatio: false,\n });\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n deserializeIntoBoard(board as any, parsed, { palette });\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (board as any).update();\n return renderGeometryToSvg(container);\n } finally {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (board) (JXG as any).JSXGraph.freeBoard(board);\n } catch { /* ignore */ }\n if (container.parentNode) container.parentNode.removeChild(container);\n }\n}\n","import type { BaseStampCustomData } from '../shared/types';\n\nexport interface GeometryCustomData extends BaseStampCustomData {\n kind: 'geometry';\n version: 1;\n jsonState: string;\n svgWidth: number;\n svgHeight: number;\n}\n\nexport function isGeometryCustomData(data: unknown): data is GeometryCustomData {\n if (!data || typeof data !== 'object') return false;\n const d = data as Partial<GeometryCustomData>;\n return d.kind === 'geometry' && d.version === 1 && typeof d.jsonState === 'string';\n}\n"]}
|
package/dist/chunk-DJTBZEAR.mjs
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
// src/stamps/geometry-3d/serialize.ts
|
|
3
|
-
function isGeometry3DCustomData(data) {
|
|
4
|
-
if (!data || typeof data !== "object") return false;
|
|
5
|
-
const d = data;
|
|
6
|
-
return d.kind === "geometry3d" && d.version === 1 && typeof d.jsonState === "string";
|
|
7
|
-
}
|
|
8
|
-
function parseSerializedBoard3D(json) {
|
|
9
|
-
const parsed = JSON.parse(json);
|
|
10
|
-
if (!parsed || typeof parsed !== "object") {
|
|
11
|
-
throw new Error("parseSerializedBoard3D: not an object");
|
|
12
|
-
}
|
|
13
|
-
const p = parsed;
|
|
14
|
-
if (p.version !== 1) {
|
|
15
|
-
throw new Error(`parseSerializedBoard3D: unsupported version ${String(p.version)}`);
|
|
16
|
-
}
|
|
17
|
-
if (!Array.isArray(p.elements)) {
|
|
18
|
-
throw new Error("parseSerializedBoard3D: elements missing");
|
|
19
|
-
}
|
|
20
|
-
return parsed;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export { isGeometry3DCustomData, parseSerializedBoard3D };
|
|
24
|
-
//# sourceMappingURL=chunk-DJTBZEAR.mjs.map
|
|
25
|
-
//# sourceMappingURL=chunk-DJTBZEAR.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stamps/geometry-3d/serialize.ts"],"names":[],"mappings":";AAUO,SAAS,uBAAuB,IAAA,EAA6C;AAClF,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,UAAU,OAAO,KAAA;AAC9C,EAAA,MAAM,CAAA,GAAI,IAAA;AACV,EAAA,OAAO,CAAA,CAAE,SAAS,YAAA,IAAgB,CAAA,CAAE,YAAY,CAAA,IAAK,OAAO,EAAE,SAAA,KAAc,QAAA;AAC9E;AAwCO,SAAS,uBAAuB,IAAA,EAAiC;AACtE,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AAC9B,EAAA,IAAI,CAAC,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,EAAU;AACzC,IAAA,MAAM,IAAI,MAAM,uCAAuC,CAAA;AAAA,EACzD;AACA,EAAA,MAAM,CAAA,GAAI,MAAA;AACV,EAAA,IAAI,CAAA,CAAE,YAAY,CAAA,EAAG;AACnB,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,4CAAA,EAA+C,OAAO,CAAA,CAAE,OAAO,CAAC,CAAA,CAAE,CAAA;AAAA,EACpF;AACA,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAE,QAAQ,CAAA,EAAG;AAC9B,IAAA,MAAM,IAAI,MAAM,0CAA0C,CAAA;AAAA,EAC5D;AACA,EAAA,OAAO,MAAA;AACT","file":"chunk-DJTBZEAR.mjs","sourcesContent":["import type { BaseStampCustomData } from '../shared/types';\n\nexport interface Geometry3DCustomData extends BaseStampCustomData {\n kind: 'geometry3d';\n version: 1;\n jsonState: string;\n svgWidth: number;\n svgHeight: number;\n}\n\nexport function isGeometry3DCustomData(data: unknown): data is Geometry3DCustomData {\n if (!data || typeof data !== 'object') return false;\n const d = data as Partial<Geometry3DCustomData>;\n return d.kind === 'geometry3d' && d.version === 1 && typeof d.jsonState === 'string';\n}\n\nexport type Element3DType =\n | 'point3d'\n | 'line3d'\n | 'plane3d'\n | 'polygon3d'\n | 'sphere3d'\n | 'text3d';\n\nexport interface SerializedElement3D {\n type: Element3DType;\n /**\n * Parents passed to JSXGraph view.create. Either literal values (numbers,\n * strings) or `\"@id:<id>\"` placeholder strings referencing earlier created\n * objects in the log (resolved at deserialize time).\n */\n parents: unknown[];\n attributes: Record<string, unknown>;\n id: string;\n label?: string;\n}\n\nexport interface SerializedBoard3D {\n version: 1;\n bbox: [number, number, number, number];\n view: {\n azimuth: number;\n elevation: number;\n bbox3D: [number, number, number, number, number, number];\n };\n showAxes: boolean;\n showMesh: boolean;\n elements: SerializedElement3D[];\n}\n\nexport function serializeBoard3D(state: SerializedBoard3D): string {\n return JSON.stringify(state);\n}\n\nexport function parseSerializedBoard3D(json: string): SerializedBoard3D {\n const parsed = JSON.parse(json) as unknown;\n if (!parsed || typeof parsed !== 'object') {\n throw new Error('parseSerializedBoard3D: not an object');\n }\n const p = parsed as Partial<SerializedBoard3D>;\n if (p.version !== 1) {\n throw new Error(`parseSerializedBoard3D: unsupported version ${String(p.version)}`);\n }\n if (!Array.isArray(p.elements)) {\n throw new Error('parseSerializedBoard3D: elements missing');\n }\n return parsed as SerializedBoard3D;\n}\n"]}
|