@agent-canvas/cli 0.1.2 → 0.2.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.
Files changed (108) hide show
  1. package/dist/commands/start.d.ts +0 -2
  2. package/dist/commands/start.js +15 -112
  3. package/dist/index.js +162 -27
  4. package/dist/lib/protocol.d.ts +12 -0
  5. package/dist/lib/ws-client.d.ts +0 -1
  6. package/dist/lib/ws-client.js +0 -18
  7. package/dist/server/index.d.ts +1 -0
  8. package/dist/server/index.js +37 -0
  9. package/dist/static/assets/{ar-SA-G6X2FPQ2-DyNcQEG6.js → ar-SA-G6X2FPQ2-DH2n1f9Q.js} +1 -1
  10. package/dist/static/assets/{arc-BbhT3kpR.js → arc-DSwghfDE.js} +1 -1
  11. package/dist/static/assets/{az-AZ-76LH7QW2-6FRN2HRu.js → az-AZ-76LH7QW2-Cxm-bt5B.js} +1 -1
  12. package/dist/static/assets/{bg-BG-XCXSNQG7-BAatOQKg.js → bg-BG-XCXSNQG7-Cj0Sip-L.js} +1 -1
  13. package/dist/static/assets/{blockDiagram-38ab4fdb-CWMvXzur.js → blockDiagram-38ab4fdb-CvagfZR2.js} +1 -1
  14. package/dist/static/assets/{bn-BD-2XOGV67Q-CS2Tej4d.js → bn-BD-2XOGV67Q-BNoWI4Vi.js} +1 -1
  15. package/dist/static/assets/{c4Diagram-3d4e48cf-C7AOUEVw.js → c4Diagram-3d4e48cf-CF5c7HOc.js} +1 -1
  16. package/dist/static/assets/{ca-ES-6MX7JW3Y-CrRON-ta.js → ca-ES-6MX7JW3Y-Buz-sA9F.js} +1 -1
  17. package/dist/static/assets/channel-Bh1oWGVH.js +1 -0
  18. package/dist/static/assets/{classDiagram-70f12bd4-Dy_Zdc-c.js → classDiagram-70f12bd4-DEy5qx_x.js} +1 -1
  19. package/dist/static/assets/{classDiagram-v2-f2320105-CjSxB_UF.js → classDiagram-v2-f2320105-BWJFthkP.js} +1 -1
  20. package/dist/static/assets/clone-BeiLdjSo.js +1 -0
  21. package/dist/static/assets/{createText-2e5e7dd3-CREXrDg4.js → createText-2e5e7dd3-DT0MMTMt.js} +1 -1
  22. package/dist/static/assets/{cs-CZ-2BRQDIVT-BLn2zwD5.js → cs-CZ-2BRQDIVT-DkiVtR_5.js} +1 -1
  23. package/dist/static/assets/{da-DK-5WZEPLOC-DTfqWS1m.js → da-DK-5WZEPLOC-DoVTEeYq.js} +1 -1
  24. package/dist/static/assets/{de-DE-XR44H4JA-CBm33ToK.js → de-DE-XR44H4JA-D02LO9rN.js} +1 -1
  25. package/dist/static/assets/{edges-e0da2a9e-XV6mdrlk.js → edges-e0da2a9e-DSJ5Cn_i.js} +1 -1
  26. package/dist/static/assets/{el-GR-BZB4AONW-CovIUFtq.js → el-GR-BZB4AONW-BAtInWEI.js} +1 -1
  27. package/dist/static/assets/{erDiagram-9861fffd-BHiuXMbj.js → erDiagram-9861fffd-9K08ZFDe.js} +1 -1
  28. package/dist/static/assets/{es-ES-U4NZUMDT-xhwf3Gvs.js → es-ES-U4NZUMDT-cPh2cdP7.js} +1 -1
  29. package/dist/static/assets/{eu-ES-A7QVB2H4-CY-s9u74.js → eu-ES-A7QVB2H4-g1PzZ-pb.js} +1 -1
  30. package/dist/static/assets/{fa-IR-HGAKTJCU-CqiYv3_M.js → fa-IR-HGAKTJCU-Dotngoci.js} +1 -1
  31. package/dist/static/assets/{fi-FI-Z5N7JZ37-ByFmziTt.js → fi-FI-Z5N7JZ37-CMkSfDt1.js} +1 -1
  32. package/dist/static/assets/{flowDb-956e92f1--YaaQ1az.js → flowDb-956e92f1-BF28mVE5.js} +1 -1
  33. package/dist/static/assets/{flowDiagram-66a62f08-CiStLJ_j.js → flowDiagram-66a62f08-CBq8pz-r.js} +1 -1
  34. package/dist/static/assets/flowDiagram-v2-96b9c2cf-Dja0Izfz.js +1 -0
  35. package/dist/static/assets/{flowchart-elk-definition-4a651766-iZZsNsFB.js → flowchart-elk-definition-4a651766-Cz5NLAVQ.js} +1 -1
  36. package/dist/static/assets/{fr-FR-RHASNOE6-BDM0Orh-.js → fr-FR-RHASNOE6--ee0QaBP.js} +1 -1
  37. package/dist/static/assets/{ganttDiagram-c361ad54--D8Gb02S.js → ganttDiagram-c361ad54-B41GjRL_.js} +1 -1
  38. package/dist/static/assets/{gitGraphDiagram-72cf32ee-BP9V2H_i.js → gitGraphDiagram-72cf32ee-Dt6aWBpu.js} +1 -1
  39. package/dist/static/assets/{gl-ES-HMX3MZ6V-YzWSA_U5.js → gl-ES-HMX3MZ6V-BANNW5oV.js} +1 -1
  40. package/dist/static/assets/{graph-CDY5v4hg.js → graph-BViLJDpH.js} +1 -1
  41. package/dist/static/assets/{he-IL-6SHJWFNN-DGhuVKeh.js → he-IL-6SHJWFNN-BIQP0u6P.js} +1 -1
  42. package/dist/static/assets/{hi-IN-IWLTKZ5I-Bf2OxFpo.js → hi-IN-IWLTKZ5I-BMNE591o.js} +1 -1
  43. package/dist/static/assets/{hu-HU-A5ZG7DT2-Bgm6MfVV.js → hu-HU-A5ZG7DT2-B59Uuq-D.js} +1 -1
  44. package/dist/static/assets/{id-ID-SAP4L64H-Be4CWRpp.js → id-ID-SAP4L64H-DWKrSOEI.js} +1 -1
  45. package/dist/static/assets/{index-3862675e-DO7_p44e.js → index-3862675e-rxi3xVjP.js} +1 -1
  46. package/dist/static/assets/{index-M3ercR-c.js → index-ChPLID-A.js} +4 -4
  47. package/dist/static/assets/{index-D9K5p6xj.js → index-Dq9tezYn.js} +50 -50
  48. package/dist/static/assets/{infoDiagram-f8f76790-R_fZ7_IN.js → infoDiagram-f8f76790-gXSChgmZ.js} +1 -1
  49. package/dist/static/assets/{it-IT-JPQ66NNP-BNcyVwD2.js → it-IT-JPQ66NNP-5YgGFtR8.js} +1 -1
  50. package/dist/static/assets/{ja-JP-DBVTYXUO-CLiGkSXX.js → ja-JP-DBVTYXUO-CQe8gSy1.js} +1 -1
  51. package/dist/static/assets/{journeyDiagram-49397b02-DXa6Hio7.js → journeyDiagram-49397b02-D7dTRcMS.js} +1 -1
  52. package/dist/static/assets/{kaa-6HZHGXH3-CtCiUe72.js → kaa-6HZHGXH3-qgzLbYWr.js} +1 -1
  53. package/dist/static/assets/{kab-KAB-ZGHBKWFO-jaWR3c0c.js → kab-KAB-ZGHBKWFO-he6LsMVd.js} +1 -1
  54. package/dist/static/assets/{kk-KZ-P5N5QNE5-Dz3mQQBP.js → kk-KZ-P5N5QNE5-BAGrvKw-.js} +1 -1
  55. package/dist/static/assets/{km-KH-HSX4SM5Z-CQlDP0cg.js → km-KH-HSX4SM5Z-YQK2wUqV.js} +1 -1
  56. package/dist/static/assets/{ko-KR-MTYHY66A-SADoSfWj.js → ko-KR-MTYHY66A-DgUXWHhI.js} +1 -1
  57. package/dist/static/assets/{ku-TR-6OUDTVRD-B27fDZ8n.js → ku-TR-6OUDTVRD-CllKA2Q8.js} +1 -1
  58. package/dist/static/assets/{layout-DFkOIywR.js → layout-PtECFbDA.js} +1 -1
  59. package/dist/static/assets/{line-BkGGz1Gi.js → line--4PuTgFs.js} +1 -1
  60. package/dist/static/assets/{linear-bj4xbXj8.js → linear-mpRFIrCY.js} +1 -1
  61. package/dist/static/assets/{lt-LT-XHIRWOB4-CoJ8AQlv.js → lt-LT-XHIRWOB4-Lc06xM_t.js} +1 -1
  62. package/dist/static/assets/{lv-LV-5QDEKY6T-BNu6FZuj.js → lv-LV-5QDEKY6T-DjPQsp57.js} +1 -1
  63. package/dist/static/assets/{mindmap-definition-fc14e90a-DmZ8jzp5.js → mindmap-definition-fc14e90a-DVb-paYk.js} +1 -1
  64. package/dist/static/assets/{mr-IN-CRQNXWMA-BvurhLbm.js → mr-IN-CRQNXWMA-C_oyBWpj.js} +1 -1
  65. package/dist/static/assets/{my-MM-5M5IBNSE-mRTevT97.js → my-MM-5M5IBNSE-5e9rxU8V.js} +1 -1
  66. package/dist/static/assets/{nb-NO-T6EIAALU-BcOTaqeP.js → nb-NO-T6EIAALU-DiatY1a3.js} +1 -1
  67. package/dist/static/assets/{nl-NL-IS3SIHDZ-Bd8Xm5YF.js → nl-NL-IS3SIHDZ-DYho2XpO.js} +1 -1
  68. package/dist/static/assets/{nn-NO-6E72VCQL-DEvQB3Jv.js → nn-NO-6E72VCQL--exG3tqa.js} +1 -1
  69. package/dist/static/assets/{oc-FR-POXYY2M6--hbNWw9E.js → oc-FR-POXYY2M6-D5YMexgJ.js} +1 -1
  70. package/dist/static/assets/{pa-IN-N4M65BXN-pIQoan9P.js → pa-IN-N4M65BXN-B-QpbuPU.js} +1 -1
  71. package/dist/static/assets/{pica-C10VOhpQ.js → pica-CYGCxyFx.js} +1 -1
  72. package/dist/static/assets/{pieDiagram-8a3498a8-CiEHu0OP.js → pieDiagram-8a3498a8-BIb0XBmP.js} +1 -1
  73. package/dist/static/assets/{pl-PL-T2D74RX3-DFiZqgo0.js → pl-PL-T2D74RX3-CyQ9ScCZ.js} +1 -1
  74. package/dist/static/assets/{pt-BR-5N22H2LF-Cc0xlz4q.js → pt-BR-5N22H2LF-C2fBatBI.js} +1 -1
  75. package/dist/static/assets/{pt-PT-UZXXM6DQ-B3zd49KM.js → pt-PT-UZXXM6DQ-lSWbA8ta.js} +1 -1
  76. package/dist/static/assets/{quadrantDiagram-120e2f19-DKTSsmZy.js → quadrantDiagram-120e2f19-B0IUKZHd.js} +1 -1
  77. package/dist/static/assets/{requirementDiagram-deff3bca-DQOiJiwY.js → requirementDiagram-deff3bca-DQtENapr.js} +1 -1
  78. package/dist/static/assets/{ro-RO-JPDTUUEW-C9N7IthB.js → ro-RO-JPDTUUEW-CA8EbRFB.js} +1 -1
  79. package/dist/static/assets/{ru-RU-B4JR7IUQ-CIZdU2_B.js → ru-RU-B4JR7IUQ-DLFEoBBN.js} +1 -1
  80. package/dist/static/assets/{sankeyDiagram-04a897e0-TilwwYL-.js → sankeyDiagram-04a897e0-CqarZxqc.js} +1 -1
  81. package/dist/static/assets/{sequenceDiagram-704730f1-BiVJk4lg.js → sequenceDiagram-704730f1-D_k2eCYQ.js} +1 -1
  82. package/dist/static/assets/{si-LK-N5RQ5JYF-CxFkliCr.js → si-LK-N5RQ5JYF-BNFow9lY.js} +1 -1
  83. package/dist/static/assets/{sk-SK-C5VTKIMK-JZtsQcSw.js → sk-SK-C5VTKIMK-DGB0M6nr.js} +1 -1
  84. package/dist/static/assets/{sl-SI-NN7IZMDC-BUYqyq49.js → sl-SI-NN7IZMDC-CJDYDrpB.js} +1 -1
  85. package/dist/static/assets/{stateDiagram-587899a1-B1sCio7H.js → stateDiagram-587899a1-CmJUP4x5.js} +1 -1
  86. package/dist/static/assets/{stateDiagram-v2-d93cdb3a-CoGzFssB.js → stateDiagram-v2-d93cdb3a-shPgA-Re.js} +1 -1
  87. package/dist/static/assets/{styles-6aaf32cf-C1s7Ul3J.js → styles-6aaf32cf-D6ni1cH-.js} +1 -1
  88. package/dist/static/assets/{styles-9a916d00-Bp5-VH98.js → styles-9a916d00-DF1cbr_V.js} +1 -1
  89. package/dist/static/assets/{styles-c10674c1-DfO5CrB5.js → styles-c10674c1-pZpeeixm.js} +1 -1
  90. package/dist/static/assets/{subset-shared.chunk-QCqXuXCH.js → subset-shared.chunk-B7Bsj1CE.js} +1 -1
  91. package/dist/static/assets/{subset-worker.chunk-BjKI4tmi.js → subset-worker.chunk-DvVFmVNJ.js} +1 -1
  92. package/dist/static/assets/{sv-SE-XGPEYMSR-Cz1rfwXY.js → sv-SE-XGPEYMSR-Ct3zlqNH.js} +1 -1
  93. package/dist/static/assets/{svgDrawCommon-08f97a94-mI-dkcv_.js → svgDrawCommon-08f97a94-ihzL1PNw.js} +1 -1
  94. package/dist/static/assets/{ta-IN-2NMHFXQM-CtHc6ooj.js → ta-IN-2NMHFXQM-Ss8wmPcU.js} +1 -1
  95. package/dist/static/assets/{th-TH-HPSO5L25-xIQ7sAKy.js → th-TH-HPSO5L25-RgqhD66T.js} +1 -1
  96. package/dist/static/assets/{timeline-definition-85554ec2-BylSkqlD.js → timeline-definition-85554ec2-Ck3wPUBk.js} +1 -1
  97. package/dist/static/assets/{tr-TR-DEFEU3FU-CrIfozru.js → tr-TR-DEFEU3FU-CYb8OLTu.js} +1 -1
  98. package/dist/static/assets/{uk-UA-QMV73CPH-Dr07MVsF.js → uk-UA-QMV73CPH-BirNsE4V.js} +1 -1
  99. package/dist/static/assets/{vi-VN-M7AON7JQ-ChRYxYRs.js → vi-VN-M7AON7JQ-BxI4Rmvs.js} +1 -1
  100. package/dist/static/assets/{xychartDiagram-e933f94c-ByBCDYvY.js → xychartDiagram-e933f94c-D2woJW1O.js} +1 -1
  101. package/dist/static/assets/{zh-CN-LNUGB5OW-B9Dg6rSU.js → zh-CN-LNUGB5OW-DCBQmThi.js} +1 -1
  102. package/dist/static/assets/{zh-HK-E62DVLB3-17W7WnGJ.js → zh-HK-E62DVLB3-C1h46zXW.js} +1 -1
  103. package/dist/static/assets/{zh-TW-RAJ6MFWO-B1yJY7cJ.js → zh-TW-RAJ6MFWO-3TGZr7NK.js} +1 -1
  104. package/dist/static/index.html +1 -1
  105. package/package.json +1 -1
  106. package/dist/static/assets/channel-DfDbkfoc.js +0 -1
  107. package/dist/static/assets/clone-C-cGF6mM.js +0 -1
  108. package/dist/static/assets/flowDiagram-v2-96b9c2cf-D8VcptSb.js +0 -1
@@ -1,3 +1 @@
1
- export declare function startBrowser(filePath?: string): Promise<void>;
2
- export declare function startApp(filePath?: string): Promise<void>;
3
1
  export declare function start(filePath?: string): Promise<void>;
@@ -1,76 +1,10 @@
1
- import { spawn, exec } from 'child_process';
2
- import { resolve, dirname } from 'path';
3
- import { fileURLToPath } from 'url';
1
+ import { exec } from 'child_process';
2
+ import { resolve } from 'path';
4
3
  import { readFileSync, existsSync } from 'fs';
5
4
  import { promisify } from 'util';
6
- import { connectToCanvas, isCanvasRunning, generateId } from '../lib/ws-client.js';
7
- import { startServer, isBrowserServerRunning } from '../server/index.js';
5
+ import { connectToCanvas, generateId } from '../lib/ws-client.js';
6
+ import { startServer, isBrowserServerRunning, isBrowserConnected } from '../server/index.js';
8
7
  const execAsync = promisify(exec);
9
- const __dirname = dirname(fileURLToPath(import.meta.url));
10
- function getDevElectronAppPath() {
11
- // Check if running in monorepo dev mode
12
- const devPath = resolve(__dirname, '../../../electron-app');
13
- if (existsSync(resolve(devPath, 'package.json'))) {
14
- return devPath;
15
- }
16
- return null;
17
- }
18
- async function findElectronAppCommand() {
19
- // Check if agent-canvas-app command is available
20
- try {
21
- const cmd = process.platform === 'win32' ? 'where' : 'which';
22
- await execAsync(`${cmd} agent-canvas-app`);
23
- return 'agent-canvas-app';
24
- }
25
- catch {
26
- return null;
27
- }
28
- }
29
- async function launchElectronApp() {
30
- // First, try to find installed electron-app command
31
- const appCommand = await findElectronAppCommand();
32
- if (appCommand) {
33
- // Use installed electron-app
34
- const child = spawn(appCommand, [], {
35
- detached: true,
36
- stdio: 'ignore',
37
- });
38
- child.unref();
39
- }
40
- else {
41
- // Check if running in dev mode (monorepo)
42
- const devPath = getDevElectronAppPath();
43
- if (devPath) {
44
- const child = spawn('bun', ['run', 'dev'], {
45
- cwd: devPath,
46
- detached: true,
47
- stdio: 'ignore',
48
- });
49
- child.unref();
50
- }
51
- else {
52
- // electron-app not found
53
- console.error('Electron app not found.');
54
- console.error('');
55
- console.error('To use --app mode, install the electron app:');
56
- console.error(' npm install -g @agent-canvas/electron-app');
57
- console.error('');
58
- console.error('Or use browser mode (default):');
59
- console.error(' agent-canvas start');
60
- process.exit(1);
61
- }
62
- }
63
- // Wait for app to start
64
- const maxRetries = 30;
65
- const retryInterval = 500;
66
- for (let i = 0; i < maxRetries; i++) {
67
- await new Promise((r) => setTimeout(r, retryInterval));
68
- if (await isCanvasRunning()) {
69
- return;
70
- }
71
- }
72
- throw new Error('Failed to start electron app');
73
- }
74
8
  async function openBrowser(url) {
75
9
  const platform = process.platform;
76
10
  try {
@@ -118,8 +52,7 @@ async function loadFile(filePath) {
118
52
  process.exit(1);
119
53
  }
120
54
  }
121
- // Browser mode (default)
122
- export async function startBrowser(filePath) {
55
+ export async function start(filePath) {
123
56
  if (filePath) {
124
57
  const absolutePath = resolve(filePath);
125
58
  if (!existsSync(absolutePath)) {
@@ -128,16 +61,19 @@ export async function startBrowser(filePath) {
128
61
  }
129
62
  }
130
63
  const running = await isBrowserServerRunning();
131
- if (running) {
132
- console.log('Canvas server already running');
133
- console.log('Opening browser...');
134
- await openBrowser('http://localhost:7891');
64
+ if (!running) {
65
+ console.log('Starting canvas server...');
66
+ await startServer();
67
+ }
68
+ // Give existing browser tabs a moment to reconnect (browser reconnects every 1s)
69
+ await new Promise((r) => setTimeout(r, 1500));
70
+ const browserConnected = await isBrowserConnected();
71
+ if (browserConnected) {
72
+ console.log('Canvas already running at http://localhost:7891');
135
73
  }
136
74
  else {
137
- console.log('Starting canvas server...');
138
- const { httpUrl } = await startServer();
139
75
  console.log('Opening browser...');
140
- await openBrowser(httpUrl);
76
+ await openBrowser('http://localhost:7891');
141
77
  }
142
78
  // Wait for browser to connect, then load file if specified
143
79
  if (filePath) {
@@ -162,36 +98,3 @@ export async function startBrowser(filePath) {
162
98
  console.log('\nCanvas is ready. Press Ctrl+C to stop.');
163
99
  await new Promise(() => { }); // Block forever
164
100
  }
165
- // Electron app mode (--app)
166
- export async function startApp(filePath) {
167
- if (filePath) {
168
- const absolutePath = resolve(filePath);
169
- if (!existsSync(absolutePath)) {
170
- console.error(`File not found: ${absolutePath}`);
171
- process.exit(1);
172
- }
173
- }
174
- console.log('Checking if canvas app is running...');
175
- const running = await isCanvasRunning();
176
- if (!running) {
177
- console.log('Starting canvas app...');
178
- await launchElectronApp();
179
- }
180
- console.log('Connecting to canvas app...');
181
- try {
182
- const client = await connectToCanvas();
183
- console.log('Connected to canvas app');
184
- if (filePath) {
185
- await loadFile(filePath);
186
- }
187
- client.close();
188
- }
189
- catch (err) {
190
- console.error('Failed to connect:', err instanceof Error ? err.message : err);
191
- process.exit(1);
192
- }
193
- }
194
- // Legacy export for backward compatibility
195
- export async function start(filePath) {
196
- return startBrowser(filePath);
197
- }
package/dist/index.js CHANGED
@@ -1,25 +1,19 @@
1
1
  import { Command } from 'commander';
2
2
  import { writeFileSync } from 'node:fs';
3
3
  import { encode as toToon } from '@toon-format/toon';
4
- import { startBrowser, startApp } from './commands/start.js';
4
+ import { start } from './commands/start.js';
5
5
  import { connectToCanvas, generateId } from './lib/ws-client.js';
6
6
  const program = new Command();
7
7
  program
8
8
  .name('agent-canvas')
9
9
  .description('CLI for Agent Canvas - Excalidraw interface for AI agents')
10
- .version('0.1.0');
10
+ .version('0.2.1');
11
11
  program
12
12
  .command('start')
13
- .description('Start the canvas (browser mode by default, use --app for Electron)')
13
+ .description('Start the canvas server and open in browser')
14
14
  .option('-f, --file <path>', 'Load an .excalidraw file on start')
15
- .option('--app', 'Use Electron app instead of browser')
16
15
  .action(async (options) => {
17
- if (options.app) {
18
- await startApp(options.file);
19
- }
20
- else {
21
- await startBrowser(options.file);
22
- }
16
+ await start(options.file);
23
17
  });
24
18
  // ============================================================================
25
19
  // Add Shape
@@ -39,6 +33,7 @@ program
39
33
  .option('--fill-style <style>', 'Fill style: hachure, cross-hatch, solid, or zigzag')
40
34
  .option('-l, --label <text>', 'Text label inside the shape')
41
35
  .option('--label-font-size <number>', 'Label font size', parseFloat)
36
+ .option('-n, --note <text>', 'Note for this element (stored in customData)')
42
37
  .action(async (options) => {
43
38
  const client = await connectToCanvas();
44
39
  const params = {
@@ -52,6 +47,7 @@ program
52
47
  strokeWidth: options.strokeWidth,
53
48
  strokeStyle: options.strokeStyle,
54
49
  fillStyle: options.fillStyle,
50
+ customData: options.note ? { note: options.note } : undefined,
55
51
  };
56
52
  if (options.label) {
57
53
  params.label = { text: options.label, fontSize: options.labelFontSize };
@@ -78,6 +74,7 @@ program
78
74
  .option('--font-size <number>', 'Font size', parseFloat)
79
75
  .option('--text-align <align>', 'Text alignment: left, center, or right')
80
76
  .option('--stroke-color <color>', 'Text color (hex)')
77
+ .option('-n, --note <text>', 'Note for this element (stored in customData)')
81
78
  .action(async (options) => {
82
79
  const client = await connectToCanvas();
83
80
  const result = await client.send({
@@ -90,6 +87,7 @@ program
90
87
  fontSize: options.fontSize,
91
88
  textAlign: options.textAlign,
92
89
  strokeColor: options.strokeColor,
90
+ customData: options.note ? { note: options.note } : undefined,
93
91
  },
94
92
  });
95
93
  if (result.success) {
@@ -114,6 +112,7 @@ program
114
112
  .option('--stroke-color <color>', 'Line color (hex)')
115
113
  .option('--stroke-width <number>', 'Line width in pixels', parseFloat)
116
114
  .option('--stroke-style <style>', 'Line style: solid, dashed, or dotted')
115
+ .option('-n, --note <text>', 'Note for this element (stored in customData)')
117
116
  .action(async (options) => {
118
117
  const client = await connectToCanvas();
119
118
  const result = await client.send({
@@ -127,6 +126,7 @@ program
127
126
  strokeColor: options.strokeColor,
128
127
  strokeWidth: options.strokeWidth,
129
128
  strokeStyle: options.strokeStyle,
129
+ customData: options.note ? { note: options.note } : undefined,
130
130
  },
131
131
  });
132
132
  if (result.success) {
@@ -153,6 +153,7 @@ program
153
153
  .option('--stroke-style <style>', 'Arrow style: solid, dashed, or dotted')
154
154
  .option('--start-arrowhead <type>', 'Start arrowhead: arrow, bar, dot, triangle, diamond, none')
155
155
  .option('--end-arrowhead <type>', 'End arrowhead: arrow, bar, dot, triangle, diamond, none')
156
+ .option('-n, --note <text>', 'Note for this element (stored in customData)')
156
157
  .action(async (options) => {
157
158
  const client = await connectToCanvas();
158
159
  const result = await client.send({
@@ -168,6 +169,7 @@ program
168
169
  strokeStyle: options.strokeStyle,
169
170
  startArrowhead: options.startArrowhead,
170
171
  endArrowhead: options.endArrowhead,
172
+ customData: options.note ? { note: options.note } : undefined,
171
173
  },
172
174
  });
173
175
  if (result.success) {
@@ -191,6 +193,7 @@ program
191
193
  .option('--stroke-width <number>', 'Stroke width in pixels', parseFloat)
192
194
  .option('--stroke-style <style>', 'Stroke style: solid, dashed, or dotted')
193
195
  .option('--fill-style <style>', 'Fill style: hachure, cross-hatch, solid, or zigzag')
196
+ .option('-n, --note <text>', 'Note for this element (stored in customData)')
194
197
  .action(async (options) => {
195
198
  let points;
196
199
  try {
@@ -211,6 +214,7 @@ program
211
214
  strokeWidth: options.strokeWidth,
212
215
  strokeStyle: options.strokeStyle,
213
216
  fillStyle: options.fillStyle,
217
+ customData: options.note ? { note: options.note } : undefined,
214
218
  },
215
219
  });
216
220
  if (result.success) {
@@ -350,31 +354,162 @@ program
350
354
  program
351
355
  .command('read')
352
356
  .description('Read all elements from the canvas (TOON format by default)')
353
- .option('--json', 'Output as JSON')
357
+ .option('--json', 'Output raw Excalidraw scene JSON')
358
+ .option('--with-style', 'Include style info (stroke, bg) in TOON output')
354
359
  .action(async (options) => {
355
360
  const client = await connectToCanvas();
361
+ if (options.json) {
362
+ // Return raw Excalidraw scene data
363
+ const result = await client.send({
364
+ type: 'saveScene',
365
+ id: generateId(),
366
+ });
367
+ if (result.success && result.data) {
368
+ console.log(JSON.stringify(result.data, null, 2));
369
+ }
370
+ else {
371
+ console.error(`Failed: ${result.error}`);
372
+ process.exit(1);
373
+ }
374
+ client.close();
375
+ return;
376
+ }
356
377
  const result = await client.send({
357
378
  type: 'readScene',
358
379
  id: generateId(),
359
380
  });
360
381
  if (result.success && result.elements) {
361
- if (options.json) {
362
- console.log(JSON.stringify(result.elements, null, 2));
363
- }
364
- else {
365
- // Simplify elements for TOON output
366
- const elements = result.elements.map(el => ({
367
- id: el.id,
368
- type: el.type,
369
- x: Math.round(el.x),
370
- y: Math.round(el.y),
371
- ...(el.width !== undefined && { w: Math.round(el.width) }),
372
- ...(el.height !== undefined && { h: Math.round(el.height) }),
373
- ...(el.text && { text: el.text }),
374
- ...(el.groupIds?.length && { groups: el.groupIds }),
375
- }));
376
- console.log(toToon({ elements }));
382
+ const withStyle = options.withStyle;
383
+ // Separate elements into shapes, lines, labels (bound text), texts (standalone text), and groups
384
+ const shapes = [];
385
+ const lines = [];
386
+ const labels = [];
387
+ const texts = [];
388
+ const groupsMap = new Map(); // groupId -> elementIds
389
+ for (const el of result.elements) {
390
+ const angle = el.angle ? Math.round(el.angle * 180 / Math.PI) : 0; // Convert radians to degrees
391
+ // Collect group memberships
392
+ if (el.groupIds?.length) {
393
+ for (const groupId of el.groupIds) {
394
+ if (!groupsMap.has(groupId)) {
395
+ groupsMap.set(groupId, []);
396
+ }
397
+ groupsMap.get(groupId).push(el.id);
398
+ }
399
+ }
400
+ if (el.type === 'text') {
401
+ if (el.containerId) {
402
+ // Bound text (label)
403
+ labels.push({
404
+ id: el.id,
405
+ containerId: el.containerId,
406
+ content: el.text ?? '',
407
+ x: Math.round(el.x),
408
+ y: Math.round(el.y),
409
+ w: el.width !== undefined ? Math.round(el.width) : null,
410
+ h: el.height !== undefined ? Math.round(el.height) : null,
411
+ });
412
+ }
413
+ else {
414
+ // Standalone text
415
+ const text = {
416
+ id: el.id,
417
+ content: el.text ?? '',
418
+ x: Math.round(el.x),
419
+ y: Math.round(el.y),
420
+ w: el.width !== undefined ? Math.round(el.width) : null,
421
+ h: el.height !== undefined ? Math.round(el.height) : null,
422
+ angle,
423
+ note: el.customData?.note ?? null,
424
+ };
425
+ if (withStyle) {
426
+ text.stroke = el.strokeColor ?? null;
427
+ }
428
+ texts.push(text);
429
+ }
430
+ }
431
+ else if (el.type === 'line' || el.type === 'arrow') {
432
+ const pts = el.points ?? [];
433
+ // Check if it's a closed polygon (first and last point within 8px threshold)
434
+ const isPolygon = el.type === 'line' && pts.length >= 3 && (() => {
435
+ const first = pts[0];
436
+ const last = pts[pts.length - 1];
437
+ const distance = Math.sqrt((last[0] - first[0]) ** 2 + (last[1] - first[1]) ** 2);
438
+ return distance <= 8;
439
+ })();
440
+ if (isPolygon) {
441
+ // Polygon - treat as shape
442
+ // Calculate bounding box from points
443
+ const xs = pts.map(p => p[0]);
444
+ const ys = pts.map(p => p[1]);
445
+ const minX = Math.min(...xs);
446
+ const maxX = Math.max(...xs);
447
+ const minY = Math.min(...ys);
448
+ const maxY = Math.max(...ys);
449
+ const shape = {
450
+ id: el.id,
451
+ type: 'polygon',
452
+ x: Math.round(el.x + minX),
453
+ y: Math.round(el.y + minY),
454
+ w: Math.round(maxX - minX),
455
+ h: Math.round(maxY - minY),
456
+ angle,
457
+ labelId: null,
458
+ note: el.customData?.note ?? null,
459
+ };
460
+ if (withStyle) {
461
+ shape.stroke = el.strokeColor ?? null;
462
+ shape.bg = el.backgroundColor ?? null;
463
+ }
464
+ shapes.push(shape);
465
+ }
466
+ else {
467
+ // Line/Arrow element
468
+ const lastPt = pts.length > 0 ? pts[pts.length - 1] : [0, 0];
469
+ const line = {
470
+ id: el.id,
471
+ type: el.type,
472
+ x: Math.round(el.x),
473
+ y: Math.round(el.y),
474
+ endX: Math.round(el.x + lastPt[0]),
475
+ endY: Math.round(el.y + lastPt[1]),
476
+ points: pts.length,
477
+ angle,
478
+ note: el.customData?.note ?? null,
479
+ };
480
+ if (withStyle) {
481
+ line.stroke = el.strokeColor ?? null;
482
+ }
483
+ lines.push(line);
484
+ }
485
+ }
486
+ else {
487
+ // Shape element (rectangle, ellipse, diamond, etc.)
488
+ const boundText = el.boundElements?.find(b => b.type === 'text');
489
+ const shape = {
490
+ id: el.id,
491
+ type: el.type,
492
+ x: Math.round(el.x),
493
+ y: Math.round(el.y),
494
+ w: el.width !== undefined ? Math.round(el.width) : null,
495
+ h: el.height !== undefined ? Math.round(el.height) : null,
496
+ angle,
497
+ labelId: boundText?.id ?? null,
498
+ note: el.customData?.note ?? null,
499
+ };
500
+ if (withStyle) {
501
+ shape.stroke = el.strokeColor ?? null;
502
+ shape.bg = el.backgroundColor ?? null;
503
+ }
504
+ shapes.push(shape);
505
+ }
377
506
  }
507
+ // Build groups array
508
+ const groups = Array.from(groupsMap.entries()).map(([id, elementIds]) => ({
509
+ id,
510
+ elementIds: elementIds.join(','),
511
+ }));
512
+ console.log(toToon({ shapes, lines, labels, texts, groups }));
378
513
  }
379
514
  else {
380
515
  console.error(`Failed: ${result.error}`);
@@ -17,6 +17,7 @@ export interface AddShapeParams {
17
17
  verticalAlign?: 'top' | 'middle' | 'bottom';
18
18
  strokeColor?: string;
19
19
  };
20
+ customData?: Record<string, unknown>;
20
21
  }
21
22
  export interface AddShapeResponse {
22
23
  type: 'addShapeResult';
@@ -32,6 +33,7 @@ export interface AddTextParams {
32
33
  fontSize?: number;
33
34
  textAlign?: 'left' | 'center' | 'right';
34
35
  strokeColor?: string;
36
+ customData?: Record<string, unknown>;
35
37
  }
36
38
  export interface AddTextResponse {
37
39
  type: 'addTextResult';
@@ -48,6 +50,7 @@ export interface AddLineParams {
48
50
  strokeColor?: string;
49
51
  strokeWidth?: number;
50
52
  strokeStyle?: 'solid' | 'dashed' | 'dotted';
53
+ customData?: Record<string, unknown>;
51
54
  }
52
55
  export interface AddLineResponse {
53
56
  type: 'addLineResult';
@@ -66,6 +69,7 @@ export interface AddArrowParams {
66
69
  strokeStyle?: 'solid' | 'dashed' | 'dotted';
67
70
  startArrowhead?: 'arrow' | 'bar' | 'dot' | 'triangle' | 'diamond' | 'none';
68
71
  endArrowhead?: 'arrow' | 'bar' | 'dot' | 'triangle' | 'diamond' | 'none';
72
+ customData?: Record<string, unknown>;
69
73
  }
70
74
  export interface AddArrowResponse {
71
75
  type: 'addArrowResult';
@@ -84,6 +88,7 @@ export interface AddPolygonParams {
84
88
  strokeWidth?: number;
85
89
  strokeStyle?: 'solid' | 'dashed' | 'dotted';
86
90
  fillStyle?: 'hachure' | 'cross-hatch' | 'solid' | 'zigzag';
91
+ customData?: Record<string, unknown>;
87
92
  }
88
93
  export interface AddPolygonResponse {
89
94
  type: 'addPolygonResult';
@@ -144,6 +149,10 @@ export interface MoveElementsResponse {
144
149
  movedCount?: number;
145
150
  error?: string;
146
151
  }
152
+ export interface BoundElement {
153
+ id: string;
154
+ type: 'arrow' | 'text';
155
+ }
147
156
  export interface SceneElement {
148
157
  id: string;
149
158
  type: string;
@@ -157,9 +166,12 @@ export interface SceneElement {
157
166
  groupIds?: string[];
158
167
  text?: string;
159
168
  fontSize?: number;
169
+ containerId?: string | null;
170
+ boundElements?: BoundElement[] | null;
160
171
  points?: number[][];
161
172
  startArrowhead?: string | null;
162
173
  endArrowhead?: string | null;
174
+ customData?: Record<string, unknown>;
163
175
  }
164
176
  export interface ReadSceneResponse {
165
177
  type: 'readSceneResult';
@@ -9,5 +9,4 @@ export interface WsClient {
9
9
  }) => Promise<T>;
10
10
  }
11
11
  export declare function connectToCanvas(timeout?: number): Promise<WsClient>;
12
- export declare function isCanvasRunning(): Promise<boolean>;
13
12
  export declare function generateId(): string;
@@ -65,24 +65,6 @@ export function connectToCanvas(timeout = 5000) {
65
65
  });
66
66
  });
67
67
  }
68
- export function isCanvasRunning() {
69
- return new Promise((resolve) => {
70
- const ws = new WebSocket(WS_URL);
71
- const timer = setTimeout(() => {
72
- ws.close();
73
- resolve(false);
74
- }, 1000);
75
- ws.on('open', () => {
76
- clearTimeout(timer);
77
- ws.close();
78
- resolve(true);
79
- });
80
- ws.on('error', () => {
81
- clearTimeout(timer);
82
- resolve(false);
83
- });
84
- });
85
- }
86
68
  export function generateId() {
87
69
  return Math.random().toString(36).substring(2, 15);
88
70
  }
@@ -4,3 +4,4 @@ export declare function startServer(): Promise<{
4
4
  close: () => void;
5
5
  }>;
6
6
  export declare function isBrowserServerRunning(): Promise<boolean>;
7
+ export declare function isBrowserConnected(): Promise<boolean>;
@@ -92,6 +92,14 @@ export function startServer() {
92
92
  ws.send(JSON.stringify({ type: 'pong' }));
93
93
  return;
94
94
  }
95
+ // Check if browser is connected
96
+ if (message.type === 'isBrowserConnected') {
97
+ ws.send(JSON.stringify({
98
+ type: 'browserStatus',
99
+ connected: browserClient !== null && browserClient.readyState === WebSocket.OPEN,
100
+ }));
101
+ return;
102
+ }
95
103
  // If this is from browser (response), forward to CLI client
96
104
  if (message.id && pendingRequests.has(message.id)) {
97
105
  const cliClient = pendingRequests.get(message.id);
@@ -160,3 +168,32 @@ export function isBrowserServerRunning() {
160
168
  });
161
169
  });
162
170
  }
171
+ export function isBrowserConnected() {
172
+ return new Promise((resolve) => {
173
+ const ws = new WebSocket(`ws://localhost:${WS_PORT}`);
174
+ const timer = setTimeout(() => {
175
+ ws.close();
176
+ resolve(false);
177
+ }, 1000);
178
+ ws.on('open', () => {
179
+ ws.send(JSON.stringify({ type: 'isBrowserConnected' }));
180
+ });
181
+ ws.on('message', (data) => {
182
+ try {
183
+ const msg = JSON.parse(data.toString());
184
+ if (msg.type === 'browserStatus') {
185
+ clearTimeout(timer);
186
+ ws.close();
187
+ resolve(msg.connected);
188
+ }
189
+ }
190
+ catch {
191
+ // ignore
192
+ }
193
+ });
194
+ ws.on('error', () => {
195
+ clearTimeout(timer);
196
+ resolve(false);
197
+ });
198
+ });
199
+ }
@@ -1,4 +1,4 @@
1
- import"./index-D9K5p6xj.js";var u={paste:"لصق",pasteAsPlaintext:"اللصق كنص عادي",pasteCharts:"لصق الرسوم البيانية",selectAll:"تحديد الكل",multiSelect:"إضافة عنصر للتحديد",moveCanvas:"نقل لوح الرسم",cut:"قص",copy:"نسخ",copyAsPng:"نسخ إلى الحافظة بصيغة PNG",copyAsSvg:"نسخ إلى الحافظة بصيغة SVG",copyText:"نسخ إلى الحافظة كنص",copySource:"",convertToCode:"",bringForward:"جلب للأمام",sendToBack:"أرسل للخلف",bringToFront:"أحضر للأمام",sendBackward:"أرسل للخلف",delete:"حذف",copyStyles:"نسخ الأنماط",pasteStyles:"لصق الأنماط",stroke:"الخط",background:"الخلفية",fill:"التعبئة",strokeWidth:"سُمك الخط",strokeStyle:"نمط الخط",strokeStyle_solid:"متصل",strokeStyle_dashed:"متقطع",strokeStyle_dotted:"منقط",sloppiness:"الإمالة",opacity:"الشفافية",textAlign:"محاذاة النص",edges:"الحواف",sharp:"حادة",round:"دائرية",arrowheads:"رؤوس الأسهم",arrowhead_none:"لا شيء",arrowhead_arrow:"سهم",arrowhead_bar:"شريط",arrowhead_circle:"",arrowhead_circle_outline:"",arrowhead_triangle:"مثلث",arrowhead_triangle_outline:"",arrowhead_diamond:"",arrowhead_diamond_outline:"",fontSize:"حجم الخط",fontFamily:"نوع الخط",addWatermark:'إضافة "مصنوعة بواسطة Excalidraw"',handDrawn:"رسم باليد",normal:"عادي",code:"رمز",small:"صغير",medium:"متوسط",large:"كبير",veryLarge:"كبير جدا",solid:"كامل",hachure:"خطوط",zigzag:"متعرج",crossHatch:"خطوط متقطعة",thin:"نحيف",bold:"داكن",left:"الـيسار",center:"وسط",right:"يمين",extraBold:"عريض",architect:"معماري",artist:"رسام",cartoonist:"كرتوني",fileTitle:"إسم الملف",colorPicker:"منتقي اللون",canvasColors:"تستخدم على القماش",canvasBackground:"خلفية اللوحة",drawingCanvas:"لوحة الرسم",layers:"الطبقات",actions:"الإجراءات",language:"اللغة",liveCollaboration:"التعاون المباشر...",duplicateSelection:"تكرار",untitled:"غير معنون",name:"الاسم",yourName:"اسمك",madeWithExcalidraw:"مصنوعة بواسطة Excalidraw",group:"تحديد مجموعة",ungroup:"إلغاء تحديد مجموعة",collaborators:"المتعاونون",showGrid:"إظهار الشبكة",addToLibrary:"أضف إلى المكتبة",removeFromLibrary:"حذف من المكتبة",libraryLoadingMessage:"جارٍ تحميل المكتبة…",libraries:"تصفح المكتبات",loadingScene:"جاري تحميل المشهد…",align:"محاذاة",alignTop:"محاذاة إلى اﻷعلى",alignBottom:"محاذاة إلى اﻷسفل",alignLeft:"محاذاة إلى اليسار",alignRight:"محاذاة إلى اليمين",centerVertically:"توسيط عمودي",centerHorizontally:"توسيط أفقي",distributeHorizontally:"التوزيع الأفقي",distributeVertically:"التوزيع عمودياً",flipHorizontal:"قلب عامودي",flipVertical:"قلب أفقي",viewMode:"نمط العرض",share:"مشاركة",showStroke:"إظهار منتقي لون الخط",showBackground:"إظهار منتقي لون الخلفية",toggleTheme:"غير النمط",personalLib:"المكتبة الشخصية",excalidrawLib:"مكتبتنا",decreaseFontSize:"تصغير حجم الخط",increaseFontSize:"تكبير حجم الخط",unbindText:"فك ربط النص",bindText:"ربط النص بالحاوية",createContainerFromText:"نص مغلف في حاوية",link:{edit:"تعديل الرابط",editEmbed:"تحرير الرابط وإدراجه",create:"إنشاء رابط",createEmbed:"إنشاء رابط و إدراجه",label:"رابط",labelEmbed:"رابط و إدراج",empty:"لم يتم تعيين رابط"},lineEditor:{edit:"تحرير السطر",exit:"الخروج من المُحرر"},elementLock:{lock:"قفل",unlock:"فتح",lockAll:"قفل الكل",unlockAll:"فتح الكل"},statusPublished:"نُشر",sidebarLock:"إبقاء الشريط الجانبي مفتوح",selectAllElementsInFrame:"تحديد جميع العناصر في الإطار",removeAllElementsFromFrame:"إزالة جميع العناصر من الإطار",eyeDropper:"اختيار اللون من القماش",textToDiagram:"",prompt:""},A={noItems:"لا توجد عناصر أضيفت بعد...",hint_emptyLibrary:"حدد عنصر على القماش لإضافته هنا، أو تثبيت مكتبة من المستودع العام أدناه.",hint_emptyPrivateLibrary:"حدد عنصر على القماش لإضافته هنا."},e={clearReset:"إعادة تعيين اللوحة",exportJSON:"صدر الملف",exportImage:"تصدير الصورة...",export:"حفظ إلى...",copyToClipboard:"نسخ إلى الحافظة",save:"احفظ للملف الحالي",saveAs:"حفظ كـ",load:"فتح",getShareableLink:"احصل على رابط المشاركة",close:"غلق",selectLanguage:"اختر اللغة",scrollBackToContent:"الرجوع إلى المحتوى",zoomIn:"تكبير",zoomOut:"تصغير",resetZoom:"إعادة تعيين الشاشة",menu:"القائمة",done:"تم",edit:"تعديل",undo:"تراجع",redo:"إعادة تنفيذ",resetLibrary:"إعادة ضبط المكتبة",createNewRoom:"إنشاء غرفة جديدة",fullScreen:"شاشة كاملة",darkMode:"الوضع المظلم",lightMode:"الوضع المضيء",zenMode:"وضع التأمل",objectsSnapMode:"التقط إلى العناصر",exitZenMode:"إلغاء الوضع الليلى",cancel:"إلغاء",clear:"مسح",remove:"إزالة",embed:"تبديل الإدراج",publishLibrary:"انشر",submit:"أرسل",confirm:"تأكيد",embeddableInteractionButton:"اضغط للتفاعل"},a={clearReset:"هذا سيُزيل كامل اللوحة. هل أنت متأكد؟",couldNotCreateShareableLink:"تعذر إنشاء رابطة المشاركة.",couldNotCreateShareableLinkTooBig:"تعذر إنشاء رابط قابل للمشاركة: المشهد كبير جدًا",couldNotLoadInvalidFile:"تعذر التحميل، الملف غير صالح",importBackendFailed:"فشل الاستيراد من الخادوم.",cannotExportEmptyCanvas:"لا يمكن تصدير لوحة فارغة.",couldNotCopyToClipboard:"تعذر النسخ إلى الحافظة.",decryptFailed:"تعذر فك تشفير البيانات.",uploadedSecurly:"تم تأمين التحميل بتشفير النهاية إلى النهاية، مما يعني أن خادوم Excalidraw والأطراف الثالثة لا يمكنها قراءة المحتوى.",loadSceneOverridePrompt:"تحميل الرسم الخارجي سيحل محل المحتوى الموجود لديك. هل ترغب في المتابعة؟",collabStopOverridePrompt:`إيقاف الجلسة سيؤدي إلى الكتابة فوق رسومك السابقة المخزنة داخليا. هل أنت متأكد؟
1
+ import"./index-Dq9tezYn.js";var u={paste:"لصق",pasteAsPlaintext:"اللصق كنص عادي",pasteCharts:"لصق الرسوم البيانية",selectAll:"تحديد الكل",multiSelect:"إضافة عنصر للتحديد",moveCanvas:"نقل لوح الرسم",cut:"قص",copy:"نسخ",copyAsPng:"نسخ إلى الحافظة بصيغة PNG",copyAsSvg:"نسخ إلى الحافظة بصيغة SVG",copyText:"نسخ إلى الحافظة كنص",copySource:"",convertToCode:"",bringForward:"جلب للأمام",sendToBack:"أرسل للخلف",bringToFront:"أحضر للأمام",sendBackward:"أرسل للخلف",delete:"حذف",copyStyles:"نسخ الأنماط",pasteStyles:"لصق الأنماط",stroke:"الخط",background:"الخلفية",fill:"التعبئة",strokeWidth:"سُمك الخط",strokeStyle:"نمط الخط",strokeStyle_solid:"متصل",strokeStyle_dashed:"متقطع",strokeStyle_dotted:"منقط",sloppiness:"الإمالة",opacity:"الشفافية",textAlign:"محاذاة النص",edges:"الحواف",sharp:"حادة",round:"دائرية",arrowheads:"رؤوس الأسهم",arrowhead_none:"لا شيء",arrowhead_arrow:"سهم",arrowhead_bar:"شريط",arrowhead_circle:"",arrowhead_circle_outline:"",arrowhead_triangle:"مثلث",arrowhead_triangle_outline:"",arrowhead_diamond:"",arrowhead_diamond_outline:"",fontSize:"حجم الخط",fontFamily:"نوع الخط",addWatermark:'إضافة "مصنوعة بواسطة Excalidraw"',handDrawn:"رسم باليد",normal:"عادي",code:"رمز",small:"صغير",medium:"متوسط",large:"كبير",veryLarge:"كبير جدا",solid:"كامل",hachure:"خطوط",zigzag:"متعرج",crossHatch:"خطوط متقطعة",thin:"نحيف",bold:"داكن",left:"الـيسار",center:"وسط",right:"يمين",extraBold:"عريض",architect:"معماري",artist:"رسام",cartoonist:"كرتوني",fileTitle:"إسم الملف",colorPicker:"منتقي اللون",canvasColors:"تستخدم على القماش",canvasBackground:"خلفية اللوحة",drawingCanvas:"لوحة الرسم",layers:"الطبقات",actions:"الإجراءات",language:"اللغة",liveCollaboration:"التعاون المباشر...",duplicateSelection:"تكرار",untitled:"غير معنون",name:"الاسم",yourName:"اسمك",madeWithExcalidraw:"مصنوعة بواسطة Excalidraw",group:"تحديد مجموعة",ungroup:"إلغاء تحديد مجموعة",collaborators:"المتعاونون",showGrid:"إظهار الشبكة",addToLibrary:"أضف إلى المكتبة",removeFromLibrary:"حذف من المكتبة",libraryLoadingMessage:"جارٍ تحميل المكتبة…",libraries:"تصفح المكتبات",loadingScene:"جاري تحميل المشهد…",align:"محاذاة",alignTop:"محاذاة إلى اﻷعلى",alignBottom:"محاذاة إلى اﻷسفل",alignLeft:"محاذاة إلى اليسار",alignRight:"محاذاة إلى اليمين",centerVertically:"توسيط عمودي",centerHorizontally:"توسيط أفقي",distributeHorizontally:"التوزيع الأفقي",distributeVertically:"التوزيع عمودياً",flipHorizontal:"قلب عامودي",flipVertical:"قلب أفقي",viewMode:"نمط العرض",share:"مشاركة",showStroke:"إظهار منتقي لون الخط",showBackground:"إظهار منتقي لون الخلفية",toggleTheme:"غير النمط",personalLib:"المكتبة الشخصية",excalidrawLib:"مكتبتنا",decreaseFontSize:"تصغير حجم الخط",increaseFontSize:"تكبير حجم الخط",unbindText:"فك ربط النص",bindText:"ربط النص بالحاوية",createContainerFromText:"نص مغلف في حاوية",link:{edit:"تعديل الرابط",editEmbed:"تحرير الرابط وإدراجه",create:"إنشاء رابط",createEmbed:"إنشاء رابط و إدراجه",label:"رابط",labelEmbed:"رابط و إدراج",empty:"لم يتم تعيين رابط"},lineEditor:{edit:"تحرير السطر",exit:"الخروج من المُحرر"},elementLock:{lock:"قفل",unlock:"فتح",lockAll:"قفل الكل",unlockAll:"فتح الكل"},statusPublished:"نُشر",sidebarLock:"إبقاء الشريط الجانبي مفتوح",selectAllElementsInFrame:"تحديد جميع العناصر في الإطار",removeAllElementsFromFrame:"إزالة جميع العناصر من الإطار",eyeDropper:"اختيار اللون من القماش",textToDiagram:"",prompt:""},A={noItems:"لا توجد عناصر أضيفت بعد...",hint_emptyLibrary:"حدد عنصر على القماش لإضافته هنا، أو تثبيت مكتبة من المستودع العام أدناه.",hint_emptyPrivateLibrary:"حدد عنصر على القماش لإضافته هنا."},e={clearReset:"إعادة تعيين اللوحة",exportJSON:"صدر الملف",exportImage:"تصدير الصورة...",export:"حفظ إلى...",copyToClipboard:"نسخ إلى الحافظة",save:"احفظ للملف الحالي",saveAs:"حفظ كـ",load:"فتح",getShareableLink:"احصل على رابط المشاركة",close:"غلق",selectLanguage:"اختر اللغة",scrollBackToContent:"الرجوع إلى المحتوى",zoomIn:"تكبير",zoomOut:"تصغير",resetZoom:"إعادة تعيين الشاشة",menu:"القائمة",done:"تم",edit:"تعديل",undo:"تراجع",redo:"إعادة تنفيذ",resetLibrary:"إعادة ضبط المكتبة",createNewRoom:"إنشاء غرفة جديدة",fullScreen:"شاشة كاملة",darkMode:"الوضع المظلم",lightMode:"الوضع المضيء",zenMode:"وضع التأمل",objectsSnapMode:"التقط إلى العناصر",exitZenMode:"إلغاء الوضع الليلى",cancel:"إلغاء",clear:"مسح",remove:"إزالة",embed:"تبديل الإدراج",publishLibrary:"انشر",submit:"أرسل",confirm:"تأكيد",embeddableInteractionButton:"اضغط للتفاعل"},a={clearReset:"هذا سيُزيل كامل اللوحة. هل أنت متأكد؟",couldNotCreateShareableLink:"تعذر إنشاء رابطة المشاركة.",couldNotCreateShareableLinkTooBig:"تعذر إنشاء رابط قابل للمشاركة: المشهد كبير جدًا",couldNotLoadInvalidFile:"تعذر التحميل، الملف غير صالح",importBackendFailed:"فشل الاستيراد من الخادوم.",cannotExportEmptyCanvas:"لا يمكن تصدير لوحة فارغة.",couldNotCopyToClipboard:"تعذر النسخ إلى الحافظة.",decryptFailed:"تعذر فك تشفير البيانات.",uploadedSecurly:"تم تأمين التحميل بتشفير النهاية إلى النهاية، مما يعني أن خادوم Excalidraw والأطراف الثالثة لا يمكنها قراءة المحتوى.",loadSceneOverridePrompt:"تحميل الرسم الخارجي سيحل محل المحتوى الموجود لديك. هل ترغب في المتابعة؟",collabStopOverridePrompt:`إيقاف الجلسة سيؤدي إلى الكتابة فوق رسومك السابقة المخزنة داخليا. هل أنت متأكد؟
2
2
 
3
3
  (إذا كنت ترغب في الاحتفاظ برسمك المخزن داخليا، ببساطة أغلق علامة تبويب المتصفح بدلاً من ذلك.)`,errorAddingToLibrary:"تعذر إضافة العنصر للمكتبة",errorRemovingFromLibrary:"تعذر إزالة العنصر من المكتبة",confirmAddLibrary:"هذا سيضيف {{numShapes}} شكل إلى مكتبتك. هل أنت متأكد؟",imageDoesNotContainScene:"يبدو أن هذه الصورة لا تحتوي على أي بيانات مشهد. هل قمت بتمكين تضمين المشهد أثناء التصدير؟",cannotRestoreFromImage:"تعذر استعادة المشهد من ملف الصورة",invalidSceneUrl:"تعذر استيراد المشهد من عنوان URL المتوفر. إما أنها مشوهة، أو لا تحتوي على بيانات Excalidraw JSON صالحة.",resetLibrary:"هذا سوف يمسح مكتبتك. هل أنت متأكد؟",removeItemsFromsLibrary:"حذف {{count}} عنصر (عناصر) من المكتبة؟",invalidEncryptionKey:"مفتاح التشفير يجب أن يكون من 22 حرفاً. التعاون المباشر معطل.",collabOfflineWarning:`لا يوجد اتصال بالانترنت.
4
4
  لن يتم حفظ التغييرات التي قمت بها!`},r={unsupportedFileType:"نوع الملف غير مدعوم.",imageInsertError:"تعذر إدراج الصورة. حاول مرة أخرى لاحقاً...",fileTooBig:"الملف كبير جداً. الحد الأقصى المسموح به للحجم هو {{maxSize}}.",svgImageInsertError:"تعذر إدراج صورة SVG. يبدو أن ترميز SVG غير صحيح.",failedToFetchImage:"",invalidSVGString:"SVG غير صالح.",cannotResolveCollabServer:"تعذر الاتصال بخادم التعاون. الرجاء إعادة تحميل الصفحة والمحاولة مرة أخرى.",importLibraryError:"تعذر تحميل المكتبة",collabSaveFailed:"تعذر الحفظ في قاعدة البيانات. إذا استمرت المشاكل، يفضل أن تحفظ ملفك محليا كي لا تفقد عملك.",collabSaveFailed_sizeExceeded:"تعذر الحفظ في قاعدة البيانات، يبدو أن القماش كبير للغاية، يفضّل حفظ الملف محليا كي لا تفقد عملك.",imageToolNotSupported:"",brave_measure_text_error:{line1:"يبدو أنك تستخدم متصفح Brave مع إعداد <bold>حظر صارم لتتبع البصمة</bold>.",line2:"قد يؤدي هذا إلى كسر <bold>عناصر النص</bold> في الرسومات الخاصة بك.",line3:"من المستحسن إلغاء تفعيل هذا الإعداد. يمكنك اتباع <link>هذه الخطوات</link> لفعل ذلك.",line4:"إذا لم يصلح تعطيل هذا الإعداد طريقة عرض النصوص، الرجاء كتابة <issueLink>بلاغ</issueLink> على حسابنا في GitHub، أو راسلنا على <discordLink>Discord</discordLink>"},libraryElementTypeError:{embeddable:"لا يمكن إضافة العناصر القابلة للتضمين في المكتبة.",iframe:"",image:"سوف يتم دعم إضافة صور إلى المكتبة قريباً!"},asyncPasteFailedOnRead:"",asyncPasteFailedOnParse:"",copyToSystemClipboardFailed:""},o={selection:"تحديد",image:"إدراج صورة",rectangle:"مستطيل",diamond:"مضلع",ellipse:"دائرة",arrow:"سهم",line:"خط",freedraw:"رسم",text:"نص",library:"مكتبة",lock:"الحفاظ على أداة التحديد نشطة بعد الرسم",penMode:"وضع القلم - امنع اللمس",link:"إضافة/تحديث الرابط للشكل المحدد",eraser:"ممحاة",frame:"أداة الإطار",magicframe:"",embeddable:"تضمين ويب",laser:"مؤشر ليزر",hand:"يد (أداة الإزاحة)",extraTools:"المزيد من أﻷدوات",mermaidToExcalidraw:"",magicSettings:""},t={canvasActions:"إجراءات اللوحة",selectedShapeActions:"إجراءات الشكل المحدد",shapes:"الأشكال"},i={canvasPanning:"لتحريك القماش، اضغط على عجلة الفأرة أو مفتاح المسافة أثناء السحب، أو استخدم أداة اليد",linearElement:"انقر لبدء نقاط متعددة، اسحب لخط واحد",freeDraw:"انقر واسحب، افرج عند الانتهاء",text:"نصيحة: يمكنك أيضًا إضافة نص بالنقر المزدوج في أي مكان بأداة الاختيار",embeddable:"اضغط مع السحب لإنشاء موقع ويب مضمّن",text_selected:"انقر نقراً مزدوجاً أو اضغط ادخال لتعديل النص",text_editing:"اضغط على Esc أو (Ctrl أو Cmd) + Enter لإنهاء التعديل",linearElementMulti:"انقر فوق النقطة الأخيرة أو اضغط على Esc أو Enter للإنهاء",lockAngle:"يمكنك تقييد الزاوية بالضغط على SHIFT",resize:`يمكنك تقييد النسب بالضغط على SHIFT أثناء تغيير الحجم،