@1agh/maude 0.37.0 → 0.38.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.
- package/README.md +2 -0
- package/apps/studio/acp/bootstrap-brief.ts +93 -0
- package/apps/studio/acp/bridge.ts +114 -1
- package/apps/studio/acp/index.ts +184 -21
- package/apps/studio/acp/plugin-bootstrap.ts +115 -0
- package/apps/studio/acp/transcript.ts +36 -3
- package/apps/studio/activity.ts +45 -0
- package/apps/studio/api.ts +265 -47
- package/apps/studio/bin/_ensure-browser.mjs +305 -0
- package/apps/studio/bin/ensure-browser.sh +26 -0
- package/apps/studio/bin/screenshot.sh +33 -8
- package/apps/studio/bin/smoke.sh +46 -0
- package/apps/studio/canvas-edit.ts +422 -6
- package/apps/studio/canvas-lib.tsx +48 -0
- package/apps/studio/canvas-shell.tsx +684 -12
- package/apps/studio/client/app.jsx +683 -33
- package/apps/studio/client/panels/ChatPanel.jsx +593 -31
- package/apps/studio/client/panels/acp-runtime.js +227 -70
- package/apps/studio/client/panels/chat-context.js +124 -0
- package/apps/studio/client/panels/slash-commands.js +147 -0
- package/apps/studio/client/styles/3-shell-maude.css +15 -0
- package/apps/studio/client/styles/6-acp-chat.css +244 -0
- package/apps/studio/commands/reorder-command.ts +77 -0
- package/apps/studio/config.schema.json +6 -0
- package/apps/studio/dist/client.bundle.js +25 -53617
- package/apps/studio/dist/styles.css +1 -1
- package/apps/studio/hmr-broadcast.ts +27 -19
- package/apps/studio/http.ts +168 -26
- package/apps/studio/inspect.ts +108 -1
- package/apps/studio/paths.ts +32 -0
- package/apps/studio/readiness.ts +79 -30
- package/apps/studio/server.ts +11 -2
- package/apps/studio/test/acp-activity.test.ts +154 -0
- package/apps/studio/test/acp-ai-activity.test.ts +182 -0
- package/apps/studio/test/acp-bootstrap-brief.test.ts +167 -0
- package/apps/studio/test/acp-commands.test.ts +108 -0
- package/apps/studio/test/acp-origin-gate.test.ts +64 -1
- package/apps/studio/test/acp-plugin-bootstrap.test.ts +89 -0
- package/apps/studio/test/acp-session-plugins.test.ts +132 -0
- package/apps/studio/test/acp-transcript.test.ts +53 -0
- package/apps/studio/test/active-state.test.ts +41 -0
- package/apps/studio/test/canvas-freshness-deps.test.ts +64 -0
- package/apps/studio/test/canvas-origin-gate.test.ts +7 -0
- package/apps/studio/test/canvas-reorder.test.ts +211 -0
- package/apps/studio/test/chat-context.test.ts +129 -0
- package/apps/studio/test/csrf-write-guard.test.ts +24 -0
- package/apps/studio/test/edit-suppress.test.ts +170 -0
- package/apps/studio/test/ensure-browser.test.ts +92 -0
- package/apps/studio/test/fixtures/mock-acp-agent-commands.mjs +44 -0
- package/apps/studio/test/hmr-broadcast.test.ts +44 -0
- package/apps/studio/test/inspect-selections.test.ts +219 -0
- package/apps/studio/test/paths.test.ts +57 -0
- package/apps/studio/test/readiness.test.ts +83 -13
- package/apps/studio/test/reorder-api.test.ts +210 -0
- package/apps/studio/test/slash-commands.test.ts +117 -0
- package/apps/studio/undo-stack.ts +6 -0
- package/apps/studio/whats-new.json +45 -0
- package/apps/studio/ws.ts +37 -0
- package/cli/commands/design.mjs +1 -0
- package/package.json +8 -8
- package/plugins/design/dependencies.json +3 -3
|
@@ -289,6 +289,12 @@
|
|
|
289
289
|
font-weight: 600;
|
|
290
290
|
color: var(--presence-agent);
|
|
291
291
|
}
|
|
292
|
+
/* Live elapsed counter — dimmer than the label; tabular so it doesn't jitter. */
|
|
293
|
+
.chat-activity-elapsed {
|
|
294
|
+
font-variant-numeric: tabular-nums;
|
|
295
|
+
color: var(--fg-2);
|
|
296
|
+
opacity: 0.8;
|
|
297
|
+
}
|
|
292
298
|
|
|
293
299
|
/* Feed (scrolls live) */
|
|
294
300
|
.chat-feed {
|
|
@@ -324,6 +330,41 @@
|
|
|
324
330
|
line-height: var(--lh-sm);
|
|
325
331
|
max-width: 240px;
|
|
326
332
|
}
|
|
333
|
+
/* One-time "Create new design system" CTA — the prominent accent button in the
|
|
334
|
+
empty state (prefills /design:setup-ds). Sits above the muted suggestion list. */
|
|
335
|
+
.chat-empty-cta {
|
|
336
|
+
display: inline-flex;
|
|
337
|
+
align-items: center;
|
|
338
|
+
justify-content: center;
|
|
339
|
+
gap: var(--space-2);
|
|
340
|
+
width: 100%;
|
|
341
|
+
margin-top: var(--space-2);
|
|
342
|
+
font-family: var(--font-body);
|
|
343
|
+
font-size: var(--type-sm);
|
|
344
|
+
font-weight: 600;
|
|
345
|
+
color: var(--accent-fg);
|
|
346
|
+
background: var(--accent);
|
|
347
|
+
border: 1px solid var(--accent);
|
|
348
|
+
border-radius: var(--radius-sm);
|
|
349
|
+
padding: var(--space-3) var(--space-4);
|
|
350
|
+
cursor: pointer;
|
|
351
|
+
transition:
|
|
352
|
+
background var(--dur-soft) var(--ease-out),
|
|
353
|
+
border-color var(--dur-soft) var(--ease-out);
|
|
354
|
+
}
|
|
355
|
+
.chat-empty-cta:hover {
|
|
356
|
+
background: var(--accent-hover);
|
|
357
|
+
border-color: var(--accent-hover);
|
|
358
|
+
}
|
|
359
|
+
.chat-empty-cta:focus-visible {
|
|
360
|
+
outline: none;
|
|
361
|
+
border-color: var(--accent);
|
|
362
|
+
box-shadow: 0 0 0 3px var(--accent-tint);
|
|
363
|
+
}
|
|
364
|
+
.chat-empty-cta-ic {
|
|
365
|
+
display: inline-flex;
|
|
366
|
+
color: currentColor;
|
|
367
|
+
}
|
|
327
368
|
.chat-sugs {
|
|
328
369
|
display: flex;
|
|
329
370
|
flex-direction: column;
|
|
@@ -408,6 +449,14 @@
|
|
|
408
449
|
line-height: var(--lh-base);
|
|
409
450
|
}
|
|
410
451
|
|
|
452
|
+
/* Continuation bubble — background work (subagent results / consolidation)
|
|
453
|
+
that streamed in after the turn's premature settle (RCA F2). A dashed rule
|
|
454
|
+
marks it as a continuation of the message above, not a fresh reply. */
|
|
455
|
+
.chat-msg--continued {
|
|
456
|
+
border-top: 1px dashed var(--border-subtle);
|
|
457
|
+
padding-top: var(--space-3);
|
|
458
|
+
}
|
|
459
|
+
|
|
411
460
|
/* Rendered markdown inside the assistant bubble (chat-markdown.jsx) */
|
|
412
461
|
.chat-msg--assistant .chat-bubble > * + * {
|
|
413
462
|
margin-top: var(--space-2);
|
|
@@ -589,6 +638,37 @@
|
|
|
589
638
|
color: var(--fg-1);
|
|
590
639
|
font-weight: 600;
|
|
591
640
|
}
|
|
641
|
+
/* Context attachment chip (feature-acp-context-hardening) — the visible
|
|
642
|
+
reveal of the frozen <maude-context> block; label + a remove ×. */
|
|
643
|
+
.chat-ctx-label {
|
|
644
|
+
display: inline-block;
|
|
645
|
+
max-width: 100%;
|
|
646
|
+
overflow: hidden;
|
|
647
|
+
text-overflow: ellipsis;
|
|
648
|
+
white-space: nowrap;
|
|
649
|
+
padding: 2px var(--space-2);
|
|
650
|
+
border: 1px solid var(--border-subtle);
|
|
651
|
+
border-radius: var(--radius-sm);
|
|
652
|
+
background: var(--bg-2);
|
|
653
|
+
color: var(--fg-1);
|
|
654
|
+
}
|
|
655
|
+
.chat-ctx--stale .chat-ctx-label {
|
|
656
|
+
border-color: var(--warn, hsl(38 92% 50%));
|
|
657
|
+
color: var(--warn, hsl(38 92% 50%));
|
|
658
|
+
}
|
|
659
|
+
.chat-ctx-x {
|
|
660
|
+
flex: none;
|
|
661
|
+
padding: 0 var(--space-1);
|
|
662
|
+
border: 0;
|
|
663
|
+
background: none;
|
|
664
|
+
color: var(--fg-2);
|
|
665
|
+
font-size: var(--type-sm);
|
|
666
|
+
line-height: 1;
|
|
667
|
+
cursor: pointer;
|
|
668
|
+
}
|
|
669
|
+
.chat-ctx-x:hover {
|
|
670
|
+
color: var(--fg-0);
|
|
671
|
+
}
|
|
592
672
|
|
|
593
673
|
/* Composer box — textarea on top, a bottom toolbar (model · effort · send),
|
|
594
674
|
Zed-style. One border + focus ring around the whole box. */
|
|
@@ -679,6 +759,42 @@
|
|
|
679
759
|
opacity: 0.45;
|
|
680
760
|
cursor: default;
|
|
681
761
|
}
|
|
762
|
+
/* Attachment reveal strip — shows what each paste chip expands to BEFORE send,
|
|
763
|
+
so a collapsed [file-1]/[link-1]/[image-N] can never carry a value the user
|
|
764
|
+
can't see into the auto-approving agent (DDR-140 / security-review mitigation). */
|
|
765
|
+
.chat-attach {
|
|
766
|
+
display: flex;
|
|
767
|
+
flex-direction: column;
|
|
768
|
+
gap: 2px;
|
|
769
|
+
margin-top: var(--space-2);
|
|
770
|
+
}
|
|
771
|
+
.chat-attach-row {
|
|
772
|
+
display: flex;
|
|
773
|
+
align-items: center;
|
|
774
|
+
gap: var(--space-2);
|
|
775
|
+
min-width: 0;
|
|
776
|
+
font-size: var(--type-xs);
|
|
777
|
+
}
|
|
778
|
+
.chat-attach-tok {
|
|
779
|
+
flex-shrink: 0;
|
|
780
|
+
font-family: var(--font-mono);
|
|
781
|
+
color: var(--fg-1);
|
|
782
|
+
background: var(--bg-4, var(--bg-3));
|
|
783
|
+
border-radius: var(--radius-xs);
|
|
784
|
+
padding: 0 4px;
|
|
785
|
+
}
|
|
786
|
+
.chat-attach-arrow {
|
|
787
|
+
flex-shrink: 0;
|
|
788
|
+
color: var(--fg-3);
|
|
789
|
+
}
|
|
790
|
+
.chat-attach-val {
|
|
791
|
+
min-width: 0;
|
|
792
|
+
overflow: hidden;
|
|
793
|
+
text-overflow: ellipsis;
|
|
794
|
+
white-space: nowrap;
|
|
795
|
+
color: var(--fg-2);
|
|
796
|
+
font-family: var(--font-mono);
|
|
797
|
+
}
|
|
682
798
|
.chat-foot {
|
|
683
799
|
display: flex;
|
|
684
800
|
align-items: center;
|
|
@@ -691,6 +807,134 @@
|
|
|
691
807
|
flex: 1;
|
|
692
808
|
}
|
|
693
809
|
|
|
810
|
+
/* ── Slash-command autocomplete + inline highlight ────────────────────────
|
|
811
|
+
The composer becomes command-aware: typing "/" opens `.chat-cmd-menu`
|
|
812
|
+
(populated from the static list ∪ the live ACP catalogue), and a recognized
|
|
813
|
+
command renders as a `.chat-cmd-pill` INSIDE the input via a mirror overlay. */
|
|
814
|
+
|
|
815
|
+
/* Relative anchor so the popover floats above the composer box. */
|
|
816
|
+
.chat-cmd-anchor {
|
|
817
|
+
position: relative;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/* Mirror-overlay: a text layer behind a transparent-text textarea. The mirror
|
|
821
|
+
is the visible text; the textarea owns the caret + input. Their box models
|
|
822
|
+
MUST stay identical (padding / font / line-height / wrap) or the pill drifts. */
|
|
823
|
+
.chat-input-wrap {
|
|
824
|
+
position: relative;
|
|
825
|
+
display: block;
|
|
826
|
+
}
|
|
827
|
+
.chat-input-mirror {
|
|
828
|
+
position: absolute;
|
|
829
|
+
inset: 0;
|
|
830
|
+
z-index: 0;
|
|
831
|
+
padding: var(--space-3) var(--space-4);
|
|
832
|
+
font-family: var(--font-body);
|
|
833
|
+
font-size: var(--type-sm);
|
|
834
|
+
line-height: var(--lh-base);
|
|
835
|
+
letter-spacing: 0;
|
|
836
|
+
color: var(--fg-0);
|
|
837
|
+
white-space: pre-wrap;
|
|
838
|
+
overflow-wrap: break-word;
|
|
839
|
+
word-break: break-word;
|
|
840
|
+
overflow: hidden;
|
|
841
|
+
pointer-events: none;
|
|
842
|
+
}
|
|
843
|
+
.chat-input--overlay {
|
|
844
|
+
position: relative;
|
|
845
|
+
z-index: 1;
|
|
846
|
+
line-height: var(--lh-base);
|
|
847
|
+
letter-spacing: 0;
|
|
848
|
+
color: transparent;
|
|
849
|
+
caret-color: var(--fg-0);
|
|
850
|
+
background: transparent;
|
|
851
|
+
}
|
|
852
|
+
/* The recognized-command pill. NO padding/margin/letter-spacing — those would
|
|
853
|
+
change glyph advance and desync the mirror from the textarea caret. The 2px
|
|
854
|
+
box-shadow fakes padding without touching layout. */
|
|
855
|
+
.chat-cmd-pill {
|
|
856
|
+
background: var(--accent-tint);
|
|
857
|
+
color: var(--accent);
|
|
858
|
+
border-radius: var(--radius-xs);
|
|
859
|
+
box-shadow: 0 0 0 2px var(--accent-tint);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/* Pasted-path / URL chip ([image-1] / [file-1] / [link-1]). Same zero-advance
|
|
863
|
+
discipline as .chat-cmd-pill (no padding/margin/letter-spacing → the mirror
|
|
864
|
+
stays glyph-aligned with the textarea caret). Neutral tint so it reads as an
|
|
865
|
+
attachment badge, distinct from the accent command pill. Reused verbatim in
|
|
866
|
+
the sent-message bubble, where the box-shadow padding is harmless. */
|
|
867
|
+
.chat-paste-chip {
|
|
868
|
+
background: var(--bg-4, var(--bg-3));
|
|
869
|
+
color: var(--fg-1);
|
|
870
|
+
border-radius: var(--radius-xs);
|
|
871
|
+
box-shadow: 0 0 0 2px var(--bg-4, var(--bg-3));
|
|
872
|
+
}
|
|
873
|
+
.chat-msg--user .chat-paste-chip {
|
|
874
|
+
color: var(--accent);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/* Autocomplete popover — opens upward (composer sits at the panel bottom). */
|
|
878
|
+
.chat-cmd-menu {
|
|
879
|
+
position: absolute;
|
|
880
|
+
left: 0;
|
|
881
|
+
right: 0;
|
|
882
|
+
bottom: calc(100% + var(--space-2));
|
|
883
|
+
z-index: 20;
|
|
884
|
+
max-height: 280px;
|
|
885
|
+
overflow-y: auto;
|
|
886
|
+
display: flex;
|
|
887
|
+
flex-direction: column;
|
|
888
|
+
gap: 1px;
|
|
889
|
+
padding: var(--space-1);
|
|
890
|
+
background: var(--bg-2);
|
|
891
|
+
border: 1px solid var(--border-default);
|
|
892
|
+
border-radius: var(--radius-sm);
|
|
893
|
+
box-shadow: var(--shadow-lg);
|
|
894
|
+
}
|
|
895
|
+
.chat-cmd-item {
|
|
896
|
+
display: flex;
|
|
897
|
+
align-items: baseline;
|
|
898
|
+
gap: var(--space-2);
|
|
899
|
+
width: 100%;
|
|
900
|
+
text-align: left;
|
|
901
|
+
appearance: none;
|
|
902
|
+
border: 0;
|
|
903
|
+
background: transparent;
|
|
904
|
+
border-radius: var(--radius-xs);
|
|
905
|
+
padding: var(--space-2) var(--space-3);
|
|
906
|
+
cursor: pointer;
|
|
907
|
+
color: var(--fg-1);
|
|
908
|
+
}
|
|
909
|
+
.chat-cmd-item:hover,
|
|
910
|
+
.chat-cmd-item.is-active {
|
|
911
|
+
background: var(--bg-3);
|
|
912
|
+
}
|
|
913
|
+
.chat-cmd-name {
|
|
914
|
+
flex-shrink: 0;
|
|
915
|
+
font-family: var(--font-mono);
|
|
916
|
+
font-size: var(--type-xs);
|
|
917
|
+
color: var(--accent);
|
|
918
|
+
}
|
|
919
|
+
.chat-cmd-name--flow {
|
|
920
|
+
color: var(--presence-agent);
|
|
921
|
+
}
|
|
922
|
+
.chat-cmd-desc {
|
|
923
|
+
flex: 1;
|
|
924
|
+
min-width: 0;
|
|
925
|
+
font-size: var(--type-xs);
|
|
926
|
+
color: var(--fg-2);
|
|
927
|
+
overflow: hidden;
|
|
928
|
+
text-overflow: ellipsis;
|
|
929
|
+
white-space: nowrap;
|
|
930
|
+
}
|
|
931
|
+
.chat-cmd-arg {
|
|
932
|
+
flex-shrink: 0;
|
|
933
|
+
font-family: var(--font-mono);
|
|
934
|
+
font-size: var(--type-xs);
|
|
935
|
+
color: var(--fg-3);
|
|
936
|
+
}
|
|
937
|
+
|
|
694
938
|
/* Streaming → Stop bar */
|
|
695
939
|
.chat-stopbar {
|
|
696
940
|
display: flex;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file commands/reorder-command.ts — undo entry for element reorders
|
|
3
|
+
* @scope apps/studio/commands/reorder-command.ts
|
|
4
|
+
* @purpose Reversible record of a Phase 12.1 node-move (canvas drag /
|
|
5
|
+
* Layers-panel drag / Alt+arrow keyboard move). Before this command
|
|
6
|
+
* existed a reorder was covered by NEITHER the Cmd+Z stack NOR any
|
|
7
|
+
* in-app affordance — only /design:rollback could recover it.
|
|
8
|
+
*
|
|
9
|
+
* Why not an inverse move descriptor. A reorder churns every positional
|
|
10
|
+
* data-cd-id at or after the touched span, so a stored "move A back before B"
|
|
11
|
+
* goes stale the moment it's recorded. Instead the SERVER logs the whole-file
|
|
12
|
+
* {before, after} under a monotonic `seq` (api.ts reorderLog) and this record
|
|
13
|
+
* stores only { canvas, seq }: undo() asks the server to swap back to `before`,
|
|
14
|
+
* redo() to `after`. The server refuses (409) when the file changed since —
|
|
15
|
+
* honest failure instead of corrupting an interleaved edit.
|
|
16
|
+
*
|
|
17
|
+
* Origin split (DDR-054). This command runs INSIDE the untrusted canvas iframe,
|
|
18
|
+
* but `/_api/reorder-revert` is main-origin-only — so do()/undo() route through
|
|
19
|
+
* the injected `reorderRevertFn` sink, which posts `dgn:'reorder-revert'` to the
|
|
20
|
+
* parent shell; the shell performs the privileged write. Serializable payload
|
|
21
|
+
* (DDR-050 rev 2) so the stack survives the HMR reload the write triggers.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type { CommandRecord, EditCommand } from '../undo-stack.ts';
|
|
25
|
+
import { registerCommand } from '../undo-stack.ts';
|
|
26
|
+
|
|
27
|
+
export interface ReorderPayload {
|
|
28
|
+
/** Canvas file path (same string the reorder POST used). */
|
|
29
|
+
canvas: string;
|
|
30
|
+
/** Server-assigned revert-log sequence number (api.ts reorderLog). */
|
|
31
|
+
seq: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Posts the revert request to the shell (production) or a spy (tests). */
|
|
35
|
+
export type ReorderRevertFn = (revert: {
|
|
36
|
+
canvas: string;
|
|
37
|
+
seq: number;
|
|
38
|
+
dir: 'undo' | 'redo';
|
|
39
|
+
}) => void | Promise<void>;
|
|
40
|
+
|
|
41
|
+
export const REORDER_KIND = 'reorder';
|
|
42
|
+
|
|
43
|
+
export function createReorderCommand(init: {
|
|
44
|
+
payload: ReorderPayload;
|
|
45
|
+
revertFn: ReorderRevertFn;
|
|
46
|
+
label?: string;
|
|
47
|
+
}): EditCommand {
|
|
48
|
+
const { payload, revertFn } = init;
|
|
49
|
+
return {
|
|
50
|
+
kind: REORDER_KIND,
|
|
51
|
+
label: init.label ?? 'move element',
|
|
52
|
+
async do() {
|
|
53
|
+
await revertFn({ canvas: payload.canvas, seq: payload.seq, dir: 'redo' });
|
|
54
|
+
},
|
|
55
|
+
async undo() {
|
|
56
|
+
await revertFn({ canvas: payload.canvas, seq: payload.seq, dir: 'undo' });
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Build a persistable record. The reorder already landed (the shell POSTed
|
|
63
|
+
* /_api/reorder before telling the canvas), so push via `record()` — appends
|
|
64
|
+
* WITHOUT re-running do().
|
|
65
|
+
*/
|
|
66
|
+
export function buildReorderRecord(
|
|
67
|
+
payload: ReorderPayload,
|
|
68
|
+
label?: string
|
|
69
|
+
): CommandRecord<ReorderPayload> {
|
|
70
|
+
return { kind: REORDER_KIND, label: label ?? 'move element', payload };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
registerCommand<ReorderPayload>(REORDER_KIND, (record, sinks) => {
|
|
74
|
+
const revertFn = sinks.reorderRevertFn as ReorderRevertFn | undefined;
|
|
75
|
+
if (!revertFn) return null;
|
|
76
|
+
return createReorderCommand({ payload: record.payload, revertFn, label: record.label });
|
|
77
|
+
});
|
|
@@ -134,6 +134,12 @@
|
|
|
134
134
|
"description": "How chromatically/decoratively expressive the DS is. Inferred by ux-research-agent from brand character during /design:setup-ds (NOT a picker), then anchors the structural knobs (accentStrategy, shadow/decor) and sets the default opt_out_scope for canvases under this DS (restrained|confident → palette, expressive → aesthetic, maximalist → full). Default 'restrained' is the legacy/no-bootstrap fallback only — a bootstrapped DS always writes the inferred value. Per DDR-073.",
|
|
135
135
|
"default": "restrained"
|
|
136
136
|
},
|
|
137
|
+
"dsFidelity": {
|
|
138
|
+
"type": "string",
|
|
139
|
+
"enum": ["advisory", "strict"],
|
|
140
|
+
"description": "How hard DS reuse is enforced during canvas generation/edit. 'advisory' (default) = reuse findings (invented brand mark/icons, reinvented components, parallel product shell) surface as warnings. 'strict' = when the DS ships a canonical specimen (logo, iconography, component, platform showcase), reinventing it is a blocker the auto-fix loop must clear. Orthogonal to opt_out_scope on the SAME axis of 'how much the DS binds': a canvas's explicit opt_out_scope=full always wins over strict (explicit free-use beats project policy). A11y is independent of this knob. Per DDR-141.",
|
|
141
|
+
"default": "advisory"
|
|
142
|
+
},
|
|
137
143
|
"designSystems": {
|
|
138
144
|
"type": "array",
|
|
139
145
|
"description": "Design systems available in this project. Single-DS projects have one entry; multi-DS projects (marketing vs. admin vs. mobile) list each here. Each canvas's .meta.json declares which DS it uses via the 'designSystem' field. The System view's DS picker iterates this array.",
|