@xiaobuyu/nodesign 0.0.1 → 0.0.2
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 +156 -106
- package/bin/nodesign.js +0 -0
- package/package.json +1 -1
- package/server/api/README.md +45 -45
- package/server/api/_guard.js +75 -75
- package/server/api/admin.js +187 -187
- package/server/api/assets/docx-page.js +103 -103
- package/server/api/assets/helpers.js +22 -22
- package/server/api/assets/notes.js +144 -144
- package/server/api/assets.js +896 -896
- package/server/api/board.js +57 -57
- package/server/api/browse.js +138 -138
- package/server/api/canvas.js +444 -444
- package/server/api/chatai.js +324 -324
- package/server/api/exports/build-standalone.js +980 -980
- package/server/api/exports/cards.js +118 -118
- package/server/api/exports/docx-pdf.js +46 -46
- package/server/api/exports/export-page.js +142 -142
- package/server/api/exports/handoff.js +147 -147
- package/server/api/exports.js +690 -690
- package/server/api/instruction.js +75 -75
- package/server/api/me.js +116 -116
- package/server/api/memory.js +185 -185
- package/server/api/pending-changes.js +366 -366
- package/server/api/plugins.js +164 -164
- package/server/api/projects.js +220 -220
- package/server/api/publish.js +64 -64
- package/server/api/recent.js +73 -73
- package/server/api/sessions.js +589 -589
- package/server/api/skills.js +77 -77
- package/server/api/standalone-fit.js +168 -168
- package/server/api/turn-compose.js +180 -180
- package/server/api/turn-inflight.js +44 -44
- package/server/auth/README.md +17 -17
- package/server/auth/middleware.js +138 -138
- package/server/auth/origin-guard.js +71 -71
- package/server/auth/session.js +108 -108
- package/server/auth/tier.js +113 -113
- package/server/auth/users-store.js +348 -348
- package/server/edit/README.md +29 -29
- package/server/engine/README.md +113 -113
- package/server/engine/agent/agent-shared.js +550 -550
- package/server/engine/agent/auto-mode-default-hard-deny.txt +4 -4
- package/server/engine/agent/auto-mode-rules.js +78 -78
- package/server/engine/agent/context.js +418 -418
- package/server/engine/agent/events.js +464 -464
- package/server/engine/agent/hooks/canvas-validate.js +199 -199
- package/server/engine/agent/hooks/failure.js +120 -120
- package/server/engine/agent/hooks/file-events.js +75 -75
- package/server/engine/agent/hooks/lifecycle.js +233 -233
- package/server/engine/agent/hooks/post-canvas-focus.js +80 -80
- package/server/engine/agent/hooks/post-guidance.js +206 -206
- package/server/engine/agent/hooks/post-subagent-report.js +104 -104
- package/server/engine/agent/hooks/post-trim.js +58 -58
- package/server/engine/agent/hooks/pre-board-neighborhood.js +34 -34
- package/server/engine/agent/hooks/pre-defaults.js +84 -84
- package/server/engine/agent/hooks/pre-injectors.js +277 -277
- package/server/engine/agent/hooks/pre-performance-log-guard.js +78 -78
- package/server/engine/agent/hooks/pre-starter-files.js +61 -61
- package/server/engine/agent/hooks/pre-workspace-scope-guard.js +78 -78
- package/server/engine/agent/hooks/site-validate.js +94 -94
- package/server/engine/agent/hooks/tool-prompts.js +31 -31
- package/server/engine/agent/hooks/turn-state-memory.js +56 -56
- package/server/engine/agent/hooks/user-prompt-submit.js +215 -215
- package/server/engine/agent/hooks.js +354 -354
- package/server/engine/agent/init-contract.js +69 -69
- package/server/engine/agent/isolation.js +159 -159
- package/server/engine/agent/model-table.js +292 -292
- package/server/engine/agent/plugin-loader.js +266 -266
- package/server/engine/agent/prompts/nodesign-prelude.md +336 -336
- package/server/engine/agent/prompts/tools/ask-user-question-protocol.md +71 -71
- package/server/engine/agent/prompts/tools/direct-edit-protocol.md +98 -98
- package/server/engine/agent/prompts/tools/generate-image-cookbook.gemini-gateway.md +583 -583
- package/server/engine/agent/prompts/tools/generate-image-cookbook.md +483 -483
- package/server/engine/agent/prompts/tools/hybrid-reference.md +100 -100
- package/server/engine/agent/prompts/tools/paint-still-cookbook.md +270 -270
- package/server/engine/agent/prompts/tools/roll-film-cookbook.md +70 -70
- package/server/engine/agent/prompts/tools/site-reference.md +106 -106
- package/server/engine/agent/prompts/tools/tweaks-syntax.md +125 -125
- package/server/engine/agent/prompts/tools/vision-checker-dispatch.md +48 -48
- package/server/engine/agent/session-loop.js +1024 -1024
- package/server/engine/agent/session-model.js +164 -164
- package/server/engine/agent/skill.js +204 -204
- package/server/engine/agent/task-events.js +78 -78
- package/server/engine/agents/ds-extractor.md +132 -132
- package/server/engine/agents/explorer.md +190 -190
- package/server/engine/agents/index.js +230 -230
- package/server/engine/agents/schemas/design-system.json +156 -156
- package/server/engine/agents/schemas/tweak-schema.json +113 -113
- package/server/engine/agents/tweak-proposer.md +127 -127
- package/server/engine/agents/vision-checker.md +254 -254
- package/server/engine/browse/capture.js +422 -422
- package/server/engine/browse/card.js +137 -137
- package/server/engine/browse/handover.js +59 -59
- package/server/engine/browse/page-digest.js +119 -119
- package/server/engine/browse/refs.js +196 -196
- package/server/engine/browse/registry.js +307 -307
- package/server/engine/browse/screencast.js +170 -170
- package/server/engine/browse/state.js +121 -121
- package/server/engine/chatai/chat-log.js +79 -79
- package/server/engine/chatai/index.js +193 -193
- package/server/engine/chatai/openai-compat.js +152 -152
- package/server/engine/chatai/orchestrate.js +309 -309
- package/server/engine/chatai/perform.js +40 -40
- package/server/engine/chatai/summarize.js +79 -79
- package/server/engine/mcp/capability-gate.js +52 -52
- package/server/engine/mcp/index.js +339 -339
- package/server/engine/mcp/param-sanitizer.js +142 -142
- package/server/engine/mcp/tools/arrange-on-board.js +124 -124
- package/server/engine/mcp/tools/artifact-session.js +278 -278
- package/server/engine/mcp/tools/browse-computer.js +421 -421
- package/server/engine/mcp/tools/browse-find-batch.js +183 -183
- package/server/engine/mcp/tools/browse-screenshot.js +157 -157
- package/server/engine/mcp/tools/browse.js +480 -480
- package/server/engine/mcp/tools/build-docx.js +101 -101
- package/server/engine/mcp/tools/clear-pending-changes.js +99 -99
- package/server/engine/mcp/tools/create-on-board.js +155 -155
- package/server/engine/mcp/tools/crystallize-skill.js +168 -168
- package/server/engine/mcp/tools/explain-style.js +237 -237
- package/server/engine/mcp/tools/export-handoff.js +133 -133
- package/server/engine/mcp/tools/expose-tweaks.js +149 -149
- package/server/engine/mcp/tools/generate-image.js +842 -842
- package/server/engine/mcp/tools/get-computed-styles.js +164 -164
- package/server/engine/mcp/tools/get-pending-changes.js +205 -205
- package/server/engine/mcp/tools/h3box-ssh.js +84 -84
- package/server/engine/mcp/tools/helpers/acquire-page.js +82 -82
- package/server/engine/mcp/tools/helpers/motion-lab.js +554 -554
- package/server/engine/mcp/tools/helpers/motion-scroll.js +112 -112
- package/server/engine/mcp/tools/helpers/perception-page.js +215 -215
- package/server/engine/mcp/tools/helpers/reference-download.js +103 -103
- package/server/engine/mcp/tools/helpers/rembg-bridge.py +80 -80
- package/server/engine/mcp/tools/helpers/shot-pipeline.js +280 -280
- package/server/engine/mcp/tools/highlight.js +68 -68
- package/server/engine/mcp/tools/list-pages.js +222 -222
- package/server/engine/mcp/tools/lookup-tags.js +59 -59
- package/server/engine/mcp/tools/navigate-to-page.js +60 -60
- package/server/engine/mcp/tools/organize-board.js +64 -64
- package/server/engine/mcp/tools/paint-still.js +434 -434
- package/server/engine/mcp/tools/pin-to-board.js +130 -130
- package/server/engine/mcp/tools/preview-deck.js +130 -130
- package/server/engine/mcp/tools/profile-scroll.js +305 -305
- package/server/engine/mcp/tools/publish-site.js +102 -102
- package/server/engine/mcp/tools/query-elements.js +195 -195
- package/server/engine/mcp/tools/read-board.js +103 -103
- package/server/engine/mcp/tools/read-document.js +93 -93
- package/server/engine/mcp/tools/read-page.js +264 -264
- package/server/engine/mcp/tools/read-tavern-json.js +143 -143
- package/server/engine/mcp/tools/record-decision.js +140 -140
- package/server/engine/mcp/tools/relate-on-board.js +110 -110
- package/server/engine/mcp/tools/report-issue.js +115 -115
- package/server/engine/mcp/tools/roll-film.js +266 -266
- package/server/engine/mcp/tools/screenshot-docx.js +138 -138
- package/server/engine/mcp/tools/screenshot-url.js +195 -195
- package/server/engine/mcp/tools/screenshot.js +583 -583
- package/server/engine/mcp/tools/tier-gate.js +96 -96
- package/server/engine/mcp/tools/trace-motion.js +215 -215
- package/server/engine/mcp/tools/web-search.js +548 -548
- package/server/engine/motion/inventory.js +349 -349
- package/server/engine/perception/session.js +235 -235
- package/server/engine/plugins/nodesign/.claude-plugin/plugin.json +5 -5
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +192 -192
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/canvas.template.html +667 -667
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/hybrid-grid.md +22 -22
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/image-led-cover.md +25 -25
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/portrait.md +23 -23
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/quote-backdrop.md +21 -21
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/section-divider.md +23 -23
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/text-led.md +21 -21
- package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +162 -162
- package/server/engine/plugins/nodesign/skills/docx-craft/references/token-schema.md +407 -407
- package/server/engine/plugins/nodesign/skills/docx-craft//346/226/207/346/241/243.template.json +80 -80
- package/server/engine/plugins/nodesign/skills/rp-craft/SKILL.md +228 -228
- package/server/engine/plugins/nodesign/skills/rp-craft/patterns//346/274/224/345/207/272/351/241/265-/346/234/200/345/260/217/345/256/236/347/216/260.html +78 -78
- package/server/engine/plugins/nodesign/skills/rp-craft//346/274/224/345/207/272.template.js +268 -268
- package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +323 -323
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/build-lane.md +134 -134
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/cutout-collage.md +61 -61
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/mock-app.md +163 -163
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/page-transitions.md +86 -86
- package/server/engine/runs/active-runs.js +698 -698
- package/server/engine/runs/live-turn.js +252 -252
- package/server/engine/runs/store.js +364 -364
- package/server/engine/runs/turn-relay.js +217 -217
- package/server/engine/runtime/workspace.js +164 -164
- package/server/lib/artifact-file-path.js +80 -80
- package/server/lib/artifact-target.js +284 -284
- package/server/lib/asset-refs.js +239 -239
- package/server/lib/async-queue.js +104 -104
- package/server/lib/auto-relations.js +130 -130
- package/server/lib/binding-types.js +60 -60
- package/server/lib/board-kind-sizes.js +62 -62
- package/server/lib/board-relations.js +133 -133
- package/server/lib/browse-proxy.js +180 -180
- package/server/lib/canvas-id.js +36 -36
- package/server/lib/cover.js +227 -227
- package/server/lib/danbooru-tags.js +291 -291
- package/server/lib/doc-extract.js +156 -156
- package/server/lib/docx/build-from-source.js +260 -260
- package/server/lib/docx/build.js +490 -490
- package/server/lib/docx/dump-styles.js +287 -287
- package/server/lib/docx/fonts/nodesign-cjk.conf +164 -164
- package/server/lib/docx/merge-runs.js +129 -129
- package/server/lib/docx/numbering.js +218 -218
- package/server/lib/docx/order.js +152 -152
- package/server/lib/docx/rawzip.js +172 -172
- package/server/lib/docx/render.js +92 -92
- package/server/lib/docx/text-lint.js +225 -225
- package/server/lib/docx/tokens.js +335 -335
- package/server/lib/docx/units.js +29 -29
- package/server/lib/docx/xml.js +291 -291
- package/server/lib/docx-pages.js +187 -187
- package/server/lib/export-collect.js +289 -289
- package/server/lib/export-package.js +210 -210
- package/server/lib/html-srcset.js +145 -145
- package/server/lib/image-variant.js +460 -460
- package/server/lib/ingress/forward-openai-chat.js +305 -305
- package/server/lib/ingress/openai-chat.js +491 -491
- package/server/lib/ingress/session-notice.js +66 -66
- package/server/lib/ingress/session-routes.js +68 -68
- package/server/lib/ingress/slot-probe.js +130 -130
- package/server/lib/ingress/upstream-billing.js +52 -52
- package/server/lib/ingress/upstream-fail-streak.js +72 -72
- package/server/lib/ingress/upstream-truncation.js +48 -48
- package/server/lib/issues-store.js +182 -182
- package/server/lib/kinds/deck.js +72 -72
- package/server/lib/kinds/docx.js +283 -283
- package/server/lib/kinds/file-kinds.js +113 -113
- package/server/lib/kinds/index.js +202 -202
- package/server/lib/kinds/site.js +161 -161
- package/server/lib/model-ingress.js +543 -543
- package/server/lib/moderation.js +255 -255
- package/server/lib/notice-store.js +103 -103
- package/server/lib/plugin-install.js +104 -104
- package/server/lib/plugin-validator.js +721 -721
- package/server/lib/publish-store.js +115 -115
- package/server/lib/quick-summary.js +52 -52
- package/server/lib/quota.js +270 -270
- package/server/lib/rate-window.js +29 -29
- package/server/lib/reference-assets.js +92 -92
- package/server/lib/region-shot.js +135 -135
- package/server/lib/safe-path.js +73 -73
- package/server/lib/sdk-session.js +33 -33
- package/server/lib/showcase-store.js +92 -92
- package/server/lib/site-publish.js +465 -465
- package/server/lib/ssrf-guard.js +432 -432
- package/server/lib/task-scan.js +158 -158
- package/server/lib/tavern-json.js +154 -154
- package/server/lib/video-variant.js +237 -237
- package/server/lib/workspace-path.js +33 -33
- package/server/ops/fix-sdk-musl.mjs +40 -40
- package/server/ops/install-macos-fonts.sh +114 -114
- package/server/ops/macos-fonts.conf +336 -336
- package/server/ops/sandbox-shim/bwrap +57 -57
- package/server/projects/assets-summary.js +119 -119
- package/server/projects/auto-name.js +53 -53
- package/server/projects/board-store.js +640 -640
- package/server/projects/move-entry.js +103 -103
- package/server/projects/store.js +270 -270
- package/server/projects/ui-config.js +54 -54
- package/server/projects/workspace-templates.js +53 -53
- package/server/projects/workspace.js +1025 -1025
- package/server/runtime/local-config.js +177 -177
- package/server/runtime/profile.js +72 -72
- package/server/services/rembg-service.py +334 -334
- package/server/shared/README.md +25 -25
- package/server/shared/deck.js +54 -54
- package/server/shared/time.js +49 -49
- package/server/style-pipeline/README.md +53 -53
- package/server/ws/broker.js +36 -36
- package/server/ws/browse-channel.js +177 -177
- package/server/ws/index.js +386 -386
- package/web/README.md +89 -89
- package/web/dist/assets/BrowserWindow-BYCgizHw.js +11 -0
- package/web/dist/assets/{DeckWindow-4G0xX7P3.js → DeckWindow-pNydEM4L.js} +1 -1
- package/web/dist/assets/{DocxWindow-BXQsoPkh.js → DocxWindow-PPgzh3Gq.js} +1 -1
- package/web/dist/assets/{SiteWindow-D6eS-2gM.js → SiteWindow-DM0LQPCO.js} +1 -1
- package/web/dist/assets/{index-Cbl3ATvp.js → index-BHTBJlpY.js} +32 -32
- package/web/dist/assets/{pending-edit-apply-DTjo2geW.js → pending-edit-apply-C0I13CbD.js} +1 -1
- package/web/dist/assets/{rotate-cw-LMT3x1Zj.js → rotate-cw-B1UQ8Hld.js} +1 -1
- package/web/dist/assets/{square-dashed-mouse-pointer-CBoAkfhi.js → square-dashed-mouse-pointer-8lrgqmfv.js} +1 -1
- package/web/dist/index.html +17 -17
- package/web/dist/assets/BrowserWindow-Dc7J7CYc.js +0 -11
|
@@ -1,164 +1,164 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* mcp/tools/get-computed-styles.js — get_computed_styles MCP tool
|
|
3
|
-
*
|
|
4
|
-
* 拿一组元素在 canvas.html 真实渲染后的 computed style(不是 inline 也不是
|
|
5
|
-
* CSS 文件里的 raw 值)—— agent 不用猜"用户看到的字号实际是多少 px",直接调。
|
|
6
|
-
*
|
|
7
|
-
* 典型场景:
|
|
8
|
-
* - "把 H1 字号缩小一点" → 先拿当前 H1 是多少 px 再决定改多少
|
|
9
|
-
* - "对比度够不够" → 拿 color 和 background-color 算对比度
|
|
10
|
-
*
|
|
11
|
-
* 默认拿一组常用样式属性(color/fontSize/fontFamily/...);agent 可以传
|
|
12
|
-
* props 数组只拿需要的(省 token)。
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import path from 'node:path';
|
|
16
|
-
import fs from 'node:fs/promises';
|
|
17
|
-
import { tool } from '@anthropic-ai/claude-agent-sdk';
|
|
18
|
-
import { z } from 'zod';
|
|
19
|
-
import { degradedNote } from './helpers/perception-page.js';
|
|
20
|
-
import { acquireArtifactPage, LIVE_PARAM_DESC } from './helpers/acquire-page.js';
|
|
21
|
-
import { resolveDeckSize, extractDeckAspect } from '../../../shared/deck.js';
|
|
22
|
-
import { resolveCanvasTarget, CANVAS_PATH_DESC, KIND_SITE, requireBrowsable,
|
|
23
|
-
} from '../../../lib/artifact-target.js';
|
|
24
|
-
|
|
25
|
-
const DEFAULT_PROPS = [
|
|
26
|
-
'color', 'backgroundColor',
|
|
27
|
-
'fontSize', 'fontFamily', 'fontWeight', 'lineHeight', 'letterSpacing',
|
|
28
|
-
'textAlign', 'textTransform',
|
|
29
|
-
'padding', 'margin',
|
|
30
|
-
'borderRadius', 'borderWidth', 'borderColor',
|
|
31
|
-
'display', 'width', 'height',
|
|
32
|
-
];
|
|
33
|
-
|
|
34
|
-
const MAX_RESULTS = 30;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @param {object} deps
|
|
38
|
-
* @param {string} deps.workspaceRoot
|
|
39
|
-
* @param {import('../../agent/context.js').AgentContext} [deps.ctx]
|
|
40
|
-
*/
|
|
41
|
-
export function makeGetComputedStylesTool({ workspaceRoot, projectId, sessionId, ctx: _ctx }) {
|
|
42
|
-
return tool(
|
|
43
|
-
'get_computed_styles',
|
|
44
|
-
`Get the actual rendered computed styles for elements matching a CSS
|
|
45
|
-
selector in your artifact page (site page / deck). Returns the post-CSS-cascade values (px / rgb()
|
|
46
|
-
form), not raw stylesheet declarations.
|
|
47
|
-
|
|
48
|
-
Use when:
|
|
49
|
-
- Before changing a style property, check the current value (don't guess)
|
|
50
|
-
- Verifying contrast ratios (need both color and backgroundColor)
|
|
51
|
-
- Debugging why a layout looks wrong
|
|
52
|
-
|
|
53
|
-
By default returns a generic style set; pass \`props\` to restrict to just
|
|
54
|
-
the properties you care about (saves tokens).
|
|
55
|
-
|
|
56
|
-
Returns up to 30 elements.`,
|
|
57
|
-
{
|
|
58
|
-
selector: z.string().min(1).describe('CSS selector to inspect. Plain CSS only — no playwright syntax (:has-text, >>, nth=), ASCII quotes not HTML entities (" breaks the parse)'),
|
|
59
|
-
props: z
|
|
60
|
-
.array(z.string())
|
|
61
|
-
.optional()
|
|
62
|
-
.describe('Optional: subset of CSS properties to read (camelCase, e.g. ["color","fontSize"]). Default: a generic set.'),
|
|
63
|
-
page: z
|
|
64
|
-
.number()
|
|
65
|
-
.int()
|
|
66
|
-
.min(1)
|
|
67
|
-
.optional()
|
|
68
|
-
.describe('Optional: scope to a specific page (prepends section[data-page="N"])'),
|
|
69
|
-
path: z.string().optional().describe(CANVAS_PATH_DESC),
|
|
70
|
-
live: z.boolean().optional().describe(LIVE_PARAM_DESC),
|
|
71
|
-
},
|
|
72
|
-
async ({ selector, props, page: pageIndex, path: relPath, live }) => {
|
|
73
|
-
if (!workspaceRoot) {
|
|
74
|
-
return {
|
|
75
|
-
content: [{ type: 'text', text: 'No workspace bound; cannot read computed styles.' }],
|
|
76
|
-
isError: true,
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
const target = await resolveCanvasTarget(workspaceRoot, relPath, sessionId);
|
|
80
|
-
if (!target.ok) return { content: [{ type: 'text', text: target.message }], isError: true };
|
|
81
|
-
const notBrowsable = requireBrowsable(target);
|
|
82
|
-
if (notBrowsable) return { content: [{ type: 'text', text: notBrowsable }], isError: true };
|
|
83
|
-
const canvasPath = target.absPath;
|
|
84
|
-
let html;
|
|
85
|
-
try {
|
|
86
|
-
html = await fs.readFile(canvasPath, 'utf8');
|
|
87
|
-
} catch {
|
|
88
|
-
return { content: [{ type: 'text', text: `${target.relPath} not found in workspace.` }], isError: true };
|
|
89
|
-
}
|
|
90
|
-
// 站点没有 deck 比例:extractDeckAspect 找不到 __nd-deck-wrap 会静默回落到
|
|
91
|
-
// 16:9,于是按 1920x1080 量出来的盒子被当成真实布局报给 agent。站点按桌面档
|
|
92
|
-
// 1440 量,跟 screenshot_canvas 的 desktop 档一致。
|
|
93
|
-
const dims = target.kind === KIND_SITE
|
|
94
|
-
? { width: 1440, height: 900 }
|
|
95
|
-
: resolveDeckSize(extractDeckAspect(html));
|
|
96
|
-
|
|
97
|
-
const finalSelector = pageIndex
|
|
98
|
-
? `section[data-page="${pageIndex}"] ${selector}`
|
|
99
|
-
: selector;
|
|
100
|
-
const finalProps = (Array.isArray(props) && props.length > 0) ? props : DEFAULT_PROPS;
|
|
101
|
-
|
|
102
|
-
let acq;
|
|
103
|
-
try {
|
|
104
|
-
// 页面从统一口拿(helpers/acquire-page.js):live:true = 会话里现在这一页;否则新开走 http
|
|
105
|
-
acq = await acquireArtifactPage({ projectId, workspaceRoot, target, live, viewport: { width: dims.width, height: dims.height } });
|
|
106
|
-
const page = acq.page;
|
|
107
|
-
const opened = acq;
|
|
108
|
-
|
|
109
|
-
const result = await page.evaluate(({ sel, p, max }) => {
|
|
110
|
-
const els = Array.from(document.querySelectorAll(sel));
|
|
111
|
-
const total = els.length;
|
|
112
|
-
const slice = els.slice(0, max);
|
|
113
|
-
const items = slice.map((el) => {
|
|
114
|
-
const cs = getComputedStyle(el);
|
|
115
|
-
const styles = {};
|
|
116
|
-
for (const prop of p) {
|
|
117
|
-
try {
|
|
118
|
-
styles[prop] = cs[prop];
|
|
119
|
-
} catch {
|
|
120
|
-
styles[prop] = null;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return {
|
|
124
|
-
tag: el.tagName.toLowerCase(),
|
|
125
|
-
dataAnchor: el.getAttribute('data-anchor') || null,
|
|
126
|
-
text: (el.textContent || '').trim().slice(0, 80),
|
|
127
|
-
computed: styles,
|
|
128
|
-
};
|
|
129
|
-
});
|
|
130
|
-
return { items, total };
|
|
131
|
-
}, { sel: finalSelector, p: finalProps, max: MAX_RESULTS });
|
|
132
|
-
|
|
133
|
-
if (result.total === 0) {
|
|
134
|
-
return {
|
|
135
|
-
content: [{
|
|
136
|
-
type: 'text',
|
|
137
|
-
text: `No elements match selector: ${finalSelector}`,
|
|
138
|
-
}],
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const truncatedNote = result.total > MAX_RESULTS
|
|
143
|
-
? ` (showing first ${MAX_RESULTS} of ${result.total})`
|
|
144
|
-
: '';
|
|
145
|
-
|
|
146
|
-
const degraded = degradedNote(opened); // 契约:note 非空必须写进返回文本
|
|
147
|
-
return {
|
|
148
|
-
content: [{
|
|
149
|
-
type: 'text',
|
|
150
|
-
text: `${degraded ? `${degraded}\n\n` : ''}${acq.liveNote ? `${acq.liveNote}\n\n` : ''}Computed styles for ${result.total} element(s)${truncatedNote}:`
|
|
151
|
-
+ `\n\n${JSON.stringify(result.items, null, 2)}`,
|
|
152
|
-
}],
|
|
153
|
-
};
|
|
154
|
-
} catch (err) {
|
|
155
|
-
return {
|
|
156
|
-
content: [{ type: 'text', text: `get_computed_styles failed: ${err?.message || String(err)}` }],
|
|
157
|
-
isError: true,
|
|
158
|
-
};
|
|
159
|
-
} finally {
|
|
160
|
-
await acq?.release?.(); // 一次性:关浏览器;live:松会话锁
|
|
161
|
-
}
|
|
162
|
-
},
|
|
163
|
-
);
|
|
164
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* mcp/tools/get-computed-styles.js — get_computed_styles MCP tool
|
|
3
|
+
*
|
|
4
|
+
* 拿一组元素在 canvas.html 真实渲染后的 computed style(不是 inline 也不是
|
|
5
|
+
* CSS 文件里的 raw 值)—— agent 不用猜"用户看到的字号实际是多少 px",直接调。
|
|
6
|
+
*
|
|
7
|
+
* 典型场景:
|
|
8
|
+
* - "把 H1 字号缩小一点" → 先拿当前 H1 是多少 px 再决定改多少
|
|
9
|
+
* - "对比度够不够" → 拿 color 和 background-color 算对比度
|
|
10
|
+
*
|
|
11
|
+
* 默认拿一组常用样式属性(color/fontSize/fontFamily/...);agent 可以传
|
|
12
|
+
* props 数组只拿需要的(省 token)。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import fs from 'node:fs/promises';
|
|
17
|
+
import { tool } from '@anthropic-ai/claude-agent-sdk';
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
import { degradedNote } from './helpers/perception-page.js';
|
|
20
|
+
import { acquireArtifactPage, LIVE_PARAM_DESC } from './helpers/acquire-page.js';
|
|
21
|
+
import { resolveDeckSize, extractDeckAspect } from '../../../shared/deck.js';
|
|
22
|
+
import { resolveCanvasTarget, CANVAS_PATH_DESC, KIND_SITE, requireBrowsable,
|
|
23
|
+
} from '../../../lib/artifact-target.js';
|
|
24
|
+
|
|
25
|
+
const DEFAULT_PROPS = [
|
|
26
|
+
'color', 'backgroundColor',
|
|
27
|
+
'fontSize', 'fontFamily', 'fontWeight', 'lineHeight', 'letterSpacing',
|
|
28
|
+
'textAlign', 'textTransform',
|
|
29
|
+
'padding', 'margin',
|
|
30
|
+
'borderRadius', 'borderWidth', 'borderColor',
|
|
31
|
+
'display', 'width', 'height',
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
const MAX_RESULTS = 30;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {object} deps
|
|
38
|
+
* @param {string} deps.workspaceRoot
|
|
39
|
+
* @param {import('../../agent/context.js').AgentContext} [deps.ctx]
|
|
40
|
+
*/
|
|
41
|
+
export function makeGetComputedStylesTool({ workspaceRoot, projectId, sessionId, ctx: _ctx }) {
|
|
42
|
+
return tool(
|
|
43
|
+
'get_computed_styles',
|
|
44
|
+
`Get the actual rendered computed styles for elements matching a CSS
|
|
45
|
+
selector in your artifact page (site page / deck). Returns the post-CSS-cascade values (px / rgb()
|
|
46
|
+
form), not raw stylesheet declarations.
|
|
47
|
+
|
|
48
|
+
Use when:
|
|
49
|
+
- Before changing a style property, check the current value (don't guess)
|
|
50
|
+
- Verifying contrast ratios (need both color and backgroundColor)
|
|
51
|
+
- Debugging why a layout looks wrong
|
|
52
|
+
|
|
53
|
+
By default returns a generic style set; pass \`props\` to restrict to just
|
|
54
|
+
the properties you care about (saves tokens).
|
|
55
|
+
|
|
56
|
+
Returns up to 30 elements.`,
|
|
57
|
+
{
|
|
58
|
+
selector: z.string().min(1).describe('CSS selector to inspect. Plain CSS only — no playwright syntax (:has-text, >>, nth=), ASCII quotes not HTML entities (" breaks the parse)'),
|
|
59
|
+
props: z
|
|
60
|
+
.array(z.string())
|
|
61
|
+
.optional()
|
|
62
|
+
.describe('Optional: subset of CSS properties to read (camelCase, e.g. ["color","fontSize"]). Default: a generic set.'),
|
|
63
|
+
page: z
|
|
64
|
+
.number()
|
|
65
|
+
.int()
|
|
66
|
+
.min(1)
|
|
67
|
+
.optional()
|
|
68
|
+
.describe('Optional: scope to a specific page (prepends section[data-page="N"])'),
|
|
69
|
+
path: z.string().optional().describe(CANVAS_PATH_DESC),
|
|
70
|
+
live: z.boolean().optional().describe(LIVE_PARAM_DESC),
|
|
71
|
+
},
|
|
72
|
+
async ({ selector, props, page: pageIndex, path: relPath, live }) => {
|
|
73
|
+
if (!workspaceRoot) {
|
|
74
|
+
return {
|
|
75
|
+
content: [{ type: 'text', text: 'No workspace bound; cannot read computed styles.' }],
|
|
76
|
+
isError: true,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const target = await resolveCanvasTarget(workspaceRoot, relPath, sessionId);
|
|
80
|
+
if (!target.ok) return { content: [{ type: 'text', text: target.message }], isError: true };
|
|
81
|
+
const notBrowsable = requireBrowsable(target);
|
|
82
|
+
if (notBrowsable) return { content: [{ type: 'text', text: notBrowsable }], isError: true };
|
|
83
|
+
const canvasPath = target.absPath;
|
|
84
|
+
let html;
|
|
85
|
+
try {
|
|
86
|
+
html = await fs.readFile(canvasPath, 'utf8');
|
|
87
|
+
} catch {
|
|
88
|
+
return { content: [{ type: 'text', text: `${target.relPath} not found in workspace.` }], isError: true };
|
|
89
|
+
}
|
|
90
|
+
// 站点没有 deck 比例:extractDeckAspect 找不到 __nd-deck-wrap 会静默回落到
|
|
91
|
+
// 16:9,于是按 1920x1080 量出来的盒子被当成真实布局报给 agent。站点按桌面档
|
|
92
|
+
// 1440 量,跟 screenshot_canvas 的 desktop 档一致。
|
|
93
|
+
const dims = target.kind === KIND_SITE
|
|
94
|
+
? { width: 1440, height: 900 }
|
|
95
|
+
: resolveDeckSize(extractDeckAspect(html));
|
|
96
|
+
|
|
97
|
+
const finalSelector = pageIndex
|
|
98
|
+
? `section[data-page="${pageIndex}"] ${selector}`
|
|
99
|
+
: selector;
|
|
100
|
+
const finalProps = (Array.isArray(props) && props.length > 0) ? props : DEFAULT_PROPS;
|
|
101
|
+
|
|
102
|
+
let acq;
|
|
103
|
+
try {
|
|
104
|
+
// 页面从统一口拿(helpers/acquire-page.js):live:true = 会话里现在这一页;否则新开走 http
|
|
105
|
+
acq = await acquireArtifactPage({ projectId, workspaceRoot, target, live, viewport: { width: dims.width, height: dims.height } });
|
|
106
|
+
const page = acq.page;
|
|
107
|
+
const opened = acq;
|
|
108
|
+
|
|
109
|
+
const result = await page.evaluate(({ sel, p, max }) => {
|
|
110
|
+
const els = Array.from(document.querySelectorAll(sel));
|
|
111
|
+
const total = els.length;
|
|
112
|
+
const slice = els.slice(0, max);
|
|
113
|
+
const items = slice.map((el) => {
|
|
114
|
+
const cs = getComputedStyle(el);
|
|
115
|
+
const styles = {};
|
|
116
|
+
for (const prop of p) {
|
|
117
|
+
try {
|
|
118
|
+
styles[prop] = cs[prop];
|
|
119
|
+
} catch {
|
|
120
|
+
styles[prop] = null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
tag: el.tagName.toLowerCase(),
|
|
125
|
+
dataAnchor: el.getAttribute('data-anchor') || null,
|
|
126
|
+
text: (el.textContent || '').trim().slice(0, 80),
|
|
127
|
+
computed: styles,
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
return { items, total };
|
|
131
|
+
}, { sel: finalSelector, p: finalProps, max: MAX_RESULTS });
|
|
132
|
+
|
|
133
|
+
if (result.total === 0) {
|
|
134
|
+
return {
|
|
135
|
+
content: [{
|
|
136
|
+
type: 'text',
|
|
137
|
+
text: `No elements match selector: ${finalSelector}`,
|
|
138
|
+
}],
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const truncatedNote = result.total > MAX_RESULTS
|
|
143
|
+
? ` (showing first ${MAX_RESULTS} of ${result.total})`
|
|
144
|
+
: '';
|
|
145
|
+
|
|
146
|
+
const degraded = degradedNote(opened); // 契约:note 非空必须写进返回文本
|
|
147
|
+
return {
|
|
148
|
+
content: [{
|
|
149
|
+
type: 'text',
|
|
150
|
+
text: `${degraded ? `${degraded}\n\n` : ''}${acq.liveNote ? `${acq.liveNote}\n\n` : ''}Computed styles for ${result.total} element(s)${truncatedNote}:`
|
|
151
|
+
+ `\n\n${JSON.stringify(result.items, null, 2)}`,
|
|
152
|
+
}],
|
|
153
|
+
};
|
|
154
|
+
} catch (err) {
|
|
155
|
+
return {
|
|
156
|
+
content: [{ type: 'text', text: `get_computed_styles failed: ${err?.message || String(err)}` }],
|
|
157
|
+
isError: true,
|
|
158
|
+
};
|
|
159
|
+
} finally {
|
|
160
|
+
await acq?.release?.(); // 一次性:关浏览器;live:松会话锁
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
);
|
|
164
|
+
}
|