@xom11/whiteboard 0.6.5 → 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 +87 -1
- package/dist/chunk-74VEEZBV.mjs +619 -0
- package/dist/chunk-74VEEZBV.mjs.map +1 -0
- package/dist/chunk-7P7SQFOW.mjs +39 -0
- package/dist/chunk-7P7SQFOW.mjs.map +1 -0
- package/dist/chunk-C6SCVOMC.mjs +111 -0
- package/dist/chunk-C6SCVOMC.mjs.map +1 -0
- package/dist/chunk-DU2NFHRR.mjs +103 -0
- package/dist/chunk-DU2NFHRR.mjs.map +1 -0
- package/dist/chunk-DU3RHKT5.mjs +44 -0
- package/dist/chunk-DU3RHKT5.mjs.map +1 -0
- package/dist/chunk-HTBLO5JO.mjs +41 -0
- package/dist/chunk-HTBLO5JO.mjs.map +1 -0
- package/dist/chunk-IUVV52HO.mjs +144 -0
- package/dist/chunk-IUVV52HO.mjs.map +1 -0
- package/dist/chunk-KEYZ5EZT.mjs +154 -0
- package/dist/chunk-KEYZ5EZT.mjs.map +1 -0
- package/dist/chunk-P2AOIF7S.mjs +40 -0
- package/dist/chunk-P2AOIF7S.mjs.map +1 -0
- package/dist/chunk-SBDMF4NQ.mjs +212 -0
- package/dist/chunk-SBDMF4NQ.mjs.map +1 -0
- package/dist/chunk-X5R72SSJ.mjs +52 -0
- package/dist/chunk-X5R72SSJ.mjs.map +1 -0
- package/dist/chunk-ZVN356JZ.mjs +58 -0
- package/dist/chunk-ZVN356JZ.mjs.map +1 -0
- package/dist/geometry-2d.d.mts +16 -0
- package/dist/geometry-2d.d.ts +16 -0
- package/dist/geometry-2d.js +3581 -0
- package/dist/geometry-2d.js.map +1 -0
- package/dist/geometry-2d.mjs +7 -0
- package/dist/geometry-2d.mjs.map +1 -0
- package/dist/geometry-3d.d.mts +16 -0
- package/dist/geometry-3d.d.ts +16 -0
- package/dist/geometry-3d.js +4105 -0
- package/dist/geometry-3d.js.map +1 -0
- package/dist/geometry-3d.mjs +7 -0
- package/dist/geometry-3d.mjs.map +1 -0
- package/dist/graph-2d.d.mts +16 -0
- package/dist/graph-2d.d.ts +16 -0
- package/dist/graph-2d.js +2019 -0
- package/dist/graph-2d.js.map +1 -0
- package/dist/graph-2d.mjs +6 -0
- package/dist/graph-2d.mjs.map +1 -0
- package/dist/host-LZH2FZ2N.mjs +1066 -0
- package/dist/host-LZH2FZ2N.mjs.map +1 -0
- package/dist/host-PIIDSMVE.mjs +3187 -0
- package/dist/host-PIIDSMVE.mjs.map +1 -0
- package/dist/host-VDNAJMLC.mjs +2864 -0
- package/dist/host-VDNAJMLC.mjs.map +1 -0
- package/dist/host-Z3TEJKZA.mjs +466 -0
- package/dist/host-Z3TEJKZA.mjs.map +1 -0
- package/dist/index.d.mts +30 -148
- package/dist/index.d.ts +30 -148
- package/dist/index.js +8370 -5614
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +395 -7294
- package/dist/index.mjs.map +1 -1
- package/dist/latex.d.mts +15 -0
- package/dist/latex.d.ts +15 -0
- package/dist/latex.js +750 -0
- package/dist/latex.js.map +1 -0
- package/dist/latex.mjs +6 -0
- package/dist/latex.mjs.map +1 -0
- package/dist/types-CinstD7T.d.mts +110 -0
- package/dist/types-CinstD7T.d.ts +110 -0
- package/package.json +26 -7
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/stamps/shared/useChordShortcut.ts","../src/stamps/shared/MobileToolDrawer.tsx"],"names":[],"mappings":";;;;AAcA,IAAM,MAAA,GAAS,GAAA,CAAI,UAAA,CAAW,CAAC,CAAA;AAE/B,SAAS,cAAA,GAA0B;AACjC,EAAA,MAAM,EAAA,GAAM,OAAO,QAAA,KAAa,WAAA,GAC3B,SAAS,aAAA,GACV,IAAA;AACJ,EAAA,OAAO,CAAC,EACN,EAAA,KACC,EAAA,CAAG,YAAY,OAAA,IACd,EAAA,CAAG,OAAA,KAAY,UAAA,IACf,EAAA,CAAG,iBAAA,CAAA,CAAA;AAET;AAEO,SAAS,iBACd,IAAA,EAC2B;AAC3B,EAAA,MAAM,EAAE,UAAA,EAAY,KAAA,EAAO,QAAA,EAAU,SAAQ,GAAI,IAAA;AAEjD,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,SAAmB,IAAI,CAAA;AAE3D,EAAA,MAAM,aAAA,GAAgB,OAAO,UAAU,CAAA;AACvC,EAAA,MAAM,QAAA,GAAW,OAAO,KAAK,CAAA;AAC7B,EAAA,MAAM,WAAA,GAAc,OAAO,QAAQ,CAAA;AACnC,EAAA,MAAM,aAAA,GAAgB,OAAiB,IAAI,CAAA;AAE3C,EAAA,aAAA,CAAc,OAAA,GAAU,UAAA;AACxB,EAAA,QAAA,CAAS,OAAA,GAAU,KAAA;AACnB,EAAA,WAAA,CAAY,OAAA,GAAU,QAAA;AAKtB,EAAA,MAAM,MAAA,GAAS,YAAY,MAAM;AAC/B,IAAA,aAAA,CAAc,OAAA,GAAU,IAAA;AACxB,IAAA,aAAA,CAAc,IAAI,CAAA;AAAA,EACpB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,EAAS;AAEd,IAAA,MAAM,QAAA,GAAW,CAAC,IAAA,KAAmB;AACnC,MAAA,aAAA,CAAc,OAAA,GAAU,IAAA;AACxB,MAAA,aAAA,CAAc,IAAI,CAAA;AAAA,IACpB,CAAA;AAEA,IAAA,MAAM,KAAA,GAAQ,CAAC,CAAA,KAAqB;AAClC,MAAA,IAAI,CAAA,CAAE,OAAA,IAAW,CAAA,CAAE,OAAA,IAAW,EAAE,MAAA,EAAQ;AACxC,MAAA,IAAI,gBAAe,EAAG;AAEtB,MAAA,MAAM,MAAM,CAAA,CAAE,GAAA;AACd,MAAA,MAAM,QAAQ,GAAA,CAAI,MAAA,KAAW,CAAA,GAAI,GAAA,CAAI,aAAY,GAAI,GAAA;AAErD,MAAA,IAAI,QAAQ,QAAA,EAAU;AACpB,QAAA,IAAI,aAAA,CAAc,YAAY,IAAA,EAAM;AAClC,UAAA,CAAA,CAAE,cAAA,EAAe;AACjB,UAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,UAAA,QAAA,CAAS,IAAI,CAAA;AAAA,QACf;AACA,QAAA;AAAA,MACF;AAEA,MAAA,IAAI,MAAM,MAAA,KAAW,CAAA,IAAK,KAAA,IAAS,GAAA,IAAO,SAAS,GAAA,EAAK;AACtD,QAAA,MAAM,GAAA,GAAM,KAAA,CAAM,UAAA,CAAW,CAAC,CAAA,GAAI,MAAA;AAClC,QAAA,IAAI,GAAA,GAAM,aAAA,CAAc,OAAA,CAAQ,MAAA,EAAQ;AACtC,UAAA,CAAA,CAAE,cAAA,EAAe;AACjB,UAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,UAAA,QAAA,CAAS,aAAA,CAAc,OAAA,CAAQ,GAAG,CAAC,CAAA;AAAA,QACrC;AACA,QAAA;AAAA,MACF;AAEA,MAAA,IAAI,GAAA,IAAO,GAAA,IAAO,GAAA,IAAO,GAAA,EAAK;AAC5B,QAAA,MAAM,SAAS,aAAA,CAAc,OAAA;AAC7B,QAAA,IAAI,WAAW,IAAA,EAAM;AACrB,QAAA,MAAM,IAAI,GAAA,CAAI,UAAA,CAAW,CAAC,CAAA,GAAI,GAAA,CAAI,WAAW,CAAC,CAAA;AAC9C,QAAA,MAAM,YAAA,GAAe,SAAS,OAAA,CAAQ,MAAA;AAAA,UACpC,CAAC,CAAA,KAAM,CAAA,CAAE,KAAA,KAAU;AAAA,SACrB;AACA,QAAA,CAAA,CAAE,cAAA,EAAe;AACjB,QAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,QAAA,IAAI,CAAA,GAAI,aAAa,MAAA,EAAQ;AAC3B,UAAA,WAAA,CAAY,OAAA,CAAQ,YAAA,CAAa,CAAC,CAAA,CAAE,GAAG,CAAA;AAAA,QACzC;AACA,QAAA,QAAA,CAAS,IAAI,CAAA;AACb,QAAA;AAAA,MACF;AAAA,IACF,CAAA;AAEA,IAAA,MAAA,CAAO,iBAAiB,SAAA,EAAW,KAAA,EAAO,EAAE,OAAA,EAAS,MAAM,CAAA;AAC3D,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,CAAO,oBAAoB,SAAA,EAAW,KAAA,EAAO,EAAE,OAAA,EAAS,MAAM,CAAA;AAAA,IAChE,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,OAAO,EAAE,YAAY,MAAA,EAAO;AAC9B;AChDO,SAAS,gBAAA,CAA6D;AAAA,EAC3E,KAAA;AAAA,EACA,UAAA;AAAA,EACA,KAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,UAAA;AAAA,EACA,YAAA;AAAA,EACA,UAAA;AAAA,EACA,aAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAAA,EAAwC;AACtC,EAAA,uBACE,IAAA,CAAA,QAAA,EAAA,EACG,QAAA,EAAA;AAAA,IAAA,UAAA,oBACC,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAU,uBAAA;AAAA,QACV,aAAA,EAAe,aAAA;AAAA,QACf,aAAA,EAAY;AAAA;AAAA,KACd;AAAA,oBAEF,IAAA;AAAA,MAAC,OAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAK,eAAA;AAAA,QACL,YAAA,EAAY,KAAA;AAAA,QACZ,aAAA,EAAa,CAAC,UAAA,GAAa,MAAA,GAAS,MAAA;AAAA,QACpC,aAAA,EAAa,MAAA;AAAA,QACb,iBAAA,EAAgB,MAAA;AAAA,QAChB,oBAAA,EAAmB,MAAA;AAAA,QACnB,iBAAA,EAAgB,MAAA;AAAA,QAChB,mBAAA,EAAmB,aAAa,MAAA,GAAS,QAAA;AAAA,QACzC,SAAA,EAAW;AAAA,UACT,SAAS,cAAA,GAAiB,EAAA;AAAA,UAC1B;AAAA,SACF,CAAE,KAAK,EAAE,CAAA;AAAA,QAGT,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,QAAA,EAAA,EAAO,WAAU,+GAAA,EAChB,QAAA,EAAA;AAAA,4BAAA,IAAA,CAAC,IAAA,EAAA,EAAG,WAAU,gEAAA,EACZ,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,2FAAA,EACb,QAAA,EAAA,UAAA,EACH,CAAA;AAAA,cACC;AAAA,aAAA,EACH,CAAA;AAAA,4BACA,GAAA;AAAA,cAAC,QAAA;AAAA,cAAA;AAAA,gBACC,IAAA,EAAK,QAAA;AAAA,gBACL,OAAA,EAAS,aAAA;AAAA,gBACT,YAAA,EAAW,wCAAA;AAAA,gBACX,SAAA,EAAU,gIAAA;AAAA,gBAEV,+BAAC,KAAA,EAAA,EAAI,KAAA,EAAM,IAAA,EAAK,MAAA,EAAO,MAAK,OAAA,EAAQ,WAAA,EAAY,IAAA,EAAK,MAAA,EAAO,QAAO,cAAA,EAAe,WAAA,EAAY,KAAI,aAAA,EAAc,OAAA,EAAQ,gBAAe,OAAA,EACrI,QAAA,EAAA;AAAA,kCAAA,GAAA,CAAC,MAAA,EAAA,EAAK,IAAG,GAAA,EAAI,EAAA,EAAG,KAAI,EAAA,EAAG,IAAA,EAAK,IAAG,IAAA,EAAK,CAAA;AAAA,kCACpC,GAAA,CAAC,UAAK,EAAA,EAAG,IAAA,EAAK,IAAG,GAAA,EAAI,EAAA,EAAG,GAAA,EAAI,EAAA,EAAG,IAAA,EAAK;AAAA,iBAAA,EACtC;AAAA;AAAA;AACF,WAAA,EACF,CAAA;AAAA,0BAGA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yGAAA,EACZ,QAAA,EAAA;AAAA,YAAA,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,qBACV,IAAA;AAAA,cAAC,QAAA;AAAA,cAAA;AAAA,gBAEC,IAAA,EAAK,QAAA;AAAA,gBACL,IAAA,EAAK,QAAA;AAAA,gBACL,gBAAc,CAAA,CAAE,OAAA;AAAA,gBAChB,cAAY,CAAA,CAAE,KAAA;AAAA,gBACd,eAAa,CAAA,CAAE,MAAA;AAAA,gBACf,SAAS,MAAM,CAAA,CAAE,QAAA,CAAS,CAAC,EAAE,OAAO,CAAA;AAAA,gBACpC,SAAA,EAAU,iBAAA;AAAA,gBAET,QAAA,EAAA;AAAA,kBAAA,CAAA,CAAE,IAAA;AAAA,kBACF,CAAA,CAAE;AAAA;AAAA,eAAA;AAAA,cAVE,CAAA,CAAE;AAAA,aAYV,CAAA;AAAA,YACA,OAAA,CAAQ,MAAA,GAAS,CAAA,oBAAK,GAAA,CAAC,KAAA,EAAA,EAAI,WAAU,iCAAA,EACnC,QAAA,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,qBACZ,GAAA;AAAA,cAAC,QAAA;AAAA,cAAA;AAAA,gBAEC,IAAA,EAAK,QAAA;AAAA,gBACL,SAAS,CAAA,CAAE,OAAA;AAAA,gBACX,UAAU,CAAA,CAAE,QAAA;AAAA,gBACZ,cAAY,CAAA,CAAE,KAAA;AAAA,gBACd,KAAA,EAAO,CAAA,CAAE,KAAA,IAAS,CAAA,CAAE,KAAA;AAAA,gBACpB,eAAa,CAAA,CAAE,MAAA;AAAA,gBACf,SAAA,EAAU,kNAAA;AAAA,gBAET,QAAA,EAAA,CAAA,CAAE;AAAA,eAAA;AAAA,cATE,CAAA,CAAE;AAAA,aAWV,CAAA,EACH;AAAA,WAAA,EACF,CAAA;AAAA,0BAGA,GAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAU,gCAAA;AAAA,cACV,KAAA,EAAO,EAAE,aAAA,EAAe,6CAAA,EAA8C;AAAA,cAErE,iBAAO,GAAA,CAAI,CAAC,sBACX,IAAA,CAAC,SAAA,EAAA,EAAsB,WAAU,gBAAA,EAC/B,QAAA,EAAA;AAAA,gCAAA,IAAA,CAAC,IAAA,EAAA,EAAG,WAAU,gGAAA,EACZ,QAAA,EAAA;AAAA,kCAAA,GAAA,CAAC,MAAA,EAAA,EAAK,WAAU,qCAAA,EAAsC,CAAA;AAAA,kBACrD,CAAA,CAAE;AAAA,iBAAA,EACL,CAAA;AAAA,gCACA,GAAA,CAAC,SAAI,SAAA,EAAU,wBAAA,EACZ,YAAE,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM;AAClB,kBAAA,MAAM,MAAA,GAAS,eAAe,CAAA,CAAE,GAAA;AAChC,kBAAA,uBACE,IAAA;AAAA,oBAAC,QAAA;AAAA,oBAAA;AAAA,sBAEC,IAAA,EAAK,QAAA;AAAA,sBACL,cAAY,CAAA,CAAE,KAAA;AAAA,sBACd,cAAA,EAAc,MAAA;AAAA,sBACd,aAAW,CAAA,CAAE,GAAA;AAAA,sBACb,SAAS,MAAM;AACb,wBAAA,YAAA,CAAa,EAAE,GAAG,CAAA;AAClB,wBAAA,aAAA,EAAc;AAAA,sBAChB,CAAA;AAAA,sBACA,SAAA,EAAW;AAAA,wBACT,oGAAA;AAAA,wBACA,SACI,wBAAA,GACA;AAAA,uBACN,CAAE,KAAK,GAAG,CAAA;AAAA,sBAEV,QAAA,EAAA;AAAA,wCAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,0CAAA,EAA4C,QAAA,EAAA,CAAA,CAAE,IAAA,EAAK,CAAA;AAAA,wCACnE,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,gEAAA,EACb,YAAE,KAAA,EACL;AAAA;AAAA,qBAAA;AAAA,oBAnBK,CAAA,CAAE;AAAA,mBAoBT;AAAA,gBAEJ,CAAC,CAAA,EACH;AAAA,eAAA,EAAA,EAjCY,CAAA,CAAE,KAkChB,CACD;AAAA;AAAA;AACH;AAAA;AAAA;AACF,GAAA,EACF,CAAA;AAEJ","file":"chunk-SBDMF4NQ.mjs","sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\ninterface UseChordShortcutArgs<G extends string> {\n groupOrder: readonly G[];\n tools: ReadonlyArray<{ key: string; group: G }>;\n onSelect: (toolKey: string) => void;\n enabled: boolean;\n}\n\ninterface UseChordShortcutResult<G extends string> {\n chordGroup: G | null;\n cancel: () => void;\n}\n\nconst A_CODE = 'a'.charCodeAt(0);\n\nfunction isFieldFocused(): boolean {\n const ae = (typeof document !== 'undefined'\n ? (document.activeElement as HTMLElement | null)\n : null);\n return !!(\n ae &&\n (ae.tagName === 'INPUT' ||\n ae.tagName === 'TEXTAREA' ||\n ae.isContentEditable)\n );\n}\n\nexport function useChordShortcut<G extends string>(\n args: UseChordShortcutArgs<G>,\n): UseChordShortcutResult<G> {\n const { groupOrder, tools, onSelect, enabled } = args;\n\n const [chordGroup, setChordGroup] = useState<G | null>(null);\n\n const groupOrderRef = useRef(groupOrder);\n const toolsRef = useRef(tools);\n const onSelectRef = useRef(onSelect);\n const chordGroupRef = useRef<G | null>(null);\n\n groupOrderRef.current = groupOrder;\n toolsRef.current = tools;\n onSelectRef.current = onSelect;\n // chordGroupRef được sync ngay trong handler (xem `setChord` dưới đây)\n // thay vì ghi từ render body — nếu ghi ở body sẽ bị React batch hoá khi\n // hai event xảy ra trong cùng một act() (event sau đọc giá trị cũ).\n\n const cancel = useCallback(() => {\n chordGroupRef.current = null;\n setChordGroup(null);\n }, []);\n\n useEffect(() => {\n if (!enabled) return;\n\n const setChord = (next: G | null) => {\n chordGroupRef.current = next;\n setChordGroup(next);\n };\n\n const onKey = (e: KeyboardEvent) => {\n if (e.metaKey || e.ctrlKey || e.altKey) return;\n if (isFieldFocused()) return;\n\n const key = e.key;\n const lower = key.length === 1 ? key.toLowerCase() : key;\n\n if (key === 'Escape') {\n if (chordGroupRef.current !== null) {\n e.preventDefault();\n e.stopPropagation();\n setChord(null);\n }\n return;\n }\n\n if (lower.length === 1 && lower >= 'a' && lower <= 'z') {\n const idx = lower.charCodeAt(0) - A_CODE;\n if (idx < groupOrderRef.current.length) {\n e.preventDefault();\n e.stopPropagation();\n setChord(groupOrderRef.current[idx]);\n }\n return;\n }\n\n if (key >= '1' && key <= '9') {\n const active = chordGroupRef.current;\n if (active === null) return;\n const n = key.charCodeAt(0) - '1'.charCodeAt(0); // 0-indexed\n const toolsInGroup = toolsRef.current.filter(\n (t) => t.group === active,\n );\n e.preventDefault();\n e.stopPropagation();\n if (n < toolsInGroup.length) {\n onSelectRef.current(toolsInGroup[n].key);\n }\n setChord(null);\n return;\n }\n };\n\n window.addEventListener('keydown', onKey, { capture: true });\n return () => {\n window.removeEventListener('keydown', onKey, { capture: true });\n };\n }, [enabled]);\n\n return { chordGroup, cancel };\n}\n","'use client';\n\nimport React from 'react';\n\n/**\n * Generic mobile tool drawer dùng chung cho geometry-2d + geometry-3d.\n *\n * Layout:\n * - Header: icon + title + close\n * - Sticky toolbar: chip switches (Trục/Lưới) + icon-actions (Reset, Undo)\n * - Body: section dọc, mỗi section là 1 nhóm tools, grid 3-col card có nhãn\n *\n * Style: soft-modern, emerald accent, khớp các class trong shared/stamp.css.\n */\n\nexport interface MobileChip {\n /** Label hiển thị + aria-label */\n label: string;\n icon: React.ReactNode;\n pressed: boolean;\n onToggle: (next: boolean) => void;\n /** data-testid optional (để test cũ chạy được) */\n testId?: string;\n}\n\nexport interface MobileActionButton {\n label: string;\n icon: React.ReactNode;\n onClick: () => void;\n disabled?: boolean;\n title?: string;\n /** data-testid optional (cho phép test target button cụ thể) */\n testId?: string;\n}\n\nexport interface MobileTool<TKey extends string> {\n key: TKey;\n label: string;\n icon: React.ReactNode;\n}\n\nexport interface MobileToolGroup<TKey extends string, TGroup extends string> {\n group: TGroup;\n groupLabel: string;\n tools: MobileTool<TKey>[];\n}\n\ninterface MobileToolDrawerProps<TKey extends string, TGroup extends string> {\n title: string;\n headerIcon: React.ReactNode;\n chips: MobileChip[];\n actions: MobileActionButton[];\n groups: MobileToolGroup<TKey, TGroup>[];\n activeTool: TKey;\n onToolSelect: (key: TKey) => void;\n drawerOpen: boolean;\n onDrawerClose: () => void;\n isDark?: boolean;\n /** data-testid trên <aside> — giữ để test cũ tìm được panel */\n testId?: string;\n}\n\nexport function MobileToolDrawer<TKey extends string, TGroup extends string>({\n title,\n headerIcon,\n chips,\n actions,\n groups,\n activeTool,\n onToolSelect,\n drawerOpen,\n onDrawerClose,\n isDark,\n testId,\n}: MobileToolDrawerProps<TKey, TGroup>) {\n return (\n <>\n {drawerOpen && (\n <div\n className=\"stamp-drawer-backdrop\"\n onPointerDown={onDrawerClose}\n aria-hidden=\"true\"\n />\n )}\n <aside\n role=\"complementary\"\n aria-label={title}\n aria-hidden={!drawerOpen ? 'true' : undefined}\n data-testid={testId}\n data-stamp-area=\"true\"\n data-mobile-drawer=\"true\"\n data-geo-mobile=\"true\"\n data-drawer-state={drawerOpen ? 'open' : 'closed'}\n className={[\n isDark ? 'theme--dark ' : '',\n 'stamp-drawer-mobile flex flex-col border-r border-slate-200 bg-white shadow-md',\n ].join('')}\n >\n {/* Header */}\n <header className=\"flex items-center justify-between border-b border-slate-200 bg-gradient-to-r from-slate-50 to-white px-4 py-3\">\n <h3 className=\"flex items-center gap-2 text-base font-semibold text-slate-800\">\n <span className=\"inline-flex h-7 w-7 items-center justify-center rounded-lg bg-emerald-50 text-emerald-700\">\n {headerIcon}\n </span>\n {title}\n </h3>\n <button\n type=\"button\"\n onClick={onDrawerClose}\n aria-label=\"Đóng ngăn công cụ\"\n className=\"inline-flex h-9 w-9 items-center justify-center rounded-full text-slate-500 transition hover:bg-slate-100 hover:text-slate-800\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n </svg>\n </button>\n </header>\n\n {/* Sticky toolbar: chips + actions */}\n <div className=\"sticky top-0 z-10 flex items-center gap-2 border-b border-slate-200 bg-white/95 px-3 py-2 backdrop-blur\">\n {chips.map((c) => (\n <button\n key={c.label}\n type=\"button\"\n role=\"switch\"\n aria-pressed={c.pressed}\n aria-label={c.label}\n data-testid={c.testId}\n onClick={() => c.onToggle(!c.pressed)}\n className=\"geo-mobile-chip\"\n >\n {c.icon}\n {c.label}\n </button>\n ))}\n {actions.length > 0 && <div className=\"ml-auto flex items-center gap-1\">\n {actions.map((a) => (\n <button\n key={a.label}\n type=\"button\"\n onClick={a.onClick}\n disabled={a.disabled}\n aria-label={a.label}\n title={a.title ?? a.label}\n data-testid={a.testId}\n className=\"inline-flex h-9 w-9 items-center justify-center rounded-full text-slate-600 transition hover:bg-slate-100 hover:text-slate-900 disabled:cursor-not-allowed disabled:text-slate-300 disabled:hover:bg-transparent\"\n >\n {a.icon}\n </button>\n ))}\n </div>}\n </div>\n\n {/* Body: groups xếp dọc */}\n <div\n className=\"min-h-0 flex-1 overflow-y-auto\"\n style={{ paddingBottom: 'calc(0.75rem + env(safe-area-inset-bottom))' }}\n >\n {groups.map((g) => (\n <section key={g.group} className=\"px-3 pt-3 pb-1\">\n <h4 className=\"mb-2 flex items-center gap-2 text-[11px] font-semibold uppercase tracking-wider text-slate-500\">\n <span className=\"h-1 w-1 rounded-full bg-emerald-500\" />\n {g.groupLabel}\n </h4>\n <div className=\"grid grid-cols-3 gap-2\">\n {g.tools.map((t) => {\n const active = activeTool === t.key;\n return (\n <button\n key={t.key}\n type=\"button\"\n aria-label={t.label}\n aria-pressed={active}\n data-tool={t.key}\n onClick={() => {\n onToolSelect(t.key);\n onDrawerClose();\n }}\n className={[\n 'flex flex-col items-center justify-center gap-1.5 rounded-2xl px-2 py-3 transition active:scale-95',\n active\n ? 'geo-mobile-tool-active'\n : 'bg-slate-50 text-slate-700 hover:bg-slate-100',\n ].join(' ')}\n >\n <span className=\"flex h-6 w-6 items-center justify-center\">{t.icon}</span>\n <span className=\"text-center text-[11px] font-medium leading-tight line-clamp-2\">\n {t.label}\n </span>\n </button>\n );\n })}\n </div>\n </section>\n ))}\n </div>\n </aside>\n </>\n );\n}\n"]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
// src/stamps/latex/render.ts
|
|
3
|
+
var cachedCss = null;
|
|
4
|
+
function absoluteOrigin() {
|
|
5
|
+
if (typeof window !== "undefined" && window.location) return window.location.origin;
|
|
6
|
+
return "";
|
|
7
|
+
}
|
|
8
|
+
async function loadKatexCss() {
|
|
9
|
+
if (cachedCss !== null) return cachedCss;
|
|
10
|
+
try {
|
|
11
|
+
if (typeof fetch === "function") {
|
|
12
|
+
const res = await fetch("/katex.min.css");
|
|
13
|
+
if (res.ok) {
|
|
14
|
+
let css = await res.text();
|
|
15
|
+
const origin = absoluteOrigin();
|
|
16
|
+
if (origin) {
|
|
17
|
+
css = css.replace(/url\((['"]?)(fonts\/)/g, `url($1${origin}/$2`);
|
|
18
|
+
}
|
|
19
|
+
cachedCss = css;
|
|
20
|
+
return css;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
} catch {
|
|
24
|
+
}
|
|
25
|
+
cachedCss = "";
|
|
26
|
+
return "";
|
|
27
|
+
}
|
|
28
|
+
async function renderLatexToSvg(src, displayMode) {
|
|
29
|
+
const katex = await import('katex');
|
|
30
|
+
const html = katex.default.renderToString(src, { displayMode, throwOnError: true, output: "html" });
|
|
31
|
+
const measureDiv = document.createElement("div");
|
|
32
|
+
measureDiv.style.cssText = "position:absolute;top:-9999px;left:-9999px;visibility:hidden;display:inline-block;";
|
|
33
|
+
measureDiv.innerHTML = html;
|
|
34
|
+
document.body.appendChild(measureDiv);
|
|
35
|
+
const rect = measureDiv.getBoundingClientRect();
|
|
36
|
+
const width = Math.ceil(rect.width) || 50;
|
|
37
|
+
const height = Math.ceil(rect.height) || 20;
|
|
38
|
+
document.body.removeChild(measureDiv);
|
|
39
|
+
const cssText = await loadKatexCss();
|
|
40
|
+
return '<svg xmlns="http://www.w3.org/2000/svg" width="' + width + '" height="' + height + '" viewBox="0 0 ' + width + " " + height + '"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml" style="font-size:16px;line-height:1.2;"><style>' + cssText + "</style>" + html + "</div></foreignObject></svg>";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/stamps/latex/types.ts
|
|
44
|
+
function isLatexCustomData(data) {
|
|
45
|
+
if (!data || typeof data !== "object") return false;
|
|
46
|
+
const d = data;
|
|
47
|
+
return d.kind === "latex" && d.version === 1 && typeof d.src === "string";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export { isLatexCustomData, renderLatexToSvg };
|
|
51
|
+
//# sourceMappingURL=chunk-X5R72SSJ.mjs.map
|
|
52
|
+
//# sourceMappingURL=chunk-X5R72SSJ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/stamps/latex/render.ts","../src/stamps/latex/types.ts"],"names":[],"mappings":";AAAA,IAAI,SAAA,GAA2B,IAAA;AAI/B,SAAS,cAAA,GAAyB;AAChC,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,OAAO,QAAA,EAAU,OAAO,OAAO,QAAA,CAAS,MAAA;AAC7E,EAAA,OAAO,EAAA;AACT;AAEA,eAAe,YAAA,GAAgC;AAC7C,EAAA,IAAI,SAAA,KAAc,MAAM,OAAO,SAAA;AAC/B,EAAA,IAAI;AACF,IAAA,IAAI,OAAO,UAAU,UAAA,EAAY;AAC/B,MAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,gBAAgB,CAAA;AACxC,MAAA,IAAI,IAAI,EAAA,EAAI;AACV,QAAA,IAAI,GAAA,GAAM,MAAM,GAAA,CAAI,IAAA,EAAK;AAKzB,QAAA,MAAM,SAAS,cAAA,EAAe;AAC9B,QAAA,IAAI,MAAA,EAAQ;AACV,UAAA,GAAA,GAAM,GAAA,CAAI,OAAA,CAAQ,wBAAA,EAA0B,CAAA,MAAA,EAAS,MAAM,CAAA,GAAA,CAAK,CAAA;AAAA,QAClE;AACA,QAAA,SAAA,GAAY,GAAA;AACZ,QAAA,OAAO,GAAA;AAAA,MACT;AAAA,IACF;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,SAAA,GAAY,EAAA;AACZ,EAAA,OAAO,EAAA;AACT;AAEA,eAAsB,gBAAA,CAAiB,KAAa,WAAA,EAAuC;AACzF,EAAA,MAAM,KAAA,GAAQ,MAAM,OAAO,OAAO,CAAA;AAClC,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,OAAA,CAAQ,cAAA,CAAe,GAAA,EAAK,EAAE,WAAA,EAAa,YAAA,EAAc,IAAA,EAAM,MAAA,EAAQ,MAAA,EAAQ,CAAA;AAElG,EAAA,MAAM,UAAA,GAAa,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAC/C,EAAA,UAAA,CAAW,MAAM,OAAA,GAAU,oFAAA;AAC3B,EAAA,UAAA,CAAW,SAAA,GAAY,IAAA;AACvB,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,UAAU,CAAA;AACpC,EAAA,MAAM,IAAA,GAAO,WAAW,qBAAA,EAAsB;AAC9C,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,IAAA,CAAK,IAAA,CAAK,KAAK,CAAA,IAAK,EAAA;AACvC,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,IAAA,CAAK,IAAA,CAAK,MAAM,CAAA,IAAK,EAAA;AACzC,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,UAAU,CAAA;AAEpC,EAAA,MAAM,OAAA,GAAU,MAAM,YAAA,EAAa;AAKnC,EAAA,OAAO,iDAAA,GAAoD,KAAA,GAAQ,YAAA,GAAe,MAAA,GAAS,iBAAA,GAAoB,KAAA,GAAQ,GAAA,GAAM,MAAA,GAAS,uIAAA,GAGxH,OAAA,GAAU,UAAA,GACtB,IAAA,GACA,8BAAA;AAGJ;;;ACpDO,SAAS,kBAAkB,IAAA,EAAwC;AACxE,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,UAAU,OAAO,KAAA;AAC9C,EAAA,MAAM,CAAA,GAAI,IAAA;AACV,EAAA,OAAO,CAAA,CAAE,SAAS,OAAA,IAAW,CAAA,CAAE,YAAY,CAAA,IAAK,OAAO,EAAE,GAAA,KAAQ,QAAA;AACnE","file":"chunk-X5R72SSJ.mjs","sourcesContent":["let cachedCss: string | null = null;\n\n// Absolute origin để inlined CSS có thể load fonts khi SVG render trong\n// Excalidraw / image context (relative paths fail trong nested URL contexts).\nfunction absoluteOrigin(): string {\n if (typeof window !== 'undefined' && window.location) return window.location.origin;\n return '';\n}\n\nasync function loadKatexCss(): Promise<string> {\n if (cachedCss !== null) return cachedCss;\n try {\n if (typeof fetch === 'function') {\n const res = await fetch('/katex.min.css');\n if (res.ok) {\n let css = await res.text();\n // Rewrite relative font URLs → absolute origin URLs.\n // KaTeX CSS uses url(fonts/...) — relative to /katex.min.css → /fonts/...\n // Trong SVG <foreignObject> được render thành image, relative resolves\n // tới page URL (/room/...) thay vì root, gây 404.\n const origin = absoluteOrigin();\n if (origin) {\n css = css.replace(/url\\((['\"]?)(fonts\\/)/g, `url($1${origin}/$2`);\n }\n cachedCss = css;\n return css;\n }\n }\n } catch {\n /* ignore */\n }\n cachedCss = '';\n return '';\n}\n\nexport async function renderLatexToSvg(src: string, displayMode: boolean): Promise<string> {\n const katex = await import('katex');\n const html = katex.default.renderToString(src, { displayMode, throwOnError: true, output: 'html' });\n\n const measureDiv = document.createElement('div');\n measureDiv.style.cssText = 'position:absolute;top:-9999px;left:-9999px;visibility:hidden;display:inline-block;';\n measureDiv.innerHTML = html;\n document.body.appendChild(measureDiv);\n const rect = measureDiv.getBoundingClientRect();\n const width = Math.ceil(rect.width) || 50;\n const height = Math.ceil(rect.height) || 20;\n document.body.removeChild(measureDiv);\n\n const cssText = await loadKatexCss();\n\n // KaTeX render với text màu đen mặc định. Excalidraw apply CSS filter\n // invert+hue-rotate trên canvas khi dark mode → text tự thành sáng. Đừng\n // override color ở đây vì sẽ đánh nhau với filter.\n return '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"' + width + '\" height=\"' + height + '\" viewBox=\"0 0 ' + width + ' ' + height + '\">' +\n '<foreignObject width=\"100%\" height=\"100%\">' +\n '<div xmlns=\"http://www.w3.org/1999/xhtml\" style=\"font-size:16px;line-height:1.2;\">' +\n '<style>' + cssText + '</style>' +\n html +\n '</div>' +\n '</foreignObject>' +\n '</svg>';\n}\n","import type { BaseStampCustomData } from '../shared/types';\n\nexport interface LatexCustomData extends BaseStampCustomData {\n kind: 'latex';\n version: 1;\n src: string;\n displayMode: boolean;\n}\n\nexport function isLatexCustomData(data: unknown): data is LatexCustomData {\n if (!data || typeof data !== 'object') return false;\n const d = data as Partial<LatexCustomData>;\n return d.kind === 'latex' && d.version === 1 && typeof d.src === 'string';\n}\n"]}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { isGraph2DCustomData, renderGraph2dSvgFromState } from './chunk-74VEEZBV.mjs';
|
|
3
|
+
import { lazy } from 'react';
|
|
4
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
var Graph2DStampHost = lazy(
|
|
7
|
+
() => import('./host-LZH2FZ2N.mjs').then((m) => ({ default: m.Graph2DStampHost }))
|
|
8
|
+
);
|
|
9
|
+
var Graph2DIcon = /* @__PURE__ */ jsxs(
|
|
10
|
+
"svg",
|
|
11
|
+
{
|
|
12
|
+
width: "20",
|
|
13
|
+
height: "20",
|
|
14
|
+
viewBox: "0 0 24 24",
|
|
15
|
+
fill: "none",
|
|
16
|
+
stroke: "currentColor",
|
|
17
|
+
strokeWidth: "1.6",
|
|
18
|
+
strokeLinecap: "round",
|
|
19
|
+
strokeLinejoin: "round",
|
|
20
|
+
"aria-hidden": "true",
|
|
21
|
+
children: [
|
|
22
|
+
/* @__PURE__ */ jsx("path", { d: "M3 21 V3" }),
|
|
23
|
+
/* @__PURE__ */ jsx("path", { d: "M3 21 H21" }),
|
|
24
|
+
/* @__PURE__ */ jsx("path", { d: "M5 19 C8 5, 14 5, 19 17" })
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
var graph2dStamp = {
|
|
29
|
+
kind: "graph2d",
|
|
30
|
+
experimental: true,
|
|
31
|
+
shortcutKey: "h",
|
|
32
|
+
toolbarLabel: "H",
|
|
33
|
+
toolbarTitle: "Ch\xE8n \u0111\u1ED3 th\u1ECB 2D (H)",
|
|
34
|
+
toolbarIcon: Graph2DIcon,
|
|
35
|
+
toolbarTestId: "stamp-toolbar-graph2d",
|
|
36
|
+
matchesCustomData: isGraph2DCustomData,
|
|
37
|
+
async renderSvgFromCustomData(data) {
|
|
38
|
+
if (!isGraph2DCustomData(data)) {
|
|
39
|
+
throw new Error("graph2dStamp.renderSvgFromCustomData: customData kh\xF4ng ph\u1EA3i graph2d");
|
|
40
|
+
}
|
|
41
|
+
return renderGraph2dSvgFromState(data.jsonState);
|
|
42
|
+
},
|
|
43
|
+
async restoreFileFromCustomData(element) {
|
|
44
|
+
const data = element.customData;
|
|
45
|
+
const fileId = element.fileId;
|
|
46
|
+
if (!data || !fileId) return null;
|
|
47
|
+
if (!isGraph2DCustomData(data)) return null;
|
|
48
|
+
const svgString = await renderGraph2dSvgFromState(data.jsonState);
|
|
49
|
+
const utf8 = unescape(encodeURIComponent(svgString));
|
|
50
|
+
const dataURL = "data:image/svg+xml;base64," + (typeof btoa !== "undefined" ? btoa(utf8) : Buffer.from(utf8).toString("base64"));
|
|
51
|
+
return { fileId, dataURL, mimeType: "image/svg+xml" };
|
|
52
|
+
},
|
|
53
|
+
Host: Graph2DStampHost
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export { graph2dStamp };
|
|
57
|
+
//# sourceMappingURL=chunk-ZVN356JZ.mjs.map
|
|
58
|
+
//# sourceMappingURL=chunk-ZVN356JZ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/stamps/graph-2d/index.tsx"],"names":[],"mappings":";;;;AAaA,IAAM,gBAAA,GAAmB,IAAA;AAAA,EAAK,MAC5B,OAAO,qBAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,MAAO,EAAE,OAAA,EAAS,CAAA,CAAE,gBAAA,EAAiB,CAAE;AAChE,CAAA;AAEA,IAAM,WAAA,mBACJ,IAAA;AAAA,EAAC,KAAA;AAAA,EAAA;AAAA,IACC,KAAA,EAAM,IAAA;AAAA,IACN,MAAA,EAAO,IAAA;AAAA,IACP,OAAA,EAAQ,WAAA;AAAA,IACR,IAAA,EAAK,MAAA;AAAA,IACL,MAAA,EAAO,cAAA;AAAA,IACP,WAAA,EAAY,KAAA;AAAA,IACZ,aAAA,EAAc,OAAA;AAAA,IACd,cAAA,EAAe,OAAA;AAAA,IACf,aAAA,EAAY,MAAA;AAAA,IAEZ,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,GAAE,UAAA,EAAW,CAAA;AAAA,sBACnB,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,WAAA,EAAY,CAAA;AAAA,sBACpB,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,yBAAA,EAA0B;AAAA;AAAA;AACpC,CAAA;AAGK,IAAM,YAAA,GAA0B;AAAA,EACrC,IAAA,EAAM,SAAA;AAAA,EACN,YAAA,EAAc,IAAA;AAAA,EACd,WAAA,EAAa,GAAA;AAAA,EACb,YAAA,EAAc,GAAA;AAAA,EACd,YAAA,EAAc,sCAAA;AAAA,EACd,WAAA,EAAa,WAAA;AAAA,EACb,aAAA,EAAe,uBAAA;AAAA,EACf,iBAAA,EAAmB,mBAAA;AAAA,EACnB,MAAM,wBAAwB,IAAA,EAAM;AAClC,IAAA,IAAI,CAAC,mBAAA,CAAoB,IAAI,CAAA,EAAG;AAC9B,MAAA,MAAM,IAAI,MAAM,6EAAqE,CAAA;AAAA,IACvF;AACA,IAAA,OAAO,yBAAA,CAA0B,KAAK,SAAS,CAAA;AAAA,EACjD,CAAA;AAAA,EACA,MAAM,0BAA0B,OAAA,EAA4C;AAC1E,IAAA,MAAM,OAAO,OAAA,CAAQ,UAAA;AACrB,IAAA,MAAM,SAAU,OAAA,CAAuC,MAAA;AACvD,IAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,MAAA,EAAQ,OAAO,IAAA;AAC7B,IAAA,IAAI,CAAC,mBAAA,CAAoB,IAAI,CAAA,EAAG,OAAO,IAAA;AACvC,IAAA,MAAM,SAAA,GAAY,MAAM,yBAAA,CAA0B,IAAA,CAAK,SAAS,CAAA;AAChE,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,kBAAA,CAAmB,SAAS,CAAC,CAAA;AACnD,IAAA,MAAM,OAAA,GACJ,4BAAA,IACC,OAAO,IAAA,KAAS,WAAA,GAAc,IAAA,CAAK,IAAI,CAAA,GAAI,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,CAAE,SAAS,QAAQ,CAAA,CAAA;AACjF,IAAA,OAAO,EAAE,MAAA,EAAQ,OAAA,EAAS,QAAA,EAAU,eAAA,EAAgB;AAAA,EACtD,CAAA;AAAA,EACA,IAAA,EAAM;AACR","file":"chunk-ZVN356JZ.mjs","sourcesContent":["'use client';\n\nimport { lazy } from 'react';\nimport { renderGraph2dSvgFromState } from './render';\nimport type {\n RestoredStampFile,\n StampType,\n} from '../shared/types';\nimport { isGraph2DCustomData, type Graph2DCustomData } from './types';\n\nexport { isGraph2DCustomData };\nexport type { Graph2DCustomData };\n\nconst Graph2DStampHost = lazy(() =>\n import('./host').then((m) => ({ default: m.Graph2DStampHost })),\n);\n\nconst Graph2DIcon = (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M3 21 V3\" />\n <path d=\"M3 21 H21\" />\n <path d=\"M5 19 C8 5, 14 5, 19 17\" />\n </svg>\n);\n\nexport const graph2dStamp: StampType = {\n kind: 'graph2d',\n experimental: true,\n shortcutKey: 'h',\n toolbarLabel: 'H',\n toolbarTitle: 'Chèn đồ thị 2D (H)',\n toolbarIcon: Graph2DIcon,\n toolbarTestId: 'stamp-toolbar-graph2d',\n matchesCustomData: isGraph2DCustomData,\n async renderSvgFromCustomData(data) {\n if (!isGraph2DCustomData(data)) {\n throw new Error('graph2dStamp.renderSvgFromCustomData: customData không phải graph2d');\n }\n return renderGraph2dSvgFromState(data.jsonState);\n },\n async restoreFileFromCustomData(element): Promise<RestoredStampFile | null> {\n const data = element.customData as Graph2DCustomData | undefined;\n const fileId = (element as { fileId?: string | null }).fileId;\n if (!data || !fileId) return null;\n if (!isGraph2DCustomData(data)) return null;\n const svgString = await renderGraph2dSvgFromState(data.jsonState);\n const utf8 = unescape(encodeURIComponent(svgString));\n const dataURL =\n 'data:image/svg+xml;base64,' +\n (typeof btoa !== 'undefined' ? btoa(utf8) : Buffer.from(utf8).toString('base64'));\n return { fileId, dataURL, mimeType: 'image/svg+xml' };\n },\n Host: Graph2DStampHost,\n};\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { B as BaseStampCustomData, S as StampType } from './types-CinstD7T.mjs';
|
|
2
|
+
import 'react';
|
|
3
|
+
import '@excalidraw/excalidraw/element/types';
|
|
4
|
+
|
|
5
|
+
interface GeometryCustomData extends BaseStampCustomData {
|
|
6
|
+
kind: 'geometry';
|
|
7
|
+
version: 1;
|
|
8
|
+
jsonState: string;
|
|
9
|
+
svgWidth: number;
|
|
10
|
+
svgHeight: number;
|
|
11
|
+
}
|
|
12
|
+
declare function isGeometryCustomData(data: unknown): data is GeometryCustomData;
|
|
13
|
+
|
|
14
|
+
declare const geometryStamp: StampType;
|
|
15
|
+
|
|
16
|
+
export { type GeometryCustomData, geometryStamp, isGeometryCustomData };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { B as BaseStampCustomData, S as StampType } from './types-CinstD7T.js';
|
|
2
|
+
import 'react';
|
|
3
|
+
import '@excalidraw/excalidraw/element/types';
|
|
4
|
+
|
|
5
|
+
interface GeometryCustomData extends BaseStampCustomData {
|
|
6
|
+
kind: 'geometry';
|
|
7
|
+
version: 1;
|
|
8
|
+
jsonState: string;
|
|
9
|
+
svgWidth: number;
|
|
10
|
+
svgHeight: number;
|
|
11
|
+
}
|
|
12
|
+
declare function isGeometryCustomData(data: unknown): data is GeometryCustomData;
|
|
13
|
+
|
|
14
|
+
declare const geometryStamp: StampType;
|
|
15
|
+
|
|
16
|
+
export { type GeometryCustomData, geometryStamp, isGeometryCustomData };
|