sketchmark 1.1.1 → 1.1.3
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.
Potentially problematic release.
This version of sketchmark might be problematic. Click here for more details.
- package/dist/animation/index.d.ts +5 -0
- package/dist/animation/index.d.ts.map +1 -1
- package/dist/index.cjs +213 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +213 -19
- package/dist/index.js.map +1 -1
- package/dist/sketchmark.iife.js +213 -19
- package/dist/ui/editor.d.ts +4 -0
- package/dist/ui/editor.d.ts.map +1 -1
- package/package.json +71 -72
package/dist/index.js
CHANGED
|
@@ -9246,11 +9246,13 @@ class AnimationController {
|
|
|
9246
9246
|
this._config = _config;
|
|
9247
9247
|
this._step = -1;
|
|
9248
9248
|
this._pendingStepTimers = new Set();
|
|
9249
|
+
this._pendingNarrationTimers = new Set();
|
|
9249
9250
|
this._transforms = new Map();
|
|
9250
9251
|
this._listeners = [];
|
|
9251
9252
|
// ── Narration caption ──
|
|
9252
9253
|
this._captionEl = null;
|
|
9253
9254
|
this._captionTextEl = null;
|
|
9255
|
+
this._narrationRunId = 0;
|
|
9254
9256
|
// ── Annotations ──
|
|
9255
9257
|
this._annotationLayer = null;
|
|
9256
9258
|
this._annotations = [];
|
|
@@ -9513,20 +9515,30 @@ class AnimationController {
|
|
|
9513
9515
|
}
|
|
9514
9516
|
this.emit("step-change");
|
|
9515
9517
|
}
|
|
9518
|
+
_clearTimerBucket(bucket) {
|
|
9519
|
+
bucket.forEach((id) => window.clearTimeout(id));
|
|
9520
|
+
bucket.clear();
|
|
9521
|
+
}
|
|
9516
9522
|
_clearPendingStepTimers() {
|
|
9517
|
-
this.
|
|
9518
|
-
this._pendingStepTimers.clear();
|
|
9523
|
+
this._clearTimerBucket(this._pendingStepTimers);
|
|
9519
9524
|
}
|
|
9520
|
-
|
|
9525
|
+
_cancelNarrationTyping() {
|
|
9526
|
+
this._narrationRunId += 1;
|
|
9527
|
+
this._clearTimerBucket(this._pendingNarrationTimers);
|
|
9528
|
+
}
|
|
9529
|
+
_scheduleTimer(fn, delayMs, bucket = this._pendingStepTimers) {
|
|
9521
9530
|
if (delayMs <= 0) {
|
|
9522
9531
|
fn();
|
|
9523
9532
|
return;
|
|
9524
9533
|
}
|
|
9525
9534
|
const id = window.setTimeout(() => {
|
|
9526
|
-
|
|
9535
|
+
bucket.delete(id);
|
|
9527
9536
|
fn();
|
|
9528
9537
|
}, delayMs);
|
|
9529
|
-
|
|
9538
|
+
bucket.add(id);
|
|
9539
|
+
}
|
|
9540
|
+
_scheduleStep(fn, delayMs) {
|
|
9541
|
+
this._scheduleTimer(fn, delayMs, this._pendingStepTimers);
|
|
9530
9542
|
}
|
|
9531
9543
|
_stepWaitMs(step, fallbackMs) {
|
|
9532
9544
|
const delay = Math.max(0, step.delay ?? 0);
|
|
@@ -9570,6 +9582,7 @@ class AnimationController {
|
|
|
9570
9582
|
}
|
|
9571
9583
|
_clearAll() {
|
|
9572
9584
|
this._clearPendingStepTimers();
|
|
9585
|
+
this._cancelNarrationTyping();
|
|
9573
9586
|
this._cancelSpeech();
|
|
9574
9587
|
this._transforms.clear();
|
|
9575
9588
|
// Nodes
|
|
@@ -10124,6 +10137,7 @@ class AnimationController {
|
|
|
10124
10137
|
_doNarrate(text, silent) {
|
|
10125
10138
|
if (!this._captionEl || !this._captionTextEl)
|
|
10126
10139
|
return;
|
|
10140
|
+
this._cancelNarrationTyping();
|
|
10127
10141
|
this._captionEl.style.opacity = "1";
|
|
10128
10142
|
if (silent || !text) {
|
|
10129
10143
|
this._captionTextEl.textContent = text;
|
|
@@ -10134,12 +10148,16 @@ class AnimationController {
|
|
|
10134
10148
|
this._speak(text);
|
|
10135
10149
|
// Typing effect
|
|
10136
10150
|
this._captionTextEl.textContent = "";
|
|
10151
|
+
const narrationRunId = this._narrationRunId;
|
|
10137
10152
|
let charIdx = 0;
|
|
10138
10153
|
const typeNext = () => {
|
|
10154
|
+
if (this._narrationRunId !== narrationRunId || !this._captionTextEl)
|
|
10155
|
+
return;
|
|
10139
10156
|
if (charIdx < text.length) {
|
|
10140
10157
|
this._captionTextEl.textContent += text[charIdx++];
|
|
10141
|
-
|
|
10142
|
-
|
|
10158
|
+
if (charIdx < text.length) {
|
|
10159
|
+
this._scheduleTimer(typeNext, ANIMATION.narrationTypeMs, this._pendingNarrationTimers);
|
|
10160
|
+
}
|
|
10143
10161
|
}
|
|
10144
10162
|
};
|
|
10145
10163
|
typeNext();
|
|
@@ -10249,12 +10267,11 @@ class AnimationController {
|
|
|
10249
10267
|
requestAnimationFrame(animate);
|
|
10250
10268
|
}
|
|
10251
10269
|
// After guide finishes: reveal rough.js element, remove guide
|
|
10252
|
-
|
|
10270
|
+
this._scheduleTimer(() => {
|
|
10253
10271
|
roughEl.style.transition = `opacity 120ms ease`;
|
|
10254
10272
|
roughEl.style.opacity = "1";
|
|
10255
10273
|
guide.remove();
|
|
10256
10274
|
}, dur + 30);
|
|
10257
|
-
this._pendingStepTimers.add(id);
|
|
10258
10275
|
}));
|
|
10259
10276
|
}
|
|
10260
10277
|
_doAnnotationCircle(target, silent) {
|
|
@@ -10565,13 +10582,13 @@ function exportHTML(svg, dslSource, opts = {}) {
|
|
|
10565
10582
|
</head>
|
|
10566
10583
|
<body>
|
|
10567
10584
|
<div class="diagram">${svgStr}</div>
|
|
10568
|
-
<details class="dsl"><summary style="cursor:pointer;color:#f0c96a">DSL source</summary><pre>${escapeHtml(dslSource)}</pre></details>
|
|
10585
|
+
<details class="dsl"><summary style="cursor:pointer;color:#f0c96a">DSL source</summary><pre>${escapeHtml$1(dslSource)}</pre></details>
|
|
10569
10586
|
</body>
|
|
10570
10587
|
</html>`;
|
|
10571
10588
|
const blob = new Blob([html], { type: 'text/html;charset=utf-8' });
|
|
10572
10589
|
download(blob, opts.filename ?? 'diagram.html');
|
|
10573
10590
|
}
|
|
10574
|
-
function escapeHtml(s) {
|
|
10591
|
+
function escapeHtml$1(s) {
|
|
10575
10592
|
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
10576
10593
|
}
|
|
10577
10594
|
// ── GIF stub (requires gifshot or gif.js at runtime) ──────
|
|
@@ -10797,7 +10814,7 @@ const CANVAS_CSS = `
|
|
|
10797
10814
|
.skm-canvas__viewport.is-panning{cursor:grabbing}
|
|
10798
10815
|
.skm-canvas--dark .skm-canvas__viewport{background:#12100a}
|
|
10799
10816
|
.skm-canvas__grid{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
|
|
10800
|
-
.skm-canvas__world{position:absolute;top:0;left:0;transform-origin:0 0;
|
|
10817
|
+
.skm-canvas__world{position:absolute;top:0;left:0;transform-origin:0 0;}
|
|
10801
10818
|
.skm-canvas__controls{position:absolute;right:14px;bottom:14px;display:flex;flex-direction:column;align-items:center;gap:4px;z-index:2}
|
|
10802
10819
|
.skm-canvas__zoom{min-width:40px;text-align:center;color:#8a6040;font-size:10px}
|
|
10803
10820
|
.skm-canvas__minimap{position:absolute;left:14px;bottom:14px;width:120px;height:80px;background:rgba(255,248,234,.94);border:1px solid #caba98;border-radius:6px;overflow:hidden;z-index:2}
|
|
@@ -11447,20 +11464,44 @@ const EDITOR_CSS = `
|
|
|
11447
11464
|
color: #fff;
|
|
11448
11465
|
}
|
|
11449
11466
|
|
|
11450
|
-
.skm-
|
|
11467
|
+
.skm-editor__surface {
|
|
11468
|
+
position: relative;
|
|
11451
11469
|
flex: 1;
|
|
11452
|
-
width: 100%;
|
|
11453
11470
|
min-height: 0;
|
|
11454
|
-
border: 0;
|
|
11455
|
-
outline: 0;
|
|
11456
|
-
resize: none;
|
|
11457
11471
|
background: #1c1608;
|
|
11458
|
-
|
|
11472
|
+
overflow: hidden;
|
|
11473
|
+
}
|
|
11474
|
+
|
|
11475
|
+
.skm-editor__highlight,
|
|
11476
|
+
.skm-editor__input {
|
|
11477
|
+
position: absolute;
|
|
11478
|
+
inset: 0;
|
|
11479
|
+
width: 100%;
|
|
11480
|
+
height: 100%;
|
|
11459
11481
|
padding: 12px 14px;
|
|
11460
11482
|
font: inherit;
|
|
11461
11483
|
font-size: 12px;
|
|
11462
11484
|
line-height: 1.7;
|
|
11463
11485
|
tab-size: 2;
|
|
11486
|
+
white-space: pre-wrap;
|
|
11487
|
+
overflow: auto;
|
|
11488
|
+
}
|
|
11489
|
+
|
|
11490
|
+
.skm-editor__highlight {
|
|
11491
|
+
margin: 0;
|
|
11492
|
+
border: 0;
|
|
11493
|
+
background: #1c1608;
|
|
11494
|
+
color: #e0c898;
|
|
11495
|
+
pointer-events: none;
|
|
11496
|
+
word-break: break-word;
|
|
11497
|
+
}
|
|
11498
|
+
|
|
11499
|
+
.skm-editor__input {
|
|
11500
|
+
border: 0;
|
|
11501
|
+
outline: 0;
|
|
11502
|
+
resize: none;
|
|
11503
|
+
background: transparent;
|
|
11504
|
+
color: transparent;
|
|
11464
11505
|
caret-color: #f0c96a;
|
|
11465
11506
|
}
|
|
11466
11507
|
|
|
@@ -11468,6 +11509,40 @@ const EDITOR_CSS = `
|
|
|
11468
11509
|
color: #80633b;
|
|
11469
11510
|
}
|
|
11470
11511
|
|
|
11512
|
+
.skm-editor__input::selection {
|
|
11513
|
+
background: rgba(240, 201, 106, 0.22);
|
|
11514
|
+
}
|
|
11515
|
+
|
|
11516
|
+
.skm-editor__token--keyword {
|
|
11517
|
+
color: #e07040;
|
|
11518
|
+
}
|
|
11519
|
+
|
|
11520
|
+
.skm-editor__token--property {
|
|
11521
|
+
color: #70a8d0;
|
|
11522
|
+
}
|
|
11523
|
+
|
|
11524
|
+
.skm-editor__token--string {
|
|
11525
|
+
color: #8db870;
|
|
11526
|
+
}
|
|
11527
|
+
|
|
11528
|
+
.skm-editor__token--number {
|
|
11529
|
+
color: #d4a020;
|
|
11530
|
+
}
|
|
11531
|
+
|
|
11532
|
+
.skm-editor__token--comment {
|
|
11533
|
+
color: #6a5a3a;
|
|
11534
|
+
}
|
|
11535
|
+
|
|
11536
|
+
.skm-editor__token--connector {
|
|
11537
|
+
color: #c8b070;
|
|
11538
|
+
}
|
|
11539
|
+
|
|
11540
|
+
.skm-editor__token--color {
|
|
11541
|
+
color: var(--skm-editor-color, #f0c96a);
|
|
11542
|
+
box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.08);
|
|
11543
|
+
font-weight: 600;
|
|
11544
|
+
}
|
|
11545
|
+
|
|
11471
11546
|
.skm-editor__error {
|
|
11472
11547
|
display: none;
|
|
11473
11548
|
flex-shrink: 0;
|
|
@@ -11484,12 +11559,112 @@ const EDITOR_CSS = `
|
|
|
11484
11559
|
display: block;
|
|
11485
11560
|
}
|
|
11486
11561
|
`;
|
|
11562
|
+
const CONNECTORS = ["<-->", "<->", "-->", "<--", "---", "--", "->", "<-"];
|
|
11563
|
+
const HEX_COLOR_RE = /#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})\b/g;
|
|
11487
11564
|
function defaultFormatter(value) {
|
|
11488
11565
|
return normalizeNewlines(value)
|
|
11489
11566
|
.split("\n")
|
|
11490
11567
|
.map((line) => line.replace(/[ \t]+$/g, ""))
|
|
11491
11568
|
.join("\n");
|
|
11492
11569
|
}
|
|
11570
|
+
function escapeHtml(value) {
|
|
11571
|
+
return value
|
|
11572
|
+
.replace(/&/g, "&")
|
|
11573
|
+
.replace(/</g, "<")
|
|
11574
|
+
.replace(/>/g, ">")
|
|
11575
|
+
.replace(/"/g, """);
|
|
11576
|
+
}
|
|
11577
|
+
function wrapToken(kind, value) {
|
|
11578
|
+
return `<span class="skm-editor__token skm-editor__token--${kind}">${escapeHtml(value)}</span>`;
|
|
11579
|
+
}
|
|
11580
|
+
function renderColorLiteral(value) {
|
|
11581
|
+
return `<span class="skm-editor__token skm-editor__token--color" style="--skm-editor-color:${value}">${escapeHtml(value)}</span>`;
|
|
11582
|
+
}
|
|
11583
|
+
function renderStringToken(value) {
|
|
11584
|
+
HEX_COLOR_RE.lastIndex = 0;
|
|
11585
|
+
if (!HEX_COLOR_RE.test(value)) {
|
|
11586
|
+
return wrapToken("string", value);
|
|
11587
|
+
}
|
|
11588
|
+
HEX_COLOR_RE.lastIndex = 0;
|
|
11589
|
+
let html = "";
|
|
11590
|
+
let lastIndex = 0;
|
|
11591
|
+
let match = null;
|
|
11592
|
+
while ((match = HEX_COLOR_RE.exec(value))) {
|
|
11593
|
+
if (match.index > lastIndex) {
|
|
11594
|
+
html += wrapToken("string", value.slice(lastIndex, match.index));
|
|
11595
|
+
}
|
|
11596
|
+
html += renderColorLiteral(match[0]);
|
|
11597
|
+
lastIndex = match.index + match[0].length;
|
|
11598
|
+
}
|
|
11599
|
+
if (lastIndex < value.length) {
|
|
11600
|
+
html += wrapToken("string", value.slice(lastIndex));
|
|
11601
|
+
}
|
|
11602
|
+
return html;
|
|
11603
|
+
}
|
|
11604
|
+
function renderPlainToken(value, nextChar) {
|
|
11605
|
+
if (/^-?\d/.test(value)) {
|
|
11606
|
+
return wrapToken("number", value);
|
|
11607
|
+
}
|
|
11608
|
+
if (nextChar === "=") {
|
|
11609
|
+
return wrapToken("property", value);
|
|
11610
|
+
}
|
|
11611
|
+
if (KEYWORDS.has(value)) {
|
|
11612
|
+
return wrapToken("keyword", value);
|
|
11613
|
+
}
|
|
11614
|
+
return escapeHtml(value);
|
|
11615
|
+
}
|
|
11616
|
+
function highlightLine(line) {
|
|
11617
|
+
let html = "";
|
|
11618
|
+
let index = 0;
|
|
11619
|
+
while (index < line.length) {
|
|
11620
|
+
const rest = line.slice(index);
|
|
11621
|
+
if (rest.startsWith("//") || rest.startsWith("#")) {
|
|
11622
|
+
html += wrapToken("comment", rest);
|
|
11623
|
+
break;
|
|
11624
|
+
}
|
|
11625
|
+
if (line[index] === "\"") {
|
|
11626
|
+
let end = index + 1;
|
|
11627
|
+
while (end < line.length) {
|
|
11628
|
+
if (line[end] === "\"" && line[end - 1] !== "\\") {
|
|
11629
|
+
end += 1;
|
|
11630
|
+
break;
|
|
11631
|
+
}
|
|
11632
|
+
end += 1;
|
|
11633
|
+
}
|
|
11634
|
+
html += renderStringToken(line.slice(index, end));
|
|
11635
|
+
index = end;
|
|
11636
|
+
continue;
|
|
11637
|
+
}
|
|
11638
|
+
const connector = CONNECTORS.find((candidate) => line.startsWith(candidate, index));
|
|
11639
|
+
if (connector) {
|
|
11640
|
+
html += wrapToken("connector", connector);
|
|
11641
|
+
index += connector.length;
|
|
11642
|
+
continue;
|
|
11643
|
+
}
|
|
11644
|
+
const wordMatch = /^[A-Za-z_][A-Za-z0-9_-]*/.exec(rest);
|
|
11645
|
+
if (wordMatch) {
|
|
11646
|
+
const word = wordMatch[0];
|
|
11647
|
+
const nextChar = line[index + word.length] ?? "";
|
|
11648
|
+
html += renderPlainToken(word, nextChar);
|
|
11649
|
+
index += word.length;
|
|
11650
|
+
continue;
|
|
11651
|
+
}
|
|
11652
|
+
const numberMatch = /^-?\d+(?:\.\d+)?/.exec(rest);
|
|
11653
|
+
if (numberMatch) {
|
|
11654
|
+
html += wrapToken("number", numberMatch[0]);
|
|
11655
|
+
index += numberMatch[0].length;
|
|
11656
|
+
continue;
|
|
11657
|
+
}
|
|
11658
|
+
html += escapeHtml(line[index]);
|
|
11659
|
+
index += 1;
|
|
11660
|
+
}
|
|
11661
|
+
return html;
|
|
11662
|
+
}
|
|
11663
|
+
function renderHighlightedValue(value) {
|
|
11664
|
+
const normalized = normalizeNewlines(value);
|
|
11665
|
+
const html = normalized.split("\n").map(highlightLine).join("\n");
|
|
11666
|
+
return html || " ";
|
|
11667
|
+
}
|
|
11493
11668
|
class SketchmarkEditor {
|
|
11494
11669
|
constructor(options) {
|
|
11495
11670
|
this.emitter = new EventEmitter();
|
|
@@ -11526,16 +11701,23 @@ class SketchmarkEditor {
|
|
|
11526
11701
|
if (options.showClearButton !== false)
|
|
11527
11702
|
this.toolbar.appendChild(clearButton);
|
|
11528
11703
|
this.toolbar.appendChild(hint);
|
|
11704
|
+
this.surface = document.createElement("div");
|
|
11705
|
+
this.surface.className = "skm-editor__surface";
|
|
11706
|
+
this.highlightElement = document.createElement("pre");
|
|
11707
|
+
this.highlightElement.className = "skm-editor__highlight";
|
|
11708
|
+
this.highlightElement.setAttribute("aria-hidden", "true");
|
|
11529
11709
|
this.textarea = document.createElement("textarea");
|
|
11530
11710
|
this.textarea.className = "skm-editor__input";
|
|
11531
11711
|
this.textarea.spellcheck = false;
|
|
11532
11712
|
this.textarea.placeholder = options.placeholder ?? "diagram\nbox a label=\"Hello\"\nend";
|
|
11533
11713
|
this.textarea.value = normalizeNewlines(options.value ?? DEFAULT_CLEAR_VALUE);
|
|
11534
11714
|
this.textarea.addEventListener("input", () => {
|
|
11715
|
+
this.syncHighlight();
|
|
11535
11716
|
const payload = { value: this.getValue(), editor: this };
|
|
11536
11717
|
options.onChange?.(payload.value, this);
|
|
11537
11718
|
this.emitter.emit("change", payload);
|
|
11538
11719
|
});
|
|
11720
|
+
this.textarea.addEventListener("scroll", () => this.syncScroll());
|
|
11539
11721
|
this.textarea.addEventListener("keydown", (event) => {
|
|
11540
11722
|
if ((event.ctrlKey || event.metaKey) && event.key === "Enter") {
|
|
11541
11723
|
event.preventDefault();
|
|
@@ -11547,9 +11729,12 @@ class SketchmarkEditor {
|
|
|
11547
11729
|
if (options.showToolbar !== false) {
|
|
11548
11730
|
this.root.appendChild(this.toolbar);
|
|
11549
11731
|
}
|
|
11550
|
-
this.
|
|
11732
|
+
this.surface.appendChild(this.highlightElement);
|
|
11733
|
+
this.surface.appendChild(this.textarea);
|
|
11734
|
+
this.root.appendChild(this.surface);
|
|
11551
11735
|
this.root.appendChild(this.errorElement);
|
|
11552
11736
|
host.appendChild(this.root);
|
|
11737
|
+
this.syncHighlight();
|
|
11553
11738
|
if (options.autoFocus) {
|
|
11554
11739
|
this.focus();
|
|
11555
11740
|
}
|
|
@@ -11559,6 +11744,7 @@ class SketchmarkEditor {
|
|
|
11559
11744
|
}
|
|
11560
11745
|
setValue(value, emitChange = false) {
|
|
11561
11746
|
this.textarea.value = normalizeNewlines(value);
|
|
11747
|
+
this.syncHighlight();
|
|
11562
11748
|
if (emitChange) {
|
|
11563
11749
|
const payload = { value: this.getValue(), editor: this };
|
|
11564
11750
|
this.options.onChange?.(payload.value, this);
|
|
@@ -11600,6 +11786,14 @@ class SketchmarkEditor {
|
|
|
11600
11786
|
destroy() {
|
|
11601
11787
|
this.root.remove();
|
|
11602
11788
|
}
|
|
11789
|
+
syncHighlight() {
|
|
11790
|
+
this.highlightElement.innerHTML = renderHighlightedValue(this.textarea.value);
|
|
11791
|
+
this.syncScroll();
|
|
11792
|
+
}
|
|
11793
|
+
syncScroll() {
|
|
11794
|
+
this.highlightElement.scrollTop = this.textarea.scrollTop;
|
|
11795
|
+
this.highlightElement.scrollLeft = this.textarea.scrollLeft;
|
|
11796
|
+
}
|
|
11603
11797
|
}
|
|
11604
11798
|
|
|
11605
11799
|
const EMBED_STYLE_ID = "sketchmark-embed-ui";
|