sketchmark 1.1.1 → 1.1.2

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/index.js CHANGED
@@ -10565,13 +10565,13 @@ function exportHTML(svg, dslSource, opts = {}) {
10565
10565
  </head>
10566
10566
  <body>
10567
10567
  <div class="diagram">${svgStr}</div>
10568
- <details class="dsl"><summary style="cursor:pointer;color:#f0c96a">DSL source</summary><pre>${escapeHtml(dslSource)}</pre></details>
10568
+ <details class="dsl"><summary style="cursor:pointer;color:#f0c96a">DSL source</summary><pre>${escapeHtml$1(dslSource)}</pre></details>
10569
10569
  </body>
10570
10570
  </html>`;
10571
10571
  const blob = new Blob([html], { type: 'text/html;charset=utf-8' });
10572
10572
  download(blob, opts.filename ?? 'diagram.html');
10573
10573
  }
10574
- function escapeHtml(s) {
10574
+ function escapeHtml$1(s) {
10575
10575
  return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
10576
10576
  }
10577
10577
  // ── GIF stub (requires gifshot or gif.js at runtime) ──────
@@ -10797,7 +10797,7 @@ const CANVAS_CSS = `
10797
10797
  .skm-canvas__viewport.is-panning{cursor:grabbing}
10798
10798
  .skm-canvas--dark .skm-canvas__viewport{background:#12100a}
10799
10799
  .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;will-change:transform}
10800
+ .skm-canvas__world{position:absolute;top:0;left:0;transform-origin:0 0;}
10801
10801
  .skm-canvas__controls{position:absolute;right:14px;bottom:14px;display:flex;flex-direction:column;align-items:center;gap:4px;z-index:2}
10802
10802
  .skm-canvas__zoom{min-width:40px;text-align:center;color:#8a6040;font-size:10px}
10803
10803
  .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 +11447,44 @@ const EDITOR_CSS = `
11447
11447
  color: #fff;
11448
11448
  }
11449
11449
 
11450
- .skm-editor__input {
11450
+ .skm-editor__surface {
11451
+ position: relative;
11451
11452
  flex: 1;
11452
- width: 100%;
11453
11453
  min-height: 0;
11454
- border: 0;
11455
- outline: 0;
11456
- resize: none;
11457
11454
  background: #1c1608;
11458
- color: #e0c898;
11455
+ overflow: hidden;
11456
+ }
11457
+
11458
+ .skm-editor__highlight,
11459
+ .skm-editor__input {
11460
+ position: absolute;
11461
+ inset: 0;
11462
+ width: 100%;
11463
+ height: 100%;
11459
11464
  padding: 12px 14px;
11460
11465
  font: inherit;
11461
11466
  font-size: 12px;
11462
11467
  line-height: 1.7;
11463
11468
  tab-size: 2;
11469
+ white-space: pre-wrap;
11470
+ overflow: auto;
11471
+ }
11472
+
11473
+ .skm-editor__highlight {
11474
+ margin: 0;
11475
+ border: 0;
11476
+ background: #1c1608;
11477
+ color: #e0c898;
11478
+ pointer-events: none;
11479
+ word-break: break-word;
11480
+ }
11481
+
11482
+ .skm-editor__input {
11483
+ border: 0;
11484
+ outline: 0;
11485
+ resize: none;
11486
+ background: transparent;
11487
+ color: transparent;
11464
11488
  caret-color: #f0c96a;
11465
11489
  }
11466
11490
 
@@ -11468,6 +11492,40 @@ const EDITOR_CSS = `
11468
11492
  color: #80633b;
11469
11493
  }
11470
11494
 
11495
+ .skm-editor__input::selection {
11496
+ background: rgba(240, 201, 106, 0.22);
11497
+ }
11498
+
11499
+ .skm-editor__token--keyword {
11500
+ color: #e07040;
11501
+ }
11502
+
11503
+ .skm-editor__token--property {
11504
+ color: #70a8d0;
11505
+ }
11506
+
11507
+ .skm-editor__token--string {
11508
+ color: #8db870;
11509
+ }
11510
+
11511
+ .skm-editor__token--number {
11512
+ color: #d4a020;
11513
+ }
11514
+
11515
+ .skm-editor__token--comment {
11516
+ color: #6a5a3a;
11517
+ }
11518
+
11519
+ .skm-editor__token--connector {
11520
+ color: #c8b070;
11521
+ }
11522
+
11523
+ .skm-editor__token--color {
11524
+ color: var(--skm-editor-color, #f0c96a);
11525
+ box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.08);
11526
+ font-weight: 600;
11527
+ }
11528
+
11471
11529
  .skm-editor__error {
11472
11530
  display: none;
11473
11531
  flex-shrink: 0;
@@ -11484,12 +11542,112 @@ const EDITOR_CSS = `
11484
11542
  display: block;
11485
11543
  }
11486
11544
  `;
11545
+ const CONNECTORS = ["<-->", "<->", "-->", "<--", "---", "--", "->", "<-"];
11546
+ const HEX_COLOR_RE = /#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})\b/g;
11487
11547
  function defaultFormatter(value) {
11488
11548
  return normalizeNewlines(value)
11489
11549
  .split("\n")
11490
11550
  .map((line) => line.replace(/[ \t]+$/g, ""))
11491
11551
  .join("\n");
11492
11552
  }
11553
+ function escapeHtml(value) {
11554
+ return value
11555
+ .replace(/&/g, "&amp;")
11556
+ .replace(/</g, "&lt;")
11557
+ .replace(/>/g, "&gt;")
11558
+ .replace(/"/g, "&quot;");
11559
+ }
11560
+ function wrapToken(kind, value) {
11561
+ return `<span class="skm-editor__token skm-editor__token--${kind}">${escapeHtml(value)}</span>`;
11562
+ }
11563
+ function renderColorLiteral(value) {
11564
+ return `<span class="skm-editor__token skm-editor__token--color" style="--skm-editor-color:${value}">${escapeHtml(value)}</span>`;
11565
+ }
11566
+ function renderStringToken(value) {
11567
+ HEX_COLOR_RE.lastIndex = 0;
11568
+ if (!HEX_COLOR_RE.test(value)) {
11569
+ return wrapToken("string", value);
11570
+ }
11571
+ HEX_COLOR_RE.lastIndex = 0;
11572
+ let html = "";
11573
+ let lastIndex = 0;
11574
+ let match = null;
11575
+ while ((match = HEX_COLOR_RE.exec(value))) {
11576
+ if (match.index > lastIndex) {
11577
+ html += wrapToken("string", value.slice(lastIndex, match.index));
11578
+ }
11579
+ html += renderColorLiteral(match[0]);
11580
+ lastIndex = match.index + match[0].length;
11581
+ }
11582
+ if (lastIndex < value.length) {
11583
+ html += wrapToken("string", value.slice(lastIndex));
11584
+ }
11585
+ return html;
11586
+ }
11587
+ function renderPlainToken(value, nextChar) {
11588
+ if (/^-?\d/.test(value)) {
11589
+ return wrapToken("number", value);
11590
+ }
11591
+ if (nextChar === "=") {
11592
+ return wrapToken("property", value);
11593
+ }
11594
+ if (KEYWORDS.has(value)) {
11595
+ return wrapToken("keyword", value);
11596
+ }
11597
+ return escapeHtml(value);
11598
+ }
11599
+ function highlightLine(line) {
11600
+ let html = "";
11601
+ let index = 0;
11602
+ while (index < line.length) {
11603
+ const rest = line.slice(index);
11604
+ if (rest.startsWith("//") || rest.startsWith("#")) {
11605
+ html += wrapToken("comment", rest);
11606
+ break;
11607
+ }
11608
+ if (line[index] === "\"") {
11609
+ let end = index + 1;
11610
+ while (end < line.length) {
11611
+ if (line[end] === "\"" && line[end - 1] !== "\\") {
11612
+ end += 1;
11613
+ break;
11614
+ }
11615
+ end += 1;
11616
+ }
11617
+ html += renderStringToken(line.slice(index, end));
11618
+ index = end;
11619
+ continue;
11620
+ }
11621
+ const connector = CONNECTORS.find((candidate) => line.startsWith(candidate, index));
11622
+ if (connector) {
11623
+ html += wrapToken("connector", connector);
11624
+ index += connector.length;
11625
+ continue;
11626
+ }
11627
+ const wordMatch = /^[A-Za-z_][A-Za-z0-9_-]*/.exec(rest);
11628
+ if (wordMatch) {
11629
+ const word = wordMatch[0];
11630
+ const nextChar = line[index + word.length] ?? "";
11631
+ html += renderPlainToken(word, nextChar);
11632
+ index += word.length;
11633
+ continue;
11634
+ }
11635
+ const numberMatch = /^-?\d+(?:\.\d+)?/.exec(rest);
11636
+ if (numberMatch) {
11637
+ html += wrapToken("number", numberMatch[0]);
11638
+ index += numberMatch[0].length;
11639
+ continue;
11640
+ }
11641
+ html += escapeHtml(line[index]);
11642
+ index += 1;
11643
+ }
11644
+ return html;
11645
+ }
11646
+ function renderHighlightedValue(value) {
11647
+ const normalized = normalizeNewlines(value);
11648
+ const html = normalized.split("\n").map(highlightLine).join("\n");
11649
+ return html || " ";
11650
+ }
11493
11651
  class SketchmarkEditor {
11494
11652
  constructor(options) {
11495
11653
  this.emitter = new EventEmitter();
@@ -11526,16 +11684,23 @@ class SketchmarkEditor {
11526
11684
  if (options.showClearButton !== false)
11527
11685
  this.toolbar.appendChild(clearButton);
11528
11686
  this.toolbar.appendChild(hint);
11687
+ this.surface = document.createElement("div");
11688
+ this.surface.className = "skm-editor__surface";
11689
+ this.highlightElement = document.createElement("pre");
11690
+ this.highlightElement.className = "skm-editor__highlight";
11691
+ this.highlightElement.setAttribute("aria-hidden", "true");
11529
11692
  this.textarea = document.createElement("textarea");
11530
11693
  this.textarea.className = "skm-editor__input";
11531
11694
  this.textarea.spellcheck = false;
11532
11695
  this.textarea.placeholder = options.placeholder ?? "diagram\nbox a label=\"Hello\"\nend";
11533
11696
  this.textarea.value = normalizeNewlines(options.value ?? DEFAULT_CLEAR_VALUE);
11534
11697
  this.textarea.addEventListener("input", () => {
11698
+ this.syncHighlight();
11535
11699
  const payload = { value: this.getValue(), editor: this };
11536
11700
  options.onChange?.(payload.value, this);
11537
11701
  this.emitter.emit("change", payload);
11538
11702
  });
11703
+ this.textarea.addEventListener("scroll", () => this.syncScroll());
11539
11704
  this.textarea.addEventListener("keydown", (event) => {
11540
11705
  if ((event.ctrlKey || event.metaKey) && event.key === "Enter") {
11541
11706
  event.preventDefault();
@@ -11547,9 +11712,12 @@ class SketchmarkEditor {
11547
11712
  if (options.showToolbar !== false) {
11548
11713
  this.root.appendChild(this.toolbar);
11549
11714
  }
11550
- this.root.appendChild(this.textarea);
11715
+ this.surface.appendChild(this.highlightElement);
11716
+ this.surface.appendChild(this.textarea);
11717
+ this.root.appendChild(this.surface);
11551
11718
  this.root.appendChild(this.errorElement);
11552
11719
  host.appendChild(this.root);
11720
+ this.syncHighlight();
11553
11721
  if (options.autoFocus) {
11554
11722
  this.focus();
11555
11723
  }
@@ -11559,6 +11727,7 @@ class SketchmarkEditor {
11559
11727
  }
11560
11728
  setValue(value, emitChange = false) {
11561
11729
  this.textarea.value = normalizeNewlines(value);
11730
+ this.syncHighlight();
11562
11731
  if (emitChange) {
11563
11732
  const payload = { value: this.getValue(), editor: this };
11564
11733
  this.options.onChange?.(payload.value, this);
@@ -11600,6 +11769,14 @@ class SketchmarkEditor {
11600
11769
  destroy() {
11601
11770
  this.root.remove();
11602
11771
  }
11772
+ syncHighlight() {
11773
+ this.highlightElement.innerHTML = renderHighlightedValue(this.textarea.value);
11774
+ this.syncScroll();
11775
+ }
11776
+ syncScroll() {
11777
+ this.highlightElement.scrollTop = this.textarea.scrollTop;
11778
+ this.highlightElement.scrollLeft = this.textarea.scrollLeft;
11779
+ }
11603
11780
  }
11604
11781
 
11605
11782
  const EMBED_STYLE_ID = "sketchmark-embed-ui";