@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.
- package/README.md +5 -5
- package/apps/studio/acp/bridge.ts +285 -0
- package/apps/studio/acp/env.ts +48 -0
- package/apps/studio/acp/index.ts +132 -0
- package/apps/studio/acp/probe.ts +112 -0
- package/apps/studio/acp/transcript.ts +149 -0
- package/apps/studio/annotations-layer.tsx +6 -1
- package/apps/studio/api.ts +225 -66
- package/apps/studio/bin/chat-open.sh +44 -0
- package/apps/studio/canvas-lib.tsx +112 -19
- package/apps/studio/canvas-list-watch.ts +177 -0
- package/apps/studio/canvas-shell.tsx +22 -2
- package/apps/studio/client/app.jsx +781 -26
- package/apps/studio/client/canvas-url.js +5 -0
- package/apps/studio/client/github.js +99 -0
- package/apps/studio/client/panels/ChatPanel.jsx +770 -0
- package/apps/studio/client/panels/CollabModelInfographic.jsx +71 -0
- package/apps/studio/client/panels/CreateProject.jsx +334 -0
- package/apps/studio/client/panels/DiffView.jsx +590 -0
- package/apps/studio/client/panels/GitPanel.jsx +767 -0
- package/apps/studio/client/panels/IdentityBar.jsx +294 -0
- package/apps/studio/client/panels/OnboardingWizard.jsx +563 -0
- package/apps/studio/client/panels/RepoBranchSwitcher.jsx +349 -0
- package/apps/studio/client/panels/acp-runtime.js +286 -0
- package/apps/studio/client/panels/chat-markdown.jsx +138 -0
- package/apps/studio/client/panels/git-grouping.js +86 -0
- package/apps/studio/client/styles/0-reset.css +4 -0
- package/apps/studio/client/styles/3-shell-maude.css +613 -3
- package/apps/studio/client/styles/5-maude-overrides.css +25 -0
- package/apps/studio/client/styles/6-acp-chat.css +771 -0
- package/apps/studio/client/styles/_index.css +2 -0
- package/apps/studio/client/tour/collab-tour.js +61 -0
- package/apps/studio/client/tour/overlay.jsx +11 -1
- package/apps/studio/client/whats-new.jsx +25 -10
- package/apps/studio/collab/registry.ts +13 -0
- package/apps/studio/collab/room.ts +36 -0
- package/apps/studio/cursors-overlay.tsx +17 -1
- package/apps/studio/dist/client.bundle.js +28780 -1534
- package/apps/studio/dist/comment-mount.js +4 -2
- package/apps/studio/dist/styles.css +5633 -1617
- package/apps/studio/git/endpoints.ts +338 -0
- package/apps/studio/git/service.ts +1334 -0
- package/apps/studio/git/watch.ts +97 -0
- package/apps/studio/github/endpoints.ts +358 -0
- package/apps/studio/github/service.ts +231 -0
- package/apps/studio/github/token.ts +53 -0
- package/apps/studio/hmr-broadcast.ts +9 -2
- package/apps/studio/http.ts +384 -1
- package/apps/studio/participants-chrome.tsx +69 -9
- package/apps/studio/paths.ts +12 -0
- package/apps/studio/scaffold-design.ts +57 -0
- package/apps/studio/server.ts +65 -2
- package/apps/studio/sync/agent.ts +81 -1
- package/apps/studio/sync/codec.ts +24 -0
- package/apps/studio/sync/cold-start.ts +40 -0
- package/apps/studio/sync/hub-link.ts +137 -0
- package/apps/studio/test/acp-bridge.test.ts +127 -0
- package/apps/studio/test/acp-env.test.ts +65 -0
- package/apps/studio/test/acp-origin-gate.test.ts +78 -0
- package/apps/studio/test/acp-transcript.test.ts +112 -0
- package/apps/studio/test/canvas-create-api.test.ts +72 -0
- package/apps/studio/test/canvas-list-watch.test.ts +322 -0
- package/apps/studio/test/canvas-meta-api.test.ts +161 -27
- package/apps/studio/test/canvas-origin-gate.test.ts +35 -0
- package/apps/studio/test/chat-markdown.test.tsx +58 -0
- package/apps/studio/test/collab-session-survival.test.tsx +176 -0
- package/apps/studio/test/csrf-write-guard.test.ts +26 -0
- package/apps/studio/test/editing-presence.test.ts +103 -0
- package/apps/studio/test/fixtures/mock-acp-agent.mjs +45 -0
- package/apps/studio/test/git-api.test.ts +0 -0
- package/apps/studio/test/git-branches.test.ts +106 -0
- package/apps/studio/test/git-grouping.test.ts +106 -0
- package/apps/studio/test/git-watch.test.ts +97 -0
- package/apps/studio/test/github-api.test.ts +465 -0
- package/apps/studio/test/hub-link.test.ts +69 -0
- package/apps/studio/test/participants-chrome.test.ts +36 -1
- package/apps/studio/test/sync-cold-seed-dedup.test.ts +187 -0
- package/apps/studio/test/sync-cold-start.test.ts +61 -1
- package/apps/studio/test/tour-overlay.test.tsx +18 -0
- package/apps/studio/tool-palette.tsx +18 -9
- package/apps/studio/use-chrome-visibility.tsx +66 -0
- package/apps/studio/use-collab.tsx +414 -187
- package/apps/studio/whats-new.json +73 -0
- package/apps/studio/ws.ts +44 -1
- package/cli/commands/design.mjs +1 -0
- package/cli/lib/gitignore-block.mjs +16 -3
- package/cli/lib/gitignore-block.test.mjs +13 -1
- package/package.json +11 -9
- package/plugins/design/dependencies.json +17 -0
- 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; }
|