@1agh/maude 0.19.1 → 0.21.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 +7 -0
- package/cli/bin/maude.mjs +5 -1
- package/cli/commands/design-link.test.mjs +207 -0
- package/cli/commands/design.mjs +42 -12
- package/cli/commands/doctor.mjs +350 -0
- package/cli/commands/doctor.test.mjs +185 -0
- package/cli/commands/help.mjs +24 -0
- package/cli/commands/hub.mjs +231 -0
- package/cli/commands/hub.test.mjs +87 -0
- package/cli/lib/config-lint.mjs +141 -0
- package/cli/lib/config-lint.test.mjs +117 -0
- package/cli/lib/design-link.mjs +216 -0
- package/cli/lib/hubs-config.mjs +123 -0
- package/cli/lib/hubs-config.test.mjs +100 -0
- package/cli/lib/preflight.mjs +232 -0
- package/cli/lib/stack-detect.mjs +344 -0
- package/cli/lib/stack-detect.test.mjs +121 -0
- package/package.json +17 -9
- package/plugins/design/dependencies.json +147 -0
- package/plugins/design/dependencies.schema.json +107 -0
- package/plugins/design/dev-server/ai-banner.tsx +188 -0
- package/plugins/design/dev-server/annotations-context-toolbar.tsx +115 -41
- package/plugins/design/dev-server/annotations-layer.tsx +256 -107
- package/plugins/design/dev-server/api.ts +17 -1
- package/plugins/design/dev-server/artboard-marquee.tsx +10 -6
- package/plugins/design/dev-server/bin/asset-sweep.sh +180 -0
- package/plugins/design/dev-server/bin/preflight.sh +32 -0
- package/plugins/design/dev-server/bin/runtime-health.sh +169 -0
- package/plugins/design/dev-server/bin/visual-sanity.sh +221 -0
- package/plugins/design/dev-server/canvas-lib.tsx +534 -32
- package/plugins/design/dev-server/canvas-shell.tsx +479 -29
- package/plugins/design/dev-server/client/app.jsx +72 -0
- package/plugins/design/dev-server/client/hmr.mjs +28 -0
- package/plugins/design/dev-server/collab/ai-activity.ts +127 -0
- package/plugins/design/dev-server/collab/git-lifecycle.ts +124 -0
- package/plugins/design/dev-server/collab/index.ts +47 -0
- package/plugins/design/dev-server/collab/persistence.ts +123 -0
- package/plugins/design/dev-server/collab/protocol.ts +108 -0
- package/plugins/design/dev-server/collab/registry.ts +110 -0
- package/plugins/design/dev-server/collab/room.ts +215 -0
- package/plugins/design/dev-server/commands/annotation-strokes-command.ts +127 -0
- package/plugins/design/dev-server/commands/equal-spacing-command.ts +28 -0
- package/plugins/design/dev-server/commands/move-artboards-command.ts +158 -0
- package/plugins/design/dev-server/comments-overlay.tsx +41 -4
- package/plugins/design/dev-server/contextual-toolbar.tsx +241 -0
- package/plugins/design/dev-server/cursors-overlay.tsx +296 -0
- package/plugins/design/dev-server/dist/client.bundle.js +75 -3
- package/plugins/design/dev-server/dist/runtime/motion.js +165 -0
- package/plugins/design/dev-server/dist/runtime/motion_react.js +409 -0
- package/plugins/design/dev-server/dist/runtime/y-protocols_awareness.js +587 -0
- package/plugins/design/dev-server/dist/runtime/y-protocols_sync.js +257 -0
- package/plugins/design/dev-server/dist/runtime/yjs.js +7107 -0
- package/plugins/design/dev-server/equal-spacing-detector.ts +98 -0
- package/plugins/design/dev-server/equal-spacing-handles.tsx +289 -0
- package/plugins/design/dev-server/export-dialog.tsx +1 -1
- package/plugins/design/dev-server/handoff.ts +24 -0
- package/plugins/design/dev-server/hmr-broadcast.ts +15 -2
- package/plugins/design/dev-server/http.ts +91 -1
- package/plugins/design/dev-server/input-router.tsx +52 -2
- package/plugins/design/dev-server/marquee-overlay.tsx +282 -0
- package/plugins/design/dev-server/participants-chrome.tsx +261 -0
- package/plugins/design/dev-server/runtime-bundle.ts +15 -0
- package/plugins/design/dev-server/server.ts +78 -11
- package/plugins/design/dev-server/test/ai-activity.test.ts +113 -0
- package/plugins/design/dev-server/test/annotation-strokes-command.test.ts +149 -0
- package/plugins/design/dev-server/test/annotations-layer.test.ts +44 -0
- package/plugins/design/dev-server/test/canvas-lib-motion.test.ts +131 -0
- package/plugins/design/dev-server/test/collab-annotations-bridge.test.ts +55 -0
- package/plugins/design/dev-server/test/collab-bridge.test.ts +81 -0
- package/plugins/design/dev-server/test/collab-loopback.test.ts +63 -0
- package/plugins/design/dev-server/test/collab-protocol.test.ts +146 -0
- package/plugins/design/dev-server/test/collab-room.test.ts +182 -0
- package/plugins/design/dev-server/test/collab-stress.test.ts +121 -0
- package/plugins/design/dev-server/test/equal-spacing-detector.test.ts +93 -0
- package/plugins/design/dev-server/test/git-lifecycle.test.ts +101 -0
- package/plugins/design/dev-server/test/input-router.test.ts +87 -1
- package/plugins/design/dev-server/test/marquee-overlay.test.ts +94 -0
- package/plugins/design/dev-server/test/move-artboards-command.test.ts +108 -0
- package/plugins/design/dev-server/test/participants-chrome.test.ts +30 -0
- package/plugins/design/dev-server/test/undo-stack.test.ts +211 -0
- package/plugins/design/dev-server/test/use-collab.test.ts +71 -0
- package/plugins/design/dev-server/test/use-cursor-modifiers.test.ts +59 -0
- package/plugins/design/dev-server/test/use-keyboard-discipline.test.ts +27 -0
- package/plugins/design/dev-server/test/use-undo-stack.test.tsx +193 -0
- package/plugins/design/dev-server/tool-palette.tsx +7 -7
- package/plugins/design/dev-server/undo-hud.tsx +95 -0
- package/plugins/design/dev-server/undo-stack.ts +240 -0
- package/plugins/design/dev-server/use-annotation-resize.tsx +1 -1
- package/plugins/design/dev-server/use-artboard-drag.tsx +6 -3
- package/plugins/design/dev-server/use-collab.tsx +478 -0
- package/plugins/design/dev-server/use-cursor-modifiers.tsx +122 -0
- package/plugins/design/dev-server/use-keyboard-discipline.tsx +125 -0
- package/plugins/design/dev-server/use-undo-stack.tsx +355 -0
- package/plugins/design/dev-server/ws.ts +123 -7
- package/plugins/design/templates/_shell.html +51 -6
- package/plugins/design/templates/design-system-inspiration/SUB-AGENT-PROMPTS.md +245 -0
- package/plugins/design/templates/design-system-inspiration/_MAPPING.md +2 -2
- package/plugins/design/templates/design-system-inspiration/core/preview/_components.css.tpl +129 -0
- package/plugins/design/templates/design-system-inspiration/core/preview/_motion-readme.md.tpl +63 -0
- package/plugins/design/templates/design-system-inspiration/core/preview/motion.css.tpl +106 -0
- package/plugins/design/templates/design-system-inspiration/core/preview/motion.tsx.tpl +208 -0
- package/plugins/flow/.claude-plugin/config.schema.json +12 -0
- package/plugins/flow/dependencies.json +143 -0
- package/plugins/flow/dependencies.schema.json +107 -0
- /package/plugins/design/templates/design-system-inspiration/core/preview/{motion.html → .archive/motion.html} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1agh/maude",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "Marketplace of Claude Code plugins by Michal Dovrtěl: `design` (canvas-first design iteration) + `flow` (generic agentic workflow loop with .ai second brain). Ships the `maude` CLI (with `mdcc` legacy alias) to scaffold workspace, run the design dev server, and manage configs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
20
|
"start": "bun run plugins/design/dev-server/server.ts",
|
|
21
|
-
"dev": "bun run plugins/design/dev-server/server.ts --port
|
|
21
|
+
"dev": "bun run plugins/design/dev-server/server.ts --port 4555",
|
|
22
22
|
"dev:site": "pnpm --filter @maude/site dev --port 4398",
|
|
23
23
|
"build": "pnpm -r --if-present run build",
|
|
24
24
|
"build:binary": "bun run plugins/design/dev-server/build.ts --release",
|
|
@@ -41,18 +41,22 @@
|
|
|
41
41
|
"prepublishOnly": "bash scripts/check-version-parity.sh"
|
|
42
42
|
},
|
|
43
43
|
"optionalDependencies": {
|
|
44
|
-
"@1agh/maude-darwin-arm64": "0.
|
|
45
|
-
"@1agh/maude-darwin-x64": "0.
|
|
46
|
-
"@1agh/maude-linux-arm64": "0.
|
|
47
|
-
"@1agh/maude-linux-arm64-musl": "0.
|
|
48
|
-
"@1agh/maude-linux-x64": "0.
|
|
49
|
-
"@1agh/maude-linux-x64-musl": "0.
|
|
50
|
-
"@1agh/maude-win32-x64": "0.
|
|
44
|
+
"@1agh/maude-darwin-arm64": "0.21.0",
|
|
45
|
+
"@1agh/maude-darwin-x64": "0.21.0",
|
|
46
|
+
"@1agh/maude-linux-arm64": "0.21.0",
|
|
47
|
+
"@1agh/maude-linux-arm64-musl": "0.21.0",
|
|
48
|
+
"@1agh/maude-linux-x64": "0.21.0",
|
|
49
|
+
"@1agh/maude-linux-x64-musl": "0.21.0",
|
|
50
|
+
"@1agh/maude-win32-x64": "0.21.0"
|
|
51
51
|
},
|
|
52
52
|
"files": [
|
|
53
53
|
"cli",
|
|
54
54
|
"plugins/design/dev-server",
|
|
55
|
+
"plugins/design/dependencies.json",
|
|
56
|
+
"plugins/design/dependencies.schema.json",
|
|
55
57
|
"plugins/design/templates",
|
|
58
|
+
"plugins/flow/dependencies.json",
|
|
59
|
+
"plugins/flow/dependencies.schema.json",
|
|
56
60
|
"plugins/flow/templates",
|
|
57
61
|
"plugins/flow/.claude-plugin/config.schema.json",
|
|
58
62
|
"LICENSE",
|
|
@@ -84,5 +88,9 @@
|
|
|
84
88
|
"@biomejs/biome": "^1.9.4",
|
|
85
89
|
"@changesets/cli": "^2.27.10",
|
|
86
90
|
"@playwright/test": "^1.60.0"
|
|
91
|
+
},
|
|
92
|
+
"dependencies": {
|
|
93
|
+
"ajv": "^8.20.0",
|
|
94
|
+
"ajv-formats": "^3.0.1"
|
|
87
95
|
}
|
|
88
96
|
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./dependencies.schema.json",
|
|
3
|
+
"version": "1",
|
|
4
|
+
"plugin": "design",
|
|
5
|
+
"dependencies": [
|
|
6
|
+
{
|
|
7
|
+
"id": "node",
|
|
8
|
+
"type": "cli",
|
|
9
|
+
"hardness": "hard",
|
|
10
|
+
"check": {
|
|
11
|
+
"command": "node --version",
|
|
12
|
+
"expectExit": 0,
|
|
13
|
+
"minVersion": "20.0.0"
|
|
14
|
+
},
|
|
15
|
+
"install": {
|
|
16
|
+
"preferred": "https://nodejs.org/en/download",
|
|
17
|
+
"darwin": "brew install node@22",
|
|
18
|
+
"linux": "https://nodejs.org/en/download/package-manager"
|
|
19
|
+
},
|
|
20
|
+
"autoInstall": false,
|
|
21
|
+
"usedBy": ["commands/init.md", "commands/browse.md", "dev-server/server.ts"],
|
|
22
|
+
"docsUrl": "https://nodejs.org"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "git",
|
|
26
|
+
"type": "cli",
|
|
27
|
+
"hardness": "hard",
|
|
28
|
+
"check": {
|
|
29
|
+
"command": "git --version",
|
|
30
|
+
"expectExit": 0
|
|
31
|
+
},
|
|
32
|
+
"install": {
|
|
33
|
+
"darwin": "brew install git",
|
|
34
|
+
"linux": "apt-get install -y git || dnf install -y git"
|
|
35
|
+
},
|
|
36
|
+
"autoInstall": false,
|
|
37
|
+
"usedBy": ["commands/init.md", "dev-server/collab/git-lifecycle.ts"],
|
|
38
|
+
"docsUrl": "https://git-scm.com"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": "bun",
|
|
42
|
+
"type": "cli",
|
|
43
|
+
"hardness": "hard",
|
|
44
|
+
"check": {
|
|
45
|
+
"command": "bun --version",
|
|
46
|
+
"expectExit": 0,
|
|
47
|
+
"minVersion": "1.3.0"
|
|
48
|
+
},
|
|
49
|
+
"install": {
|
|
50
|
+
"preferred": "curl -fsSL https://bun.sh/install | bash",
|
|
51
|
+
"darwin": "brew install oven-sh/bun/bun",
|
|
52
|
+
"win32": "powershell -c \"irm bun.sh/install.ps1 | iex\""
|
|
53
|
+
},
|
|
54
|
+
"autoInstall": false,
|
|
55
|
+
"fallbackBehavior": "dev-server is migrating to Bun authoritatively (DDR-009). Some build steps (bun run build.ts, bun test) require Bun and have no Node fallback.",
|
|
56
|
+
"usedBy": ["dev-server/package.json", "dev-server/build.ts", "dev-server/bin/smoke.sh"],
|
|
57
|
+
"docsUrl": "https://bun.com"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"id": "agent-browser",
|
|
61
|
+
"type": "cli",
|
|
62
|
+
"hardness": "soft",
|
|
63
|
+
"check": {
|
|
64
|
+
"command": "agent-browser --version",
|
|
65
|
+
"expectExit": 0
|
|
66
|
+
},
|
|
67
|
+
"install": {
|
|
68
|
+
"preferred": "npm i -g @anthropic-ai/agent-browser"
|
|
69
|
+
},
|
|
70
|
+
"autoInstall": true,
|
|
71
|
+
"fallbackBehavior": "Screenshot helpers fall back to `npx playwright` (slower, less reliable). /design:smoke and several critics need agent-browser for full coverage.",
|
|
72
|
+
"usedBy": [
|
|
73
|
+
"commands/screenshot.md",
|
|
74
|
+
"commands/new.md",
|
|
75
|
+
"commands/edit.md",
|
|
76
|
+
"commands/smoke.md",
|
|
77
|
+
"dev-server/bin/screenshot.sh",
|
|
78
|
+
"dev-server/bin/smoke.sh",
|
|
79
|
+
"agents/design-critic.md",
|
|
80
|
+
"agents/graphic-design-critic.md",
|
|
81
|
+
"agents/a11y-critic.md"
|
|
82
|
+
],
|
|
83
|
+
"docsUrl": "https://github.com/anthropics/agent-browser"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"id": "playwright",
|
|
87
|
+
"type": "cli",
|
|
88
|
+
"hardness": "soft",
|
|
89
|
+
"check": {
|
|
90
|
+
"command": "npx --no-install playwright --version",
|
|
91
|
+
"expectExit": 0
|
|
92
|
+
},
|
|
93
|
+
"install": {
|
|
94
|
+
"preferred": "npm i -g playwright && npx playwright install chromium"
|
|
95
|
+
},
|
|
96
|
+
"autoInstall": false,
|
|
97
|
+
"fallbackBehavior": "Screenshot scripts use playwright as a fallback when agent-browser is missing. Without either, screenshot helpers will fail.",
|
|
98
|
+
"usedBy": ["dev-server/bin/_screenshot-playwright.mjs", "dev-server/bin/screenshot.sh"],
|
|
99
|
+
"docsUrl": "https://playwright.dev"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"id": "maude",
|
|
103
|
+
"type": "cli",
|
|
104
|
+
"hardness": "soft",
|
|
105
|
+
"check": {
|
|
106
|
+
"command": "maude --version",
|
|
107
|
+
"expectExit": 0
|
|
108
|
+
},
|
|
109
|
+
"install": {
|
|
110
|
+
"preferred": "npm i -g @1agh/maude"
|
|
111
|
+
},
|
|
112
|
+
"autoInstall": false,
|
|
113
|
+
"fallbackBehavior": "Slash commands still work; `maude` CLI provides faster scaffolding (init / config / design serve) without going through Claude.",
|
|
114
|
+
"usedBy": ["commands/init.md", "skills/design/SKILL.md"],
|
|
115
|
+
"docsUrl": "https://github.com/1aGh/maude"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"id": "jq",
|
|
119
|
+
"type": "system-tool",
|
|
120
|
+
"hardness": "soft",
|
|
121
|
+
"check": {
|
|
122
|
+
"command": "jq --version",
|
|
123
|
+
"expectExit": 0
|
|
124
|
+
},
|
|
125
|
+
"install": {
|
|
126
|
+
"darwin": "brew install jq",
|
|
127
|
+
"linux": "apt-get install -y jq || dnf install -y jq",
|
|
128
|
+
"win32": "winget install jqlang.jq"
|
|
129
|
+
},
|
|
130
|
+
"autoInstall": false,
|
|
131
|
+
"fallbackBehavior": "Slash commands' bash blocks that read .ai/workflows.config.json via jq degrade to inline node one-liners; harder to read but functional.",
|
|
132
|
+
"usedBy": ["dev-server/bin/bootstrap-check.sh", "dev-server/bin/server-up.sh"]
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"id": "askuserquestion-mcp",
|
|
136
|
+
"type": "mcp",
|
|
137
|
+
"hardness": "soft",
|
|
138
|
+
"check": {
|
|
139
|
+
"mcp": "AskUserQuestion",
|
|
140
|
+
"tool": "ask"
|
|
141
|
+
},
|
|
142
|
+
"fallbackBehavior": "Discovery + critic-panel rounds fall back to numbered-prose chat. Slower, fewer options per round, no preview side-pane.",
|
|
143
|
+
"usedBy": ["skills/design-system/SKILL.md", "commands/setup-ds.md", "commands/new.md"],
|
|
144
|
+
"docsUrl": "https://docs.claude.com/en/docs/claude-code"
|
|
145
|
+
}
|
|
146
|
+
]
|
|
147
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "maude plugin — runtime dependency manifest",
|
|
4
|
+
"description": "Declares CLI binaries, MCP servers, and runtime packages the plugin's commands / agents / skills shell out to. Consumed by maude doctor (cli/commands/doctor.mjs) and preflight.sh / preflight.mjs. Schema is shared between plugins — each plugin ships its own dependencies.json + (symlink to) this schema.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["version", "plugin", "dependencies"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"version": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"const": "1",
|
|
12
|
+
"description": "Manifest schema version. Bumped when the dependency entry shape changes incompatibly."
|
|
13
|
+
},
|
|
14
|
+
"plugin": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"minLength": 1,
|
|
17
|
+
"description": "Plugin name (matches plugins/<plugin>/.claude-plugin/plugin.json `name`)."
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"type": "array",
|
|
21
|
+
"minItems": 0,
|
|
22
|
+
"items": { "$ref": "#/$defs/Dependency" }
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"$defs": {
|
|
26
|
+
"Dependency": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"additionalProperties": false,
|
|
29
|
+
"required": ["id", "type", "hardness", "check"],
|
|
30
|
+
"properties": {
|
|
31
|
+
"id": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"pattern": "^[a-z0-9][a-z0-9._-]*$",
|
|
34
|
+
"description": "Unique identifier within the manifest. Lowercase, hyphenated."
|
|
35
|
+
},
|
|
36
|
+
"type": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"enum": ["cli", "mcp", "node-package", "bun-package", "system-tool"],
|
|
39
|
+
"description": "What kind of dependency. `cli` = on PATH; `mcp` = MCP server / tool; `node-package` / `bun-package` = installed via npm/bun; `system-tool` = OS-provided (curl, jq)."
|
|
40
|
+
},
|
|
41
|
+
"hardness": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"enum": ["hard", "soft"],
|
|
44
|
+
"description": "`hard` = missing blocks plugin use; `soft` = graceful degradation possible (fallback path, warning only)."
|
|
45
|
+
},
|
|
46
|
+
"check": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"additionalProperties": false,
|
|
49
|
+
"description": "How to detect presence. For CLI/system-tool/package: shell command + expected exit. For MCP: tool name to probe.",
|
|
50
|
+
"properties": {
|
|
51
|
+
"command": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"description": "Shell command executed via `bash -c`. Pipes allowed. Defaults to `<id> --version`."
|
|
54
|
+
},
|
|
55
|
+
"expectExit": {
|
|
56
|
+
"type": "integer",
|
|
57
|
+
"default": 0,
|
|
58
|
+
"description": "Expected exit code. Anything else = missing/broken."
|
|
59
|
+
},
|
|
60
|
+
"minVersion": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"description": "Minimum semver. Comparator extracts `vX.Y.Z` from check.command stdout."
|
|
63
|
+
},
|
|
64
|
+
"mcp": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"description": "MCP server name. For type=mcp only."
|
|
67
|
+
},
|
|
68
|
+
"tool": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"description": "Specific tool name on the MCP server. For type=mcp only."
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"install": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"additionalProperties": false,
|
|
77
|
+
"description": "Install commands per platform. `preferred` is OS-agnostic / fallback; per-platform keys override.",
|
|
78
|
+
"properties": {
|
|
79
|
+
"preferred": { "type": "string" },
|
|
80
|
+
"darwin": { "type": "string" },
|
|
81
|
+
"linux": { "type": "string" },
|
|
82
|
+
"win32": { "type": "string" }
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"autoInstall": {
|
|
86
|
+
"type": "boolean",
|
|
87
|
+
"default": false,
|
|
88
|
+
"description": "If true, `maude doctor --fix` may run install.preferred after per-dep confirmation prompt."
|
|
89
|
+
},
|
|
90
|
+
"fallbackBehavior": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"description": "What the plugin does when this soft dep is missing. Human-readable; surfaces in maude doctor's report."
|
|
93
|
+
},
|
|
94
|
+
"usedBy": {
|
|
95
|
+
"type": "array",
|
|
96
|
+
"items": { "type": "string" },
|
|
97
|
+
"description": "Repo-relative paths (or path globs) of plugin files that reference this dependency. Aids debugging — doctor surfaces this when explaining a miss."
|
|
98
|
+
},
|
|
99
|
+
"docsUrl": {
|
|
100
|
+
"type": "string",
|
|
101
|
+
"format": "uri",
|
|
102
|
+
"description": "Where to read more."
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file ai-banner.tsx — yellow "Claude is editing this canvas" banner
|
|
3
|
+
* @scope plugins/design/dev-server/ai-banner.tsx
|
|
4
|
+
* @purpose Mounted by canvas-shell. Subscribes to the inspector WS
|
|
5
|
+
* `ai-activity` event for the canvas's own file path; shows a
|
|
6
|
+
* non-modal yellow banner while AI work is in progress and clears
|
|
7
|
+
* on explicit /end OR 30-second heartbeat-grace expiry.
|
|
8
|
+
*
|
|
9
|
+
* The banner is a soft notice, NOT a lock — the user can still type, click,
|
|
10
|
+
* drag annotations, etc. It exists so the user doesn't unknowingly fight a
|
|
11
|
+
* `/design:edit` that's about to rewrite their HTML.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { useEffect, useState } from 'react';
|
|
15
|
+
|
|
16
|
+
function deriveFile(): string | null {
|
|
17
|
+
if (typeof window === 'undefined') return null;
|
|
18
|
+
try {
|
|
19
|
+
const p = window.location.pathname;
|
|
20
|
+
if (p === '/_canvas-shell.html' || p === '/_canvas-shell') {
|
|
21
|
+
const qs = new URLSearchParams(window.location.search);
|
|
22
|
+
const canvas = qs.get('canvas') ?? '';
|
|
23
|
+
const designRel = (qs.get('designRel') ?? '.design').replace(/^\/+|\/+$/g, '');
|
|
24
|
+
return canvas ? `${designRel}/${canvas}` : null;
|
|
25
|
+
}
|
|
26
|
+
return decodeURIComponent(p).replace(/^\//, '');
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const BANNER_CSS = `
|
|
33
|
+
.dc-ai-banner {
|
|
34
|
+
position: fixed;
|
|
35
|
+
top: 14px;
|
|
36
|
+
left: 50%;
|
|
37
|
+
transform: translateX(-50%);
|
|
38
|
+
z-index: 10001;
|
|
39
|
+
padding: 8px 14px;
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
gap: 10px;
|
|
43
|
+
background: var(--maude-hud-accent-tint, oklch(92% 0.040 55));
|
|
44
|
+
color: var(--fg-0, #111);
|
|
45
|
+
border: 1px solid var(--border-default, rgba(0,0,0,0.16));
|
|
46
|
+
border-radius: var(--radius-md, 4px);
|
|
47
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
48
|
+
font-family: var(--font-sans, system-ui, -apple-system, sans-serif);
|
|
49
|
+
font-weight: 500;
|
|
50
|
+
font-size: 12px;
|
|
51
|
+
line-height: 1.2;
|
|
52
|
+
letter-spacing: 0.01em;
|
|
53
|
+
pointer-events: none;
|
|
54
|
+
user-select: none;
|
|
55
|
+
}
|
|
56
|
+
.dc-ai-banner__dot {
|
|
57
|
+
width: 7px;
|
|
58
|
+
height: 7px;
|
|
59
|
+
border-radius: 50%;
|
|
60
|
+
background: var(--maude-hud-accent, oklch(56% 0.170 50));
|
|
61
|
+
flex: 0 0 auto;
|
|
62
|
+
animation: dc-ai-banner-pulse 1.4s ease-in-out infinite;
|
|
63
|
+
}
|
|
64
|
+
@keyframes dc-ai-banner-pulse {
|
|
65
|
+
0%, 100% { opacity: 1; transform: scale(1); }
|
|
66
|
+
50% { opacity: 0.5; transform: scale(0.75); }
|
|
67
|
+
}
|
|
68
|
+
@media (prefers-reduced-motion: reduce) {
|
|
69
|
+
.dc-ai-banner__dot { animation: none; }
|
|
70
|
+
}
|
|
71
|
+
`.trim();
|
|
72
|
+
|
|
73
|
+
function ensureBannerStyles(): void {
|
|
74
|
+
if (typeof document === 'undefined') return;
|
|
75
|
+
if (document.getElementById('dc-ai-banner-css')) return;
|
|
76
|
+
const s = document.createElement('style');
|
|
77
|
+
s.id = 'dc-ai-banner-css';
|
|
78
|
+
s.textContent = BANNER_CSS;
|
|
79
|
+
document.head.appendChild(s);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
interface AiEntry {
|
|
83
|
+
file: string;
|
|
84
|
+
author: string;
|
|
85
|
+
startedAt: number;
|
|
86
|
+
lastHeartbeat: number;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function AiBanner(): JSX.Element | null {
|
|
90
|
+
ensureBannerStyles();
|
|
91
|
+
const [entry, setEntry] = useState<AiEntry | null>(null);
|
|
92
|
+
const file = deriveFile();
|
|
93
|
+
|
|
94
|
+
// Mount-time backfill — the bus only emits on changes, so a freshly opened
|
|
95
|
+
// tab needs to GET the snapshot to learn that an edit is already in flight.
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
if (!file) return;
|
|
98
|
+
let cancelled = false;
|
|
99
|
+
fetch('/_api/ai')
|
|
100
|
+
.then((r) => r.json())
|
|
101
|
+
.then((j: { entries?: AiEntry[] }) => {
|
|
102
|
+
if (cancelled) return;
|
|
103
|
+
const hit = (j.entries ?? []).find((e) => e.file === file);
|
|
104
|
+
if (hit) setEntry(hit);
|
|
105
|
+
})
|
|
106
|
+
.catch(() => {
|
|
107
|
+
/* offline / restart — silently no-op */
|
|
108
|
+
});
|
|
109
|
+
return () => {
|
|
110
|
+
cancelled = true;
|
|
111
|
+
};
|
|
112
|
+
}, [file]);
|
|
113
|
+
|
|
114
|
+
// Subscribe to the parent shell's WS broadcast — when the inspector channel
|
|
115
|
+
// receives an `ai-activity` event for OUR file path, update local state.
|
|
116
|
+
// The shell relays via postMessage so we don't need our own WS connection
|
|
117
|
+
// (the iframe already piggybacks on the parent's inspector session).
|
|
118
|
+
//
|
|
119
|
+
// FALLBACK — when the canvas is opened standalone (export iframe, _shell.html
|
|
120
|
+
// direct URL), no parent WS exists. We open a thin reader-only inspector WS
|
|
121
|
+
// ourselves and watch for the matching event.
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
if (!file) return;
|
|
124
|
+
if (typeof window === 'undefined') return;
|
|
125
|
+
|
|
126
|
+
// postMessage relay path — parent sends `ai-activity` events.
|
|
127
|
+
const onMessage = (e: MessageEvent) => {
|
|
128
|
+
const m = e.data as { dgn?: string; file?: string; entry?: AiEntry | null } | null;
|
|
129
|
+
if (!m || typeof m !== 'object') return;
|
|
130
|
+
if (m.dgn !== 'ai-activity') return;
|
|
131
|
+
if (m.file !== file) return;
|
|
132
|
+
setEntry(m.entry ?? null);
|
|
133
|
+
};
|
|
134
|
+
window.addEventListener('message', onMessage);
|
|
135
|
+
|
|
136
|
+
// Standalone fallback — if no parent is relaying, dial /_ws directly.
|
|
137
|
+
let standaloneWs: WebSocket | null = null;
|
|
138
|
+
let reconnect: ReturnType<typeof setTimeout> | null = null;
|
|
139
|
+
function connectStandalone() {
|
|
140
|
+
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
141
|
+
const ws = new WebSocket(`${proto}//${location.host}/_ws`);
|
|
142
|
+
standaloneWs = ws;
|
|
143
|
+
ws.addEventListener('message', (ev) => {
|
|
144
|
+
try {
|
|
145
|
+
const data = typeof ev.data === 'string' ? ev.data : '';
|
|
146
|
+
if (!data) return;
|
|
147
|
+
const m = JSON.parse(data) as { type?: string; file?: string; entry?: AiEntry | null };
|
|
148
|
+
if (m.type !== 'ai-activity') return;
|
|
149
|
+
if (m.file !== file) return;
|
|
150
|
+
setEntry(m.entry ?? null);
|
|
151
|
+
} catch {
|
|
152
|
+
/* not a JSON frame (binary from collab WS) — ignore */
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
ws.addEventListener('close', () => {
|
|
156
|
+
standaloneWs = null;
|
|
157
|
+
reconnect = setTimeout(connectStandalone, 2000);
|
|
158
|
+
});
|
|
159
|
+
ws.addEventListener('error', () => {
|
|
160
|
+
/* close handler reconnects */
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
// Only open the standalone WS when we're not inside an iframe — the parent
|
|
164
|
+
// already drives the postMessage relay for embedded canvases.
|
|
165
|
+
if (window.parent === window) connectStandalone();
|
|
166
|
+
|
|
167
|
+
return () => {
|
|
168
|
+
window.removeEventListener('message', onMessage);
|
|
169
|
+
if (reconnect) clearTimeout(reconnect);
|
|
170
|
+
if (standaloneWs && standaloneWs.readyState === WebSocket.OPEN) {
|
|
171
|
+
try {
|
|
172
|
+
standaloneWs.close();
|
|
173
|
+
} catch {
|
|
174
|
+
/* ignore */
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}, [file]);
|
|
179
|
+
|
|
180
|
+
if (!entry) return null;
|
|
181
|
+
return (
|
|
182
|
+
// biome-ignore lint/a11y/useSemanticElements: <output> is a form-result element; role="status" on a div is the canonical live-region banner pattern (APG).
|
|
183
|
+
<div className="dc-ai-banner" role="status" aria-live="polite">
|
|
184
|
+
<span className="dc-ai-banner__dot" aria-hidden="true" />
|
|
185
|
+
<span>{entry.author} is editing this canvas — your changes may conflict</span>
|
|
186
|
+
</div>
|
|
187
|
+
);
|
|
188
|
+
}
|