@1agh/maude 0.45.1 → 0.46.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 (91) hide show
  1. package/apps/studio/acp/bridge.ts +628 -74
  2. package/apps/studio/acp/index.ts +172 -23
  3. package/apps/studio/acp/probe.ts +31 -7
  4. package/apps/studio/acp/transcript.ts +91 -5
  5. package/apps/studio/annotations-snap.ts +50 -10
  6. package/apps/studio/api.ts +344 -14
  7. package/apps/studio/artboard-guides-overlay.tsx +270 -0
  8. package/apps/studio/bin/_import-asset.mjs +35 -5
  9. package/apps/studio/bin/_pdf-playwright.mjs +35 -4
  10. package/apps/studio/bin/_png-playwright.mjs +38 -4
  11. package/apps/studio/bin/_pw-launch.mjs +52 -0
  12. package/apps/studio/bin/_pw-launch.test.mjs +90 -0
  13. package/apps/studio/bin/_smart-frames.mjs +419 -0
  14. package/apps/studio/bin/_smart-frames.test.mjs +140 -0
  15. package/apps/studio/bin/smart-frames.sh +30 -0
  16. package/apps/studio/canvas-edit.ts +302 -3
  17. package/apps/studio/canvas-lib.tsx +203 -11
  18. package/apps/studio/canvas-shell.tsx +167 -28
  19. package/apps/studio/client/app.jsx +678 -176
  20. package/apps/studio/client/panels/CapabilityBar.jsx +101 -0
  21. package/apps/studio/client/panels/ChatPanel.jsx +846 -133
  22. package/apps/studio/client/panels/ElicitationPrompt.jsx +429 -0
  23. package/apps/studio/client/panels/PermissionPrompt.jsx +119 -0
  24. package/apps/studio/client/panels/ReadinessList.jsx +17 -3
  25. package/apps/studio/client/panels/SettingsPanel.jsx +211 -0
  26. package/apps/studio/client/panels/ToolGroup.jsx +79 -0
  27. package/apps/studio/client/panels/acp-capabilities.js +82 -0
  28. package/apps/studio/client/panels/acp-elicitation.js +194 -0
  29. package/apps/studio/client/panels/acp-runtime.js +248 -9
  30. package/apps/studio/client/panels/acp-usage.js +65 -0
  31. package/apps/studio/client/panels/transcript-view.js +36 -0
  32. package/apps/studio/client/styles/6-acp-chat.css +689 -11
  33. package/apps/studio/dist/client.bundle.js +1738 -1736
  34. package/apps/studio/dist/comment-mount.js +2 -2
  35. package/apps/studio/dist/styles.css +1 -1
  36. package/apps/studio/dom-selection.ts +20 -0
  37. package/apps/studio/export-dialog.tsx +138 -18
  38. package/apps/studio/exporters/pdf.ts +332 -18
  39. package/apps/studio/exporters/png.ts +45 -3
  40. package/apps/studio/footage/schema.ts +17 -1
  41. package/apps/studio/generation/gemma-models.ts +224 -0
  42. package/apps/studio/generation/prefs.ts +43 -0
  43. package/apps/studio/generation/whisper-models.test.ts +28 -1
  44. package/apps/studio/generation/whisper-models.ts +22 -7
  45. package/apps/studio/http.ts +246 -9
  46. package/apps/studio/print/marks.ts +113 -0
  47. package/apps/studio/print/units.ts +269 -0
  48. package/apps/studio/print-overlay-content.tsx +132 -0
  49. package/apps/studio/test/acp-bridge.test.ts +11 -6
  50. package/apps/studio/test/acp-capabilities.test.ts +123 -0
  51. package/apps/studio/test/acp-caps-bridge.test.ts +274 -0
  52. package/apps/studio/test/acp-elicitation-bridge.test.ts +475 -0
  53. package/apps/studio/test/acp-elicitation.test.ts +251 -0
  54. package/apps/studio/test/acp-mode-banner.test.ts +45 -0
  55. package/apps/studio/test/acp-permission-prompt.test.ts +77 -0
  56. package/apps/studio/test/acp-permission.test.ts +262 -0
  57. package/apps/studio/test/acp-session-allowed-tools.test.ts +91 -0
  58. package/apps/studio/test/acp-toolgroup.test.ts +76 -0
  59. package/apps/studio/test/acp-transcript-view.test.ts +71 -0
  60. package/apps/studio/test/acp-transcript.test.ts +75 -2
  61. package/apps/studio/test/acp-usage-bridge.test.ts +136 -0
  62. package/apps/studio/test/acp-usage.test.ts +143 -0
  63. package/apps/studio/test/annotations-snap.test.ts +56 -0
  64. package/apps/studio/test/artboard-guides-overlay.test.tsx +152 -0
  65. package/apps/studio/test/artboard-kinds.test.tsx +83 -0
  66. package/apps/studio/test/artboard-selection-attrs.test.ts +69 -0
  67. package/apps/studio/test/canvas-meta-api.test.ts +167 -0
  68. package/apps/studio/test/element-structural-edit.test.ts +250 -0
  69. package/apps/studio/test/exporters/png.test.ts +49 -1
  70. package/apps/studio/test/fixtures/mock-acp-agent-caps.mjs +158 -0
  71. package/apps/studio/test/fixtures/mock-acp-agent-elicit-flood.mjs +46 -0
  72. package/apps/studio/test/fixtures/mock-acp-agent-elicit-url.mjs +42 -0
  73. package/apps/studio/test/fixtures/mock-acp-agent-elicit.mjs +63 -0
  74. package/apps/studio/test/fixtures/mock-acp-agent-permission-flood.mjs +51 -0
  75. package/apps/studio/test/fixtures/mock-acp-agent-permission.mjs +50 -0
  76. package/apps/studio/test/fixtures/mock-acp-agent-usage.mjs +56 -0
  77. package/apps/studio/test/import-asset.test.ts +31 -3
  78. package/apps/studio/test/pdf-print-boxes.test.ts +272 -0
  79. package/apps/studio/test/print-marks.test.ts +113 -0
  80. package/apps/studio/test/print-units.test.ts +173 -0
  81. package/apps/studio/test/use-snap-guides.test.ts +81 -0
  82. package/apps/studio/use-chrome-visibility.tsx +19 -0
  83. package/apps/studio/use-element-resize.tsx +21 -2
  84. package/apps/studio/use-snap-guides.tsx +73 -5
  85. package/apps/studio/use-spacing-handles.tsx +9 -5
  86. package/apps/studio/whats-new.json +62 -0
  87. package/cli/commands/design.mjs +116 -3
  88. package/cli/lib/pkg-root.mjs +42 -10
  89. package/cli/lib/pkg-root.test.mjs +33 -1
  90. package/package.json +11 -9
  91. package/plugins/design/dependencies.json +35 -0
@@ -3598,12 +3598,16 @@ export async function setArtboardHug(
3598
3598
  }
3599
3599
 
3600
3600
  /** Patch shape for {@link applySetArtboardStyle} — `null` resets/removes that
3601
- * prop back to the engine default; `undefined`/absent leaves it untouched. */
3601
+ * prop back to the engine default; `undefined`/absent leaves it untouched.
3602
+ * padding/gap accept a `var(--token)` STRING alongside raw px numbers
3603
+ * (dogfood: the Inspector's artboard panel binds space tokens the same way
3604
+ * the CSS panel's ValueTokenField does) — validation of the string shape
3605
+ * lives at the api.ts layer, same split as every other artboard writer. */
3602
3606
  export interface ArtboardStylePatch {
3603
3607
  background?: string | null;
3604
- padding?: number | null;
3608
+ padding?: number | string | null;
3605
3609
  layout?: string | null;
3606
- gap?: number | null;
3610
+ gap?: number | string | null;
3607
3611
  }
3608
3612
 
3609
3613
  /**
@@ -3647,10 +3651,14 @@ export function applySetArtboardStyle(
3647
3651
  }
3648
3652
  if ('padding' in patch) {
3649
3653
  if (patch.padding == null) removeStringAttr(s, opening, 'padding', source);
3654
+ else if (typeof patch.padding === 'string')
3655
+ editStringAttr(s, opening, 'padding', patch.padding, canvasAbsPath, artboardId);
3650
3656
  else writeNumericAttr(s, opening, 'padding', patch.padding, 0);
3651
3657
  }
3652
3658
  if ('gap' in patch) {
3653
3659
  if (patch.gap == null) removeStringAttr(s, opening, 'gap', source);
3660
+ else if (typeof patch.gap === 'string')
3661
+ editStringAttr(s, opening, 'gap', patch.gap, canvasAbsPath, artboardId);
3654
3662
  else writeNumericAttr(s, opening, 'gap', patch.gap, 0);
3655
3663
  }
3656
3664
  const out = s.toString();
@@ -3689,6 +3697,297 @@ export async function setArtboardStyle(
3689
3697
  });
3690
3698
  }
3691
3699
 
3700
+ const ARTBOARD_KIND_VALUES = new Set(['digital', 'print', 'web', 'video']);
3701
+
3702
+ /**
3703
+ * Write/clear the DCArtboard `kind` prop (feature-1-artboard-kinds-foundation
3704
+ * T5/T8) — kind-switch surfaces (context menu, Inspector) route here. Plain
3705
+ * string prop, so this reuses the same `editStringAttr`/`removeStringAttr`
3706
+ * toolkit + id-prop addressing as `applySetArtboardStyle`. `kind: null` clears
3707
+ * back to the implicit default (`digital`, or the `subtreeHasVideoComp`
3708
+ * fallback). Pure; reparse-gated.
3709
+ */
3710
+ export function applySetArtboardKind(
3711
+ canvasAbsPath: string,
3712
+ source: string,
3713
+ artboardId: string,
3714
+ kind: string | null
3715
+ ): { source: string } {
3716
+ if (kind !== null && !ARTBOARD_KIND_VALUES.has(kind)) {
3717
+ throw new CanvasEditError(`invalid artboard kind "${kind}"`, {
3718
+ canvas: canvasAbsPath,
3719
+ id: artboardId,
3720
+ });
3721
+ }
3722
+ const parsed = parseSync(canvasAbsPath, source, { sourceType: 'module' });
3723
+ if (parsed.errors && parsed.errors.length > 0) {
3724
+ throw new CanvasEditError(
3725
+ `oxc-parser failed on ${canvasAbsPath}: ${parsed.errors[0]?.message ?? 'unknown'}`,
3726
+ { canvas: canvasAbsPath, id: artboardId }
3727
+ );
3728
+ }
3729
+ const artboards = collectJsxByTag(parsed.program, 'DCArtboard');
3730
+ const target = artboards.find((a) => getStringAttr(a.openingElement, 'id') === artboardId);
3731
+ if (!target) {
3732
+ throw new CanvasEditError(`<DCArtboard id="${artboardId}"> not found in ${canvasAbsPath}`, {
3733
+ canvas: canvasAbsPath,
3734
+ id: artboardId,
3735
+ });
3736
+ }
3737
+ const s = new MagicString(source);
3738
+ const opening = target.openingElement;
3739
+ if (kind === null) removeStringAttr(s, opening, 'kind', source);
3740
+ else editStringAttr(s, opening, 'kind', kind, canvasAbsPath, artboardId);
3741
+ const out = s.toString();
3742
+ const check = parseSync(canvasAbsPath, out, { sourceType: 'module' });
3743
+ if (check.errors && check.errors.length > 0) {
3744
+ throw new CanvasEditError(
3745
+ `artboard kind edit produced invalid source (${check.errors[0]?.message ?? 'parse error'})`,
3746
+ { canvas: canvasAbsPath, id: artboardId }
3747
+ );
3748
+ }
3749
+ return { source: out };
3750
+ }
3751
+
3752
+ /** Set an artboard's `kind` on disk (atomic write + cross-process lock). */
3753
+ export async function setArtboardKind(
3754
+ canvasAbsPath: string,
3755
+ artboardId: string,
3756
+ kind: string | null
3757
+ ): Promise<{ source: string }> {
3758
+ return withLock(canvasAbsPath, async () => {
3759
+ const file = Bun.file(canvasAbsPath);
3760
+ if (!(await file.exists())) {
3761
+ throw new CanvasEditError(`Canvas not found: ${canvasAbsPath}`, {
3762
+ canvas: canvasAbsPath,
3763
+ id: artboardId,
3764
+ });
3765
+ }
3766
+ const source = await file.text();
3767
+ const next = applySetArtboardKind(canvasAbsPath, source, artboardId, kind);
3768
+ if (next.source === source) return next;
3769
+ const tmp = `${canvasAbsPath}.tmp.${Math.random().toString(36).slice(2, 10)}`;
3770
+ await Bun.write(tmp, next.source);
3771
+ const { rename } = await import('node:fs/promises');
3772
+ await rename(tmp, canvasAbsPath);
3773
+ return next;
3774
+ });
3775
+ }
3776
+
3777
+ /**
3778
+ * Write/clear an object-VALUED DCArtboard prop (`guides={{ columns: {...} }}`,
3779
+ * `print={{ paper: 'a4' }}`), unlike every other artboard writer above.
3780
+ * Per the plan's own gotcha, object-prop editing via AST is the hard part;
3781
+ * this scopes to REPLACE-WHOLE-PROP (stringify the given object and overwrite
3782
+ * the entire `{{...}}` span), not a deep merge — a caller that wants to keep
3783
+ * one key while adding another must read the current value back (Inspector
3784
+ * pre-fill reads `data-dc-*`-style attrs same as every other artboard knob)
3785
+ * and send the full merged object. `value: null` removes the prop entirely.
3786
+ * `JSON.stringify` output is valid as a JS object-literal expression (quoted
3787
+ * keys are legal JS, not just JSON), so no separate JS-literal serializer is
3788
+ * needed. Shape validation lives at the api.ts layer; this is pure AST
3789
+ * surgery. Pure; reparse-gated. Shared by `applySetArtboardGuides` and
3790
+ * `applySetArtboardPrint` (feature-2-print-artboards T2) below.
3791
+ */
3792
+ function applySetArtboardObjectProp(
3793
+ canvasAbsPath: string,
3794
+ source: string,
3795
+ artboardId: string,
3796
+ propName: string,
3797
+ value: Record<string, unknown> | null
3798
+ ): { source: string } {
3799
+ const parsed = parseSync(canvasAbsPath, source, { sourceType: 'module' });
3800
+ if (parsed.errors && parsed.errors.length > 0) {
3801
+ throw new CanvasEditError(
3802
+ `oxc-parser failed on ${canvasAbsPath}: ${parsed.errors[0]?.message ?? 'unknown'}`,
3803
+ { canvas: canvasAbsPath, id: artboardId }
3804
+ );
3805
+ }
3806
+ const artboards = collectJsxByTag(parsed.program, 'DCArtboard');
3807
+ const target = artboards.find((a) => getStringAttr(a.openingElement, 'id') === artboardId);
3808
+ if (!target) {
3809
+ throw new CanvasEditError(`<DCArtboard id="${artboardId}"> not found in ${canvasAbsPath}`, {
3810
+ canvas: canvasAbsPath,
3811
+ id: artboardId,
3812
+ });
3813
+ }
3814
+ const s = new MagicString(source);
3815
+ const opening = target.openingElement;
3816
+ if (value === null) {
3817
+ removeStringAttr(s, opening, propName, source);
3818
+ } else {
3819
+ const literal = JSON.stringify(value);
3820
+ const attr = findAttribute(opening, propName);
3821
+ if (!attr) {
3822
+ const insertAt: number | undefined = opening?.name?.end;
3823
+ if (typeof insertAt !== 'number') {
3824
+ throw new Error('Opening element has no resolvable name range');
3825
+ }
3826
+ s.appendLeft(insertAt, ` ${propName}={${literal}}`);
3827
+ } else if (attr.value?.type === 'JSXExpressionContainer') {
3828
+ s.overwrite(attr.value.start, attr.value.end, `{${literal}}`);
3829
+ } else {
3830
+ throw new CanvasEditError(
3831
+ `${propName} attribute is not a {{...}} expression — refusing to edit`,
3832
+ {
3833
+ canvas: canvasAbsPath,
3834
+ id: artboardId,
3835
+ }
3836
+ );
3837
+ }
3838
+ }
3839
+ const out = s.toString();
3840
+ const check = parseSync(canvasAbsPath, out, { sourceType: 'module' });
3841
+ if (check.errors && check.errors.length > 0) {
3842
+ throw new CanvasEditError(
3843
+ `artboard ${propName} edit produced invalid source (${check.errors[0]?.message ?? 'parse error'})`,
3844
+ { canvas: canvasAbsPath, id: artboardId }
3845
+ );
3846
+ }
3847
+ return { source: out };
3848
+ }
3849
+
3850
+ /** Set an artboard's object-valued prop on disk (atomic write + cross-process lock). */
3851
+ async function setArtboardObjectProp(
3852
+ canvasAbsPath: string,
3853
+ artboardId: string,
3854
+ propName: string,
3855
+ value: Record<string, unknown> | null
3856
+ ): Promise<{ source: string }> {
3857
+ return withLock(canvasAbsPath, async () => {
3858
+ const file = Bun.file(canvasAbsPath);
3859
+ if (!(await file.exists())) {
3860
+ throw new CanvasEditError(`Canvas not found: ${canvasAbsPath}`, {
3861
+ canvas: canvasAbsPath,
3862
+ id: artboardId,
3863
+ });
3864
+ }
3865
+ const source = await file.text();
3866
+ const next = applySetArtboardObjectProp(canvasAbsPath, source, artboardId, propName, value);
3867
+ if (next.source === source) return next;
3868
+ const tmp = `${canvasAbsPath}.tmp.${Math.random().toString(36).slice(2, 10)}`;
3869
+ await Bun.write(tmp, next.source);
3870
+ const { rename } = await import('node:fs/promises');
3871
+ await rename(tmp, canvasAbsPath);
3872
+ return next;
3873
+ });
3874
+ }
3875
+
3876
+ export function applySetArtboardGuides(
3877
+ canvasAbsPath: string,
3878
+ source: string,
3879
+ artboardId: string,
3880
+ guides: Record<string, unknown> | null
3881
+ ): { source: string } {
3882
+ return applySetArtboardObjectProp(canvasAbsPath, source, artboardId, 'guides', guides);
3883
+ }
3884
+
3885
+ /** Set an artboard's `guides` on disk (atomic write + cross-process lock). */
3886
+ export function setArtboardGuides(
3887
+ canvasAbsPath: string,
3888
+ artboardId: string,
3889
+ guides: Record<string, unknown> | null
3890
+ ): Promise<{ source: string }> {
3891
+ return setArtboardObjectProp(canvasAbsPath, artboardId, 'guides', guides);
3892
+ }
3893
+
3894
+ /**
3895
+ * Write/clear the DCArtboard `print` prop (feature-2-print-artboards T2) —
3896
+ * see `applySetArtboardObjectProp`'s doc comment for the shared shape.
3897
+ * `print: null` removes the prop entirely (e.g. reverting an artboard to a
3898
+ * non-print kind).
3899
+ */
3900
+ export function applySetArtboardPrint(
3901
+ canvasAbsPath: string,
3902
+ source: string,
3903
+ artboardId: string,
3904
+ print: Record<string, unknown> | null
3905
+ ): { source: string } {
3906
+ return applySetArtboardObjectProp(canvasAbsPath, source, artboardId, 'print', print);
3907
+ }
3908
+
3909
+ /** Set an artboard's `print` on disk (atomic write + cross-process lock). */
3910
+ export function setArtboardPrint(
3911
+ canvasAbsPath: string,
3912
+ artboardId: string,
3913
+ print: Record<string, unknown> | null
3914
+ ): Promise<{ source: string }> {
3915
+ return setArtboardObjectProp(canvasAbsPath, artboardId, 'print', print);
3916
+ }
3917
+
3918
+ /**
3919
+ * Read a JS object-literal AST node into a plain object WITHOUT eval —
3920
+ * `print` (like every other canvas-authored JSX prop) is part of the
3921
+ * DDR-054 untrusted-canvas surface, so evaluating its source text
3922
+ * (`Function('return ' + src)()`) would be a code-injection hole. Handles
3923
+ * string/numeric/boolean/null literals and nested object literals; any other
3924
+ * value shape (identifiers, calls, template strings, arrays) is silently
3925
+ * skipped — conservative by design, since a print-geometry reader should
3926
+ * degrade to "field absent" rather than guess. Depth-capped defensively,
3927
+ * though `print`'s own shape never nests past marginsMm (depth 2).
3928
+ */
3929
+ function objectExpressionToPlain(node: AnyNode, depth = 0): Record<string, unknown> {
3930
+ const out: Record<string, unknown> = {};
3931
+ if (depth > 6 || node?.type !== 'ObjectExpression') return out;
3932
+ for (const p of node.properties ?? []) {
3933
+ if ((p?.type !== 'Property' && p?.type !== 'ObjectProperty') || p.computed) continue;
3934
+ const key =
3935
+ p.key?.type === 'Identifier'
3936
+ ? (p.key.name as string)
3937
+ : isStringLit(p.key)
3938
+ ? String(p.key.value)
3939
+ : null;
3940
+ if (key === null) continue;
3941
+ const v = p.value;
3942
+ if (!v) continue;
3943
+ if (v.type === 'ObjectExpression') {
3944
+ out[key] = objectExpressionToPlain(v, depth + 1);
3945
+ } else if (
3946
+ (v.type === 'Literal' ||
3947
+ v.type === 'StringLiteral' ||
3948
+ v.type === 'NumericLiteral' ||
3949
+ v.type === 'BooleanLiteral') &&
3950
+ (typeof v.value === 'string' ||
3951
+ typeof v.value === 'number' ||
3952
+ typeof v.value === 'boolean' ||
3953
+ v.value === null)
3954
+ ) {
3955
+ out[key] = v.value;
3956
+ }
3957
+ // else: unsupported value shape — key silently omitted.
3958
+ }
3959
+ return out;
3960
+ }
3961
+
3962
+ /**
3963
+ * Read an artboard's `print` JSX prop as a plain object — used by the PDF
3964
+ * exporter (T5) to resolve bleed/paper geometry for the artboard being
3965
+ * exported. Read-only (no lock, no write). Returns null when the canvas/
3966
+ * artboard/prop doesn't exist, `print` isn't a `{{...}}` object-expression,
3967
+ * or `kind` isn't the literal string `"print"` — a `print` prop left over
3968
+ * from a prior kind switch (or hand-authored on a non-print artboard) must
3969
+ * NOT leak bleed/marks into an export; `kind="print"` is the sole gate
3970
+ * (unlike `video`, print has no implicit structural-fallback resolution, so
3971
+ * checking the explicit attr is sufficient — see DDR-181/canvas-lib.tsx).
3972
+ */
3973
+ export function readArtboardPrintProp(
3974
+ canvasAbsPath: string,
3975
+ source: string,
3976
+ artboardId: string
3977
+ ): Record<string, unknown> | null {
3978
+ const parsed = parseSync(canvasAbsPath, source, { sourceType: 'module' });
3979
+ if (parsed.errors && parsed.errors.length > 0) return null;
3980
+ const artboards = collectJsxByTag(parsed.program, 'DCArtboard');
3981
+ const target = artboards.find((a) => getStringAttr(a.openingElement, 'id') === artboardId);
3982
+ if (!target) return null;
3983
+ if (getStringAttr(target.openingElement, 'kind') !== 'print') return null;
3984
+ const attr = findAttribute(target.openingElement, 'print');
3985
+ if (attr?.value?.type !== 'JSXExpressionContainer') return null;
3986
+ const expr = attr.value.expression as AnyNode | undefined;
3987
+ if (expr?.type !== 'ObjectExpression') return null;
3988
+ return objectExpressionToPlain(expr);
3989
+ }
3990
+
3692
3991
  /**
3693
3992
  * Delete the `<DCArtboard id="…">` whose `id` prop equals `artboardId` — the
3694
3993
  * artboard counterpart of applyDeleteElement (an artboard is addressed by its id