@1agh/maude 0.30.0 → 0.32.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 +788 -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 +796 -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 +598 -0
- package/apps/studio/client/panels/ReadinessList.jsx +189 -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 +625 -3
- package/apps/studio/client/styles/5-maude-overrides.css +25 -0
- package/apps/studio/client/styles/6-acp-chat.css +821 -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 +29097 -1534
- package/apps/studio/dist/comment-mount.js +4 -2
- package/apps/studio/dist/styles.css +5816 -1614
- 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 +399 -1
- package/apps/studio/participants-chrome.tsx +69 -9
- package/apps/studio/paths.ts +12 -0
- package/apps/studio/readiness.ts +220 -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 +95 -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/readiness.test.ts +127 -0
- 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 +82 -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
|
@@ -30,4 +30,6 @@
|
|
|
30
30
|
@import url('./4-components-maude.css');
|
|
31
31
|
/* Plan B — maude redesign of remaining legacy shell surfaces (HelpModal, tour, what's-new, SystemView, CommentBar). */
|
|
32
32
|
@import url('./5-maude-overrides.css');
|
|
33
|
+
/* Phase 31 (DDR-123) — ACP chat sidepanel (chat-* inside the Assistant st-rpanel). */
|
|
34
|
+
@import url('./6-acp-chat.css');
|
|
33
35
|
@import url('./5-utilities.css');
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// collab-tour.js — the version-control "rychlý kurz" (Phase 29, epic E4).
|
|
2
|
+
//
|
|
3
|
+
// Teaches the two-layer collab model on the EXISTING tour engine (overlay.jsx) —
|
|
4
|
+
// never a fork. Per the teaching-model DDR (this phase REVERSES the "hide the
|
|
5
|
+
// duality" recommendation in collab-model-design.md § Part 2): we surface a visible
|
|
6
|
+
// three-verb action-cycle and teach the live-vs-async split honestly, because the
|
|
7
|
+
// cycle has to match real buttons in the app and pure-hiding is leaky.
|
|
8
|
+
//
|
|
9
|
+
// Step 1 is a centered, target-less step whose graphic is the two-layer infographic
|
|
10
|
+
// (the engine already centers target-less steps; the `render` field is the only
|
|
11
|
+
// addition). Steps 2-4 spotlight the real action controls via stable [data-tour]
|
|
12
|
+
// anchors (save-local / publish / pull) — `changes: true` opens the Changes panel
|
|
13
|
+
// first so the buttons exist. Step 5 points at the live presence layer; step 6 is
|
|
14
|
+
// the one honest hard thing, centered.
|
|
15
|
+
//
|
|
16
|
+
// Vocabulary is the canonical set ONLY (teaching-model table) — no raw git terms:
|
|
17
|
+
// Save changes locally · Publish for everyone · Pull changes · Draft · Shared version.
|
|
18
|
+
|
|
19
|
+
import CollabModelInfographic from '../panels/CollabModelInfographic.jsx';
|
|
20
|
+
|
|
21
|
+
export const COLLAB_TOUR = [
|
|
22
|
+
{
|
|
23
|
+
// Centered, no target — the engine renders target-less steps centered, and the
|
|
24
|
+
// `render` field draws the infographic above the copy.
|
|
25
|
+
render: CollabModelInfographic,
|
|
26
|
+
title: 'Working together, in one picture',
|
|
27
|
+
body: 'There are two layers. Up top, being together just happens. Below, your work moves through three simple steps. Let’s walk them.',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
target: "[data-tour='save-local']",
|
|
31
|
+
changes: true,
|
|
32
|
+
title: 'Save changes locally',
|
|
33
|
+
body: 'When something looks right, save it. That keeps a version on your computer you can always come back to — like a checkpoint, just for you.',
|
|
34
|
+
placement: 'left',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
target: "[data-tour='publish']",
|
|
38
|
+
changes: true,
|
|
39
|
+
title: 'Publish for everyone',
|
|
40
|
+
body: 'Ready to share? Publish sends your saved work to the whole team. Think of it as putting your version on the shared shelf.',
|
|
41
|
+
placement: 'left',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
target: "[data-tour='pull']",
|
|
45
|
+
changes: true,
|
|
46
|
+
title: 'Pull changes',
|
|
47
|
+
body: 'When teammates publish, Pull brings their work onto your computer so you’re both looking at the same thing.',
|
|
48
|
+
placement: 'left',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
target: "[data-tour='status']",
|
|
52
|
+
title: 'Being together is automatic',
|
|
53
|
+
body: 'Cursors, who’s here, and comments need no buttons at all. When you’re both in a canvas, you see each other live — that’s the top layer, always on.',
|
|
54
|
+
placement: 'bottom',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
// Centered — the one honest hard thing.
|
|
58
|
+
title: 'The one thing worth knowing',
|
|
59
|
+
body: 'When you’re here together, publishing already covers your teammate — they’ve seen it live, so Publish is just dropping a bookmark. Only when you work apart can two versions drift. If that happens, Maude shows you both and lets you pick — keep mine, keep theirs, or keep both. Never a confusing merge.',
|
|
60
|
+
},
|
|
61
|
+
];
|
|
@@ -225,6 +225,11 @@ export function TourOverlay({ steps, open, onClose, onComplete, bus, hasSelectio
|
|
|
225
225
|
const needSelHint = !!step.requireSelection && !hasSelection;
|
|
226
226
|
const needCanvasHint = (!!step.canvas || !!step.requireSelection) && hasCanvas === false;
|
|
227
227
|
|
|
228
|
+
// Optional per-step graphic (Phase 29 / E4): a component drawn above the copy —
|
|
229
|
+
// the collab tour uses it for the centered two-layer infographic step. Widens the
|
|
230
|
+
// card so the diagram has room.
|
|
231
|
+
const Graphic = step.render || null;
|
|
232
|
+
|
|
228
233
|
return (
|
|
229
234
|
<div className="mdcc-tour" role="presentation">
|
|
230
235
|
{spot ? (
|
|
@@ -233,7 +238,7 @@ export function TourOverlay({ steps, open, onClose, onComplete, bus, hasSelectio
|
|
|
233
238
|
<div className="mdcc-tour__scrim" aria-hidden="true" />
|
|
234
239
|
)}
|
|
235
240
|
<div
|
|
236
|
-
className=
|
|
241
|
+
className={'mdcc-tour__card' + (Graphic ? ' mdcc-tour__card--graphic' : '')}
|
|
237
242
|
ref={cardRef}
|
|
238
243
|
role="dialog"
|
|
239
244
|
aria-modal="true"
|
|
@@ -244,6 +249,11 @@ export function TourOverlay({ steps, open, onClose, onComplete, bus, hasSelectio
|
|
|
244
249
|
<div className="mdcc-tour__step">
|
|
245
250
|
{i + 1} / {steps.length}
|
|
246
251
|
</div>
|
|
252
|
+
{Graphic && (
|
|
253
|
+
<div className="mdcc-tour__graphic">
|
|
254
|
+
<Graphic />
|
|
255
|
+
</div>
|
|
256
|
+
)}
|
|
247
257
|
<div className="mdcc-tour__title" id="mdcc-tour-title">
|
|
248
258
|
{step.title}
|
|
249
259
|
</div>
|
|
@@ -48,20 +48,35 @@ export function useWhatsNew(currentVersion) {
|
|
|
48
48
|
const [toastDismissed, setToastDismissed] = useState(() => readToastDismissed());
|
|
49
49
|
const [panelOpen, setPanelOpen] = useState(false);
|
|
50
50
|
|
|
51
|
+
// Fetch the feed on mount AND whenever the window regains focus (Phase 32 /
|
|
52
|
+
// Task 5). The native shell can sit backgrounded for a long time; an auto-update
|
|
53
|
+
// (or a deploy of the web studio) bumps the feed while we're not looking, so we
|
|
54
|
+
// re-evaluate on focus to surface the ✦ badge without a full reload.
|
|
51
55
|
useEffect(() => {
|
|
52
56
|
let cancelled = false;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
const refresh = () => {
|
|
58
|
+
fetch('/_api/whats-new')
|
|
59
|
+
.then((r) => r.json())
|
|
60
|
+
.then((j) => {
|
|
61
|
+
if (cancelled) return;
|
|
62
|
+
setEntries(Array.isArray(j?.entries) ? j.entries : []);
|
|
63
|
+
if (typeof j?.version === 'string') setFeedVersion(j.version);
|
|
64
|
+
})
|
|
65
|
+
.catch(() => {
|
|
66
|
+
/* offline / restart — no banner */
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
refresh();
|
|
70
|
+
const onFocus = () => refresh();
|
|
71
|
+
const onVisible = () => {
|
|
72
|
+
if (document.visibilityState === 'visible') refresh();
|
|
73
|
+
};
|
|
74
|
+
window.addEventListener('focus', onFocus);
|
|
75
|
+
document.addEventListener('visibilitychange', onVisible);
|
|
63
76
|
return () => {
|
|
64
77
|
cancelled = true;
|
|
78
|
+
window.removeEventListener('focus', onFocus);
|
|
79
|
+
document.removeEventListener('visibilitychange', onVisible);
|
|
65
80
|
};
|
|
66
81
|
}, []);
|
|
67
82
|
|
|
@@ -58,6 +58,14 @@ export interface Registry {
|
|
|
58
58
|
* updated stroke set without waiting for a cold-open re-seed.
|
|
59
59
|
*/
|
|
60
60
|
syncRoomFromAnnotations(slug: string, svg: string): void;
|
|
61
|
+
/**
|
|
62
|
+
* Phase 30 — project agent editing-presence onto a slug's room awareness so
|
|
63
|
+
* it crosses the hub (the loopback `ai-activity` bus event does not). `null`
|
|
64
|
+
* clears it. No-op when no room is live for the slug — a peer that joins
|
|
65
|
+
* later will see the agent on the next ai-activity heartbeat. Soft heads-up;
|
|
66
|
+
* never a lock.
|
|
67
|
+
*/
|
|
68
|
+
setAgentEditing(slug: string, state: { name: string; since: number } | null): void;
|
|
61
69
|
/**
|
|
62
70
|
* Phase 9 Task 5 — attach the hub-side Awareness (from a sync provider) for
|
|
63
71
|
* a slug so the Room's Awareness (browser peers) is bridged bidirectionally
|
|
@@ -192,6 +200,10 @@ export function createRegistry(callbacks: RoomCallbacks): Registry {
|
|
|
192
200
|
}, 'inspector-write');
|
|
193
201
|
}
|
|
194
202
|
|
|
203
|
+
function setAgentEditing(slug: string, state: { name: string; since: number } | null): void {
|
|
204
|
+
rooms.get(slug)?.setAgentEditing(state);
|
|
205
|
+
}
|
|
206
|
+
|
|
195
207
|
async function flushAll(): Promise<void> {
|
|
196
208
|
await Promise.all(Array.from(rooms.values(), (r) => r.flush()));
|
|
197
209
|
}
|
|
@@ -227,6 +239,7 @@ export function createRegistry(callbacks: RoomCallbacks): Registry {
|
|
|
227
239
|
getDoc,
|
|
228
240
|
syncRoomFromComments,
|
|
229
241
|
syncRoomFromAnnotations,
|
|
242
|
+
setAgentEditing,
|
|
230
243
|
attachHubAwareness,
|
|
231
244
|
pin,
|
|
232
245
|
unpin,
|
|
@@ -57,6 +57,15 @@ export interface Room {
|
|
|
57
57
|
connect(conn: RoomConn): Promise<void>;
|
|
58
58
|
disconnect(conn: RoomConn): void;
|
|
59
59
|
receive(conn: RoomConn, payload: Uint8Array): void;
|
|
60
|
+
/**
|
|
61
|
+
* Phase 30 — project an agent's "editing" activity onto this room's awareness
|
|
62
|
+
* using the room's own (otherwise-unused) local awareness slot, so the soft
|
|
63
|
+
* editing-presence crosses the hub to remote peers (the loopback `ai-activity`
|
|
64
|
+
* bus event does not). `null` clears it. Idempotent — a re-set of the same
|
|
65
|
+
* `{name, since}` is a no-op (ai-activity heartbeats re-emit every 10 s).
|
|
66
|
+
* NOT a lock; never blocks a peer.
|
|
67
|
+
*/
|
|
68
|
+
setAgentEditing(state: { name: string; since: number } | null): void;
|
|
60
69
|
/** Force the debounced flush to fire now. DDR-051 §3 (branch-switch). */
|
|
61
70
|
flush(): Promise<void>;
|
|
62
71
|
/** Tear down — clears timers + removes awareness. */
|
|
@@ -192,6 +201,32 @@ export function createRoom(slug: string, callbacks: RoomCallbacks): Room {
|
|
|
192
201
|
if (reply) conn.send(reply);
|
|
193
202
|
}
|
|
194
203
|
|
|
204
|
+
// Phase 30 — agent editing-presence projected onto the room's own awareness
|
|
205
|
+
// slot. The key short-circuits redundant re-sets (ai-activity heartbeats
|
|
206
|
+
// re-emit the same entry every 10 s). The awareness `update` handler above
|
|
207
|
+
// broadcasts the change to local conns AND relays it to the hub via
|
|
208
|
+
// bridgeAwareness, so a remote peer's overlay attributes the edit.
|
|
209
|
+
let agentEditingKey: string | null = null;
|
|
210
|
+
function setAgentEditing(state: { name: string; since: number } | null): void {
|
|
211
|
+
if (destroyed) return;
|
|
212
|
+
const key = state ? `${state.name}${state.since}` : null;
|
|
213
|
+
if (key === agentEditingKey) return;
|
|
214
|
+
agentEditingKey = key;
|
|
215
|
+
if (state === null) {
|
|
216
|
+
awareness.setLocalState(null);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
awareness.setLocalState({
|
|
220
|
+
name: state.name,
|
|
221
|
+
color: '', // re-derived client-side from name; wire value is discarded
|
|
222
|
+
cursor: null,
|
|
223
|
+
selection: null,
|
|
224
|
+
annotationSelection: [],
|
|
225
|
+
viewport: { x: 0, y: 0, zoom: 1 },
|
|
226
|
+
editing: { since: state.since },
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
195
230
|
async function destroy(): Promise<void> {
|
|
196
231
|
destroyed = true;
|
|
197
232
|
if (flushTimer) {
|
|
@@ -211,6 +246,7 @@ export function createRoom(slug: string, callbacks: RoomCallbacks): Room {
|
|
|
211
246
|
connect,
|
|
212
247
|
disconnect,
|
|
213
248
|
receive,
|
|
249
|
+
setAgentEditing,
|
|
214
250
|
flush,
|
|
215
251
|
destroy,
|
|
216
252
|
size: () => conns.size,
|
|
@@ -89,8 +89,15 @@ const CURSOR_CSS = `
|
|
|
89
89
|
border-radius: var(--radius-sm, 2px) var(--radius-sm, 2px) 0 0;
|
|
90
90
|
white-space: nowrap;
|
|
91
91
|
}
|
|
92
|
+
/* Phase 30 / DDR-120 — soft editing-presence. When a peer (human or a bridged
|
|
93
|
+
* agent) is actively editing this canvas, their cursor pulses gently and the
|
|
94
|
+
* label carries a ✎ mark. A heads-up, never a lock. */
|
|
95
|
+
.dc-cursor--editing .dc-cursor-arrow { animation: dc-cursor-edit-pulse 1.6s ease-in-out infinite; }
|
|
96
|
+
.dc-cursor-edit-mark { margin-left: 3px; opacity: 0.85; }
|
|
97
|
+
@keyframes dc-cursor-edit-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
|
|
92
98
|
@media (prefers-reduced-motion: reduce) {
|
|
93
99
|
.dc-cursor { transition: none !important; }
|
|
100
|
+
.dc-cursor--editing .dc-cursor-arrow { animation: none; }
|
|
94
101
|
}
|
|
95
102
|
`.trim();
|
|
96
103
|
|
|
@@ -119,8 +126,12 @@ const Cursor = memo(function Cursor({ peer, viewport }: CursorProps): JSX.Elemen
|
|
|
119
126
|
// world → screen: screen = world * zoom + viewport.{x,y}
|
|
120
127
|
const screenX = peer.cursor.x * viewport.zoom + viewport.x;
|
|
121
128
|
const screenY = peer.cursor.y * viewport.zoom + viewport.y;
|
|
129
|
+
const editing = !!peer.editing;
|
|
122
130
|
return (
|
|
123
|
-
<div
|
|
131
|
+
<div
|
|
132
|
+
className={`dc-cursor${editing ? ' dc-cursor--editing' : ''}`}
|
|
133
|
+
style={{ transform: `translate(${screenX}px, ${screenY}px)` }}
|
|
134
|
+
>
|
|
124
135
|
{/* DS colors-presence Pointer — one plain triangle glyph, tinted by its
|
|
125
136
|
owner (the specimen's exact 24-grid path, no tail/notch). */}
|
|
126
137
|
<svg
|
|
@@ -134,6 +145,11 @@ const Cursor = memo(function Cursor({ peer, viewport }: CursorProps): JSX.Elemen
|
|
|
134
145
|
</svg>
|
|
135
146
|
<div className="dc-cursor-label" style={{ background: peer.color }}>
|
|
136
147
|
{peer.name}
|
|
148
|
+
{editing && (
|
|
149
|
+
<span className="dc-cursor-edit-mark" aria-hidden="true">
|
|
150
|
+
✎
|
|
151
|
+
</span>
|
|
152
|
+
)}
|
|
137
153
|
</div>
|
|
138
154
|
</div>
|
|
139
155
|
);
|