reladraw 0.28.0 → 0.29.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/.claude/skills/reladraw/SKILL.md +1 -1
- package/.claude/skills/reladraw/reference/syntax.md +18 -7
- package/README.md +1 -1
- package/SYNTAX.md +18 -7
- package/dist/page.js +544 -212
- package/dist/search.d.ts +104 -0
- package/dist/search.js +490 -0
- package/dist/viewer.js +3 -1
- package/package.json +1 -1
package/dist/page.js
CHANGED
|
@@ -26,7 +26,8 @@ export function playgroundPage(playground) {
|
|
|
26
26
|
return page(parts, editing, {
|
|
27
27
|
title: folder !== undefined ? `reladraw · ${folder}` : 'reladraw playground: write diagrams in the browser',
|
|
28
28
|
heading: 'reladraw',
|
|
29
|
-
|
|
29
|
+
// The hosted playground has no line under its name: the page shows what it is.
|
|
30
|
+
drawn: folder !== undefined ? `local playground · ${folder} · not saved to a file` : '',
|
|
30
31
|
head: playground.head ?? '',
|
|
31
32
|
library: playground.library ?? '',
|
|
32
33
|
});
|
|
@@ -92,7 +93,8 @@ const PAGE = `<!doctype html>
|
|
|
92
93
|
padding: 10px 16px;
|
|
93
94
|
border-bottom: 1px solid color-mix(in srgb, var(--rule) 45%, transparent);
|
|
94
95
|
}
|
|
95
|
-
.heading { flex:
|
|
96
|
+
.heading { flex: 0 1 auto; min-width: 0; }
|
|
97
|
+
header .tools { margin-left: auto; }
|
|
96
98
|
h1 { margin: 0; font-size: 16px; font-weight: 600; overflow-wrap: anywhere; }
|
|
97
99
|
.drawn { margin: 0; font-size: 12px; color: var(--muted); overflow-wrap: anywhere; }
|
|
98
100
|
.menu-list hr { width: 100%; margin: 2px 0; border: 0; border-top: 1px solid color-mix(in srgb, var(--rule) 45%, transparent); }
|
|
@@ -271,6 +273,10 @@ const PAGE = `<!doctype html>
|
|
|
271
273
|
@media (prefers-reduced-motion: reduce) { #view g[data-edge] :is(line, path, polyline) { animation: none !important; } }
|
|
272
274
|
aside h2 { margin: 0; padding: 10px 16px 6px; font-size: 13px; font-weight: 600; }
|
|
273
275
|
aside h2 .what { display: block; font-weight: 400; font-size: 12px; color: var(--muted); }
|
|
276
|
+
/* Close: a small square button, bordered like the buttons beside it. */
|
|
277
|
+
button.close { width: 24px; height: 24px; padding: 0; font-size: 17px; line-height: 1; color: var(--ink);
|
|
278
|
+
display: inline-flex; align-items: center; justify-content: center; }
|
|
279
|
+
aside h2 .close { float: right; margin: -2px -8px 0 8px; }
|
|
274
280
|
aside ol { list-style: none; margin: 0; padding: 0 8px 12px; overflow: auto; }
|
|
275
281
|
aside li button {
|
|
276
282
|
display: block; width: 100%; text-align: left; white-space: normal; border-color: transparent;
|
|
@@ -278,6 +284,22 @@ const PAGE = `<!doctype html>
|
|
|
278
284
|
}
|
|
279
285
|
aside li button.chosen { border-color: var(--accent); }
|
|
280
286
|
aside li.none { padding: 6px 8px; color: var(--muted); font-size: 13px; }
|
|
287
|
+
/* Findings from an older version of the source: dimmed, not cleared. */
|
|
288
|
+
aside li.again, #band .again { padding: 6px 8px; color: var(--muted); font-size: 12px; }
|
|
289
|
+
aside.stale li:not(.again):not(.error), #band.stale .found { opacity: 0.5; }
|
|
290
|
+
/* While the source is open, the findings sit in a band under the picture,
|
|
291
|
+
taking height from it, never width. */
|
|
292
|
+
#picture { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; }
|
|
293
|
+
#band { flex: none; max-height: 35%; overflow: auto; padding: 4px 12px 8px; font-size: 13px;
|
|
294
|
+
border-top: 1px solid color-mix(in srgb, var(--rule) 45%, transparent); }
|
|
295
|
+
#band[hidden] { display: none; }
|
|
296
|
+
#band .head { display: flex; align-items: center; gap: 8px; padding: 2px 0; }
|
|
297
|
+
#band .head b { font-size: 12px; font-weight: 600; color: var(--muted); }
|
|
298
|
+
#band .head .gap { flex: 1; }
|
|
299
|
+
#band .head button:not(.close) { font-size: 12px; padding: 1px 8px; }
|
|
300
|
+
#band .found button { display: block; width: 100%; text-align: left; white-space: normal; border-color: transparent; padding: 3px 6px; }
|
|
301
|
+
#band .found button.chosen { border-color: var(--accent); }
|
|
302
|
+
#band .again { padding: 2px 0; }
|
|
281
303
|
aside li.error { padding: 6px 8px; margin: 2px 0; border-left: 3px solid var(--down); font-size: 13px; }
|
|
282
304
|
aside li.error .kind { color: var(--down); }
|
|
283
305
|
#live {
|
|
@@ -295,12 +317,34 @@ const PAGE = `<!doctype html>
|
|
|
295
317
|
background: color-mix(in srgb, var(--ink) 4%, var(--page));
|
|
296
318
|
}
|
|
297
319
|
#editor[hidden] { display: none; }
|
|
298
|
-
|
|
320
|
+
/* The picture's controls float over it: Check at its top left, the zoom
|
|
321
|
+
on a card at its bottom right. They sit beside the view, not in it, so a
|
|
322
|
+
press on them never starts a pan. */
|
|
323
|
+
#canvas { position: relative; flex: 1; display: flex; min-height: 0; }
|
|
324
|
+
.floating { position: absolute; z-index: 4;
|
|
325
|
+
background: color-mix(in srgb, var(--ink) 7%, var(--page));
|
|
326
|
+
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45); }
|
|
327
|
+
.floating[hidden] { display: none; }
|
|
328
|
+
#check { left: 12px; top: 12px; padding: 5px 14px; }
|
|
329
|
+
#check:hover { background: color-mix(in srgb, var(--ink) 14%, var(--page)); }
|
|
330
|
+
#zoom-card { right: 12px; bottom: 12px; display: flex; align-items: center; gap: 5px; padding: 4px 5px; border-radius: 9px;
|
|
331
|
+
border: 1px solid color-mix(in srgb, var(--rule) 55%, transparent); }
|
|
332
|
+
/* The card a shade darker than its buttons, so each reads as a button. */
|
|
333
|
+
#zoom-card { background: color-mix(in srgb, var(--ink) 4%, var(--page)); }
|
|
334
|
+
#zoom-card button { background: color-mix(in srgb, var(--ink) 13%, var(--page)); }
|
|
335
|
+
#zoom-card button:hover { background: color-mix(in srgb, var(--ink) 20%, var(--page)); }
|
|
336
|
+
#zoom-card button.pm { font-size: 17px; line-height: 1; padding: 2px 9px; min-width: 30px; }
|
|
337
|
+
#zoom-card .divider { width: 1px; height: 18px; margin: 0 3px; background: color-mix(in srgb, var(--rule) 70%, transparent); }
|
|
338
|
+
body:has(.tools.keyed) #check kbd.tag { display: inline; }
|
|
339
|
+
.src-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 6px 10px; font-size: 12.5px;
|
|
299
340
|
border-bottom: 1px solid color-mix(in srgb, var(--rule) 45%, transparent); }
|
|
300
341
|
.src-head .file { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
301
342
|
#unsaved { color: var(--sel); }
|
|
302
343
|
#unsaved[hidden] { display: none; }
|
|
303
344
|
.src-head .gap, #changed .gap { flex: 1; }
|
|
345
|
+
#file-menu[hidden] { display: none; }
|
|
346
|
+
#file-list { left: 0; right: auto; min-width: 12rem; }
|
|
347
|
+
#file-button.open { border-color: var(--sel); background: color-mix(in srgb, var(--sel) 22%, transparent); }
|
|
304
348
|
.src-head kbd { font: 11px system-ui, sans-serif; color: var(--muted); margin-left: 4px; }
|
|
305
349
|
#changed { display: flex; align-items: center; gap: 6px; padding: 6px 10px; font-size: 12.5px;
|
|
306
350
|
background: color-mix(in srgb, var(--sel) 18%, var(--page));
|
|
@@ -326,7 +370,17 @@ const PAGE = `<!doctype html>
|
|
|
326
370
|
.t-relation, .t-arrow { color: var(--syn-relation); }
|
|
327
371
|
.t-attribute { color: var(--syn-attribute); }
|
|
328
372
|
.t-value, .t-color { color: var(--syn-value); }
|
|
329
|
-
|
|
373
|
+
/* Edit | View: which mode the page is in, the one it is in filled. Edit
|
|
374
|
+
is on the left, the side the source opens on. */
|
|
375
|
+
.mode { display: inline-flex; position: relative; }
|
|
376
|
+
.mode[hidden] { display: none; }
|
|
377
|
+
.mode button { border-radius: 0; color: var(--muted); padding: 4px 12px; }
|
|
378
|
+
.mode button:first-child { border-radius: 6px 0 0 6px; }
|
|
379
|
+
.mode button:last-child { border-radius: 0 6px 6px 0; border-left: 0; }
|
|
380
|
+
.mode button[aria-pressed="true"] { background: color-mix(in srgb, var(--ink) 20%, transparent); color: var(--ink); font-weight: 600; }
|
|
381
|
+
.mode button[aria-pressed="true"]:hover { cursor: default; }
|
|
382
|
+
header:has(.tools.keyed) .mode kbd.tag { display: inline; }
|
|
383
|
+
header:has(.tools.keyed) .mode { outline: 2px solid var(--sel); outline-offset: 4px; border-radius: 6px; }
|
|
330
384
|
.menu-list.wide { min-width: 340px; }
|
|
331
385
|
.menu-list .row .when { color: var(--muted); font-variant-numeric: tabular-nums; width: 3.2em; flex: none; }
|
|
332
386
|
.menu-list .row .who { width: 4.5em; flex: none; }
|
|
@@ -342,27 +396,57 @@ const PAGE = `<!doctype html>
|
|
|
342
396
|
aside li.error .goto { cursor: pointer; text-decoration: underline dotted; }
|
|
343
397
|
|
|
344
398
|
/* ---- a page with no file: the playground ---- */
|
|
345
|
-
|
|
346
|
-
.heading
|
|
347
|
-
.links
|
|
399
|
+
/* GitHub, as its mark beside the name. */
|
|
400
|
+
.heading h1 { display: inline; vertical-align: middle; }
|
|
401
|
+
.links { display: inline-flex; vertical-align: middle; margin-left: 10px; }
|
|
402
|
+
.links a { display: inline-flex; color: var(--muted); padding: 2px; border-radius: 4px; }
|
|
348
403
|
.links a:hover { color: var(--ink); }
|
|
404
|
+
.links a:focus-visible { outline: 2px solid var(--accent); }
|
|
405
|
+
/* A link as a row of a menu, like the buttons around it. */
|
|
406
|
+
.menu-list a.row { color: var(--ink); text-decoration: none; font-size: 13px; border: 1px solid transparent; border-radius: 6px; }
|
|
407
|
+
.menu-list a.row:hover { background: color-mix(in srgb, var(--ink) 10%, transparent); }
|
|
408
|
+
#help-menu[hidden] { display: none; }
|
|
409
|
+
.drawn:empty { display: none; }
|
|
349
410
|
/* The divider between the source and the picture: dragged, or moved with the arrows. */
|
|
350
411
|
#split { flex: none; width: 6px; margin: 0 -3px; z-index: 2; cursor: col-resize; touch-action: none; }
|
|
351
412
|
#split[hidden] { display: none; }
|
|
352
413
|
#split:hover, #split.dragging, #split:focus-visible { background: color-mix(in srgb, var(--accent) 45%, transparent); outline: 0; }
|
|
353
414
|
body.dragging { cursor: col-resize; user-select: none; }
|
|
354
|
-
/* The cheatsheet
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
#cheatsheet
|
|
358
|
-
#cheatsheet
|
|
359
|
-
#cheatsheet
|
|
360
|
-
|
|
361
|
-
#cheatsheet
|
|
362
|
-
#
|
|
363
|
-
|
|
364
|
-
#
|
|
365
|
-
#cheatsheet
|
|
415
|
+
/* The cheatsheet: a button in the source's bar opens a large window, a
|
|
416
|
+
card per topic, each row an example in the editor's colors beside what
|
|
417
|
+
it does. The filter narrows the rows as you type. */
|
|
418
|
+
#cheatsheet { width: min(1180px, calc(100vw - 32px)); height: min(820px, calc(100vh - 32px)); max-width: none; max-height: none; }
|
|
419
|
+
#cheatsheet[open] { display: flex; flex-direction: column; }
|
|
420
|
+
#cheatsheet .cs-head { display: flex; align-items: center; gap: 12px; padding: 10px 16px;
|
|
421
|
+
border-bottom: 1px solid color-mix(in srgb, var(--rule) 45%, transparent); }
|
|
422
|
+
#cheatsheet .cs-head h2 { font-size: 14px; }
|
|
423
|
+
#cheat-filter { flex: 0 1 340px; font: inherit; font-size: 13px; color: var(--ink); padding: 4px 10px; border-radius: 6px;
|
|
424
|
+
background: color-mix(in srgb, var(--ink) 4%, var(--page)); border: 1px solid color-mix(in srgb, var(--rule) 70%, transparent); }
|
|
425
|
+
#cheat-filter:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
426
|
+
#cheatsheet .cs-head .gap { flex: 1; }
|
|
427
|
+
#cheatsheet .cs-grid { flex: 1; min-height: 0; overflow: auto; padding: 14px 16px 18px; }
|
|
428
|
+
/* Cards fill three columns top to bottom; the page fills each column as the
|
|
429
|
+
window opens, so the columns come out about the same height. */
|
|
430
|
+
.cs-cols { display: grid; grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr)); gap: 14px; align-items: start; }
|
|
431
|
+
.cs-col { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
|
|
432
|
+
.cs-card { border-radius: 8px; overflow: hidden;
|
|
433
|
+
border: 1px solid color-mix(in srgb, var(--hue) 30%, transparent);
|
|
434
|
+
background: color-mix(in srgb, var(--ink) 3%, var(--page)); }
|
|
435
|
+
.cs-card[hidden] { display: none; }
|
|
436
|
+
.cs-card h3 { margin: 0; padding: 7px 12px; font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
|
|
437
|
+
color: var(--hue); background: color-mix(in srgb, var(--hue) 12%, transparent);
|
|
438
|
+
border-bottom: 1px solid color-mix(in srgb, var(--hue) 22%, transparent); }
|
|
439
|
+
.cs-row { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); gap: 4px 12px; padding: 6px 12px; font-size: 12.5px;
|
|
440
|
+
border-top: 1px solid color-mix(in srgb, var(--rule) 25%, transparent); }
|
|
441
|
+
.cs-card h3 + .cs-row { border-top: 0; }
|
|
442
|
+
.cs-row[hidden] { display: none; }
|
|
443
|
+
.cs-row code { font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; overflow-wrap: anywhere; color: var(--ink); }
|
|
444
|
+
.cs-row > span { color: var(--muted); }
|
|
445
|
+
.cs-note { margin: 0; padding: 7px 12px 9px; font-size: 12px; color: var(--muted);
|
|
446
|
+
border-top: 1px solid color-mix(in srgb, var(--rule) 25%, transparent); }
|
|
447
|
+
.cs-note code { font: 11.5px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: var(--ink); }
|
|
448
|
+
.cs-none { margin: 0; padding: 24px 16px; color: var(--muted); }
|
|
449
|
+
.cs-none[hidden] { display: none; }
|
|
366
450
|
dialog { padding: 0; color: var(--ink); background: var(--page); overflow: hidden;
|
|
367
451
|
border: 1px solid color-mix(in srgb, var(--rule) 70%, transparent); border-radius: 6px; }
|
|
368
452
|
dialog::backdrop { background: rgba(0, 0, 0, .6); }
|
|
@@ -416,20 +500,14 @@ const PAGE = `<!doctype html>
|
|
|
416
500
|
<body>
|
|
417
501
|
<header>
|
|
418
502
|
<div class="heading">
|
|
419
|
-
<h1>{{heading}}</h1>
|
|
420
|
-
<p class="drawn">{{drawn}}</p>
|
|
503
|
+
<h1>{{heading}}</h1>{{links}}
|
|
504
|
+
<p class="drawn">{{drawn}}</p>
|
|
421
505
|
</div>
|
|
506
|
+
<span id="mode" class="mode" role="group" aria-label="Mode" hidden>
|
|
507
|
+
<button id="edit" type="button" title="The source beside the picture (e)" aria-pressed="false">Edit<kbd class="tag">e</kbd></button>
|
|
508
|
+
<button id="mode-view" type="button" title="Only the picture (e)" aria-pressed="true">View</button>
|
|
509
|
+
</span>
|
|
422
510
|
<div class="tools">
|
|
423
|
-
<button id="browse" type="button" title="Open one of the examples in the editor" aria-haspopup="dialog" hidden>Examples</button>
|
|
424
|
-
<button id="edit"type="button" title="Show or hide the source (e)" aria-pressed="false" hidden>Edit<kbd class="tag">e</kbd></button>
|
|
425
|
-
<span class="gap" id="edit-gap" hidden></span>
|
|
426
|
-
<button id="out" type="button" title="Zoom out (-)" aria-label="Zoom out">−</button>
|
|
427
|
-
<span id="scale" aria-live="polite">100%</span>
|
|
428
|
-
<button id="in" type="button" title="Zoom in (+)" aria-label="Zoom in">+</button>
|
|
429
|
-
<button id="fit" type="button" title="The whole diagram in view (0)">Fit</button>
|
|
430
|
-
<button id="actual" type="button" title="The drawing at its own scale (1)">100%</button>
|
|
431
|
-
<span class="gap"></span>
|
|
432
|
-
<button id="full" type="button" title="Full screen (f)"><span class="label">Full screen</span><kbd class="tag">f</kbd></button>
|
|
433
511
|
<span class="menu" id="versions-menu" hidden>
|
|
434
512
|
<button id="versions-button" type="button" aria-haspopup="true" aria-expanded="false" aria-controls="versions-list">Versions ▾<kbd class="tag">o</kbd></button>
|
|
435
513
|
<span id="versions-list" class="menu-list wide" role="menu" hidden></span>
|
|
@@ -446,7 +524,7 @@ const PAGE = `<!doctype html>
|
|
|
446
524
|
</span>
|
|
447
525
|
</span>
|
|
448
526
|
<span class="menu">
|
|
449
|
-
<button id="view-button" type="button" aria-haspopup="true" aria-expanded="false" aria-controls="view-list">
|
|
527
|
+
<button id="view-button" type="button" aria-haspopup="true" aria-expanded="false" aria-controls="view-list">Settings ▾<kbd class="tag">s</kbd></button>
|
|
450
528
|
<span id="view-list" class="menu-list" role="menu" hidden>
|
|
451
529
|
<button id="theme-row" class="row" type="button" role="menuitem" data-key="t" aria-haspopup="true" title="Draw the diagram in another theme"><span class="name">Theme <span class="value"></span> ▸</span><kbd>t</kbd></button>
|
|
452
530
|
<hr>
|
|
@@ -454,11 +532,13 @@ const PAGE = `<!doctype html>
|
|
|
454
532
|
<button id="auto-row" class="row switch" type="button" role="menuitemcheckbox" data-key="a" title="Show the side panel only while something is selected or the findings are open"><span class="sw"></span><span class="name">Auto-hide panel</span><span class="word"></span><kbd>a</kbd></button>
|
|
455
533
|
<hr>
|
|
456
534
|
<button id="findings-row" class="row switch" type="button" role="menuitemcheckbox" data-key="f" title="Show or hide the findings in the side panel"><span class="sw"></span><span class="name">Findings</span><span class="word"></span><kbd>f</kbd></button>
|
|
535
|
+
<button id="autocheck-row" class="row switch" type="button" role="menuitemcheckbox" data-key="c" title="Look for findings every time the picture is drawn, without pressing Check" hidden><span class="sw"></span><span class="name">Check as I type</span><span class="word"></span><kbd>c</kbd></button>
|
|
457
536
|
<hr>
|
|
458
537
|
<button id="guides-row" class="row switch" type="button" role="menuitemcheckbox" data-key="g" title="Draw the guides: everything written with guide in front"><span class="sw"></span><span class="name">Guides</span><span class="word"></span><kbd>g</kbd></button>
|
|
459
538
|
<span id="theme-list" class="menu-list sub" role="menu" hidden></span>
|
|
460
539
|
</span>
|
|
461
540
|
</span>
|
|
541
|
+
<button id="full" type="button" title="Full screen (f)"><span class="label">Full screen</span><kbd class="tag">f</kbd></button>
|
|
462
542
|
</div>
|
|
463
543
|
</header>
|
|
464
544
|
<div id="strip" hidden><span class="crumbs"></span><span class="hint"></span></div>
|
|
@@ -468,11 +548,26 @@ const PAGE = `<!doctype html>
|
|
|
468
548
|
<div id="changed" role="alert" hidden><span class="what"></span><span class="gap"></span><button id="load-disk" type="button">Load it</button><button id="keep-mine" type="button">Keep mine</button></div>
|
|
469
549
|
<div class="src-head">
|
|
470
550
|
<span class="file"></span><span id="unsaved" title="Not saved to the file" hidden>●</span><span class="gap"></span>
|
|
471
|
-
<
|
|
551
|
+
<span class="menu" id="file-menu" hidden>
|
|
552
|
+
<button id="file-button" type="button" aria-haspopup="true" aria-expanded="false" aria-controls="file-list"><span class="name">File ▾</span></button>
|
|
553
|
+
<span id="file-list" class="menu-list" role="menu" hidden>
|
|
554
|
+
<button id="browse" class="row" type="button" role="menuitem" title="Open one of the examples in the editor" aria-haspopup="dialog"><span class="name">Examples…</span></button>
|
|
555
|
+
<button id="open-file" class="row" type="button" role="menuitem" title="Open a .reladraw file into the source"><span class="name">Open…</span></button>
|
|
556
|
+
<hr>
|
|
557
|
+
<button id="write-row" class="row" type="button" role="menuitem" title="Save the source as a .reladraw file"><span class="name">Save…</span><kbd>⌘S</kbd></button>
|
|
558
|
+
</span>
|
|
559
|
+
</span>
|
|
472
560
|
<input id="file-input" type="file" accept=".reladraw,text/plain" hidden>
|
|
473
561
|
<button id="format" type="button" title="Lay the source out again, as reladraw format does, with the nearest .reladraw-format (Shift+Alt+F)"><span class="name">Format</span></button>
|
|
474
562
|
<button id="format-open" type="button" aria-haspopup="dialog" title="How Format lays the source out" hidden>Format settings…</button>
|
|
475
563
|
<button id="write" type="button" title="Write the source to the file (⌘S or Ctrl+S)"><span class="name">Save</span><kbd>⌘S</kbd></button>
|
|
564
|
+
<span class="menu" id="help-menu" hidden>
|
|
565
|
+
<button id="help-button" type="button" aria-haspopup="true" aria-expanded="false" aria-controls="help-list"><span class="name">Help ▾</span></button>
|
|
566
|
+
<span id="help-list" class="menu-list" role="menu" hidden>
|
|
567
|
+
<button id="cheat-open" class="row" type="button" role="menuitem" title="The language on one screen"><span class="name">Cheatsheet</span><kbd>?</kbd></button>
|
|
568
|
+
<a id="syntax-link" class="row" role="menuitem" href="https://github.com/reladraw/reladraw/blob/main/SYNTAX.md" target="_blank" rel="noopener" title="The full syntax reference, on GitHub"><span class="name">Syntax reference ↗</span></a>
|
|
569
|
+
</span>
|
|
570
|
+
</span>
|
|
476
571
|
</div>
|
|
477
572
|
<div class="code">
|
|
478
573
|
<pre id="paint" aria-hidden="true"></pre>
|
|
@@ -480,18 +575,32 @@ const PAGE = `<!doctype html>
|
|
|
480
575
|
</div>{{cheatsheet}}
|
|
481
576
|
</section>
|
|
482
577
|
<div id="split" role="separator" aria-orientation="vertical" aria-label="Resize the source and the diagram" tabindex="0" title="Drag to resize; double-click to reset" hidden></div>
|
|
578
|
+
<div id="picture">
|
|
579
|
+
<div id="canvas">
|
|
580
|
+
<button id="check" class="floating" type="button" title="Look for overlaps and other places the picture may not read as meant (c)" hidden>Check<kbd class="tag">c</kbd></button>
|
|
581
|
+
<div id="zoom-card" class="floating">
|
|
582
|
+
<button id="out" class="pm" type="button" title="Zoom out (-)" aria-label="Zoom out">−</button>
|
|
583
|
+
<span id="scale" aria-live="polite">100%</span>
|
|
584
|
+
<button id="in" class="pm" type="button" title="Zoom in (+)" aria-label="Zoom in">+</button>
|
|
585
|
+
<span class="divider"></span>
|
|
586
|
+
<button id="fit" type="button" title="The whole diagram in view (0)">Fit</button>
|
|
587
|
+
<button id="actual" type="button" title="The drawing at its own scale (1)">100%</button>
|
|
588
|
+
</div>
|
|
483
589
|
<div id="view">
|
|
484
590
|
<div id="stage">
|
|
485
591
|
{{drawing}}
|
|
486
592
|
</div>
|
|
487
593
|
<div id="tip"></div>
|
|
488
594
|
</div>
|
|
595
|
+
</div>
|
|
596
|
+
<div id="band" aria-label="Findings" aria-live="polite" hidden></div>
|
|
597
|
+
</div>
|
|
489
598
|
<div id="side">
|
|
490
599
|
<section id="selected" aria-label="Selected" aria-live="polite">
|
|
491
600
|
<p class="empty">Click a box or a line to see its links and what it connects to.</p>
|
|
492
601
|
</section>
|
|
493
602
|
<aside id="findings" aria-label="Findings">
|
|
494
|
-
<h2>Findings <span class="what">where the picture, drawn as written, may not read as meant</span></h2>
|
|
603
|
+
<h2><button id="findings-close" class="close" type="button" title="Close the findings" aria-label="Close the findings">×</button>Findings <span class="what">where the picture, drawn as written, may not read as meant</span></h2>
|
|
495
604
|
<ol id="finding-list"></ol>
|
|
496
605
|
</aside>
|
|
497
606
|
</div>
|
|
@@ -516,7 +625,7 @@ const PAGE = `<!doctype html>
|
|
|
516
625
|
// Another theme is carried as the colors that differ from the first, put
|
|
517
626
|
// back in place the first time it is shown.
|
|
518
627
|
var VARIES = new RegExp(DATA.varies, 'gi');
|
|
519
|
-
// Whether guides are drawn, which the
|
|
628
|
+
// Whether guides are drawn, which the Settings menu's switch changes. The
|
|
520
629
|
// drawings with guides the other way are a second set, packed the same way
|
|
521
630
|
// against its own first theme.
|
|
522
631
|
var guidesShown = DATA.guidesShown;
|
|
@@ -730,9 +839,22 @@ const PAGE = `<!doctype html>
|
|
|
730
839
|
|
|
731
840
|
var panel = document.getElementById('findings');
|
|
732
841
|
var list = document.getElementById('finding-list');
|
|
733
|
-
var
|
|
734
|
-
|
|
735
|
-
//
|
|
842
|
+
var band = document.getElementById('band');
|
|
843
|
+
// A page with an editor looks for findings only when Check is pressed,
|
|
844
|
+
// unless "Check as I type" is on: on a large diagram the checks would hold
|
|
845
|
+
// up the typing. A page without one carries the findings it was made with.
|
|
846
|
+
var AUTO_CHECK = 'reladraw-page-autocheck';
|
|
847
|
+
var autoCheck = false;
|
|
848
|
+
try { autoCheck = localStorage.getItem(AUTO_CHECK) === 'on'; } catch (e) {}
|
|
849
|
+
// The text last checked and what was found in it. Findings outlive edits,
|
|
850
|
+
// dimmed, since they describe the source as it was.
|
|
851
|
+
var checked = !EDIT || autoCheck ? { text: DATA.source, findings: DATA.findings } : null;
|
|
852
|
+
var shownText = DATA.source;
|
|
853
|
+
function found() { return checked ? checked.findings : []; }
|
|
854
|
+
function stale() { return !!checked && checked.text !== shownText; }
|
|
855
|
+
var count = found().length;
|
|
856
|
+
|
|
857
|
+
// A row of the Settings menu that is on or off: its switch, and the word beside it.
|
|
736
858
|
function setSwitch(row, on) {
|
|
737
859
|
row.setAttribute('aria-checked', String(on));
|
|
738
860
|
row.querySelector('.word').textContent = on ? 'on' : 'off';
|
|
@@ -741,7 +863,6 @@ const PAGE = `<!doctype html>
|
|
|
741
863
|
var autoRow = document.getElementById('auto-row');
|
|
742
864
|
var findingsRow = document.getElementById('findings-row');
|
|
743
865
|
var guidesRow = document.getElementById('guides-row');
|
|
744
|
-
findingsRow.querySelector('.name').textContent = 'Findings (' + count + ')';
|
|
745
866
|
guidesRow.querySelector('.name').textContent = 'Guides (' + DATA.guideCount + ')';
|
|
746
867
|
|
|
747
868
|
/* ---- the side panel: shown, hidden, or shown only when wanted ---- */
|
|
@@ -760,8 +881,15 @@ const PAGE = `<!doctype html>
|
|
|
760
881
|
function keepPanel() {
|
|
761
882
|
try { localStorage.setItem(KEPT, JSON.stringify({ on: panelOn, autoHide: autoHide })); } catch (e) {}
|
|
762
883
|
}
|
|
884
|
+
// While the source is open the panel would squeeze the picture between two
|
|
885
|
+
// columns, so it behaves as if auto-hidden: it comes up for a selection or
|
|
886
|
+
// an error, and for the findings only when asked.
|
|
887
|
+
function editing() {
|
|
888
|
+
var pane = document.getElementById('editor');
|
|
889
|
+
return !!pane && !pane.hidden;
|
|
890
|
+
}
|
|
763
891
|
function layoutSide() {
|
|
764
|
-
side.hidden = !(panelOn && (!autoHide || selection || !panel.hidden));
|
|
892
|
+
side.hidden = !(panelOn && (!(autoHide || editing()) || selection || !panel.hidden));
|
|
765
893
|
// A finding lights what it is about only while the finding can be seen.
|
|
766
894
|
if ((panel.hidden || side.hidden) && lit && lit.length) unlight();
|
|
767
895
|
setSwitch(panelRow, panelOn);
|
|
@@ -793,6 +921,7 @@ const PAGE = `<!doctype html>
|
|
|
793
921
|
else showPanel(panel.hidden);
|
|
794
922
|
}
|
|
795
923
|
findingsRow.onclick = flipFindings;
|
|
924
|
+
document.getElementById('findings-close').onclick = function () { showPanel(false); };
|
|
796
925
|
if (count === 0) {
|
|
797
926
|
panel.hidden = true;
|
|
798
927
|
} else {
|
|
@@ -834,14 +963,22 @@ const PAGE = `<!doctype html>
|
|
|
834
963
|
zoom.pan((room.left + room.width / 2) - (r.l + r.r) / 2, (room.top + room.height / 2) - (r.t + r.b) / 2);
|
|
835
964
|
}
|
|
836
965
|
|
|
966
|
+
// Whether the source has stopped parsing, and whether that is what opened
|
|
967
|
+
// the findings, so they close again once it parses.
|
|
968
|
+
var failing = false;
|
|
969
|
+
var openedByError = false;
|
|
837
970
|
// The list, made again when a served page redraws. An error the file has
|
|
838
971
|
// stopped parsing with heads it, over the findings of the last drawing that
|
|
839
972
|
// parsed, which is still the one shown.
|
|
973
|
+
var lastError = null;
|
|
840
974
|
function listFindings(error) {
|
|
975
|
+
failing = !!error;
|
|
976
|
+
lastError = error;
|
|
841
977
|
list.textContent = '';
|
|
842
|
-
count =
|
|
843
|
-
findingsRow.querySelector('.name').textContent = 'Findings (' + count + ')';
|
|
978
|
+
count = found().length + (error ? 1 : 0);
|
|
979
|
+
findingsRow.querySelector('.name').textContent = checked || error ? 'Findings (' + count + ')' : 'Findings';
|
|
844
980
|
guidesRow.querySelector('.name').textContent = 'Guides (' + DATA.guideCount + ')';
|
|
981
|
+
panel.classList.toggle('stale', stale());
|
|
845
982
|
if (error) {
|
|
846
983
|
var wrong = element('li', 'error');
|
|
847
984
|
var head = element('span', 'where', error.line ? 'line ' + error.line + ' ' : '');
|
|
@@ -865,11 +1002,88 @@ const PAGE = `<!doctype html>
|
|
|
865
1002
|
document.createTextNode(error.message), element('span', 'note', 'The picture is the last version that drew.'));
|
|
866
1003
|
list.append(wrong);
|
|
867
1004
|
}
|
|
868
|
-
if (
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
1005
|
+
if (!checked) {
|
|
1006
|
+
list.append(element('li', 'none', 'Automatic checking is off. Press Check in the bar above to look for overlaps and other places the picture may not read as meant.'));
|
|
1007
|
+
} else {
|
|
1008
|
+
if (stale()) list.append(element('li', 'again', againText()));
|
|
1009
|
+
if (found().length === 0) list.append(element('li', 'none', 'No findings.'));
|
|
1010
|
+
found().forEach(function (finding) {
|
|
1011
|
+
var item = document.createElement('li');
|
|
1012
|
+
item.append(findingButton(finding));
|
|
1013
|
+
list.append(item);
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
showBand();
|
|
1017
|
+
}
|
|
1018
|
+
function againText() {
|
|
1019
|
+
return autoCheck ? 'From the last version that drew; the source has changed since.'
|
|
1020
|
+
: 'The source has changed since this check. Press Check again.';
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// The band under the picture while the source is open: the first few
|
|
1024
|
+
// findings, and View all for the panel with every one.
|
|
1025
|
+
var BAND_ROWS = 3;
|
|
1026
|
+
var bandClosed = false;
|
|
1027
|
+
function showBand() {
|
|
1028
|
+
var list = found();
|
|
1029
|
+
band.textContent = '';
|
|
1030
|
+
band.hidden = !(EDIT && editing() && checked && !bandClosed && (list.length > 0 || !autoCheck));
|
|
1031
|
+
if (band.hidden) return;
|
|
1032
|
+
band.classList.toggle('stale', stale());
|
|
1033
|
+
var head = element('div', 'head');
|
|
1034
|
+
head.append(element('b', '', list.length ? 'Findings \\u00B7 ' + list.length : 'No findings'));
|
|
1035
|
+
head.append(element('span', 'gap'));
|
|
1036
|
+
if (list.length) {
|
|
1037
|
+
var all = element('button', '', list.length > BAND_ROWS ? 'View all ' + list.length : 'View all');
|
|
1038
|
+
all.type = 'button';
|
|
1039
|
+
all.title = 'Open the findings in the side panel';
|
|
1040
|
+
all.onclick = openFindings;
|
|
1041
|
+
head.append(all);
|
|
1042
|
+
}
|
|
1043
|
+
var close = element('button', 'close', '\\u00D7');
|
|
1044
|
+
close.type = 'button';
|
|
1045
|
+
close.title = 'Close until the next check';
|
|
1046
|
+
close.setAttribute('aria-label', 'Close the findings');
|
|
1047
|
+
close.onclick = function () { bandClosed = true; showBand(); };
|
|
1048
|
+
head.append(close);
|
|
1049
|
+
band.append(head);
|
|
1050
|
+
if (stale()) band.append(element('div', 'again', againText()));
|
|
1051
|
+
var rows = element('div', 'found');
|
|
1052
|
+
list.slice(0, BAND_ROWS).forEach(function (finding) { rows.append(findingButton(finding)); });
|
|
1053
|
+
band.append(rows);
|
|
1054
|
+
}
|
|
1055
|
+
// The panel takes the findings over: the band closes until the next check,
|
|
1056
|
+
// and closing the panel doesn't bring it back.
|
|
1057
|
+
function openFindings() {
|
|
1058
|
+
if (!panelOn) { panelOn = true; keepPanel(); }
|
|
1059
|
+
bandClosed = true;
|
|
1060
|
+
showBand();
|
|
1061
|
+
showPanel(true);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
// Looks for findings in the source as it stands, drawing it again to do so.
|
|
1065
|
+
var checkButton = document.getElementById('check');
|
|
1066
|
+
function runCheck() {
|
|
1067
|
+
if (!EDIT || !lib) return;
|
|
1068
|
+
bandClosed = false;
|
|
1069
|
+
drawnText = null;
|
|
1070
|
+
draw(sourceBox.value, true);
|
|
1071
|
+
// In View mode there is no band, so what was found shows in the panel.
|
|
1072
|
+
if (!editing()) openFindings();
|
|
1073
|
+
}
|
|
1074
|
+
checkButton.onclick = runCheck;
|
|
1075
|
+
var autoCheckRow = document.getElementById('autocheck-row');
|
|
1076
|
+
function setAutoCheck(on) {
|
|
1077
|
+
autoCheck = on;
|
|
1078
|
+
try { localStorage.setItem(AUTO_CHECK, on ? 'on' : 'off'); } catch (e) {}
|
|
1079
|
+
setSwitch(autoCheckRow, on);
|
|
1080
|
+
checkButton.hidden = !EDIT || on;
|
|
1081
|
+
if (on) runCheck();
|
|
1082
|
+
else listFindings(lastError);
|
|
1083
|
+
}
|
|
1084
|
+
autoCheckRow.onclick = function () { setAutoCheck(!autoCheck); };
|
|
1085
|
+
|
|
1086
|
+
function findingButton(finding) {
|
|
873
1087
|
var button = document.createElement('button');
|
|
874
1088
|
button.type = 'button';
|
|
875
1089
|
var where = document.createElement('span');
|
|
@@ -890,8 +1104,7 @@ const PAGE = `<!doctype html>
|
|
|
890
1104
|
light(finding, button);
|
|
891
1105
|
if (EDIT && !editorPane.hidden) goToLine(finding.line);
|
|
892
1106
|
};
|
|
893
|
-
|
|
894
|
-
list.append(item);
|
|
1107
|
+
return button;
|
|
895
1108
|
}
|
|
896
1109
|
listFindings(null);
|
|
897
1110
|
|
|
@@ -1213,14 +1426,14 @@ const PAGE = `<!doctype html>
|
|
|
1213
1426
|
|
|
1214
1427
|
var STRIP = {
|
|
1215
1428
|
bar: ['<b>Menu</b>', 'letter to open \\u00B7 \\u232B back \\u00B7 Esc out'],
|
|
1216
|
-
view: ['Menu \\u203A <b>
|
|
1429
|
+
view: ['Menu \\u203A <b>Settings</b>', 'letter to flip \\u00B7 \\u2191\\u2193 Enter \\u00B7 \\u232B back \\u00B7 Esc out'],
|
|
1217
1430
|
save: ['Menu \\u203A <b>Export</b>', 'letter to save \\u00B7 \\u2191\\u2193 Enter \\u00B7 \\u232B back \\u00B7 Esc out'],
|
|
1218
1431
|
versions: ['Menu \\u203A <b>Versions</b>', '\\u2191\\u2193 Enter to put one in the editor \\u00B7 \\u232B back \\u00B7 Esc out'],
|
|
1219
|
-
theme: ['Menu \\u203A
|
|
1432
|
+
theme: ['Menu \\u203A Settings \\u203A <b>Theme</b>', '\\u2191\\u2193 to try \\u00B7 Enter keep \\u00B7 \\u232B back \\u00B7 Esc out'],
|
|
1220
1433
|
};
|
|
1221
1434
|
|
|
1222
1435
|
function rows() {
|
|
1223
|
-
if (level === 'view') return Array.prototype.slice.call(viewList.querySelectorAll(':scope > .row'));
|
|
1436
|
+
if (level === 'view') return Array.prototype.slice.call(viewList.querySelectorAll(':scope > .row:not([hidden])'));
|
|
1224
1437
|
if (level === 'save') return Array.prototype.slice.call(saveList.querySelectorAll(':scope > .row'));
|
|
1225
1438
|
if (level === 'versions') return Array.prototype.slice.call(versionsList.querySelectorAll(':scope > .row'));
|
|
1226
1439
|
if (level === 'theme') return themeItems.map(function (item) { return item.row; });
|
|
@@ -1717,10 +1930,12 @@ const PAGE = `<!doctype html>
|
|
|
1717
1930
|
var ZOOM_KEYS = { '+': 1, '=': 1, '-': 1, '_': 1, '0': 1, '1': 1 };
|
|
1718
1931
|
|
|
1719
1932
|
// On the window, before the zoom's own keys, so a menu's letters reach the
|
|
1720
|
-
// menu and not the diagram underneath: g flips guides in
|
|
1933
|
+
// menu and not the diagram underneath: g flips guides in Settings, not a grab.
|
|
1721
1934
|
window.addEventListener('keydown', function (event) {
|
|
1722
1935
|
if (event.ctrlKey || event.metaKey || event.altKey) return;
|
|
1723
1936
|
if (event.target.closest && event.target.closest('input, textarea, select, [contenteditable]')) return;
|
|
1937
|
+
// A window open over the page has the keys, Escape included.
|
|
1938
|
+
if (document.querySelector('dialog[open]')) return;
|
|
1724
1939
|
var key = event.key;
|
|
1725
1940
|
if (key === 'Escape') plain();
|
|
1726
1941
|
else if (level === 'pick' || level === 'filter') {
|
|
@@ -1728,6 +1943,8 @@ const PAGE = `<!doctype html>
|
|
|
1728
1943
|
} else if (level === null) {
|
|
1729
1944
|
if (key === 'm' || key === 'M') setLevel('bar');
|
|
1730
1945
|
else if (key === 'e' && EDIT) toggleEditor();
|
|
1946
|
+
else if (key === 'c' && !checkButton.hidden) runCheck();
|
|
1947
|
+
else if (key === '?' && cheatBox) openCheatsheet();
|
|
1731
1948
|
else if (key === 's') startHints('all');
|
|
1732
1949
|
else if (key === '/') startHints('all', true);
|
|
1733
1950
|
else if (key === 'n') startHints('neighbors');
|
|
@@ -1749,10 +1966,11 @@ const PAGE = `<!doctype html>
|
|
|
1749
1966
|
else if (key.length === 1) {
|
|
1750
1967
|
var letter = key.toLowerCase();
|
|
1751
1968
|
if (level === 'bar') {
|
|
1752
|
-
if (letter === '
|
|
1969
|
+
if (letter === 's') setLevel('view');
|
|
1753
1970
|
else if (letter === 'x') setLevel('save');
|
|
1754
1971
|
else if (letter === 'o' && tiedFile()) setLevel('versions');
|
|
1755
1972
|
else if (letter === 'e' && EDIT) { setLevel(null); toggleEditor(); }
|
|
1973
|
+
else if (letter === 'c' && !checkButton.hidden) { setLevel(null); runCheck(); }
|
|
1756
1974
|
else if (letter === 'f' && !full.hidden) { setLevel(null); toggleFull(); }
|
|
1757
1975
|
} else if (level === 'view' || level === 'save' || level === 'versions') {
|
|
1758
1976
|
rows().forEach(function (row, i) {
|
|
@@ -1812,6 +2030,7 @@ const PAGE = `<!doctype html>
|
|
|
1812
2030
|
if (selection && !(selection.kind === 'node' ? graph.nodes[selection.key] : graph.edges[selection.key])) selection = null;
|
|
1813
2031
|
if (level === 'pick' || level === 'filter') setLevel(null);
|
|
1814
2032
|
listFindings(null);
|
|
2033
|
+
if (openedByError) { openedByError = false; if (editing()) showPanel(false); }
|
|
1815
2034
|
redraw();
|
|
1816
2035
|
}
|
|
1817
2036
|
|
|
@@ -1837,6 +2056,40 @@ const PAGE = `<!doctype html>
|
|
|
1837
2056
|
var paintBox = document.getElementById('paint');
|
|
1838
2057
|
var unsavedMark = document.getElementById('unsaved');
|
|
1839
2058
|
var writeButton = document.getElementById('write');
|
|
2059
|
+
// A menu in the source's bar: its button opens and closes its list, and a
|
|
2060
|
+
// row chosen, a click elsewhere or Escape closes it. Returns its closer.
|
|
2061
|
+
function sourceMenu(menu, button, list) {
|
|
2062
|
+
function close() {
|
|
2063
|
+
list.hidden = true;
|
|
2064
|
+
button.classList.remove('open');
|
|
2065
|
+
button.setAttribute('aria-expanded', 'false');
|
|
2066
|
+
}
|
|
2067
|
+
button.onclick = function () {
|
|
2068
|
+
var open = list.hidden;
|
|
2069
|
+
list.hidden = !open;
|
|
2070
|
+
button.classList.toggle('open', open);
|
|
2071
|
+
button.setAttribute('aria-expanded', String(open));
|
|
2072
|
+
};
|
|
2073
|
+
// Choosing a row closes the menu before the row does its work.
|
|
2074
|
+
list.addEventListener('click', function () { close(); }, true);
|
|
2075
|
+
document.addEventListener('click', function (event) {
|
|
2076
|
+
if (!list.hidden && !menu.contains(event.target)) close();
|
|
2077
|
+
});
|
|
2078
|
+
document.addEventListener('keydown', function (event) {
|
|
2079
|
+
if (event.key === 'Escape' && !list.hidden) close();
|
|
2080
|
+
}, true);
|
|
2081
|
+
return close;
|
|
2082
|
+
}
|
|
2083
|
+
// A page with no file keeps Examples, Open and Save in a File menu.
|
|
2084
|
+
var fileMenu = document.getElementById('file-menu');
|
|
2085
|
+
var fileButton = document.getElementById('file-button');
|
|
2086
|
+
var closeFileMenu = sourceMenu(fileMenu, fileButton, document.getElementById('file-list'));
|
|
2087
|
+
// Help: the cheatsheet, and the full reference.
|
|
2088
|
+
var helpMenu = document.getElementById('help-menu');
|
|
2089
|
+
sourceMenu(helpMenu, document.getElementById('help-button'), document.getElementById('help-list'));
|
|
2090
|
+
document.getElementById('write-row').onclick = function () { writeButton.click(); };
|
|
2091
|
+
// Where "Saved" flashes: the Save button, or the File menu it is in.
|
|
2092
|
+
function saveFlash(text) { flash(writeButton.hidden ? fileButton : writeButton, text); }
|
|
1840
2093
|
var formatButton = document.getElementById('format');
|
|
1841
2094
|
var openButton = document.getElementById('open-file');
|
|
1842
2095
|
var formatOpen = document.getElementById('format-open');
|
|
@@ -1846,30 +2099,45 @@ const PAGE = `<!doctype html>
|
|
|
1846
2099
|
function toggleEditor(show, quiet) {
|
|
1847
2100
|
if (!EDIT) return;
|
|
1848
2101
|
var on = show === undefined ? editorPane.hidden : show;
|
|
2102
|
+
var changed = editorPane.hidden === on;
|
|
1849
2103
|
editorPane.hidden = !on;
|
|
1850
2104
|
splitBar.hidden = !on;
|
|
1851
2105
|
editButton.setAttribute('aria-pressed', String(on));
|
|
2106
|
+
lookButton.setAttribute('aria-pressed', String(!on));
|
|
2107
|
+
// The findings close with the source open, and come back as the page
|
|
2108
|
+
// first shows them when it closes.
|
|
2109
|
+
if (changed) {
|
|
2110
|
+
openedByError = openedByError && on;
|
|
2111
|
+
showPanel(on ? failing : count > 0 && !autoHide);
|
|
2112
|
+
showBand();
|
|
2113
|
+
}
|
|
1852
2114
|
if (on) { paintSource(); if (!quiet) sourceBox.focus(); }
|
|
1853
2115
|
else if (document.activeElement === sourceBox) sourceBox.blur();
|
|
1854
2116
|
}
|
|
1855
|
-
|
|
2117
|
+
// The View half of the Edit | View switch, apart from the Settings menu.
|
|
2118
|
+
var lookButton = document.getElementById('mode-view');
|
|
2119
|
+
editButton.onclick = function () { toggleEditor(true); };
|
|
2120
|
+
lookButton.onclick = function () { toggleEditor(false); };
|
|
1856
2121
|
|
|
1857
2122
|
function escapeCode(text) { return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); }
|
|
1858
2123
|
// The scanner covers each line end to end, so this rebuilds the text exactly
|
|
1859
2124
|
// and only wraps some of it in spans; a lost character would slide the rest
|
|
1860
2125
|
// of the line out from under the cursor.
|
|
2126
|
+
// One line of source as colored HTML; the library has to have loaded.
|
|
2127
|
+
function colorLine(line) {
|
|
2128
|
+
var out = '';
|
|
2129
|
+
lib.highlightLine(line).forEach(function (span) {
|
|
2130
|
+
var piece = escapeCode(line.slice(span.start, span.end));
|
|
2131
|
+
out += span.kind === 'plain' ? piece : '<span class="t-' + span.kind + '">' + piece + '</span>';
|
|
2132
|
+
});
|
|
2133
|
+
return out;
|
|
2134
|
+
}
|
|
1861
2135
|
function paintSource() {
|
|
1862
2136
|
var text = sourceBox.value;
|
|
1863
2137
|
var out = '';
|
|
1864
2138
|
try {
|
|
1865
2139
|
if (!lib) throw new Error('not loaded');
|
|
1866
|
-
text.split(/\\r?\\n/).forEach(function (line) {
|
|
1867
|
-
lib.highlightLine(line).forEach(function (span) {
|
|
1868
|
-
var piece = escapeCode(line.slice(span.start, span.end));
|
|
1869
|
-
out += span.kind === 'plain' ? piece : '<span class="t-' + span.kind + '">' + piece + '</span>';
|
|
1870
|
-
});
|
|
1871
|
-
out += '\\n';
|
|
1872
|
-
});
|
|
2140
|
+
text.split(/\\r?\\n/).forEach(function (line) { out += colorLine(line) + '\\n'; });
|
|
1873
2141
|
} catch (e) {
|
|
1874
2142
|
// Coloring is decoration; the editor has to keep working without it.
|
|
1875
2143
|
out = escapeCode(text) + '\\n';
|
|
@@ -1907,17 +2175,21 @@ const PAGE = `<!doctype html>
|
|
|
1907
2175
|
var cost = 0;
|
|
1908
2176
|
var typing = null;
|
|
1909
2177
|
|
|
1910
|
-
|
|
2178
|
+
// \`check\` looks for findings this once, as Check does.
|
|
2179
|
+
function draw(text, check) {
|
|
1911
2180
|
if (text === drawnText || !lib) return;
|
|
1912
2181
|
drawnText = text;
|
|
2182
|
+
shownText = text;
|
|
2183
|
+
var checking = autoCheck || !!check;
|
|
1913
2184
|
var started = performance.now();
|
|
1914
2185
|
var made;
|
|
1915
2186
|
try {
|
|
1916
|
-
made = lib.pageData(text, fileName, {});
|
|
2187
|
+
made = lib.pageData(text, fileName, checking ? {} : { checks: false });
|
|
2188
|
+
if (checking) checked = { text: text, findings: made.data.findings };
|
|
1917
2189
|
} catch (error) {
|
|
1918
2190
|
cost = performance.now() - started;
|
|
1919
2191
|
listFindings({ message: error.message, line: error.line });
|
|
1920
|
-
if (panelOn && panel.hidden) showPanel(true);
|
|
2192
|
+
if (panelOn && panel.hidden) { openedByError = true; showPanel(true); }
|
|
1921
2193
|
return;
|
|
1922
2194
|
}
|
|
1923
2195
|
cost = performance.now() - started;
|
|
@@ -2106,8 +2378,10 @@ const PAGE = `<!doctype html>
|
|
|
2106
2378
|
fileName = file.split('/').pop();
|
|
2107
2379
|
disk = text;
|
|
2108
2380
|
base = text;
|
|
2109
|
-
|
|
2110
|
-
|
|
2381
|
+
// A page tied to a file has only Save, so it is a button, not a File menu.
|
|
2382
|
+
closeFileMenu();
|
|
2383
|
+
fileMenu.hidden = true;
|
|
2384
|
+
writeButton.hidden = false;
|
|
2111
2385
|
formatOpen.hidden = true;
|
|
2112
2386
|
document.getElementById('versions-menu').hidden = false;
|
|
2113
2387
|
writeButton.querySelector('.name').textContent = 'Save';
|
|
@@ -2179,9 +2453,9 @@ const PAGE = `<!doctype html>
|
|
|
2179
2453
|
base = text;
|
|
2180
2454
|
changedStrip.hidden = true;
|
|
2181
2455
|
markUnsaved();
|
|
2182
|
-
|
|
2456
|
+
saveFlash('Saved');
|
|
2183
2457
|
}).catch(function (error) {
|
|
2184
|
-
|
|
2458
|
+
saveFlash('Not saved');
|
|
2185
2459
|
liveNote.textContent = 'Couldn\\u2019t save ' + file + ': ' + error.message;
|
|
2186
2460
|
liveNote.hidden = false;
|
|
2187
2461
|
});
|
|
@@ -2319,7 +2593,7 @@ const PAGE = `<!doctype html>
|
|
|
2319
2593
|
return handle.createWritable().then(function (out) {
|
|
2320
2594
|
return out.write(text).then(function () { return out.close(); }).then(function () {
|
|
2321
2595
|
keep(text, handle.name);
|
|
2322
|
-
|
|
2596
|
+
saveFlash('Saved');
|
|
2323
2597
|
});
|
|
2324
2598
|
});
|
|
2325
2599
|
})
|
|
@@ -2357,7 +2631,7 @@ const PAGE = `<!doctype html>
|
|
|
2357
2631
|
keep(text, told.file.split('/').pop());
|
|
2358
2632
|
history.replaceState(null, '', '/' + told.file.split('/').map(encodeURIComponent).join('/'));
|
|
2359
2633
|
tie(told.file, text);
|
|
2360
|
-
|
|
2634
|
+
saveFlash('Saved');
|
|
2361
2635
|
});
|
|
2362
2636
|
}).catch(function (error) {
|
|
2363
2637
|
problem.textContent = 'Couldn\\u2019t save: ' + error.message + '. Is reladraw serve still running?';
|
|
@@ -2523,11 +2797,90 @@ const PAGE = `<!doctype html>
|
|
|
2523
2797
|
};
|
|
2524
2798
|
}
|
|
2525
2799
|
|
|
2800
|
+
/* ---- the cheatsheet ---- */
|
|
2801
|
+
|
|
2802
|
+
// A window of cards, each row an example colored as the editor colors it.
|
|
2803
|
+
// The filter keeps the rows holding every word typed, matching the card's
|
|
2804
|
+
// name too, so "placement" keeps a whole card.
|
|
2805
|
+
var cheatBox = document.getElementById('cheatsheet');
|
|
2806
|
+
var openCheatsheet = function () {};
|
|
2807
|
+
if (cheatBox) {
|
|
2808
|
+
var cheatFilter = document.getElementById('cheat-filter');
|
|
2809
|
+
var cheatNone = cheatBox.querySelector('.cs-none');
|
|
2810
|
+
var cheatColored = false;
|
|
2811
|
+
var cheatGrid = cheatBox.querySelector('.cs-grid');
|
|
2812
|
+
var cheatCards = Array.prototype.slice.call(cheatGrid.querySelectorAll('.cs-card'));
|
|
2813
|
+
var cheatCols = element('div', 'cs-cols');
|
|
2814
|
+
cheatGrid.append(cheatCols);
|
|
2815
|
+
// Each card, in order, goes to the shortest column so far.
|
|
2816
|
+
var layoutCheat = function () {
|
|
2817
|
+
var width = cheatGrid.clientWidth;
|
|
2818
|
+
var count = width >= 1000 ? 3 : width >= 640 ? 2 : 1;
|
|
2819
|
+
cheatCols.style.setProperty('--cols', String(count));
|
|
2820
|
+
cheatCols.textContent = '';
|
|
2821
|
+
var cols = [];
|
|
2822
|
+
var heights = [];
|
|
2823
|
+
for (var i = 0; i < count; i++) {
|
|
2824
|
+
cols.push(element('div', 'cs-col'));
|
|
2825
|
+
heights.push(0);
|
|
2826
|
+
cheatCols.append(cols[i]);
|
|
2827
|
+
}
|
|
2828
|
+
cheatCards.forEach(function (card) {
|
|
2829
|
+
var at = heights.indexOf(Math.min.apply(null, heights));
|
|
2830
|
+
cols[at].append(card);
|
|
2831
|
+
if (!card.hidden) heights[at] += card.offsetHeight + 14;
|
|
2832
|
+
});
|
|
2833
|
+
};
|
|
2834
|
+
window.addEventListener('resize', function () { if (cheatBox.open) layoutCheat(); });
|
|
2835
|
+
openCheatsheet = function () {
|
|
2836
|
+
if (!cheatColored && lib) {
|
|
2837
|
+
cheatColored = true;
|
|
2838
|
+
each(cheatBox.querySelectorAll('.cs-row code'), function (code) {
|
|
2839
|
+
try { code.innerHTML = colorLine(code.textContent); } catch (e) {}
|
|
2840
|
+
});
|
|
2841
|
+
}
|
|
2842
|
+
cheatBox.showModal();
|
|
2843
|
+
layoutCheat();
|
|
2844
|
+
cheatFilter.focus();
|
|
2845
|
+
cheatFilter.select();
|
|
2846
|
+
};
|
|
2847
|
+
helpMenu.hidden = false;
|
|
2848
|
+
document.getElementById('cheat-open').onclick = openCheatsheet;
|
|
2849
|
+
closeOnBackdrop(cheatBox);
|
|
2850
|
+
cheatFilter.addEventListener('input', function () {
|
|
2851
|
+
var words = cheatFilter.value.toLowerCase().split(/\\s+/).filter(Boolean);
|
|
2852
|
+
var matches = function (text) {
|
|
2853
|
+
text = text.toLowerCase();
|
|
2854
|
+
return words.every(function (word) { return text.indexOf(word) >= 0; });
|
|
2855
|
+
};
|
|
2856
|
+
var any = false;
|
|
2857
|
+
each(cheatBox.querySelectorAll('.cs-card'), function (card) {
|
|
2858
|
+
var title = card.querySelector('h3').textContent;
|
|
2859
|
+
var shown = false;
|
|
2860
|
+
each(card.querySelectorAll('.cs-row'), function (row) {
|
|
2861
|
+
row.hidden = !matches(title + ' ' + row.textContent);
|
|
2862
|
+
if (!row.hidden) shown = true;
|
|
2863
|
+
});
|
|
2864
|
+
var note = card.querySelector('.cs-note');
|
|
2865
|
+
if (note) {
|
|
2866
|
+
note.hidden = !matches(title + ' ' + note.textContent);
|
|
2867
|
+
if (!note.hidden) shown = true;
|
|
2868
|
+
}
|
|
2869
|
+
card.hidden = !shown;
|
|
2870
|
+
if (shown) any = true;
|
|
2871
|
+
});
|
|
2872
|
+
cheatNone.hidden = any;
|
|
2873
|
+
layoutCheat();
|
|
2874
|
+
});
|
|
2875
|
+
}
|
|
2876
|
+
|
|
2526
2877
|
/* ---- the start ---- */
|
|
2527
2878
|
|
|
2528
2879
|
if (EDIT) {
|
|
2529
|
-
|
|
2530
|
-
|
|
2880
|
+
document.getElementById('mode').hidden = false;
|
|
2881
|
+
checkButton.hidden = autoCheck;
|
|
2882
|
+
autoCheckRow.hidden = false;
|
|
2883
|
+
setSwitch(autoCheckRow, autoCheck);
|
|
2531
2884
|
try {
|
|
2532
2885
|
var share = parseFloat(localStorage.getItem(SPLIT));
|
|
2533
2886
|
if (share > 0 && share < 1) divide(share, false);
|
|
@@ -2536,8 +2889,8 @@ const PAGE = `<!doctype html>
|
|
|
2536
2889
|
sourceBox.value = DATA.source;
|
|
2537
2890
|
tie(EDIT.file, DATA.source);
|
|
2538
2891
|
} else {
|
|
2539
|
-
|
|
2540
|
-
|
|
2892
|
+
fileMenu.hidden = false;
|
|
2893
|
+
writeButton.hidden = true;
|
|
2541
2894
|
formatOpen.hidden = false;
|
|
2542
2895
|
writeButton.querySelector('.name').textContent = 'Save\\u2026';
|
|
2543
2896
|
writeButton.title = EDIT.kind === 'local'
|
|
@@ -2574,145 +2927,124 @@ const PAGE = `<!doctype html>
|
|
|
2574
2927
|
`;
|
|
2575
2928
|
/** What a page with an editor shows under its source: the language on one screen. */
|
|
2576
2929
|
const CHEATSHEET = `
|
|
2577
|
-
<
|
|
2578
|
-
<
|
|
2579
|
-
|
|
2580
|
-
<
|
|
2581
|
-
<
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
<code>
|
|
2587
|
-
|
|
2588
|
-
<code>
|
|
2589
|
-
<code>
|
|
2590
|
-
<code>
|
|
2591
|
-
|
|
2592
|
-
<
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
<
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
<
|
|
2606
|
-
<
|
|
2607
|
-
<code>
|
|
2608
|
-
|
|
2609
|
-
<
|
|
2610
|
-
<code>
|
|
2611
|
-
<
|
|
2612
|
-
</
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
<
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
<code
|
|
2620
|
-
<code
|
|
2621
|
-
<code>
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
<code>
|
|
2625
|
-
|
|
2626
|
-
<
|
|
2627
|
-
<
|
|
2628
|
-
<code
|
|
2629
|
-
<
|
|
2630
|
-
|
|
2631
|
-
</
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
<code>
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
</
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
<
|
|
2643
|
-
<code>
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
<code>
|
|
2651
|
-
<code>
|
|
2652
|
-
<code>line:
|
|
2653
|
-
<code>
|
|
2654
|
-
<code>
|
|
2655
|
-
<code>
|
|
2656
|
-
</
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
<code>
|
|
2661
|
-
<code>
|
|
2662
|
-
<code>
|
|
2663
|
-
<
|
|
2664
|
-
<
|
|
2665
|
-
<
|
|
2666
|
-
<
|
|
2667
|
-
|
|
2668
|
-
</
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
<code>
|
|
2673
|
-
<code>
|
|
2674
|
-
<code>icon
|
|
2675
|
-
<
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
<code>
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
<code
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
<code>text: (color: theme-muted)</code>.<br />
|
|
2693
|
-
<code>" / "</code> in any text is a line break; <code>\\/</code> is a literal slash.<br />
|
|
2694
|
-
<code>"name / <b>[dim]</b>quieter<b>[/dim]</b>"</code> draws those words in the
|
|
2695
|
-
text color of <code>style dim</code>; <code>\\[</code> is a literal bracket.
|
|
2696
|
-
</dd>
|
|
2697
|
-
|
|
2698
|
-
<dt>Links</dt>
|
|
2699
|
-
<dd>
|
|
2700
|
-
A line is straight between its two boxes, through anything in between, unless told otherwise.<br />
|
|
2701
|
-
<code>from: right</code> <code>to: left</code> name a side;
|
|
2702
|
-
<code>to: bottom-center</code> or <code>to: left 3/4 down</code> a point on it.<br />
|
|
2703
|
-
<code>via: c top, between a and b</code> — the places the line passes, in order:
|
|
2704
|
-
just outside a side, a point, a box's center, <code>through <i>region</i> horizontal</code>.<br />
|
|
2705
|
-
<code>"text" (at: start)</code> <code>(at: 1/4)</code> <code>(above edge)</code>
|
|
2706
|
-
<code>(on: longest level)</code> — where a line's text sits; the middle by default.
|
|
2707
|
-
</dd>
|
|
2708
|
-
|
|
2709
|
-
<dt>Comments</dt>
|
|
2710
|
-
<dd><code>// to the end of the line</code>, anywhere, including after a statement.</dd>
|
|
2711
|
-
</dl>
|
|
2712
|
-
</details>
|
|
2930
|
+
<dialog id="cheatsheet" aria-label="Cheatsheet">
|
|
2931
|
+
<div class="cs-head">
|
|
2932
|
+
<h2>Cheatsheet</h2>
|
|
2933
|
+
<input id="cheat-filter" type="search" placeholder="Filter: above, gap, color…" autocomplete="off" spellcheck="false" aria-label="Filter the cheatsheet">
|
|
2934
|
+
<span class="gap"></span>
|
|
2935
|
+
<button class="close" type="button" data-close title="Close (Esc)" aria-label="Close the cheatsheet">×</button>
|
|
2936
|
+
</div>
|
|
2937
|
+
<div class="cs-grid">
|
|
2938
|
+
<section class="cs-card" style="--hue: var(--syn-keyword)"><h3>Statements</h3>
|
|
2939
|
+
<div class="cs-row"><code>node api "API"</code><span>a box, with its text</span></div>
|
|
2940
|
+
<div class="cs-row"><code>edge a -> b</code><span>a line from a to b</span></div>
|
|
2941
|
+
<div class="cs-row"><code>edge a <- b</code><span>the same line, written target first</span></div>
|
|
2942
|
+
<div class="cs-row"><code>edge a <-> b</code><span>arrows at both ends</span></div>
|
|
2943
|
+
<div class="cs-row"><code>point p 2 above a top-center</code><span>a named spot, to place against or pass through</span></div>
|
|
2944
|
+
<div class="cs-row"><code>region r a top-left to c bottom-right</code><span>a named area, from one corner to another</span></div>
|
|
2945
|
+
<div class="cs-row"><code>bundle trunk via: through r horizontal</code><span>a path that lines share</span></div>
|
|
2946
|
+
<div class="cs-row"><code>guide node g right of a</code><span>for reference only; Guides in Settings shows it</span></div>
|
|
2947
|
+
<div class="cs-row"><code>style hot fill: red</code><span>a named look, worn with style: hot</span></div>
|
|
2948
|
+
<div class="cs-row"><code>default edge line: (path: square)</code><span>a look every edge wears; also node, leaf, container; the more specific wins</span></div>
|
|
2949
|
+
<div class="cs-row"><code>diagram theme: nord background: #1e2229</code><span>the whole diagram's theme and colors</span></div>
|
|
2950
|
+
<p class="cs-note">The picture is drawn exactly as written: nothing moves to make room, and lines go only where they are told. Check reports anything that overlaps.</p>
|
|
2951
|
+
</section>
|
|
2952
|
+
<section class="cs-card" style="--hue: var(--syn-relation)"><h3>Placement</h3>
|
|
2953
|
+
<div class="cs-row"><code>node b right of a</code><span>beside a, centered on it; also above, below, left of</span></div>
|
|
2954
|
+
<div class="cs-row"><code>node b above-left of a</code><span>diagonally, corner to corner; also above-right, below-left, below-right</span></div>
|
|
2955
|
+
<div class="cs-row"><code>node b 3 left of a and 2 above a</code><span>3 units left of a, and 2 above it</span></div>
|
|
2956
|
+
<div class="cs-row"><code>node c right of a and b</code><span>against the box that just bounds a and b</span></div>
|
|
2957
|
+
<div class="cs-row"><code>node b even with a</code><span>on a's center line</span></div>
|
|
2958
|
+
<div class="cs-row"><code>node b top even with a</code><span>tops in line; also bottom, left, right</span></div>
|
|
2959
|
+
<div class="cs-row"><code>node f inside box top-right</code><span>in a corner of box</span></div>
|
|
2960
|
+
<div class="cs-row"><code>node g outside box right</code><span>just outside a side of box</span></div>
|
|
2961
|
+
<div class="cs-row"><code>node h on box bottom-left</code><span>centered on a part of box</span></div>
|
|
2962
|
+
<div class="cs-row"><code>node i on between a and c</code><span>midway between a and c</span></div>
|
|
2963
|
+
<div class="cs-row"><code>node j below down from b and left from a</code><span>at the point straight down from b and left from a</span></div>
|
|
2964
|
+
<p class="cs-note">The parts of a box: <code>text</code>, the sides <code>top</code> <code>bottom</code> <code>left</code> <code>right</code>, and <code>top-left</code> <code>top-center</code> <code>top-right</code> <code>left-center</code> <code>center</code> <code>right-center</code> <code>bottom-left</code> <code>bottom-center</code> <code>bottom-right</code>. One node is unplaced; every other is placed against something. Each axis is said once.</p>
|
|
2965
|
+
</section>
|
|
2966
|
+
<section class="cs-card" style="--hue: var(--syn-value)"><h3>Gaps</h3>
|
|
2967
|
+
<div class="cs-row"><code>node b 2 right of a</code><span>exactly 2 units apart</span></div>
|
|
2968
|
+
<div class="cs-row"><code>node b right of a (gap: wide)</code><span>none, tight, normal or wide: 0, 2, 4 or 8 units</span></div>
|
|
2969
|
+
<p class="cs-note">A box is about 4 units tall. A gap is exact.</p>
|
|
2970
|
+
</section>
|
|
2971
|
+
<section class="cs-card" style="--hue: var(--syn-string)"><h3>Labels</h3>
|
|
2972
|
+
<div class="cs-row"><code>node parser</code><span>no text, so its name is its text</span></div>
|
|
2973
|
+
<div class="cs-row"><code>node parser ""</code><span>deliberately blank</span></div>
|
|
2974
|
+
<div class="cs-row"><code>node a "two / lines"</code><span>a space, slash and space breaks the line</span></div>
|
|
2975
|
+
</section>
|
|
2976
|
+
<section class="cs-card" style="--hue: var(--syn-name)"><h3>Containment</h3>
|
|
2977
|
+
<div class="cs-row"><code>node server.docker "Docker"</code><span>a dotted name is a child; declare the parent first</span></div>
|
|
2978
|
+
<div class="cs-row"><code>node server layout: column</code><span>children top to bottom, in written order; or row</span></div>
|
|
2979
|
+
<div class="cs-row"><code>layout: column (gap: tight, widths: match, align-children: left)</code><span>how the children line up</span></div>
|
|
2980
|
+
<div class="cs-row"><code>layout: deck</code><span>a stack of cards, the first child in front</span></div>
|
|
2981
|
+
<div class="cs-row"><code>gather-children: left</code><span>where the children sit in spare room</span></div>
|
|
2982
|
+
<div class="cs-row"><code>shape: none</code><span>on a container, draws no box</span></div>
|
|
2983
|
+
<p class="cs-note">Without a layout, one child is unplaced and the rest are placed against it.</p>
|
|
2984
|
+
</section>
|
|
2985
|
+
<section class="cs-card" style="--hue: var(--syn-attribute)"><h3>Sizes</h3>
|
|
2986
|
+
<div class="cs-row"><code>width: 30</code><span>30 units wide</span></div>
|
|
2987
|
+
<div class="cs-row"><code>height: menu</code><span>as tall as menu</span></div>
|
|
2988
|
+
<div class="cs-row"><code>height: 0.5 menu height</code><span>half as tall as menu</span></div>
|
|
2989
|
+
<div class="cs-row"><code>height: a top to c bottom</code><span>from a's top to c's bottom</span></div>
|
|
2990
|
+
<div class="cs-row"><code>min-width: 10</code><span>at least 10 units wide</span></div>
|
|
2991
|
+
</section>
|
|
2992
|
+
<section class="cs-card" style="--hue: var(--syn-keyword)"><h3>Appearance</h3>
|
|
2993
|
+
<div class="cs-row"><code>style: hot</code><span>wear a named style</span></div>
|
|
2994
|
+
<div class="cs-row"><code>fill: #191728</code><span>a box's color</span></div>
|
|
2995
|
+
<div class="cs-row"><code>border: none</code><span>no outline</span></div>
|
|
2996
|
+
<div class="cs-row"><code>corners: sharp</code><span>square corners; boxes are rounded unless told</span></div>
|
|
2997
|
+
<div class="cs-row"><code>line: #d2904e</code><span>a line's color</span></div>
|
|
2998
|
+
<div class="cs-row"><code>overlap: allow</code><span>this one may overlap without a finding</span></div>
|
|
2999
|
+
<div class="cs-row"><code>url: "https://example.com"</code><span>a link, shown on the page</span></div>
|
|
3000
|
+
<div class="cs-row"><code>description: "What it does"</code><span>shown on the page; never drawn</span></div>
|
|
3001
|
+
</section>
|
|
3002
|
+
<section class="cs-card" style="--hue: var(--syn-string)"><h3>Lines</h3>
|
|
3003
|
+
<div class="cs-row"><code>line: (path: square, corners: rounded)</code><span>everything about a line goes in line:</span></div>
|
|
3004
|
+
<div class="cs-row"><code>line: (path: curved, curve: subtle)</code><span>straight (the default), square or curved</span></div>
|
|
3005
|
+
<div class="cs-row"><code>line: (path: square, turn: 1/3, step-out: tight)</code><span>where a square line turns, and how far it steps out</span></div>
|
|
3006
|
+
<div class="cs-row"><code>line: (crossing: arc)</code><span>where lines cross: gap, arc, square or none</span></div>
|
|
3007
|
+
<div class="cs-row"><code>line: (pattern: dashed)</code><span>solid, dashed, dotted or dash-dot</span></div>
|
|
3008
|
+
<div class="cs-row"><code>line: (thickness: thick)</code><span>thin, normal, thick, a multiple, or 3px</span></div>
|
|
3009
|
+
</section>
|
|
3010
|
+
<section class="cs-card" style="--hue: var(--syn-relation)"><h3>Where lines go</h3>
|
|
3011
|
+
<div class="cs-row"><code>from: right to: left</code><span>leave and arrive at a side</span></div>
|
|
3012
|
+
<div class="cs-row"><code>to: bottom-center</code><span>arrive at a point on a side</span></div>
|
|
3013
|
+
<div class="cs-row"><code>to: left 3/4 down</code><span>a fraction of the way along a side</span></div>
|
|
3014
|
+
<div class="cs-row"><code>via: c top, between a and b</code><span>the places a line passes, in order</span></div>
|
|
3015
|
+
<div class="cs-row"><code>via: through r horizontal</code><span>straight through a region</span></div>
|
|
3016
|
+
<div class="cs-row"><code>edge a -> b "calls" (at: start)</code><span>where a line's text sits: start, 1/4, the middle by default</span></div>
|
|
3017
|
+
<div class="cs-row"><code>edge a -> b "calls" (above edge)</code><span>text beside the line rather than on it</span></div>
|
|
3018
|
+
<div class="cs-row"><code>edge a -> b "calls" (on: longest level)</code><span>text on the longest level stretch</span></div>
|
|
3019
|
+
<p class="cs-note">A line runs straight between its two boxes, through anything in between, unless told otherwise.</p>
|
|
3020
|
+
</section>
|
|
3021
|
+
<section class="cs-card" style="--hue: var(--syn-name)"><h3>Body</h3>
|
|
3022
|
+
<div class="cs-row"><code>shape: document</code><span>rectangle, document, circle or none</span></div>
|
|
3023
|
+
<div class="cs-row"><code>node note "a note" right of a shape: none</code><span>just text: an annotation</span></div>
|
|
3024
|
+
<div class="cs-row"><code>icon: database</code><span>the node is the picture, with no box</span></div>
|
|
3025
|
+
<div class="cs-row"><code>badge: database</code><span>the picture beside the text, in the box</span></div>
|
|
3026
|
+
<div class="cs-row"><code>layout: deck cards: "v1" "v2"</code><span>a stack of cards, one per text, front first</span></div>
|
|
3027
|
+
<div class="cs-row"><code>icon logo """<svg …>…</svg>"""</code><span>your own picture, used as icon: logo</span></div>
|
|
3028
|
+
<p class="cs-note">Pictures: <code>disk</code> <code>desktop</code> <code>laptop</code> <code>package</code> <code>cubes</code> <code>cube</code> <code>database</code>.</p>
|
|
3029
|
+
</section>
|
|
3030
|
+
<section class="cs-card" style="--hue: var(--syn-value)"><h3>Text</h3>
|
|
3031
|
+
<div class="cs-row"><code>node a "Server" (size: large)</code><span>small, normal or large</span></div>
|
|
3032
|
+
<div class="cs-row"><code>node a "Server" (color: theme-muted)</code><span>the text's color</span></div>
|
|
3033
|
+
<div class="cs-row"><code>node a "A long label" (wrap: 24)</code><span>folds at 24 characters</span></div>
|
|
3034
|
+
<div class="cs-row"><code>node a "Server" (inside top, align: left)</code><span>where a container's title sits: on top, outside top, center</span></div>
|
|
3035
|
+
<div class="cs-row"><code>style dim text: (color: theme-muted)</code><span>in a style, the bracket hangs off text:</span></div>
|
|
3036
|
+
<div class="cs-row"><code>node a "name / [dim]quieter[/dim]"</code><span>those words in style dim's text color</span></div>
|
|
3037
|
+
<div class="cs-row"><code>node a "a \\/ b"</code><span>a slash that doesn't break the line; \\[ for a bracket</span></div>
|
|
3038
|
+
</section>
|
|
3039
|
+
<section class="cs-card" style="--hue: var(--syn-comment)"><h3>Comments</h3>
|
|
3040
|
+
<div class="cs-row"><code>// to the end of the line</code><span>anywhere, including after a statement</span></div>
|
|
3041
|
+
</section>
|
|
3042
|
+
</div>
|
|
3043
|
+
<p class="cs-none" hidden>Nothing matches. Try one word, such as above, gap or color.</p>
|
|
3044
|
+
</dialog>
|
|
2713
3045
|
`;
|
|
2714
3046
|
/** Where a page with no file sends someone who wants more than the cheatsheet. */
|
|
2715
|
-
const LINKS = `<nav class="links"><a href="https://github.com/reladraw/reladraw"
|
|
3047
|
+
const LINKS = `<nav class="links"><a href="https://github.com/reladraw/reladraw" title="reladraw on GitHub" aria-label="reladraw on GitHub"><svg viewBox="0 0 16 16" width="18" height="18" aria-hidden="true"><path fill="currentColor" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg></a></nav>`;
|
|
2716
3048
|
/**
|
|
2717
3049
|
* A page with no file's dialogs: the examples, the format settings, which a
|
|
2718
3050
|
* file's page takes from its nearest .reladraw-format instead, and, served,
|