@agent-canvas/cli 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/__tests__/add-arrow.test.d.ts +1 -0
- package/dist/commands/__tests__/add-arrow.test.js +186 -0
- package/dist/commands/__tests__/add-image.test.d.ts +1 -0
- package/dist/commands/__tests__/add-image.test.js +170 -0
- package/dist/commands/__tests__/add-line.test.d.ts +1 -0
- package/dist/commands/__tests__/add-line.test.js +138 -0
- package/dist/commands/__tests__/add-polygon.test.d.ts +1 -0
- package/dist/commands/__tests__/add-polygon.test.js +147 -0
- package/dist/commands/__tests__/add-shape.test.d.ts +1 -0
- package/dist/commands/__tests__/add-shape.test.js +193 -0
- package/dist/commands/__tests__/add-text.test.d.ts +1 -0
- package/dist/commands/__tests__/add-text.test.js +144 -0
- package/dist/commands/__tests__/clear.test.d.ts +1 -0
- package/dist/commands/__tests__/clear.test.js +65 -0
- package/dist/commands/__tests__/delete-elements.test.d.ts +1 -0
- package/dist/commands/__tests__/delete-elements.test.js +103 -0
- package/dist/commands/__tests__/export.test.d.ts +1 -0
- package/dist/commands/__tests__/export.test.js +187 -0
- package/dist/commands/__tests__/group-elements.test.d.ts +1 -0
- package/dist/commands/__tests__/group-elements.test.js +93 -0
- package/dist/commands/__tests__/list.test.d.ts +1 -0
- package/dist/commands/__tests__/list.test.js +134 -0
- package/dist/commands/__tests__/load.test.d.ts +1 -0
- package/dist/commands/__tests__/load.test.js +158 -0
- package/dist/commands/__tests__/move-elements.test.d.ts +1 -0
- package/dist/commands/__tests__/move-elements.test.js +117 -0
- package/dist/commands/__tests__/new-canvas.test.d.ts +1 -0
- package/dist/commands/__tests__/new-canvas.test.js +113 -0
- package/dist/commands/__tests__/read.test.d.ts +1 -0
- package/dist/commands/__tests__/read.test.js +247 -0
- package/dist/commands/__tests__/rename-canvas.test.d.ts +1 -0
- package/dist/commands/__tests__/rename-canvas.test.js +86 -0
- package/dist/commands/__tests__/resize-elements.test.d.ts +1 -0
- package/dist/commands/__tests__/resize-elements.test.js +150 -0
- package/dist/commands/__tests__/rotate-elements.test.d.ts +1 -0
- package/dist/commands/__tests__/rotate-elements.test.js +114 -0
- package/dist/commands/__tests__/save.test.d.ts +1 -0
- package/dist/commands/__tests__/save.test.js +105 -0
- package/dist/commands/__tests__/ungroup-element.test.d.ts +1 -0
- package/dist/commands/__tests__/ungroup-element.test.js +93 -0
- package/dist/commands/__tests__/use-canvas.test.d.ts +1 -0
- package/dist/commands/__tests__/use-canvas.test.js +86 -0
- package/dist/commands/add-arrow.d.ts +40 -0
- package/dist/commands/add-arrow.js +52 -0
- package/dist/commands/add-image.d.ts +26 -0
- package/dist/commands/add-image.js +66 -0
- package/dist/commands/add-line.d.ts +27 -0
- package/dist/commands/add-line.js +35 -0
- package/dist/commands/add-polygon.d.ts +26 -0
- package/dist/commands/add-polygon.js +44 -0
- package/dist/commands/add-shape.d.ts +32 -0
- package/dist/commands/add-shape.js +41 -0
- package/dist/commands/add-text.d.ts +28 -0
- package/dist/commands/add-text.js +35 -0
- package/dist/commands/clear.d.ts +17 -0
- package/dist/commands/clear.js +23 -0
- package/dist/commands/delete-elements.d.ts +20 -0
- package/dist/commands/delete-elements.js +26 -0
- package/dist/commands/export.d.ts +26 -0
- package/dist/commands/export.js +45 -0
- package/dist/commands/group-elements.d.ts +20 -0
- package/dist/commands/group-elements.js +28 -0
- package/dist/commands/list.d.ts +23 -0
- package/dist/commands/list.js +50 -0
- package/dist/commands/load.d.ts +20 -0
- package/dist/commands/load.js +67 -0
- package/dist/commands/move-elements.d.ts +22 -0
- package/dist/commands/move-elements.js +26 -0
- package/dist/commands/new-canvas.d.ts +21 -0
- package/dist/commands/new-canvas.js +29 -0
- package/dist/commands/read.d.ts +23 -0
- package/dist/commands/read.js +49 -0
- package/dist/commands/rename-canvas.d.ts +20 -0
- package/dist/commands/rename-canvas.js +27 -0
- package/dist/commands/resize-elements.d.ts +24 -0
- package/dist/commands/resize-elements.js +41 -0
- package/dist/commands/rotate-elements.d.ts +21 -0
- package/dist/commands/rotate-elements.js +26 -0
- package/dist/commands/save.d.ts +18 -0
- package/dist/commands/save.js +27 -0
- package/dist/commands/start.d.ts +0 -1
- package/dist/commands/start.js +0 -41
- package/dist/commands/ungroup-element.d.ts +20 -0
- package/dist/commands/ungroup-element.js +28 -0
- package/dist/commands/use-canvas.d.ts +20 -0
- package/dist/commands/use-canvas.js +27 -0
- package/dist/index.js +124 -518
- package/dist/lib/__tests__/image-utils.test.d.ts +1 -0
- package/dist/lib/__tests__/image-utils.test.js +123 -0
- package/dist/lib/__tests__/toon-converter.test.d.ts +1 -0
- package/dist/lib/__tests__/toon-converter.test.js +586 -0
- package/dist/lib/__tests__/ws-client.test.d.ts +1 -0
- package/dist/lib/__tests__/ws-client.test.js +22 -0
- package/dist/lib/image-utils.d.ts +3 -0
- package/dist/lib/image-utils.js +21 -0
- package/dist/lib/protocol.d.ts +24 -0
- package/dist/lib/toon-converter.d.ts +104 -0
- package/dist/lib/toon-converter.js +221 -0
- package/dist/static/assets/{ar-SA-G6X2FPQ2-DrFZc17I.js → ar-SA-G6X2FPQ2-DBOnFzSe.js} +1 -1
- package/dist/static/assets/{arc-BE4RRWho.js → arc-bbUtBUEI.js} +1 -1
- package/dist/static/assets/{az-AZ-76LH7QW2-DgHo0c8x.js → az-AZ-76LH7QW2-Cc10aZ9u.js} +1 -1
- package/dist/static/assets/{bg-BG-XCXSNQG7-D6Qfq7sN.js → bg-BG-XCXSNQG7-Ccf7m1dz.js} +1 -1
- package/dist/static/assets/{blockDiagram-38ab4fdb-j4QCKNBJ.js → blockDiagram-38ab4fdb-fpHXuuqC.js} +1 -1
- package/dist/static/assets/{bn-BD-2XOGV67Q-DtD_GeTA.js → bn-BD-2XOGV67Q-DUsn1OkS.js} +1 -1
- package/dist/static/assets/{c4Diagram-3d4e48cf-BmQ9mE4L.js → c4Diagram-3d4e48cf-R8YUsP4D.js} +1 -1
- package/dist/static/assets/{ca-ES-6MX7JW3Y-pqblEVH2.js → ca-ES-6MX7JW3Y-hu4EsJg6.js} +1 -1
- package/dist/static/assets/channel-DOejQ6Zr.js +1 -0
- package/dist/static/assets/{classDiagram-70f12bd4-CUF5vm_X.js → classDiagram-70f12bd4-Bd-nCoOe.js} +1 -1
- package/dist/static/assets/{classDiagram-v2-f2320105-Cpj_A_lB.js → classDiagram-v2-f2320105-BSqlF2Ya.js} +1 -1
- package/dist/static/assets/clone-BACvnIVb.js +1 -0
- package/dist/static/assets/{createText-2e5e7dd3-pnM7Sebc.js → createText-2e5e7dd3-DDxcRLmM.js} +1 -1
- package/dist/static/assets/{cs-CZ-2BRQDIVT-CybzkHZt.js → cs-CZ-2BRQDIVT-CvZtKTE7.js} +1 -1
- package/dist/static/assets/{da-DK-5WZEPLOC-pqELhXiQ.js → da-DK-5WZEPLOC-Bo6-ltfA.js} +1 -1
- package/dist/static/assets/{de-DE-XR44H4JA-ZL76WiIL.js → de-DE-XR44H4JA-Rki2qM_x.js} +1 -1
- package/dist/static/assets/{edges-e0da2a9e-B9extNmC.js → edges-e0da2a9e-DWJxCouB.js} +1 -1
- package/dist/static/assets/{el-GR-BZB4AONW-rKEdKItN.js → el-GR-BZB4AONW-BRy2rrt9.js} +1 -1
- package/dist/static/assets/{erDiagram-9861fffd-Bf-1lr9-.js → erDiagram-9861fffd-DXldiM4J.js} +1 -1
- package/dist/static/assets/{es-ES-U4NZUMDT-BB6riLIk.js → es-ES-U4NZUMDT-hCKbxwHs.js} +1 -1
- package/dist/static/assets/{eu-ES-A7QVB2H4-ChqJNrpA.js → eu-ES-A7QVB2H4-CmZGwoa5.js} +1 -1
- package/dist/static/assets/{fa-IR-HGAKTJCU--KEd62Xu.js → fa-IR-HGAKTJCU-C6DRPcaF.js} +1 -1
- package/dist/static/assets/{fi-FI-Z5N7JZ37-CdB0adCJ.js → fi-FI-Z5N7JZ37-DBaspnIB.js} +1 -1
- package/dist/static/assets/{flowDb-956e92f1-Dbca38tY.js → flowDb-956e92f1-C48Cmgvd.js} +1 -1
- package/dist/static/assets/{flowDiagram-66a62f08-DpQc0ZQB.js → flowDiagram-66a62f08-DV7f8TtG.js} +1 -1
- package/dist/static/assets/flowDiagram-v2-96b9c2cf-MnAxlKb6.js +1 -0
- package/dist/static/assets/{flowchart-elk-definition-4a651766-CAxl2EAj.js → flowchart-elk-definition-4a651766-C2muEpzy.js} +1 -1
- package/dist/static/assets/{fr-FR-RHASNOE6-LEJbMD3b.js → fr-FR-RHASNOE6-D1dEEEfS.js} +1 -1
- package/dist/static/assets/{ganttDiagram-c361ad54-BsKueDb1.js → ganttDiagram-c361ad54-CrooqCzc.js} +1 -1
- package/dist/static/assets/{gitGraphDiagram-72cf32ee-BySoTKcT.js → gitGraphDiagram-72cf32ee-x7BVIo_3.js} +1 -1
- package/dist/static/assets/{gl-ES-HMX3MZ6V-Bi3P3M7W.js → gl-ES-HMX3MZ6V-Czkw2ahx.js} +1 -1
- package/dist/static/assets/{graph-B_0qOa-k.js → graph-Bv0C_szp.js} +1 -1
- package/dist/static/assets/{he-IL-6SHJWFNN-DE7_aRwg.js → he-IL-6SHJWFNN-BLwbN3CV.js} +1 -1
- package/dist/static/assets/{hi-IN-IWLTKZ5I-CjARmJ8i.js → hi-IN-IWLTKZ5I-BJh3HrXc.js} +1 -1
- package/dist/static/assets/{hu-HU-A5ZG7DT2-d79urL5L.js → hu-HU-A5ZG7DT2-D00s-8EN.js} +1 -1
- package/dist/static/assets/{id-ID-SAP4L64H-bxo8Q0Jv.js → id-ID-SAP4L64H-BmyuzwXV.js} +1 -1
- package/dist/static/assets/{index-3862675e-BLm8h60G.js → index-3862675e-mZlepZqF.js} +1 -1
- package/dist/static/assets/{index-BkW4NM9R.js → index-Bnmyv9TE.js} +4 -4
- package/dist/static/assets/index-jjzEa4oB.js +316 -0
- package/dist/static/assets/{infoDiagram-f8f76790-CZ4J5sjk.js → infoDiagram-f8f76790-w7FUTsVV.js} +1 -1
- package/dist/static/assets/{it-IT-JPQ66NNP-D41R3uEK.js → it-IT-JPQ66NNP-CeVq0l8v.js} +1 -1
- package/dist/static/assets/{ja-JP-DBVTYXUO-zK8NUJnS.js → ja-JP-DBVTYXUO-BUNKxxGA.js} +1 -1
- package/dist/static/assets/{journeyDiagram-49397b02-DtFcZaIk.js → journeyDiagram-49397b02-DYjDqkQG.js} +1 -1
- package/dist/static/assets/{kaa-6HZHGXH3-CbGIc6V6.js → kaa-6HZHGXH3-BIKwULqb.js} +1 -1
- package/dist/static/assets/{kab-KAB-ZGHBKWFO-BweHPuX1.js → kab-KAB-ZGHBKWFO-BDpRo7IX.js} +1 -1
- package/dist/static/assets/{kk-KZ-P5N5QNE5-DSokMm7h.js → kk-KZ-P5N5QNE5-bI2LN3W0.js} +1 -1
- package/dist/static/assets/{km-KH-HSX4SM5Z-LYiEs3ge.js → km-KH-HSX4SM5Z-gtwrLJn5.js} +1 -1
- package/dist/static/assets/{ko-KR-MTYHY66A-BzgMEtcU.js → ko-KR-MTYHY66A-BVcwVELq.js} +1 -1
- package/dist/static/assets/{ku-TR-6OUDTVRD-D1FtjbSs.js → ku-TR-6OUDTVRD-COvDJH7d.js} +1 -1
- package/dist/static/assets/{layout-CQWIZrii.js → layout-HBWJ9_zM.js} +1 -1
- package/dist/static/assets/{line-BryEd-Ku.js → line-DxrK6d-_.js} +1 -1
- package/dist/static/assets/{linear-SXhM57Mz.js → linear-TwnxNDQ0.js} +1 -1
- package/dist/static/assets/{lt-LT-XHIRWOB4-BPrhIUrC.js → lt-LT-XHIRWOB4-BxYB7tM6.js} +1 -1
- package/dist/static/assets/{lv-LV-5QDEKY6T-CXJse_rs.js → lv-LV-5QDEKY6T-CGURbwyW.js} +1 -1
- package/dist/static/assets/{mindmap-definition-fc14e90a-C8giDsiX.js → mindmap-definition-fc14e90a-CWJfWF5a.js} +1 -1
- package/dist/static/assets/{mr-IN-CRQNXWMA-Clab-bAO.js → mr-IN-CRQNXWMA-65brZPyQ.js} +1 -1
- package/dist/static/assets/{my-MM-5M5IBNSE-Dp08JzEw.js → my-MM-5M5IBNSE-DaCAmQNv.js} +1 -1
- package/dist/static/assets/{nb-NO-T6EIAALU-CiaXJA-W.js → nb-NO-T6EIAALU-BiSJhu0W.js} +1 -1
- package/dist/static/assets/{nl-NL-IS3SIHDZ-D-9KUTnx.js → nl-NL-IS3SIHDZ-D3rj4Vme.js} +1 -1
- package/dist/static/assets/{nn-NO-6E72VCQL-Bg5gsQ4h.js → nn-NO-6E72VCQL-rxxP7Cwf.js} +1 -1
- package/dist/static/assets/{oc-FR-POXYY2M6-G4iqllhL.js → oc-FR-POXYY2M6-6emd2NQX.js} +1 -1
- package/dist/static/assets/{pa-IN-N4M65BXN-l6Lk41uX.js → pa-IN-N4M65BXN-CXwT_z_m.js} +1 -1
- package/dist/static/assets/{pica-CtkejCRD.js → pica-CtyIOuSv.js} +1 -1
- package/dist/static/assets/{pieDiagram-8a3498a8-CG6gQ14H.js → pieDiagram-8a3498a8-Ccff4vHz.js} +1 -1
- package/dist/static/assets/{pl-PL-T2D74RX3-BZPbB8S3.js → pl-PL-T2D74RX3-DMx_ZmIC.js} +1 -1
- package/dist/static/assets/{pt-BR-5N22H2LF-CIC4vRyd.js → pt-BR-5N22H2LF-D9nz7afr.js} +1 -1
- package/dist/static/assets/{pt-PT-UZXXM6DQ-rkGTBEI0.js → pt-PT-UZXXM6DQ-DeFH3geX.js} +1 -1
- package/dist/static/assets/{quadrantDiagram-120e2f19-BL9eFu4M.js → quadrantDiagram-120e2f19-RF2ZGF1U.js} +1 -1
- package/dist/static/assets/{requirementDiagram-deff3bca-crxdUpVK.js → requirementDiagram-deff3bca-CvnD9QLg.js} +1 -1
- package/dist/static/assets/{ro-RO-JPDTUUEW-GDhpTKBO.js → ro-RO-JPDTUUEW-Cy22QAjW.js} +1 -1
- package/dist/static/assets/{ru-RU-B4JR7IUQ-BzHvMwjC.js → ru-RU-B4JR7IUQ-Cw2rH2nW.js} +1 -1
- package/dist/static/assets/{sankeyDiagram-04a897e0-bM05WQw3.js → sankeyDiagram-04a897e0-CWfRjMv0.js} +1 -1
- package/dist/static/assets/{sequenceDiagram-704730f1-DjknV0Qp.js → sequenceDiagram-704730f1-pAE8N_Ym.js} +1 -1
- package/dist/static/assets/{si-LK-N5RQ5JYF-D3P3TNDH.js → si-LK-N5RQ5JYF-DmXIZ3i0.js} +1 -1
- package/dist/static/assets/{sk-SK-C5VTKIMK-CM8yTXlZ.js → sk-SK-C5VTKIMK-CboHDpHd.js} +1 -1
- package/dist/static/assets/{sl-SI-NN7IZMDC-CxoGm3aM.js → sl-SI-NN7IZMDC-8XnCHmN4.js} +1 -1
- package/dist/static/assets/{stateDiagram-587899a1-CmWqIdF5.js → stateDiagram-587899a1-B4nuLay6.js} +1 -1
- package/dist/static/assets/{stateDiagram-v2-d93cdb3a-CgzNTiHW.js → stateDiagram-v2-d93cdb3a-9cMNxr_8.js} +1 -1
- package/dist/static/assets/{styles-6aaf32cf-CI4s6NIA.js → styles-6aaf32cf-CynMMkPq.js} +1 -1
- package/dist/static/assets/{styles-9a916d00-ZcdN6rtW.js → styles-9a916d00-BTmAnC2j.js} +1 -1
- package/dist/static/assets/{styles-c10674c1-ZFkD6ta7.js → styles-c10674c1-DQ_pTuGr.js} +1 -1
- package/dist/static/assets/{subset-shared.chunk-B4F4dEPr.js → subset-shared.chunk-DPGuz0R6.js} +1 -1
- package/dist/static/assets/{subset-worker.chunk-CmgaOgn3.js → subset-worker.chunk-CMiOYLER.js} +1 -1
- package/dist/static/assets/{sv-SE-XGPEYMSR-CnVZlafq.js → sv-SE-XGPEYMSR-ATPKfqXs.js} +1 -1
- package/dist/static/assets/{svgDrawCommon-08f97a94-DbN3OiBf.js → svgDrawCommon-08f97a94-qY4BnSWc.js} +1 -1
- package/dist/static/assets/{ta-IN-2NMHFXQM-Bl-eCjzL.js → ta-IN-2NMHFXQM-Ct5-Fher.js} +1 -1
- package/dist/static/assets/{th-TH-HPSO5L25-CNIGPjSR.js → th-TH-HPSO5L25-5a6bUzcv.js} +1 -1
- package/dist/static/assets/{timeline-definition-85554ec2-Cmr82Iq1.js → timeline-definition-85554ec2-Bgexbl3C.js} +1 -1
- package/dist/static/assets/{tr-TR-DEFEU3FU-Cx7HbGIA.js → tr-TR-DEFEU3FU-WsK7dnwA.js} +1 -1
- package/dist/static/assets/{uk-UA-QMV73CPH-BxcA50Ze.js → uk-UA-QMV73CPH-B_P61mnc.js} +1 -1
- package/dist/static/assets/{vi-VN-M7AON7JQ-BvGuRh0c.js → vi-VN-M7AON7JQ-qdiQcney.js} +1 -1
- package/dist/static/assets/{xychartDiagram-e933f94c-BcWLDEHu.js → xychartDiagram-e933f94c-vkQjFADD.js} +1 -1
- package/dist/static/assets/{zh-CN-LNUGB5OW-SQH8i3l5.js → zh-CN-LNUGB5OW-DQ3BQjPe.js} +1 -1
- package/dist/static/assets/{zh-HK-E62DVLB3-COWoLNur.js → zh-HK-E62DVLB3-Ynj07W9J.js} +1 -1
- package/dist/static/assets/{zh-TW-RAJ6MFWO-Cm5B69ig.js → zh-TW-RAJ6MFWO-DNkcasuo.js} +1 -1
- package/dist/static/index.html +1 -1
- package/package.json +5 -2
- package/dist/static/assets/channel-DjiT9qJ0.js +0 -1
- package/dist/static/assets/clone-nkJpLn4s.js +0 -1
- package/dist/static/assets/flowDiagram-v2-96b9c2cf-Dlfh8z7R.js +0 -1
- package/dist/static/assets/index-B6CTdFKu.js +0 -311
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { convertElementsToToon, processTextElement, processLineElement, processShapeElement, processImageElement, buildGroupsFromElements, } from '../toon-converter';
|
|
3
|
+
// Helper to create a minimal SceneElement
|
|
4
|
+
function createElement(overrides) {
|
|
5
|
+
return {
|
|
6
|
+
x: 0,
|
|
7
|
+
y: 0,
|
|
8
|
+
...overrides,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
describe('toon-converter', () => {
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// convertElementsToToon
|
|
14
|
+
// ============================================================================
|
|
15
|
+
describe('convertElementsToToon', () => {
|
|
16
|
+
it('should return empty result for empty array', () => {
|
|
17
|
+
const result = convertElementsToToon([], false);
|
|
18
|
+
expect(result).toEqual({
|
|
19
|
+
shapes: [],
|
|
20
|
+
lines: [],
|
|
21
|
+
labels: [],
|
|
22
|
+
texts: [],
|
|
23
|
+
images: [],
|
|
24
|
+
groups: [],
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
it('should process rectangle element into shapes array', () => {
|
|
28
|
+
const elements = [
|
|
29
|
+
createElement({
|
|
30
|
+
id: 'rect-1',
|
|
31
|
+
type: 'rectangle',
|
|
32
|
+
x: 100,
|
|
33
|
+
y: 200,
|
|
34
|
+
width: 150,
|
|
35
|
+
height: 80,
|
|
36
|
+
}),
|
|
37
|
+
];
|
|
38
|
+
const result = convertElementsToToon(elements, false);
|
|
39
|
+
expect(result.shapes).toHaveLength(1);
|
|
40
|
+
expect(result.shapes[0]).toEqual({
|
|
41
|
+
id: 'rect-1',
|
|
42
|
+
type: 'rectangle',
|
|
43
|
+
x: 100,
|
|
44
|
+
y: 200,
|
|
45
|
+
w: 150,
|
|
46
|
+
h: 80,
|
|
47
|
+
angle: 0,
|
|
48
|
+
labelId: null,
|
|
49
|
+
note: null,
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
it('should process ellipse element into shapes array', () => {
|
|
53
|
+
const elements = [
|
|
54
|
+
createElement({
|
|
55
|
+
id: 'ellipse-1',
|
|
56
|
+
type: 'ellipse',
|
|
57
|
+
x: 50,
|
|
58
|
+
y: 75,
|
|
59
|
+
width: 100,
|
|
60
|
+
height: 60,
|
|
61
|
+
}),
|
|
62
|
+
];
|
|
63
|
+
const result = convertElementsToToon(elements, false);
|
|
64
|
+
expect(result.shapes).toHaveLength(1);
|
|
65
|
+
expect(result.shapes[0].type).toBe('ellipse');
|
|
66
|
+
});
|
|
67
|
+
it('should process diamond element into shapes array', () => {
|
|
68
|
+
const elements = [
|
|
69
|
+
createElement({
|
|
70
|
+
id: 'diamond-1',
|
|
71
|
+
type: 'diamond',
|
|
72
|
+
x: 0,
|
|
73
|
+
y: 0,
|
|
74
|
+
width: 80,
|
|
75
|
+
height: 80,
|
|
76
|
+
}),
|
|
77
|
+
];
|
|
78
|
+
const result = convertElementsToToon(elements, false);
|
|
79
|
+
expect(result.shapes).toHaveLength(1);
|
|
80
|
+
expect(result.shapes[0].type).toBe('diamond');
|
|
81
|
+
});
|
|
82
|
+
it('should process standalone text into texts array', () => {
|
|
83
|
+
const elements = [
|
|
84
|
+
createElement({
|
|
85
|
+
id: 'text-1',
|
|
86
|
+
type: 'text',
|
|
87
|
+
x: 10,
|
|
88
|
+
y: 20,
|
|
89
|
+
text: 'Hello World',
|
|
90
|
+
width: 100,
|
|
91
|
+
height: 20,
|
|
92
|
+
}),
|
|
93
|
+
];
|
|
94
|
+
const result = convertElementsToToon(elements, false);
|
|
95
|
+
expect(result.texts).toHaveLength(1);
|
|
96
|
+
expect(result.texts[0]).toEqual({
|
|
97
|
+
id: 'text-1',
|
|
98
|
+
content: 'Hello World',
|
|
99
|
+
x: 10,
|
|
100
|
+
y: 20,
|
|
101
|
+
w: 100,
|
|
102
|
+
h: 20,
|
|
103
|
+
angle: 0,
|
|
104
|
+
note: null,
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
it('should process bound text (with containerId) into labels array', () => {
|
|
108
|
+
const elements = [
|
|
109
|
+
createElement({
|
|
110
|
+
id: 'label-1',
|
|
111
|
+
type: 'text',
|
|
112
|
+
x: 50,
|
|
113
|
+
y: 60,
|
|
114
|
+
text: 'Label Text',
|
|
115
|
+
width: 80,
|
|
116
|
+
height: 16,
|
|
117
|
+
containerId: 'rect-1',
|
|
118
|
+
}),
|
|
119
|
+
];
|
|
120
|
+
const result = convertElementsToToon(elements, false);
|
|
121
|
+
expect(result.labels).toHaveLength(1);
|
|
122
|
+
expect(result.labels[0]).toEqual({
|
|
123
|
+
id: 'label-1',
|
|
124
|
+
containerId: 'rect-1',
|
|
125
|
+
content: 'Label Text',
|
|
126
|
+
x: 50,
|
|
127
|
+
y: 60,
|
|
128
|
+
w: 80,
|
|
129
|
+
h: 16,
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
it('should process line element into lines array', () => {
|
|
133
|
+
const elements = [
|
|
134
|
+
createElement({
|
|
135
|
+
id: 'line-1',
|
|
136
|
+
type: 'line',
|
|
137
|
+
x: 0,
|
|
138
|
+
y: 0,
|
|
139
|
+
points: [[0, 0], [100, 50]],
|
|
140
|
+
}),
|
|
141
|
+
];
|
|
142
|
+
const result = convertElementsToToon(elements, false);
|
|
143
|
+
expect(result.lines).toHaveLength(1);
|
|
144
|
+
expect(result.lines[0]).toEqual({
|
|
145
|
+
id: 'line-1',
|
|
146
|
+
type: 'line',
|
|
147
|
+
x: 0,
|
|
148
|
+
y: 0,
|
|
149
|
+
endX: 100,
|
|
150
|
+
endY: 50,
|
|
151
|
+
via: null,
|
|
152
|
+
angle: 0,
|
|
153
|
+
note: null,
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
it('should process arrow element into lines array with type arrow', () => {
|
|
157
|
+
const elements = [
|
|
158
|
+
createElement({
|
|
159
|
+
id: 'arrow-1',
|
|
160
|
+
type: 'arrow',
|
|
161
|
+
x: 10,
|
|
162
|
+
y: 20,
|
|
163
|
+
points: [[0, 0], [80, 40]],
|
|
164
|
+
}),
|
|
165
|
+
];
|
|
166
|
+
const result = convertElementsToToon(elements, false);
|
|
167
|
+
expect(result.lines).toHaveLength(1);
|
|
168
|
+
expect(result.lines[0].type).toBe('arrow');
|
|
169
|
+
expect(result.lines[0].endX).toBe(90);
|
|
170
|
+
expect(result.lines[0].endY).toBe(60);
|
|
171
|
+
});
|
|
172
|
+
it('should process closed line (polygon) into shapes array', () => {
|
|
173
|
+
// A closed polygon: points form a triangle that closes back to origin (within 8px)
|
|
174
|
+
const elements = [
|
|
175
|
+
createElement({
|
|
176
|
+
id: 'polygon-1',
|
|
177
|
+
type: 'line',
|
|
178
|
+
x: 100,
|
|
179
|
+
y: 100,
|
|
180
|
+
points: [[0, 0], [50, 0], [25, 40], [0, 0]], // closed triangle
|
|
181
|
+
}),
|
|
182
|
+
];
|
|
183
|
+
const result = convertElementsToToon(elements, false);
|
|
184
|
+
expect(result.shapes).toHaveLength(1);
|
|
185
|
+
expect(result.shapes[0].type).toBe('polygon');
|
|
186
|
+
expect(result.shapes[0].x).toBe(100);
|
|
187
|
+
expect(result.shapes[0].y).toBe(100);
|
|
188
|
+
expect(result.shapes[0].w).toBe(50);
|
|
189
|
+
expect(result.shapes[0].h).toBe(40);
|
|
190
|
+
});
|
|
191
|
+
it('should process image element into images array', () => {
|
|
192
|
+
const elements = [
|
|
193
|
+
createElement({
|
|
194
|
+
id: 'img-1',
|
|
195
|
+
type: 'image',
|
|
196
|
+
x: 200,
|
|
197
|
+
y: 300,
|
|
198
|
+
width: 400,
|
|
199
|
+
height: 300,
|
|
200
|
+
fileId: 'file-abc123',
|
|
201
|
+
}),
|
|
202
|
+
];
|
|
203
|
+
const result = convertElementsToToon(elements, false);
|
|
204
|
+
expect(result.images).toHaveLength(1);
|
|
205
|
+
expect(result.images[0]).toEqual({
|
|
206
|
+
id: 'img-1',
|
|
207
|
+
x: 200,
|
|
208
|
+
y: 300,
|
|
209
|
+
w: 400,
|
|
210
|
+
h: 300,
|
|
211
|
+
angle: 0,
|
|
212
|
+
fileId: 'file-abc123',
|
|
213
|
+
note: null,
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
it('should collect groups from elements with groupIds', () => {
|
|
217
|
+
const elements = [
|
|
218
|
+
createElement({
|
|
219
|
+
id: 'el-1',
|
|
220
|
+
type: 'rectangle',
|
|
221
|
+
x: 0,
|
|
222
|
+
y: 0,
|
|
223
|
+
groupIds: ['group-A'],
|
|
224
|
+
}),
|
|
225
|
+
createElement({
|
|
226
|
+
id: 'el-2',
|
|
227
|
+
type: 'rectangle',
|
|
228
|
+
x: 100,
|
|
229
|
+
y: 0,
|
|
230
|
+
groupIds: ['group-A'],
|
|
231
|
+
}),
|
|
232
|
+
createElement({
|
|
233
|
+
id: 'el-3',
|
|
234
|
+
type: 'rectangle',
|
|
235
|
+
x: 200,
|
|
236
|
+
y: 0,
|
|
237
|
+
groupIds: ['group-B'],
|
|
238
|
+
}),
|
|
239
|
+
];
|
|
240
|
+
const result = convertElementsToToon(elements, false);
|
|
241
|
+
expect(result.groups).toHaveLength(2);
|
|
242
|
+
expect(result.groups).toContainEqual({ id: 'group-A', elementIds: 'el-1,el-2' });
|
|
243
|
+
expect(result.groups).toContainEqual({ id: 'group-B', elementIds: 'el-3' });
|
|
244
|
+
});
|
|
245
|
+
it('should include stroke/bg when withStyle=true', () => {
|
|
246
|
+
const elements = [
|
|
247
|
+
createElement({
|
|
248
|
+
id: 'rect-1',
|
|
249
|
+
type: 'rectangle',
|
|
250
|
+
x: 0,
|
|
251
|
+
y: 0,
|
|
252
|
+
strokeColor: '#ff0000',
|
|
253
|
+
backgroundColor: '#00ff00',
|
|
254
|
+
}),
|
|
255
|
+
];
|
|
256
|
+
const result = convertElementsToToon(elements, true);
|
|
257
|
+
expect(result.shapes[0].stroke).toBe('#ff0000');
|
|
258
|
+
expect(result.shapes[0].bg).toBe('#00ff00');
|
|
259
|
+
});
|
|
260
|
+
it('should not include stroke/bg when withStyle=false', () => {
|
|
261
|
+
const elements = [
|
|
262
|
+
createElement({
|
|
263
|
+
id: 'rect-1',
|
|
264
|
+
type: 'rectangle',
|
|
265
|
+
x: 0,
|
|
266
|
+
y: 0,
|
|
267
|
+
strokeColor: '#ff0000',
|
|
268
|
+
backgroundColor: '#00ff00',
|
|
269
|
+
}),
|
|
270
|
+
];
|
|
271
|
+
const result = convertElementsToToon(elements, false);
|
|
272
|
+
expect(result.shapes[0]).not.toHaveProperty('stroke');
|
|
273
|
+
expect(result.shapes[0]).not.toHaveProperty('bg');
|
|
274
|
+
});
|
|
275
|
+
it('should convert angle from radians to degrees', () => {
|
|
276
|
+
const elements = [
|
|
277
|
+
createElement({
|
|
278
|
+
id: 'rect-1',
|
|
279
|
+
type: 'rectangle',
|
|
280
|
+
x: 0,
|
|
281
|
+
y: 0,
|
|
282
|
+
angle: Math.PI / 2, // 90 degrees
|
|
283
|
+
}),
|
|
284
|
+
];
|
|
285
|
+
const result = convertElementsToToon(elements, false);
|
|
286
|
+
expect(result.shapes[0].angle).toBe(90);
|
|
287
|
+
});
|
|
288
|
+
it('should round coordinates to integers', () => {
|
|
289
|
+
const elements = [
|
|
290
|
+
createElement({
|
|
291
|
+
id: 'rect-1',
|
|
292
|
+
type: 'rectangle',
|
|
293
|
+
x: 100.7,
|
|
294
|
+
y: 200.3,
|
|
295
|
+
width: 150.9,
|
|
296
|
+
height: 80.1,
|
|
297
|
+
}),
|
|
298
|
+
];
|
|
299
|
+
const result = convertElementsToToon(elements, false);
|
|
300
|
+
expect(result.shapes[0].x).toBe(101);
|
|
301
|
+
expect(result.shapes[0].y).toBe(200);
|
|
302
|
+
expect(result.shapes[0].w).toBe(151);
|
|
303
|
+
expect(result.shapes[0].h).toBe(80);
|
|
304
|
+
});
|
|
305
|
+
it('should extract note from customData', () => {
|
|
306
|
+
const elements = [
|
|
307
|
+
createElement({
|
|
308
|
+
id: 'rect-1',
|
|
309
|
+
type: 'rectangle',
|
|
310
|
+
x: 0,
|
|
311
|
+
y: 0,
|
|
312
|
+
customData: { note: 'This is a note' },
|
|
313
|
+
}),
|
|
314
|
+
];
|
|
315
|
+
const result = convertElementsToToon(elements, false);
|
|
316
|
+
expect(result.shapes[0].note).toBe('This is a note');
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
// ============================================================================
|
|
320
|
+
// processTextElement
|
|
321
|
+
// ============================================================================
|
|
322
|
+
describe('processTextElement', () => {
|
|
323
|
+
it('should return label type when containerId is set', () => {
|
|
324
|
+
const el = createElement({
|
|
325
|
+
id: 'text-1',
|
|
326
|
+
type: 'text',
|
|
327
|
+
x: 10,
|
|
328
|
+
y: 20,
|
|
329
|
+
text: 'Label',
|
|
330
|
+
containerId: 'rect-1',
|
|
331
|
+
});
|
|
332
|
+
const result = processTextElement(el, false);
|
|
333
|
+
expect(result.type).toBe('label');
|
|
334
|
+
expect(result.data).toEqual({
|
|
335
|
+
id: 'text-1',
|
|
336
|
+
containerId: 'rect-1',
|
|
337
|
+
content: 'Label',
|
|
338
|
+
x: 10,
|
|
339
|
+
y: 20,
|
|
340
|
+
w: null,
|
|
341
|
+
h: null,
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
it('should return text type when containerId is not set', () => {
|
|
345
|
+
const el = createElement({
|
|
346
|
+
id: 'text-1',
|
|
347
|
+
type: 'text',
|
|
348
|
+
x: 10,
|
|
349
|
+
y: 20,
|
|
350
|
+
text: 'Standalone',
|
|
351
|
+
});
|
|
352
|
+
const result = processTextElement(el, false);
|
|
353
|
+
expect(result.type).toBe('text');
|
|
354
|
+
expect(result.data.content).toBe('Standalone');
|
|
355
|
+
});
|
|
356
|
+
it('should include stroke when withStyle=true for text', () => {
|
|
357
|
+
const el = createElement({
|
|
358
|
+
id: 'text-1',
|
|
359
|
+
type: 'text',
|
|
360
|
+
x: 0,
|
|
361
|
+
y: 0,
|
|
362
|
+
text: 'Test',
|
|
363
|
+
strokeColor: '#0000ff',
|
|
364
|
+
});
|
|
365
|
+
const result = processTextElement(el, true);
|
|
366
|
+
expect(result.type).toBe('text');
|
|
367
|
+
expect(result.data.stroke).toBe('#0000ff');
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
// ============================================================================
|
|
371
|
+
// processLineElement
|
|
372
|
+
// ============================================================================
|
|
373
|
+
describe('processLineElement', () => {
|
|
374
|
+
it('should return shape type for closed polygon', () => {
|
|
375
|
+
const el = createElement({
|
|
376
|
+
id: 'line-1',
|
|
377
|
+
type: 'line',
|
|
378
|
+
x: 0,
|
|
379
|
+
y: 0,
|
|
380
|
+
points: [[0, 0], [100, 0], [50, 80], [0, 0]],
|
|
381
|
+
});
|
|
382
|
+
const result = processLineElement(el, false);
|
|
383
|
+
expect(result.type).toBe('shape');
|
|
384
|
+
expect(result.data.type).toBe('polygon');
|
|
385
|
+
});
|
|
386
|
+
it('should return line type for open line', () => {
|
|
387
|
+
const el = createElement({
|
|
388
|
+
id: 'line-1',
|
|
389
|
+
type: 'line',
|
|
390
|
+
x: 0,
|
|
391
|
+
y: 0,
|
|
392
|
+
points: [[0, 0], [100, 50]],
|
|
393
|
+
});
|
|
394
|
+
const result = processLineElement(el, false);
|
|
395
|
+
expect(result.type).toBe('line');
|
|
396
|
+
});
|
|
397
|
+
it('should return line type for arrow', () => {
|
|
398
|
+
const el = createElement({
|
|
399
|
+
id: 'arrow-1',
|
|
400
|
+
type: 'arrow',
|
|
401
|
+
x: 0,
|
|
402
|
+
y: 0,
|
|
403
|
+
points: [[0, 0], [100, 50]],
|
|
404
|
+
});
|
|
405
|
+
const result = processLineElement(el, false);
|
|
406
|
+
expect(result.type).toBe('line');
|
|
407
|
+
expect(result.data.type).toBe('arrow');
|
|
408
|
+
});
|
|
409
|
+
it('should calculate via points for multi-point lines', () => {
|
|
410
|
+
const el = createElement({
|
|
411
|
+
id: 'line-1',
|
|
412
|
+
type: 'line',
|
|
413
|
+
x: 10,
|
|
414
|
+
y: 20,
|
|
415
|
+
points: [[0, 0], [50, 25], [100, 50]],
|
|
416
|
+
});
|
|
417
|
+
const result = processLineElement(el, false);
|
|
418
|
+
expect(result.type).toBe('line');
|
|
419
|
+
expect(result.data.via).toBe('60,45');
|
|
420
|
+
});
|
|
421
|
+
it('should return null via for two-point lines', () => {
|
|
422
|
+
const el = createElement({
|
|
423
|
+
id: 'line-1',
|
|
424
|
+
type: 'line',
|
|
425
|
+
x: 0,
|
|
426
|
+
y: 0,
|
|
427
|
+
points: [[0, 0], [100, 50]],
|
|
428
|
+
});
|
|
429
|
+
const result = processLineElement(el, false);
|
|
430
|
+
expect(result.data.via).toBeNull();
|
|
431
|
+
});
|
|
432
|
+
it('should handle line with near-closed points (within 8px) as polygon', () => {
|
|
433
|
+
const el = createElement({
|
|
434
|
+
id: 'line-1',
|
|
435
|
+
type: 'line',
|
|
436
|
+
x: 0,
|
|
437
|
+
y: 0,
|
|
438
|
+
points: [[0, 0], [100, 0], [50, 80], [5, 5]], // last point within 8px of first
|
|
439
|
+
});
|
|
440
|
+
const result = processLineElement(el, false);
|
|
441
|
+
expect(result.type).toBe('shape');
|
|
442
|
+
});
|
|
443
|
+
it('should handle line with points not close enough as line', () => {
|
|
444
|
+
const el = createElement({
|
|
445
|
+
id: 'line-1',
|
|
446
|
+
type: 'line',
|
|
447
|
+
x: 0,
|
|
448
|
+
y: 0,
|
|
449
|
+
points: [[0, 0], [100, 0], [50, 80], [10, 10]], // last point > 8px from first
|
|
450
|
+
});
|
|
451
|
+
const result = processLineElement(el, false);
|
|
452
|
+
expect(result.type).toBe('line');
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
// ============================================================================
|
|
456
|
+
// processShapeElement
|
|
457
|
+
// ============================================================================
|
|
458
|
+
describe('processShapeElement', () => {
|
|
459
|
+
it('should process basic shape', () => {
|
|
460
|
+
const el = createElement({
|
|
461
|
+
id: 'rect-1',
|
|
462
|
+
type: 'rectangle',
|
|
463
|
+
x: 100,
|
|
464
|
+
y: 200,
|
|
465
|
+
width: 150,
|
|
466
|
+
height: 80,
|
|
467
|
+
});
|
|
468
|
+
const result = processShapeElement(el, false);
|
|
469
|
+
expect(result).toEqual({
|
|
470
|
+
id: 'rect-1',
|
|
471
|
+
type: 'rectangle',
|
|
472
|
+
x: 100,
|
|
473
|
+
y: 200,
|
|
474
|
+
w: 150,
|
|
475
|
+
h: 80,
|
|
476
|
+
angle: 0,
|
|
477
|
+
labelId: null,
|
|
478
|
+
note: null,
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
it('should include labelId when shape has bound text', () => {
|
|
482
|
+
const el = createElement({
|
|
483
|
+
id: 'rect-1',
|
|
484
|
+
type: 'rectangle',
|
|
485
|
+
x: 0,
|
|
486
|
+
y: 0,
|
|
487
|
+
boundElements: [{ id: 'text-1', type: 'text' }],
|
|
488
|
+
});
|
|
489
|
+
const result = processShapeElement(el, false);
|
|
490
|
+
expect(result.labelId).toBe('text-1');
|
|
491
|
+
});
|
|
492
|
+
it('should not include labelId for bound arrows', () => {
|
|
493
|
+
const el = createElement({
|
|
494
|
+
id: 'rect-1',
|
|
495
|
+
type: 'rectangle',
|
|
496
|
+
x: 0,
|
|
497
|
+
y: 0,
|
|
498
|
+
boundElements: [{ id: 'arrow-1', type: 'arrow' }],
|
|
499
|
+
});
|
|
500
|
+
const result = processShapeElement(el, false);
|
|
501
|
+
expect(result.labelId).toBeNull();
|
|
502
|
+
});
|
|
503
|
+
it('should include stroke/bg when withStyle=true', () => {
|
|
504
|
+
const el = createElement({
|
|
505
|
+
id: 'rect-1',
|
|
506
|
+
type: 'rectangle',
|
|
507
|
+
x: 0,
|
|
508
|
+
y: 0,
|
|
509
|
+
strokeColor: '#123456',
|
|
510
|
+
backgroundColor: '#abcdef',
|
|
511
|
+
});
|
|
512
|
+
const result = processShapeElement(el, true);
|
|
513
|
+
expect(result.stroke).toBe('#123456');
|
|
514
|
+
expect(result.bg).toBe('#abcdef');
|
|
515
|
+
});
|
|
516
|
+
});
|
|
517
|
+
// ============================================================================
|
|
518
|
+
// processImageElement
|
|
519
|
+
// ============================================================================
|
|
520
|
+
describe('processImageElement', () => {
|
|
521
|
+
it('should process image element', () => {
|
|
522
|
+
const el = createElement({
|
|
523
|
+
id: 'img-1',
|
|
524
|
+
type: 'image',
|
|
525
|
+
x: 100,
|
|
526
|
+
y: 200,
|
|
527
|
+
width: 400,
|
|
528
|
+
height: 300,
|
|
529
|
+
fileId: 'file-123',
|
|
530
|
+
});
|
|
531
|
+
const result = processImageElement(el);
|
|
532
|
+
expect(result).toEqual({
|
|
533
|
+
id: 'img-1',
|
|
534
|
+
x: 100,
|
|
535
|
+
y: 200,
|
|
536
|
+
w: 400,
|
|
537
|
+
h: 300,
|
|
538
|
+
angle: 0,
|
|
539
|
+
fileId: 'file-123',
|
|
540
|
+
note: null,
|
|
541
|
+
});
|
|
542
|
+
});
|
|
543
|
+
it('should handle missing fileId', () => {
|
|
544
|
+
const el = createElement({
|
|
545
|
+
id: 'img-1',
|
|
546
|
+
type: 'image',
|
|
547
|
+
x: 0,
|
|
548
|
+
y: 0,
|
|
549
|
+
});
|
|
550
|
+
const result = processImageElement(el);
|
|
551
|
+
expect(result.fileId).toBeNull();
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
// ============================================================================
|
|
555
|
+
// buildGroupsFromElements
|
|
556
|
+
// ============================================================================
|
|
557
|
+
describe('buildGroupsFromElements', () => {
|
|
558
|
+
it('should return empty array when no groups', () => {
|
|
559
|
+
const elements = [
|
|
560
|
+
createElement({ id: 'el-1', type: 'rectangle', x: 0, y: 0 }),
|
|
561
|
+
];
|
|
562
|
+
const result = buildGroupsFromElements(elements);
|
|
563
|
+
expect(result).toEqual([]);
|
|
564
|
+
});
|
|
565
|
+
it('should collect elements into groups', () => {
|
|
566
|
+
const elements = [
|
|
567
|
+
createElement({ id: 'el-1', type: 'rectangle', x: 0, y: 0, groupIds: ['g1'] }),
|
|
568
|
+
createElement({ id: 'el-2', type: 'rectangle', x: 0, y: 0, groupIds: ['g1'] }),
|
|
569
|
+
createElement({ id: 'el-3', type: 'rectangle', x: 0, y: 0, groupIds: ['g2'] }),
|
|
570
|
+
];
|
|
571
|
+
const result = buildGroupsFromElements(elements);
|
|
572
|
+
expect(result).toHaveLength(2);
|
|
573
|
+
expect(result).toContainEqual({ id: 'g1', elementIds: 'el-1,el-2' });
|
|
574
|
+
expect(result).toContainEqual({ id: 'g2', elementIds: 'el-3' });
|
|
575
|
+
});
|
|
576
|
+
it('should handle elements in multiple groups', () => {
|
|
577
|
+
const elements = [
|
|
578
|
+
createElement({ id: 'el-1', type: 'rectangle', x: 0, y: 0, groupIds: ['g1', 'g2'] }),
|
|
579
|
+
];
|
|
580
|
+
const result = buildGroupsFromElements(elements);
|
|
581
|
+
expect(result).toHaveLength(2);
|
|
582
|
+
expect(result).toContainEqual({ id: 'g1', elementIds: 'el-1' });
|
|
583
|
+
expect(result).toContainEqual({ id: 'g2', elementIds: 'el-1' });
|
|
584
|
+
});
|
|
585
|
+
});
|
|
586
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { generateId } from '../ws-client';
|
|
3
|
+
describe('generateId', () => {
|
|
4
|
+
it('should return a string with reasonable length', () => {
|
|
5
|
+
const id = generateId();
|
|
6
|
+
expect(typeof id).toBe('string');
|
|
7
|
+
// substring(2, 15) produces 11-13 characters depending on random value
|
|
8
|
+
expect(id.length).toBeGreaterThanOrEqual(10);
|
|
9
|
+
expect(id.length).toBeLessThanOrEqual(13);
|
|
10
|
+
});
|
|
11
|
+
it('should return unique IDs', () => {
|
|
12
|
+
const ids = new Set();
|
|
13
|
+
for (let i = 0; i < 100; i++) {
|
|
14
|
+
ids.add(generateId());
|
|
15
|
+
}
|
|
16
|
+
expect(ids.size).toBe(100);
|
|
17
|
+
});
|
|
18
|
+
it('should only contain alphanumeric characters', () => {
|
|
19
|
+
const id = generateId();
|
|
20
|
+
expect(id).toMatch(/^[a-z0-9]+$/);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { extname } from 'node:path';
|
|
3
|
+
// Max image file size (2MB) - localStorage has ~5-10MB limit and Base64 adds ~33%
|
|
4
|
+
export const MAX_IMAGE_SIZE = 2 * 1024 * 1024;
|
|
5
|
+
// Helper: Get MIME type from file extension
|
|
6
|
+
export function getMimeType(filepath) {
|
|
7
|
+
const ext = extname(filepath).toLowerCase();
|
|
8
|
+
const mimeTypes = {
|
|
9
|
+
'.png': 'image/png',
|
|
10
|
+
'.jpg': 'image/jpeg',
|
|
11
|
+
'.jpeg': 'image/jpeg',
|
|
12
|
+
'.gif': 'image/gif',
|
|
13
|
+
'.svg': 'image/svg+xml',
|
|
14
|
+
'.webp': 'image/webp',
|
|
15
|
+
};
|
|
16
|
+
return mimeTypes[ext] || null;
|
|
17
|
+
}
|
|
18
|
+
// Helper: Generate FileId from file content (SHA-1 hash)
|
|
19
|
+
export function generateFileId(buffer) {
|
|
20
|
+
return createHash('sha1').update(buffer).digest('hex');
|
|
21
|
+
}
|
package/dist/lib/protocol.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface ListCanvasesResponse {
|
|
|
14
14
|
id: string;
|
|
15
15
|
success: boolean;
|
|
16
16
|
activeCanvasId?: string;
|
|
17
|
+
agentActiveCanvasId?: string;
|
|
17
18
|
canvases?: CanvasMetadata[];
|
|
18
19
|
error?: string;
|
|
19
20
|
}
|
|
@@ -161,6 +162,28 @@ export interface AddPolygonResponse {
|
|
|
161
162
|
elementId?: string;
|
|
162
163
|
error?: string;
|
|
163
164
|
}
|
|
165
|
+
export interface AddImageParams {
|
|
166
|
+
x: number;
|
|
167
|
+
y: number;
|
|
168
|
+
width?: number;
|
|
169
|
+
height?: number;
|
|
170
|
+
dataUrl: string;
|
|
171
|
+
mimeType: string;
|
|
172
|
+
fileId: string;
|
|
173
|
+
customData?: Record<string, unknown>;
|
|
174
|
+
}
|
|
175
|
+
export interface AddImageResponse {
|
|
176
|
+
type: 'addImageResult';
|
|
177
|
+
id: string;
|
|
178
|
+
success: boolean;
|
|
179
|
+
elementId?: string;
|
|
180
|
+
fileId?: string;
|
|
181
|
+
x?: number;
|
|
182
|
+
y?: number;
|
|
183
|
+
width?: number;
|
|
184
|
+
height?: number;
|
|
185
|
+
error?: string;
|
|
186
|
+
}
|
|
164
187
|
export interface DeleteElementsParams {
|
|
165
188
|
elementIds: string[];
|
|
166
189
|
}
|
|
@@ -249,6 +272,7 @@ export interface SceneElement {
|
|
|
249
272
|
points?: number[][];
|
|
250
273
|
startArrowhead?: string | null;
|
|
251
274
|
endArrowhead?: string | null;
|
|
275
|
+
fileId?: string | null;
|
|
252
276
|
customData?: Record<string, unknown>;
|
|
253
277
|
}
|
|
254
278
|
export interface ReadSceneResponse {
|