@1agh/maude 0.39.1 → 0.41.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.
Files changed (137) hide show
  1. package/README.md +1 -1
  2. package/apps/studio/ai-banner.tsx +2 -2
  3. package/apps/studio/annotations-context-toolbar.tsx +44 -1
  4. package/apps/studio/annotations-layer.tsx +306 -5
  5. package/apps/studio/annotations-model.ts +107 -6
  6. package/apps/studio/api.ts +1316 -67
  7. package/apps/studio/artboard-marquee.tsx +1 -1
  8. package/apps/studio/bin/_canvas-rects-playwright.mjs +55 -0
  9. package/apps/studio/bin/_canvas-rects-static.mjs +166 -0
  10. package/apps/studio/bin/_fetch-asset.mjs +556 -0
  11. package/apps/studio/bin/_html-playwright.mjs +9 -1
  12. package/apps/studio/bin/_pdf-playwright.mjs +8 -1
  13. package/apps/studio/bin/_png-playwright.mjs +8 -1
  14. package/apps/studio/bin/_pw-launch.mjs +54 -0
  15. package/apps/studio/bin/_svg-playwright.mjs +8 -1
  16. package/apps/studio/bin/_video-playwright.mjs +452 -0
  17. package/apps/studio/bin/annotate.mjs +576 -34
  18. package/apps/studio/bin/canvas-rects.sh +152 -0
  19. package/apps/studio/bin/fetch-asset.sh +34 -0
  20. package/apps/studio/bin/prep.sh +8 -1
  21. package/apps/studio/bin/read-annotations.mjs +138 -7
  22. package/apps/studio/bin/smoke.sh +42 -6
  23. package/apps/studio/build.ts +21 -0
  24. package/apps/studio/canvas-comment-mount.tsx +138 -4
  25. package/apps/studio/canvas-edit.ts +2625 -111
  26. package/apps/studio/canvas-lib.tsx +238 -2
  27. package/apps/studio/canvas-list-watch.ts +6 -2
  28. package/apps/studio/canvas-shell.tsx +514 -20
  29. package/apps/studio/client/app.jsx +2480 -52
  30. package/apps/studio/client/comments-overlay.css +130 -126
  31. package/apps/studio/client/github.js +15 -0
  32. package/apps/studio/client/panels/TimelinePanel.jsx +860 -0
  33. package/apps/studio/client/panels/timeline-parse.js +229 -0
  34. package/apps/studio/client/panels/timeline-snap.js +55 -0
  35. package/apps/studio/client/styles/3-shell-maude.css +155 -0
  36. package/apps/studio/comments-overlay.tsx +148 -41
  37. package/apps/studio/config.schema.json +14 -0
  38. package/apps/studio/context-menu.tsx +16 -6
  39. package/apps/studio/context.ts +113 -1
  40. package/apps/studio/contextual-toolbar.tsx +262 -4
  41. package/apps/studio/cursors-overlay.tsx +4 -4
  42. package/apps/studio/dist/client.bundle.js +89 -17
  43. package/apps/studio/dist/comment-mount.js +59 -1
  44. package/apps/studio/dist/runtime/.min-sizes.json +11 -1
  45. package/apps/studio/dist/runtime/@remotion_media.js +491 -0
  46. package/apps/studio/dist/runtime/@remotion_player.js +56 -0
  47. package/apps/studio/dist/runtime/@remotion_transitions.js +300 -0
  48. package/apps/studio/dist/runtime/@remotion_transitions_clock-wipe.js +1 -0
  49. package/apps/studio/dist/runtime/@remotion_transitions_fade.js +1 -0
  50. package/apps/studio/dist/runtime/@remotion_transitions_flip.js +1 -0
  51. package/apps/studio/dist/runtime/@remotion_transitions_none.js +1 -0
  52. package/apps/studio/dist/runtime/@remotion_transitions_slide.js +1 -0
  53. package/apps/studio/dist/runtime/@remotion_transitions_wipe.js +1 -0
  54. package/apps/studio/dist/runtime/REMOTION-LICENSE.md +28 -0
  55. package/apps/studio/dist/runtime/remotion.js +42 -0
  56. package/apps/studio/dist/styles.css +1 -1
  57. package/apps/studio/dom-selection.ts +127 -1
  58. package/apps/studio/drag-state.ts +24 -0
  59. package/apps/studio/equal-spacing-detector.ts +205 -0
  60. package/apps/studio/export-dialog.tsx +1 -1
  61. package/apps/studio/exporters/_browser-bundles.ts +117 -0
  62. package/apps/studio/exporters/_runtime.ts +69 -0
  63. package/apps/studio/exporters/html.ts +4 -3
  64. package/apps/studio/exporters/index.ts +28 -2
  65. package/apps/studio/exporters/pdf.ts +4 -3
  66. package/apps/studio/exporters/png.ts +5 -3
  67. package/apps/studio/exporters/pptx.ts +6 -4
  68. package/apps/studio/exporters/svg.ts +9 -5
  69. package/apps/studio/exporters/video-encode-lib.ts +200 -0
  70. package/apps/studio/exporters/video-render-lib.ts +108 -0
  71. package/apps/studio/exporters/video.ts +184 -0
  72. package/apps/studio/history.ts +47 -1
  73. package/apps/studio/http.ts +758 -27
  74. package/apps/studio/input-router.tsx +19 -1
  75. package/apps/studio/marquee-overlay.tsx +1 -1
  76. package/apps/studio/measure-overlay.tsx +241 -0
  77. package/apps/studio/participants-chrome.tsx +3 -3
  78. package/apps/studio/runtime-bundle.ts +30 -0
  79. package/apps/studio/server.ts +34 -9
  80. package/apps/studio/sizing-mode.ts +117 -0
  81. package/apps/studio/spacing-handles.ts +166 -0
  82. package/apps/studio/test/annotate-write.test.ts +890 -0
  83. package/apps/studio/test/annotations-roundtrip.test.ts +47 -0
  84. package/apps/studio/test/camera-reveal.test.tsx +173 -0
  85. package/apps/studio/test/canvas-create-api.test.ts +76 -0
  86. package/apps/studio/test/canvas-edit.test.ts +140 -0
  87. package/apps/studio/test/canvas-list-watch.test.ts +49 -0
  88. package/apps/studio/test/canvas-media-drop.test.ts +30 -1
  89. package/apps/studio/test/canvas-origin-gate.test.ts +54 -0
  90. package/apps/studio/test/canvas-rects.test.ts +198 -0
  91. package/apps/studio/test/clip-addressing.test.ts +732 -0
  92. package/apps/studio/test/comments-overlay.test.ts +117 -0
  93. package/apps/studio/test/config-reload.test.ts +230 -0
  94. package/apps/studio/test/dns-rebinding-guard.test.ts +74 -0
  95. package/apps/studio/test/dom-selection.test.ts +130 -0
  96. package/apps/studio/test/edit-css-occurrence.test.ts +81 -0
  97. package/apps/studio/test/edit-persistence.test.ts +91 -0
  98. package/apps/studio/test/edit-scope-api.test.ts +115 -0
  99. package/apps/studio/test/element-resize.test.ts +136 -0
  100. package/apps/studio/test/element-structural-api.test.ts +360 -0
  101. package/apps/studio/test/element-structural-edit.test.ts +233 -0
  102. package/apps/studio/test/equal-spacing-detector.test.ts +165 -1
  103. package/apps/studio/test/exporters/runtime.test.ts +59 -0
  104. package/apps/studio/test/file-lock.test.ts +84 -0
  105. package/apps/studio/test/fixtures/video-comp-fixture.tsx +82 -0
  106. package/apps/studio/test/history-rollback.test.ts +26 -0
  107. package/apps/studio/test/knob-props-authored.test.ts +87 -0
  108. package/apps/studio/test/read-annotations.test.ts +154 -0
  109. package/apps/studio/test/sizing-mode.test.ts +102 -0
  110. package/apps/studio/test/spacing-handles.test.ts +138 -0
  111. package/apps/studio/test/specimen-select.test.ts +88 -0
  112. package/apps/studio/test/timeline-parse.test.ts +127 -0
  113. package/apps/studio/test/timeline-snap.test.ts +85 -0
  114. package/apps/studio/test/tool-palette-insert-anchor.test.ts +84 -0
  115. package/apps/studio/test/undo-sequence-byte-compare.test.ts +211 -0
  116. package/apps/studio/test/video-asset.test.ts +163 -0
  117. package/apps/studio/test/video-comp-fixture.test.ts +50 -0
  118. package/apps/studio/test/video-comp.test.ts +168 -0
  119. package/apps/studio/test/video-render-bridge.test.ts +149 -0
  120. package/apps/studio/tool-palette.tsx +122 -2
  121. package/apps/studio/undo-hud.tsx +2 -2
  122. package/apps/studio/use-annotation-resize.tsx +6 -3
  123. package/apps/studio/use-canvas-media-drop.tsx +66 -4
  124. package/apps/studio/use-element-resize.tsx +732 -0
  125. package/apps/studio/use-keyboard-discipline.tsx +205 -15
  126. package/apps/studio/use-selection-set.tsx +14 -0
  127. package/apps/studio/use-spacing-handles.tsx +388 -0
  128. package/apps/studio/video-comp.tsx +444 -0
  129. package/apps/studio/whats-new.json +55 -0
  130. package/apps/studio/ws.ts +5 -0
  131. package/cli/commands/design.mjs +6 -1
  132. package/cli/commands/design.test.mjs +49 -1
  133. package/cli/lib/fetch-asset.test.mjs +213 -0
  134. package/package.json +8 -8
  135. package/plugins/design/dependencies.json +10 -2
  136. package/plugins/design/templates/_shell.html +25 -2
  137. package/plugins/design/templates/design-system-inspiration/_MAPPING.md +1 -1
@@ -266,6 +266,30 @@ export interface LinkStroke extends StrokeBase {
266
266
  title: string;
267
267
  domain: string;
268
268
  }
269
+ /**
270
+ * DDR-150 P4 — a video/audio file dropped on the canvas BODY (not the timeline)
271
+ * as a reference chip. It's the "nahazet klipy → agent z toho udělá video"
272
+ * artifact: a non-destructive, versioned pointer to an `assets/…` clip the agent
273
+ * can enumerate off the saved `.annotations.svg` (via `data-src`), then assemble
274
+ * into a comp. Distinct from a timeline drop (which INSERTS a `<Sequence>`) and
275
+ * from an ImageStroke (a rendered picture). Renders as a card: a media glyph
276
+ * (▶ video / ♪ audio) + the filename. `src` is ALWAYS a relative `assets/<sha8>`
277
+ * path (never seeked by the capture spine — it's a reference, excluded from
278
+ * export by `?hide-chrome`). Persists as an allowlisted `<g>` like LinkStroke —
279
+ * `data-src`/`data-media-kind`/`data-title` are the round-trip source of truth.
280
+ */
281
+ export interface MediaRefStroke extends StrokeBase {
282
+ tool: 'mediaref';
283
+ x: number;
284
+ y: number;
285
+ w: number;
286
+ h: number;
287
+ /** Relative `assets/<sha8>.<ext>` path — the agent reads this off the SVG. */
288
+ src: string;
289
+ mediaKind: 'video' | 'audio';
290
+ /** Human-facing label (the dropped file's name). */
291
+ title: string;
292
+ }
269
293
  /**
270
294
  * FigJam v3 — section: a labelled organizing container. Renders as a soft
271
295
  * rounded region with a name chip above the top-left corner; its INTERIOR is
@@ -294,6 +318,7 @@ export type Stroke =
294
318
  | StickyStroke
295
319
  | ImageStroke
296
320
  | LinkStroke
321
+ | MediaRefStroke
297
322
  | SectionStroke;
298
323
 
299
324
  /**
@@ -528,6 +553,11 @@ export const IMAGE_MIN_SIZE = 16;
528
553
  export const IMAGE_MAX_DROP_SIDE = 480;
529
554
  export const LINK_DEFAULT_W = 260;
530
555
  export const LINK_DEFAULT_H = 76;
556
+ // DDR-150 P4 — media-reference chip (dropped video/audio → assets/ pointer).
557
+ export const MEDIAREF_DEFAULT_W = 280;
558
+ export const MEDIAREF_DEFAULT_H = 76;
559
+ /** Video chips are taller: 26px header + a 16:9 inline player area (dogfood #8). */
560
+ export const MEDIAREF_VIDEO_H = 190;
531
561
  export const LINK_CARD_FILL = '#ffffff';
532
562
  export const LINK_CARD_STROKE = '#d4d4d8';
533
563
  export const LINK_DOMAIN_FILL = '#71717a';
@@ -539,6 +569,12 @@ export const LINK_GLYPH_STROKE = '#52525b';
539
569
  // <rect> geometry), so render/serialize only need to agree visually.
540
570
  export const LINK_GLYPH_D1 = 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71';
541
571
  export const LINK_GLYPH_D2 = 'M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71';
572
+ // DDR-150 P4 — media-reference chip glyphs (24×24 viewBox, filled). One source
573
+ // for serialize + StrokeNode render, like the link glyph. Video = play triangle;
574
+ // audio = a filled note. The parser ignores the glyph (reads data-* + geometry).
575
+ export const MEDIAREF_VIDEO_GLYPH = 'M8 5v14l11-7z';
576
+ export const MEDIAREF_AUDIO_GLYPH =
577
+ 'M9 18V6l10-2v11.5a2.5 2.5 0 1 1-2-2.45V7.3L11 8.6v6.9a2.5 2.5 0 1 1-2-2.45z';
542
578
 
543
579
  /** Card text positions, derived purely from the bbox (idempotent round-trip). */
544
580
  export function linkCardLayout(x: number, y: number, w: number, h: number) {
@@ -1038,6 +1074,37 @@ function strokeToSvgElBase(s: Stroke): string {
1038
1074
  `</g>`
1039
1075
  );
1040
1076
  }
1077
+ if (s.tool === 'mediaref') {
1078
+ // DDR-150 P4 — reference chip for a dropped clip. Like the link card: the
1079
+ // data-* are the round-trip source of truth (the agent reads data-src to
1080
+ // enumerate refs); the inner rect/glyph/text are the inert, sanitizer-safe
1081
+ // visual. NEVER a <Video>/<Audio> element (that would try to play/seek) —
1082
+ // it's a pointer, drawn as a still card.
1083
+ const nx = Math.min(s.x, s.x + s.w);
1084
+ const ny = Math.min(s.y, s.y + s.h);
1085
+ const nw = Math.abs(s.w);
1086
+ const nh = Math.abs(s.h);
1087
+ const lay = linkCardLayout(nx, ny, nw, nh);
1088
+ const shownTitle = clampLinkTitle(s.title, lay.textMaxChars);
1089
+ const glyph =
1090
+ s.mediaKind === 'audio'
1091
+ ? `<path d="${MEDIAREF_AUDIO_GLYPH}"/>`
1092
+ : `<path d="${MEDIAREF_VIDEO_GLYPH}"/>`;
1093
+ return (
1094
+ `<g data-id="${esc(s.id)}" data-tool="mediaref" data-src="${escAttr(s.src)}" data-media-kind="${escAttr(
1095
+ s.mediaKind
1096
+ )}" data-title="${escAttr(s.title)}">` +
1097
+ `<rect x="${nx}" y="${ny}" width="${nw}" height="${nh}" rx="8" ry="8" fill="${LINK_CARD_FILL}" stroke="${LINK_CARD_STROKE}" stroke-width="1"/>` +
1098
+ `<svg x="${lay.glyph.x}" y="${lay.glyph.y}" width="${lay.glyph.size}" height="${lay.glyph.size}" viewBox="0 0 24 24" fill="${LINK_GLYPH_STROKE}" stroke="none">${glyph}</svg>` +
1099
+ `<text x="${lay.textX}" y="${lay.domain.y}" font-size="${lay.domain.fontSize}" fill="${LINK_DOMAIN_FILL}" dominant-baseline="hanging">${esc(
1100
+ `${s.mediaKind} · reference`
1101
+ )}</text>` +
1102
+ `<text x="${lay.textX}" y="${lay.title.y}" font-size="${lay.title.fontSize}" fill="${LINK_TITLE_FILL}" font-weight="600" dominant-baseline="hanging">${esc(
1103
+ shownTitle
1104
+ )}</text>` +
1105
+ `</g>`
1106
+ );
1107
+ }
1041
1108
  if (s.tool === 'section') {
1042
1109
  // FigJam v3 — inert persisted form: region rect + chip label text. Colors
1043
1110
  // are render-time chrome; the parser reads geometry off the rect and the
@@ -1463,6 +1530,22 @@ export function svgToStrokes(svgText: string): Stroke[] {
1463
1530
  push({ id, tool: 'link', x, y, w, h, url, title, domain });
1464
1531
  continue;
1465
1532
  }
1533
+ if (tool === 'mediaref') {
1534
+ // DDR-150 P4 — geometry off the <rect> child (mirrors link/sticky);
1535
+ // data-src is the pointer the agent enumerates. A src that fails the
1536
+ // assets/ shape is dropped to '' (an inert chip that references nothing).
1537
+ const rectEl = el.querySelector('rect');
1538
+ const x = pfloat(rectEl?.getAttribute('x'));
1539
+ const y = pfloat(rectEl?.getAttribute('y'));
1540
+ const w = pfloat(rectEl?.getAttribute('width'), MEDIAREF_DEFAULT_W);
1541
+ const h = pfloat(rectEl?.getAttribute('height'), MEDIAREF_DEFAULT_H);
1542
+ const rawSrc = el.getAttribute('data-src') || '';
1543
+ const src = ASSET_MEDIA_SRC_RE.test(rawSrc) ? rawSrc : '';
1544
+ const mediaKind = el.getAttribute('data-media-kind') === 'audio' ? 'audio' : 'video';
1545
+ const title = el.getAttribute('data-title') || src;
1546
+ push({ id, tool: 'mediaref', x, y, w, h, src, mediaKind, title });
1547
+ continue;
1548
+ }
1466
1549
  if (tool === 'text') {
1467
1550
  const rawAnchor = el.getAttribute('data-anchor-id');
1468
1551
  const fontSize =
@@ -1650,8 +1733,8 @@ export function strokeHitTest(s: Stroke, wx: number, wy: number, tol: number): b
1650
1733
  return onEdge;
1651
1734
  }
1652
1735
  const t = Math.max(tol, 'width' in s ? s.width : 2);
1653
- if (s.tool === 'sticky' || s.tool === 'image' || s.tool === 'link') {
1654
- // Sticky / image / link are solid cards — filled-rect hit anywhere inside.
1736
+ if (s.tool === 'sticky' || s.tool === 'image' || s.tool === 'link' || s.tool === 'mediaref') {
1737
+ // Sticky / image / link / mediaref are solid cards — filled-rect hit inside.
1655
1738
  const xMin = Math.min(s.x, s.x + s.w);
1656
1739
  const xMax = Math.max(s.x, s.x + s.w);
1657
1740
  const yMin = Math.min(s.y, s.y + s.h);
@@ -1844,6 +1927,8 @@ export function isStrokeMeaningful(s: Stroke): boolean {
1844
1927
  // Phase 23 — an image needs real extent; a link needs a non-empty URL.
1845
1928
  if (s.tool === 'image') return Math.abs(s.w) >= IMAGE_MIN_SIZE && Math.abs(s.h) >= IMAGE_MIN_SIZE;
1846
1929
  if (s.tool === 'link') return s.url.trim().length > 0;
1930
+ // A media-reference chip needs a non-empty assets/ src (its whole purpose).
1931
+ if (s.tool === 'mediaref') return s.src.trim().length > 0;
1847
1932
  if (s.tool === 'section')
1848
1933
  return Math.abs(s.w) >= SECTION_MIN_SIZE && Math.abs(s.h) >= SECTION_MIN_SIZE;
1849
1934
  return Math.hypot(s.x2 - s.x1, s.y2 - s.y1) >= 4;
@@ -1886,9 +1971,15 @@ export function strokeBBox(
1886
1971
  h: Math.abs(s.y2 - s.y1),
1887
1972
  };
1888
1973
  }
1889
- if (s.tool === 'sticky' || s.tool === 'image' || s.tool === 'link' || s.tool === 'section') {
1890
- // Phase 23 — image + link are rect-shaped media, same bbox as a sticky
1891
- // card; FigJam v3 sections share it too.
1974
+ if (
1975
+ s.tool === 'sticky' ||
1976
+ s.tool === 'image' ||
1977
+ s.tool === 'link' ||
1978
+ s.tool === 'mediaref' ||
1979
+ s.tool === 'section'
1980
+ ) {
1981
+ // Phase 23 — image + link + mediaref are rect-shaped cards, same bbox as a
1982
+ // sticky; FigJam v3 sections share it too.
1892
1983
  return {
1893
1984
  x: Math.min(s.x, s.x + s.w),
1894
1985
  y: Math.min(s.y, s.y + s.h),
@@ -1928,7 +2019,13 @@ export function translateOne(s: Stroke, dx: number, dy: number): Stroke {
1928
2019
  if (s.tool === 'ellipse') return { ...s, cx: s.cx + dx, cy: s.cy + dy };
1929
2020
  if (s.tool === 'arrow')
1930
2021
  return { ...s, x1: s.x1 + dx, y1: s.y1 + dy, x2: s.x2 + dx, y2: s.y2 + dy };
1931
- if (s.tool === 'sticky' || s.tool === 'image' || s.tool === 'link' || s.tool === 'section')
2022
+ if (
2023
+ s.tool === 'sticky' ||
2024
+ s.tool === 'image' ||
2025
+ s.tool === 'link' ||
2026
+ s.tool === 'mediaref' ||
2027
+ s.tool === 'section'
2028
+ )
1932
2029
  return { ...s, x: s.x + dx, y: s.y + dy };
1933
2030
  // text — anchored inherits its host's bbox (moves with the host); standalone
1934
2031
  // (Phase 21) carries its own world (x, y) and translates directly.
@@ -1997,6 +2094,10 @@ export const ANNOTATION_SVG_ELEMENTS = new Set([
1997
2094
  * is stripped by Rule 3. Pairs with the asset-write caps (DDR Task 9).
1998
2095
  */
1999
2096
  export const ASSET_IMAGE_HREF_RE = /^assets\/[A-Za-z0-9._-]+\.(?:png|jpe?g|webp|gif)$/;
2097
+ // DDR-150 P4 — a media-reference chip's data-src: a relative assets/ path to a
2098
+ // video/audio clip (no scheme, no traversal). Same shape as the image href gate.
2099
+ export const ASSET_MEDIA_SRC_RE =
2100
+ /^assets\/[A-Za-z0-9._-]+\.(?:mp4|webm|mov|m4v|mp3|wav|m4a|aac|ogg)$/;
2000
2101
 
2001
2102
  /**
2002
2103
  * A3 (DDR-060 F1 re-audit) — sanitize an annotation SVG before it is persisted /