edododraw 0.1.2 → 0.1.4

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.
@@ -252,6 +252,17 @@ const FILL_PALETTE = {
252
252
  brown: "#e5dbd4",
253
253
  white: "#ffffff"
254
254
  };
255
+ function isLightColor(color) {
256
+ if (!color) return false;
257
+ const c = color.trim().toLowerCase();
258
+ let hex = c.startsWith("#") ? c.slice(1) : "";
259
+ if (hex.length === 3) hex = hex.replace(/./g, (ch) => ch + ch);
260
+ if (hex.length !== 6 || /[^0-9a-f]/.test(hex)) return true;
261
+ const r2 = parseInt(hex.slice(0, 2), 16);
262
+ const g = parseInt(hex.slice(2, 4), 16);
263
+ const b = parseInt(hex.slice(4, 6), 16);
264
+ return 0.299 * r2 + 0.587 * g + 0.114 * b > 150;
265
+ }
255
266
  function resolveStroke(token, fallback) {
256
267
  if (!token) return fallback;
257
268
  const t = token.trim().toLowerCase();
@@ -3067,9 +3078,7 @@ class CameraController {
3067
3078
  };
3068
3079
  /** Duration heuristic: further travel + bigger zoom change => longer. */
3069
3080
  autoDuration(from, to) {
3070
- const dist2 = Math.hypot(to.cx - from.cx, to.cy - from.cy) * Math.min(from.zoom, to.zoom);
3071
- const zoomRatio = Math.abs(Math.log(to.zoom / from.zoom));
3072
- return clamp(420 + dist2 * 0.12 + zoomRatio * 320, 420, 1300);
3081
+ return autoDuration(from, to);
3073
3082
  }
3074
3083
  // ---- high-level ops -----------------------------------------------------
3075
3084
  fitAll(scene, opts = {}) {
@@ -3109,6 +3118,11 @@ class CameraController {
3109
3118
  this.setImmediate({ cx: cam.cx - dx / cam.zoom, cy: cam.cy - dy / cam.zoom, zoom: cam.zoom });
3110
3119
  }
3111
3120
  }
3121
+ function autoDuration(from, to) {
3122
+ const dist2 = Math.hypot(to.cx - from.cx, to.cy - from.cy) * Math.min(from.zoom, to.zoom);
3123
+ const zoomRatio = Math.abs(Math.log(to.zoom / from.zoom));
3124
+ return clamp(420 + dist2 * 0.12 + zoomRatio * 320, 420, 1300);
3125
+ }
3112
3126
  function lerp(a, b, t) {
3113
3127
  return a + (b - a) * t;
3114
3128
  }
@@ -3352,7 +3366,7 @@ const LIGHT_THEME = {
3352
3366
  background: "#ffffff",
3353
3367
  defaultStroke: STROKE_PALETTE.black,
3354
3368
  defaultText: STROKE_PALETTE.black,
3355
- gridColor: "#eceff3",
3369
+ gridColor: "#d5d9e0",
3356
3370
  mode: "light"
3357
3371
  };
3358
3372
  const DARK_THEME = {
@@ -3360,7 +3374,7 @@ const DARK_THEME = {
3360
3374
  background: "#121212",
3361
3375
  defaultStroke: "#e3e3e3",
3362
3376
  defaultText: "#e3e3e3",
3363
- gridColor: "#22262c",
3377
+ gridColor: "#2c313a",
3364
3378
  mode: "dark"
3365
3379
  };
3366
3380
  function defaultNodeStyle(seed, mode = "light") {
@@ -3766,7 +3780,8 @@ function compileProgram(program, opts = {}) {
3766
3780
  for (const t of themes.get(name).tokens) tokenMap.set(t.name, t.value);
3767
3781
  }
3768
3782
  }
3769
- const mode = themeName?.toLowerCase().includes("dark") ? "dark" : "light";
3783
+ const derivedMode = themeName?.toLowerCase().includes("dark") ? "dark" : "light";
3784
+ const mode = opts.mode ?? derivedMode;
3770
3785
  const classAttrs = (name, seen = /* @__PURE__ */ new Set()) => {
3771
3786
  if (seen.has(name) || !styles.has(name)) return { attrs: [] };
3772
3787
  seen.add(name);
@@ -3792,6 +3807,7 @@ function compileProgram(program, opts = {}) {
3792
3807
  const classes = collectClasses(nd);
3793
3808
  const layered = styleFor("node", classes, nd.attrs);
3794
3809
  const built = buildNodeStyle(layered, tokenMap);
3810
+ applyDarkInk(built.style, mode);
3795
3811
  const shape = mapShape(built.shape ?? nd.shape);
3796
3812
  const label = built.label ?? nd.label ?? prettyId(id);
3797
3813
  const node = makeNode({
@@ -3907,6 +3923,15 @@ function collectTags(nd) {
3907
3923
  }
3908
3924
  return tags;
3909
3925
  }
3926
+ const DARK_INK = "#1e1e1e";
3927
+ const LIGHT_INK = "#e3e3e3";
3928
+ function applyDarkInk(style, mode) {
3929
+ if (mode !== "dark") return;
3930
+ const solidLightFill = style.fillStyle === "solid" && isLightColor(style.fill ?? null);
3931
+ const ink = solidLightFill ? DARK_INK : LIGHT_INK;
3932
+ if (style.textColor === void 0) style.textColor = ink;
3933
+ if (style.stroke === void 0 && solidLightFill) style.stroke = DARK_INK;
3934
+ }
3910
3935
  function buildNodeStyle(attrs, tokens, _mode) {
3911
3936
  const style = {};
3912
3937
  const out = { style };
@@ -4104,6 +4129,7 @@ function ensureInline(scene, nd, tokens, mode, styleFor) {
4104
4129
  if (scene.nodes.some((n) => n.id === nd.id)) return;
4105
4130
  const layered = styleFor("node", nd.classes, nd.attrs);
4106
4131
  const built = buildNodeStyle(layered, tokens);
4132
+ applyDarkInk(built.style, mode);
4107
4133
  scene.nodes.push(
4108
4134
  makeNode({
4109
4135
  id: nd.id,
@@ -4276,8 +4302,30 @@ function applyRevealToStep(c, scene, step) {
4276
4302
  const ids = c.targets.flatMap((t) => resolveTargetIds(t, scene));
4277
4303
  if (c.verb === "hide" || c.verb === "fade-out" || c.verb === "remove") {
4278
4304
  step.hide.push(...ids);
4279
- } else {
4280
- step.reveal.push(...ids);
4305
+ return;
4306
+ }
4307
+ step.reveal.push(...ids);
4308
+ const effect = revealEffect(c.with ?? (c.verb !== "show" ? c.verb : void 0));
4309
+ if (effect) {
4310
+ step.revealFx = step.revealFx ?? {};
4311
+ for (const id of ids) step.revealFx[id] = effect;
4312
+ }
4313
+ }
4314
+ function revealEffect(raw) {
4315
+ switch (raw) {
4316
+ case "fade-in":
4317
+ case "fade":
4318
+ return "fade";
4319
+ case "pop":
4320
+ case "pop-in":
4321
+ case "emphasize":
4322
+ return "pop";
4323
+ case "draw-on":
4324
+ case "draw":
4325
+ case "sweep":
4326
+ return "sweep";
4327
+ default:
4328
+ return void 0;
4281
4329
  }
4282
4330
  }
4283
4331
  function cameraDirective(cam, scene, _tokens, timelineProps) {
@@ -4314,9 +4362,9 @@ function normalizeCamOp(op) {
4314
4362
  return "focus";
4315
4363
  }
4316
4364
  }
4317
- function compileEdd(source) {
4365
+ function compileEdd(source, opts = {}) {
4318
4366
  const { program, diagnostics } = parse(source);
4319
- const { scene } = compileProgram(program, { diagnostics });
4367
+ const { scene } = compileProgram(program, { diagnostics, mode: opts.mode });
4320
4368
  const report = diagnostics.items.map((d) => formatDiagnostic(d, source));
4321
4369
  return { scene, diagnostics, report };
4322
4370
  }
@@ -5149,6 +5197,11 @@ function ensureEngineStyles(doc = document) {
5149
5197
  injected = true;
5150
5198
  }
5151
5199
  const SVG_NS = "http://www.w3.org/2000/svg";
5200
+ const REVEAL_EFFECT_CLASS = {
5201
+ fade: "edd-reveal-fade",
5202
+ pop: "edd-reveal-pop",
5203
+ sweep: "edd-reveal-sweep"
5204
+ };
5152
5205
  class SvgRenderer {
5153
5206
  container;
5154
5207
  svg;
@@ -5268,9 +5321,28 @@ class SvgRenderer {
5268
5321
  else el.classList.remove("edd-hidden");
5269
5322
  });
5270
5323
  }
5324
+ /**
5325
+ * Play a per-beat reveal animation (`fade` | `pop` | `sweep`) on the named
5326
+ * element groups. Clears any prior reveal classes first and forces a reflow so
5327
+ * re-entering the same beat restarts the animation. `fx` = id -> effect.
5328
+ */
5329
+ playReveal(fx) {
5330
+ const classes = ["edd-reveal-fade", "edd-reveal-pop", "edd-reveal-sweep"];
5331
+ const els = this.svg.querySelectorAll("[data-node],[data-edge]");
5332
+ els.forEach((el) => el.classList.remove(...classes));
5333
+ if (!fx || !Object.keys(fx).length) return;
5334
+ void this.svg.getBoundingClientRect();
5335
+ els.forEach((el) => {
5336
+ const id = el.getAttribute("data-node") ?? el.getAttribute("data-edge");
5337
+ const cls = id ? REVEAL_EFFECT_CLASS[fx[id]] : void 0;
5338
+ if (cls) el.classList.add(cls);
5339
+ });
5340
+ }
5271
5341
  render(scene) {
5272
5342
  this.scene = scene;
5273
- this.bg.setAttribute("fill", scene.meta.background || scene.theme.background);
5343
+ const bgColor = scene.meta.background || scene.theme.background;
5344
+ this.bg.setAttribute("fill", "transparent");
5345
+ this.container.style.backgroundColor = bgColor;
5274
5346
  for (const name of ["groups", "edges", "nodes"]) this.layers[name].replaceChildren();
5275
5347
  for (const group of scene.groups) {
5276
5348
  if (!group.frame) continue;
@@ -5435,6 +5507,7 @@ class TimelinePlayer {
5435
5507
  const step = this.steps[i];
5436
5508
  const scene = this.scene;
5437
5509
  this.renderer.applyVisibility(this.computeHidden(i));
5510
+ if (animate) this.renderer.playReveal(step.revealFx);
5438
5511
  this.annotations.render(scene, [...scene.annotations, ...step.annotations], animate);
5439
5512
  this.emit();
5440
5513
  if (step.camera) {
@@ -5650,6 +5723,8 @@ class EdodoDraw {
5650
5723
  listeners = /* @__PURE__ */ new Map();
5651
5724
  textInput = null;
5652
5725
  closeInput = null;
5726
+ colorScheme = null;
5727
+ hasRendered = false;
5653
5728
  cleanup = [];
5654
5729
  constructor(container, options = {}) {
5655
5730
  this.container = container;
@@ -5708,7 +5783,7 @@ class EdodoDraw {
5708
5783
  async render(source) {
5709
5784
  this.source = source;
5710
5785
  const seq = ++this.renderSeq;
5711
- const { scene, diagnostics } = compileEdd(source);
5786
+ const { scene, diagnostics } = compileEdd(source, { mode: this.colorScheme ?? void 0 });
5712
5787
  let diags = diagnostics.items;
5713
5788
  const blocks = extractMermaidBlocks(source);
5714
5789
  if (blocks.length) {
@@ -5725,6 +5800,7 @@ class EdodoDraw {
5725
5800
  }
5726
5801
  if (seq !== this.renderSeq) return { scene, diagnostics: diags };
5727
5802
  this.scene = scene;
5803
+ this.hasRendered = true;
5728
5804
  this.renderer.render(scene);
5729
5805
  this.player.load(scene);
5730
5806
  this.renderer.measure();
@@ -5744,6 +5820,21 @@ class EdodoDraw {
5744
5820
  getSource() {
5745
5821
  return this.source;
5746
5822
  }
5823
+ /**
5824
+ * Force light/dark rendering, overriding the diagram's declared theme. Affects
5825
+ * the canvas background, the dotted grid, and the diagram's default ink
5826
+ * (strokes/text adapt for contrast on the dark canvas; explicit colors are
5827
+ * kept). Pass `null` to fall back to the DSL's own theme. Re-renders the
5828
+ * current source. Cheap and idempotent — no-ops if the scheme is unchanged.
5829
+ */
5830
+ setColorScheme(mode) {
5831
+ if (this.colorScheme === mode) return;
5832
+ this.colorScheme = mode;
5833
+ if (this.hasRendered) void this.render(this.source);
5834
+ }
5835
+ getColorScheme() {
5836
+ return this.colorScheme;
5837
+ }
5747
5838
  // ---- camera -------------------------------------------------------------
5748
5839
  fit(animate = true) {
5749
5840
  this.renderer.measure();
@@ -5881,7 +5972,8 @@ class EdodoDraw {
5881
5972
  c.style.backgroundImage = "none";
5882
5973
  return;
5883
5974
  }
5884
- c.style.backgroundImage = "radial-gradient(circle, #d5d9e0 1px, transparent 1px)";
5975
+ const dot = this.scene.theme.gridColor;
5976
+ c.style.backgroundImage = `radial-gradient(circle, ${dot} 1px, transparent 1px)`;
5885
5977
  c.style.backgroundSize = `${cell}px ${cell}px`;
5886
5978
  c.style.backgroundPosition = `${-cam.cx * cam.zoom + w / 2}px ${-cam.cy * cam.zoom + h / 2}px`;
5887
5979
  }
@@ -5965,11 +6057,18 @@ class EdodoDraw {
5965
6057
  }
5966
6058
  /** Inline text editor for renaming a node (double-click / new node). */
5967
6059
  showRenameInput(id, current, screen) {
6060
+ const dark = this.scene.theme.mode === "dark";
5968
6061
  this.openInlineInput({
5969
6062
  value: current,
5970
6063
  placeholder: "label…",
5971
6064
  screen,
5972
- style: { background: "#ffffff", color: "#1e1e1e", textAlign: "center", zIndex: "30", minWidth: "80px" },
6065
+ style: {
6066
+ background: dark ? "#23262d" : "#ffffff",
6067
+ color: dark ? "#e6e7ea" : "#1e1e1e",
6068
+ textAlign: "center",
6069
+ zIndex: "30",
6070
+ minWidth: "80px"
6071
+ },
5973
6072
  onCommit: (v) => this.edit.applyRename(id, v)
5974
6073
  });
5975
6074
  }
@@ -6073,26 +6172,27 @@ export {
6073
6172
  hitTestNode as a3,
6074
6173
  injectMermaid as a4,
6075
6174
  isEmptyBBox as a5,
6076
- lerp$1 as a6,
6077
- listShapePlugins as a7,
6078
- makeEdge as a8,
6079
- makeNode as a9,
6080
- nodeBBox as aa,
6081
- nodeRect as ab,
6082
- nodeRectOf as ac,
6083
- pairedStrokeForFill as ad,
6084
- pointInRect as ae,
6085
- rectCenter as af,
6086
- rectToBBox as ag,
6087
- registerShape as ah,
6088
- renameNode as ai,
6089
- resolveAnchor as aj,
6090
- resolveFill as ak,
6091
- resolveMarker as al,
6092
- resolveStroke as am,
6093
- sceneBBox as an,
6094
- styleNode as ao,
6095
- writeOverrides as ap,
6175
+ isLightColor as a6,
6176
+ lerp$1 as a7,
6177
+ listShapePlugins as a8,
6178
+ makeEdge as a9,
6179
+ makeNode as aa,
6180
+ nodeBBox as ab,
6181
+ nodeRect as ac,
6182
+ nodeRectOf as ad,
6183
+ pairedStrokeForFill as ae,
6184
+ pointInRect as af,
6185
+ rectCenter as ag,
6186
+ rectToBBox as ah,
6187
+ registerShape as ai,
6188
+ renameNode as aj,
6189
+ resolveAnchor as ak,
6190
+ resolveFill as al,
6191
+ resolveMarker as am,
6192
+ resolveStroke as an,
6193
+ sceneBBox as ao,
6194
+ styleNode as ap,
6195
+ writeOverrides as aq,
6096
6196
  EditController as b,
6097
6197
  EdodoDraw as c,
6098
6198
  FONT_FAMILY as d,
@@ -6119,4 +6219,4 @@ export {
6119
6219
  deleteElements as y,
6120
6220
  dist as z
6121
6221
  };
6122
- //# sourceMappingURL=EdodoDraw-C3Yd1I9B.js.map
6222
+ //# sourceMappingURL=EdodoDraw-BdJowisU.js.map