@1agh/maude 0.30.0 → 0.31.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 (90) hide show
  1. package/README.md +5 -5
  2. package/apps/studio/acp/bridge.ts +285 -0
  3. package/apps/studio/acp/env.ts +48 -0
  4. package/apps/studio/acp/index.ts +132 -0
  5. package/apps/studio/acp/probe.ts +112 -0
  6. package/apps/studio/acp/transcript.ts +149 -0
  7. package/apps/studio/annotations-layer.tsx +6 -1
  8. package/apps/studio/api.ts +225 -66
  9. package/apps/studio/bin/chat-open.sh +44 -0
  10. package/apps/studio/canvas-lib.tsx +112 -19
  11. package/apps/studio/canvas-list-watch.ts +177 -0
  12. package/apps/studio/canvas-shell.tsx +22 -2
  13. package/apps/studio/client/app.jsx +781 -26
  14. package/apps/studio/client/canvas-url.js +5 -0
  15. package/apps/studio/client/github.js +99 -0
  16. package/apps/studio/client/panels/ChatPanel.jsx +770 -0
  17. package/apps/studio/client/panels/CollabModelInfographic.jsx +71 -0
  18. package/apps/studio/client/panels/CreateProject.jsx +334 -0
  19. package/apps/studio/client/panels/DiffView.jsx +590 -0
  20. package/apps/studio/client/panels/GitPanel.jsx +767 -0
  21. package/apps/studio/client/panels/IdentityBar.jsx +294 -0
  22. package/apps/studio/client/panels/OnboardingWizard.jsx +563 -0
  23. package/apps/studio/client/panels/RepoBranchSwitcher.jsx +349 -0
  24. package/apps/studio/client/panels/acp-runtime.js +286 -0
  25. package/apps/studio/client/panels/chat-markdown.jsx +138 -0
  26. package/apps/studio/client/panels/git-grouping.js +86 -0
  27. package/apps/studio/client/styles/0-reset.css +4 -0
  28. package/apps/studio/client/styles/3-shell-maude.css +613 -3
  29. package/apps/studio/client/styles/5-maude-overrides.css +25 -0
  30. package/apps/studio/client/styles/6-acp-chat.css +771 -0
  31. package/apps/studio/client/styles/_index.css +2 -0
  32. package/apps/studio/client/tour/collab-tour.js +61 -0
  33. package/apps/studio/client/tour/overlay.jsx +11 -1
  34. package/apps/studio/client/whats-new.jsx +25 -10
  35. package/apps/studio/collab/registry.ts +13 -0
  36. package/apps/studio/collab/room.ts +36 -0
  37. package/apps/studio/cursors-overlay.tsx +17 -1
  38. package/apps/studio/dist/client.bundle.js +28780 -1534
  39. package/apps/studio/dist/comment-mount.js +4 -2
  40. package/apps/studio/dist/styles.css +5633 -1617
  41. package/apps/studio/git/endpoints.ts +338 -0
  42. package/apps/studio/git/service.ts +1334 -0
  43. package/apps/studio/git/watch.ts +97 -0
  44. package/apps/studio/github/endpoints.ts +358 -0
  45. package/apps/studio/github/service.ts +231 -0
  46. package/apps/studio/github/token.ts +53 -0
  47. package/apps/studio/hmr-broadcast.ts +9 -2
  48. package/apps/studio/http.ts +384 -1
  49. package/apps/studio/participants-chrome.tsx +69 -9
  50. package/apps/studio/paths.ts +12 -0
  51. package/apps/studio/scaffold-design.ts +57 -0
  52. package/apps/studio/server.ts +65 -2
  53. package/apps/studio/sync/agent.ts +81 -1
  54. package/apps/studio/sync/codec.ts +24 -0
  55. package/apps/studio/sync/cold-start.ts +40 -0
  56. package/apps/studio/sync/hub-link.ts +137 -0
  57. package/apps/studio/test/acp-bridge.test.ts +127 -0
  58. package/apps/studio/test/acp-env.test.ts +65 -0
  59. package/apps/studio/test/acp-origin-gate.test.ts +78 -0
  60. package/apps/studio/test/acp-transcript.test.ts +112 -0
  61. package/apps/studio/test/canvas-create-api.test.ts +72 -0
  62. package/apps/studio/test/canvas-list-watch.test.ts +322 -0
  63. package/apps/studio/test/canvas-meta-api.test.ts +161 -27
  64. package/apps/studio/test/canvas-origin-gate.test.ts +35 -0
  65. package/apps/studio/test/chat-markdown.test.tsx +58 -0
  66. package/apps/studio/test/collab-session-survival.test.tsx +176 -0
  67. package/apps/studio/test/csrf-write-guard.test.ts +26 -0
  68. package/apps/studio/test/editing-presence.test.ts +103 -0
  69. package/apps/studio/test/fixtures/mock-acp-agent.mjs +45 -0
  70. package/apps/studio/test/git-api.test.ts +0 -0
  71. package/apps/studio/test/git-branches.test.ts +106 -0
  72. package/apps/studio/test/git-grouping.test.ts +106 -0
  73. package/apps/studio/test/git-watch.test.ts +97 -0
  74. package/apps/studio/test/github-api.test.ts +465 -0
  75. package/apps/studio/test/hub-link.test.ts +69 -0
  76. package/apps/studio/test/participants-chrome.test.ts +36 -1
  77. package/apps/studio/test/sync-cold-seed-dedup.test.ts +187 -0
  78. package/apps/studio/test/sync-cold-start.test.ts +61 -1
  79. package/apps/studio/test/tour-overlay.test.tsx +18 -0
  80. package/apps/studio/tool-palette.tsx +18 -9
  81. package/apps/studio/use-chrome-visibility.tsx +66 -0
  82. package/apps/studio/use-collab.tsx +414 -187
  83. package/apps/studio/whats-new.json +73 -0
  84. package/apps/studio/ws.ts +44 -1
  85. package/cli/commands/design.mjs +1 -0
  86. package/cli/lib/gitignore-block.mjs +16 -3
  87. package/cli/lib/gitignore-block.test.mjs +13 -1
  88. package/package.json +11 -9
  89. package/plugins/design/dependencies.json +17 -0
  90. package/plugins/design/templates/_shell.html +30 -8
@@ -0,0 +1,138 @@
1
+ // Compact, XSS-safe markdown renderer for the ACP chat feed. Builds React nodes
2
+ // directly (never innerHTML) so agent output can't inject markup. Covers the
3
+ // subset a coding agent actually emits: fenced code blocks, headings, bullet +
4
+ // numbered lists, bold / italic / inline code, and http(s) links. Anything else
5
+ // falls through as plain (pre-wrapped) text. No new dependency.
6
+
7
+ function safeHref(url) {
8
+ // Only allow http(s) — never javascript:/data:/etc.
9
+ return /^https?:\/\//i.test(url) ? url : null;
10
+ }
11
+
12
+ // Inline tokenizer: `code`, **bold**, *italic*, [text](url).
13
+ const INLINE_RE = /(`[^`]+`)|(\*\*[^*]+\*\*)|(\*[^*\n]+\*)|(\[[^\]]+\]\([^)\s]+\))/g;
14
+
15
+ function inline(text) {
16
+ const out = [];
17
+ let last = 0;
18
+ let key = 0;
19
+ let m;
20
+ INLINE_RE.lastIndex = 0;
21
+ // biome-ignore lint/suspicious/noAssignInExpressions: standard regex exec loop
22
+ while ((m = INLINE_RE.exec(text)) !== null) {
23
+ if (m.index > last) out.push(text.slice(last, m.index));
24
+ const tok = m[0];
25
+ if (tok.startsWith('`')) {
26
+ out.push(<code key={key++}>{tok.slice(1, -1)}</code>);
27
+ } else if (tok.startsWith('**')) {
28
+ out.push(<strong key={key++}>{tok.slice(2, -2)}</strong>);
29
+ } else if (tok.startsWith('*')) {
30
+ out.push(<em key={key++}>{tok.slice(1, -1)}</em>);
31
+ } else {
32
+ const link = /\[([^\]]+)\]\(([^)\s]+)\)/.exec(tok);
33
+ const href = link && safeHref(link[2]);
34
+ out.push(
35
+ href ? (
36
+ <a key={key++} href={href} target="_blank" rel="noreferrer noopener">
37
+ {link[1]}
38
+ </a>
39
+ ) : (
40
+ link ? link[1] : tok
41
+ )
42
+ );
43
+ }
44
+ last = m.index + tok.length;
45
+ }
46
+ if (last < text.length) out.push(text.slice(last));
47
+ return out;
48
+ }
49
+
50
+ export function Markdown({ text }) {
51
+ const lines = String(text).split('\n');
52
+ const blocks = [];
53
+ let i = 0;
54
+ let key = 0;
55
+
56
+ const isList = (l) => /^\s*[-*]\s/.test(l) || /^\s*\d+\.\s/.test(l);
57
+
58
+ while (i < lines.length) {
59
+ const line = lines[i];
60
+
61
+ // Fenced code block.
62
+ if (line.trimStart().startsWith('```')) {
63
+ const buf = [];
64
+ i++;
65
+ while (i < lines.length && !lines[i].trimStart().startsWith('```')) {
66
+ buf.push(lines[i]);
67
+ i++;
68
+ }
69
+ i++; // closing fence
70
+ blocks.push(
71
+ <pre key={key++} className="chat-code">
72
+ <code>{buf.join('\n')}</code>
73
+ </pre>
74
+ );
75
+ continue;
76
+ }
77
+
78
+ // Heading → a styled paragraph (chat doesn't want giant h1s).
79
+ if (/^#{1,6}\s/.test(line)) {
80
+ blocks.push(
81
+ <p key={key++} className="chat-md-h">
82
+ {inline(line.replace(/^#{1,6}\s/, ''))}
83
+ </p>
84
+ );
85
+ i++;
86
+ continue;
87
+ }
88
+
89
+ // List (consecutive list lines).
90
+ if (isList(line)) {
91
+ const ordered = /^\s*\d+\.\s/.test(line);
92
+ const items = [];
93
+ while (i < lines.length && isList(lines[i])) {
94
+ items.push(<li key={items.length}>{inline(lines[i].replace(/^\s*(?:[-*]|\d+\.)\s/, ''))}</li>);
95
+ i++;
96
+ }
97
+ blocks.push(
98
+ ordered ? (
99
+ <ol key={key++} className="chat-md-list">
100
+ {items}
101
+ </ol>
102
+ ) : (
103
+ <ul key={key++} className="chat-md-list">
104
+ {items}
105
+ </ul>
106
+ )
107
+ );
108
+ continue;
109
+ }
110
+
111
+ // Blank line → paragraph separator.
112
+ if (line.trim() === '') {
113
+ i++;
114
+ continue;
115
+ }
116
+
117
+ // Paragraph: gather consecutive non-special lines.
118
+ const buf = [line];
119
+ i++;
120
+ while (
121
+ i < lines.length &&
122
+ lines[i].trim() !== '' &&
123
+ !lines[i].trimStart().startsWith('```') &&
124
+ !/^#{1,6}\s/.test(lines[i]) &&
125
+ !isList(lines[i])
126
+ ) {
127
+ buf.push(lines[i]);
128
+ i++;
129
+ }
130
+ blocks.push(
131
+ <p key={key++} className="chat-md-p">
132
+ {inline(buf.join('\n'))}
133
+ </p>
134
+ );
135
+ }
136
+
137
+ return <>{blocks}</>;
138
+ }
@@ -0,0 +1,86 @@
1
+ // Pure grouping helpers for the Changes panel (GitPanel.jsx) — framework-free so
2
+ // they unit-test without a DOM. DDR-115 follow-up: group changed files by parent
3
+ // canvas + its supporting sidecars (same-stem `.meta.json`, slug-matched
4
+ // `.annotations.svg`), mirroring the server's `expandSidecars` notion of "travels
5
+ // with the canvas" so the visual grouping is honest with what a unit commits.
6
+
7
+ export const CANVAS_RE = /\.(tsx|html)$/i;
8
+ export const META_RE = /\.meta\.json$/i;
9
+ export const ANNOT_RE = /\.annotations\.svg$/i;
10
+
11
+ /** Display name for a file (last path segment, sidecar/canvas extension stripped). */
12
+ export function baseName(p) {
13
+ const s = (String(p).split('/').pop() || String(p)).replace(ANNOT_RE, '').replace(META_RE, '');
14
+ return s.replace(/\.(tsx|html|css|svg|json)$/i, '');
15
+ }
16
+
17
+ /** Friendly label for a collapsed supporting file under a canvas. */
18
+ export function supportLabel(p) {
19
+ if (META_RE.test(p)) return 'Layout & settings';
20
+ if (ANNOT_RE.test(p)) return 'Annotations';
21
+ return baseName(p);
22
+ }
23
+
24
+ export function stripDesignRel(p, designRel) {
25
+ const s = String(p).replace(/^\/+|\/+$/g, '');
26
+ const prefix = `${(designRel || '.design').replace(/^\/+|\/+$/g, '')}/`;
27
+ return s.startsWith(prefix) ? s.slice(prefix.length) : s;
28
+ }
29
+
30
+ /** Mirror api.ts `fileSlug`: repo-relative canvas path → annotation slug, so the
31
+ * slug-named `<slug>.annotations.svg` (which is NOT a same-stem sibling) can be
32
+ * attached to its parent canvas. */
33
+ export function canvasSlug(p, designRel) {
34
+ return stripDesignRel(p, designRel)
35
+ .replace(/\//g, '-')
36
+ .replace(/\s+/g, '_')
37
+ .replace(CANVAS_RE, '')
38
+ .replace(/^\.+/, '')
39
+ .toLowerCase();
40
+ }
41
+
42
+ /**
43
+ * Group changed files into canvas "units" — a parent canvas + its supporting
44
+ * sidecars. Files with no parent canvas in the changeset become standalone
45
+ * "other" units (config, DS tokens, an orphan sidecar).
46
+ *
47
+ * @param {{path: string, status: string}[]} files
48
+ * @param {string} designRel
49
+ * @returns {{ canvasUnits: Unit[], otherUnits: Unit[] }}
50
+ * where Unit = { key, kind: 'canvas'|'other', primary, supporting: file[] }
51
+ */
52
+ export function buildUnits(files, designRel) {
53
+ const canvases = files.filter((f) => CANVAS_RE.test(f.path));
54
+ const canvasByBase = new Map(); // base path (no ext) → canvas file
55
+ const canvasBySlug = new Map(); // annotation slug → canvas file
56
+ for (const c of canvases) {
57
+ canvasByBase.set(c.path.replace(CANVAS_RE, ''), c);
58
+ canvasBySlug.set(canvasSlug(c.path, designRel), c);
59
+ }
60
+ const supporting = new Map(); // canvas.path → sidecar[]
61
+ const consumed = new Set(); // sidecar paths attached to a canvas
62
+ const attach = (canvas, f) => {
63
+ const arr = supporting.get(canvas.path);
64
+ if (arr) arr.push(f);
65
+ else supporting.set(canvas.path, [f]);
66
+ consumed.add(f.path);
67
+ };
68
+ for (const f of files) {
69
+ if (META_RE.test(f.path)) {
70
+ const c = canvasByBase.get(f.path.replace(META_RE, ''));
71
+ if (c) attach(c, f);
72
+ } else if (ANNOT_RE.test(f.path)) {
73
+ const c = canvasBySlug.get(stripDesignRel(f.path, designRel).replace(ANNOT_RE, '').toLowerCase());
74
+ if (c) attach(c, f);
75
+ }
76
+ }
77
+ const byName = (a, b) => baseName(a.primary.path).localeCompare(baseName(b.primary.path));
78
+ const canvasUnits = canvases
79
+ .map((c) => ({ key: c.path, kind: 'canvas', primary: c, supporting: supporting.get(c.path) ?? [] }))
80
+ .sort(byName);
81
+ const otherUnits = files
82
+ .filter((f) => !CANVAS_RE.test(f.path) && !consumed.has(f.path))
83
+ .map((f) => ({ key: f.path, kind: 'other', primary: f, supporting: [] }))
84
+ .sort(byName);
85
+ return { canvasUnits, otherUnits };
86
+ }
@@ -11,6 +11,10 @@ html, body {
11
11
  line-height: 1.45;
12
12
  -webkit-font-smoothing: antialiased;
13
13
  -moz-osx-font-smoothing: grayscale;
14
+ /* WebKit (Safari + WKWebView) text inflation can re-grow text that the canvas
15
+ scales down during zoom-out, undoing the shrink. Pin it off. No-op on Blink. */
16
+ -webkit-text-size-adjust: 100%;
17
+ text-size-adjust: 100%;
14
18
  }
15
19
 
16
20
  button { font: inherit; }