@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
package/server/lib/kinds/site.js
CHANGED
|
@@ -1,161 +1,161 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* kinds/site.js — 站点形态(网站 / 个人站 / 落地页 / 博客)
|
|
3
|
-
*
|
|
4
|
-
* 站点跟 deck 的本质区别是**源和产物可以分开**:
|
|
5
|
-
* 手写站点 —— 源即产物,index.html 在任务根(产物根 = '')
|
|
6
|
-
* 构建型站点 —— 源在任务根(md / 模板 / 构建脚本随便放),构建产物落在
|
|
7
|
-
* 约定目录(dist/ out/ build/ _site/ public/,谁有 index.html 认谁),
|
|
8
|
-
* 或 `.nd-project.json` 里 `root` 字段显式指定(08-07 从
|
|
9
|
-
* `.nd-task.json` 改名,读的是 kinds/index.js readTaskMarker)。
|
|
10
|
-
* 预览、截图、list_pages、整站 zip、发布,全部指向产物根;任务根是 agent 的地盘。
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import path from 'node:path';
|
|
14
|
-
import fs from 'node:fs/promises';
|
|
15
|
-
import { walkTaskFiles, loadIgnore, DRAFTS_DIR, RESERVED_DIRS } from '../task-scan.js';
|
|
16
|
-
|
|
17
|
-
const ENTRY = 'index.html';
|
|
18
|
-
|
|
19
|
-
/** 构建产物的约定目录,按序探测(谁有 index.html 认谁)。任务根有 index.html 时不看这些 */
|
|
20
|
-
const OUTPUT_DIRS = ['dist', 'out', 'build', '_site', 'public'];
|
|
21
|
-
|
|
22
|
-
/** 站点页面扫描深度(产物根之下;子页 / posts/ / pages/ 够用) */
|
|
23
|
-
const PAGE_DEPTH = 4;
|
|
24
|
-
|
|
25
|
-
async function exists(p) {
|
|
26
|
-
try { await fs.access(p); return true; } catch { return false; }
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function normRoot(r) {
|
|
30
|
-
const s = String(r || '').replace(/\\/g, '/').replace(/^\.?\//, '').replace(/\/+$/, '');
|
|
31
|
-
return (!s || s === '.' || s.includes('..')) ? '' : s;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* 产物根(相对任务目录,'' = 任务根)。
|
|
36
|
-
* marker.root 是显式声明,最优先 —— 哪怕入口还没构建出来(agent 刚写完源、
|
|
37
|
-
* 还没跑 build 的窗口期),寻址也该指向将要出现的地方而不是源目录。
|
|
38
|
-
*/
|
|
39
|
-
async function artifactRoot(taskDir, marker) {
|
|
40
|
-
const declared = normRoot(marker?.root);
|
|
41
|
-
if (declared) return declared;
|
|
42
|
-
if (await exists(path.join(taskDir, ENTRY))) return '';
|
|
43
|
-
for (const d of OUTPUT_DIRS) {
|
|
44
|
-
if (await exists(path.join(taskDir, d, ENTRY))) return d;
|
|
45
|
-
}
|
|
46
|
-
return '';
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* 站点产物实例发现(2026-07-29 多产物平权):
|
|
51
|
-
* - 任务根有 index.html(或声明根 / 约定构建目录)→ 整个任务是**一个**站点
|
|
52
|
-
* 实例,子目录是它的页面(about/index.html 是 pretty-URL,这是 web 事实,
|
|
53
|
-
* 不能把每个带 index.html 的子目录都拆成独立站)
|
|
54
|
-
* - 任务根没有站点证据 → 带 index.html 的**一级子目录**各算一个站点实例
|
|
55
|
-
* (两个平行版本 v1/ v2/ 就这么放)
|
|
56
|
-
* - `_drafts/<名>.html` → 各自一个**单页**实例(single:true),平等产物,
|
|
57
|
-
* 不再叫"试作"
|
|
58
|
-
*/
|
|
59
|
-
async function discoverInstances(taskDir, marker) {
|
|
60
|
-
const out = [];
|
|
61
|
-
const declared = normRoot(marker?.root);
|
|
62
|
-
const root = await artifactRoot(taskDir, marker);
|
|
63
|
-
// 根站成立条件:算出的产物根下有 index.html;或 marker 显式声明了 root
|
|
64
|
-
// (声明即意图 —— agent 刚写完源还没 build 的窗口期,站已经"在了")
|
|
65
|
-
const rootSite = declared ? true : await exists(path.join(taskDir, root || '.', ENTRY));
|
|
66
|
-
|
|
67
|
-
if (rootSite) {
|
|
68
|
-
out.push({ srcRoot: '', root, single: false });
|
|
69
|
-
} else {
|
|
70
|
-
// 无根站:一级子目录各自为站
|
|
71
|
-
let entries = [];
|
|
72
|
-
try { entries = await fs.readdir(taskDir, { withFileTypes: true }); } catch { /* */ }
|
|
73
|
-
const ignore = await loadIgnore(taskDir);
|
|
74
|
-
for (const e of entries) {
|
|
75
|
-
if (!e.isDirectory() || e.name.startsWith('.') || e.name === DRAFTS_DIR) continue;
|
|
76
|
-
if (RESERVED_DIRS.has(e.name)) continue;
|
|
77
|
-
if (ignore(e.name, true)) continue;
|
|
78
|
-
if (await exists(path.join(taskDir, e.name, ENTRY))) {
|
|
79
|
-
out.push({ srcRoot: e.name, root: e.name, single: false });
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// 单页实例(原"试作",平权后就是普通的单文件页面产物)
|
|
85
|
-
try {
|
|
86
|
-
const d = await fs.readdir(path.join(taskDir, DRAFTS_DIR), { withFileTypes: true });
|
|
87
|
-
for (const e of d) {
|
|
88
|
-
if (e.isFile() && /\.html?$/i.test(e.name) && !e.name.startsWith('.')) {
|
|
89
|
-
out.push({ srcRoot: '', root: '', single: true, file: `${DRAFTS_DIR}/${e.name}` });
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
} catch { /* 没有 _drafts/:正常 */ }
|
|
93
|
-
return out;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export default {
|
|
97
|
-
id: 'site',
|
|
98
|
-
capabilities: ['browsable'], // 入口是 html,能塞进 iframe / playwright
|
|
99
|
-
entryFile: ENTRY,
|
|
100
|
-
view: 'site',
|
|
101
|
-
injectFit: false, // 整屏翻页脚本会把长页改造成翻页器;stripFitScripts 的启发式也会误删站点动画
|
|
102
|
-
exportFormats: ['site', 'html', 'handoff'],
|
|
103
|
-
referenceDoc: { file: 'site-reference', title: '站点技术参考' },
|
|
104
|
-
// 目录型实例判据(卡即文件夹):单页(_drafts)是一个文件,其余站都是一棵树。
|
|
105
|
-
// 「这张卡是不是文件夹」全仓只问 isDirArtifact(kinds/index.js),别在调用点自判
|
|
106
|
-
directory: (a) => !a.single,
|
|
107
|
-
discoverInstances,
|
|
108
|
-
|
|
109
|
-
artifactRoot,
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* 单个站点实例的完整条目。pages 相对**实例产物根**(预览窗直接拼 URL)。
|
|
113
|
-
* 单页实例(原 _drafts)pages 就是它自己那一页。
|
|
114
|
-
*/
|
|
115
|
-
async instanceManifest(taskDir, marker, inst) {
|
|
116
|
-
if (inst.single) {
|
|
117
|
-
return {
|
|
118
|
-
kind: 'site',
|
|
119
|
-
root: '',
|
|
120
|
-
srcRoot: '',
|
|
121
|
-
entry: inst.file,
|
|
122
|
-
entryRel: inst.file,
|
|
123
|
-
file: inst.file,
|
|
124
|
-
pages: [inst.file],
|
|
125
|
-
single: true,
|
|
126
|
-
title: inst.file.replace(new RegExp(`^${DRAFTS_DIR}/`), '').replace(/\.html?$/i, ''),
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
const root = inst.root || '';
|
|
130
|
-
const rootAbs = root ? path.join(taskDir, root) : taskDir;
|
|
131
|
-
const ignore = await loadIgnore(taskDir);
|
|
132
|
-
const files = await walkTaskFiles(rootAbs, { maxDepth: PAGE_DEPTH, ignore, ignoreBase: taskDir });
|
|
133
|
-
const pages = files
|
|
134
|
-
.filter(f => /\.html?$/i.test(f.name))
|
|
135
|
-
// 根站(root='')时 canvas.html 是 deck 的保留名,不算站点页面。
|
|
136
|
-
// root 非空(dist/ 或子目录站)时目录即边界,不用滤
|
|
137
|
-
.filter(f => root || (f.rel !== 'canvas.html'))
|
|
138
|
-
.map(f => f.rel)
|
|
139
|
-
.sort((a, b) => (a === ENTRY ? -1 : b === ENTRY ? 1 : a.localeCompare(b)));
|
|
140
|
-
return {
|
|
141
|
-
kind: 'site',
|
|
142
|
-
root,
|
|
143
|
-
srcRoot: inst.srcRoot || '',
|
|
144
|
-
entry: ENTRY,
|
|
145
|
-
entryRel: root ? `${root}/${ENTRY}` : ENTRY,
|
|
146
|
-
file: null,
|
|
147
|
-
pages,
|
|
148
|
-
single: false,
|
|
149
|
-
title: inst.srcRoot || null, // 根站 null(前端用任务名),子目录站用目录名
|
|
150
|
-
};
|
|
151
|
-
},
|
|
152
|
-
|
|
153
|
-
async describe(taskDir, artifact) {
|
|
154
|
-
if (artifact.single) return `单页 ${artifact.entryRel}`;
|
|
155
|
-
const parts = [`站点${artifact.srcRoot ? ` ${artifact.srcRoot}/` : ''} · ${artifact.pages.length} 个页面:${artifact.pages.slice(0, 6).join(' / ')}${artifact.pages.length > 6 ? ' …' : ''}`];
|
|
156
|
-
if (artifact.root && artifact.root !== artifact.srcRoot) {
|
|
157
|
-
parts.push(`产物根 ${artifact.root}/(预览与导出看这里,改完源记得重新构建)`);
|
|
158
|
-
}
|
|
159
|
-
return parts.join(' · ');
|
|
160
|
-
},
|
|
161
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* kinds/site.js — 站点形态(网站 / 个人站 / 落地页 / 博客)
|
|
3
|
+
*
|
|
4
|
+
* 站点跟 deck 的本质区别是**源和产物可以分开**:
|
|
5
|
+
* 手写站点 —— 源即产物,index.html 在任务根(产物根 = '')
|
|
6
|
+
* 构建型站点 —— 源在任务根(md / 模板 / 构建脚本随便放),构建产物落在
|
|
7
|
+
* 约定目录(dist/ out/ build/ _site/ public/,谁有 index.html 认谁),
|
|
8
|
+
* 或 `.nd-project.json` 里 `root` 字段显式指定(08-07 从
|
|
9
|
+
* `.nd-task.json` 改名,读的是 kinds/index.js readTaskMarker)。
|
|
10
|
+
* 预览、截图、list_pages、整站 zip、发布,全部指向产物根;任务根是 agent 的地盘。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import fs from 'node:fs/promises';
|
|
15
|
+
import { walkTaskFiles, loadIgnore, DRAFTS_DIR, RESERVED_DIRS } from '../task-scan.js';
|
|
16
|
+
|
|
17
|
+
const ENTRY = 'index.html';
|
|
18
|
+
|
|
19
|
+
/** 构建产物的约定目录,按序探测(谁有 index.html 认谁)。任务根有 index.html 时不看这些 */
|
|
20
|
+
const OUTPUT_DIRS = ['dist', 'out', 'build', '_site', 'public'];
|
|
21
|
+
|
|
22
|
+
/** 站点页面扫描深度(产物根之下;子页 / posts/ / pages/ 够用) */
|
|
23
|
+
const PAGE_DEPTH = 4;
|
|
24
|
+
|
|
25
|
+
async function exists(p) {
|
|
26
|
+
try { await fs.access(p); return true; } catch { return false; }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function normRoot(r) {
|
|
30
|
+
const s = String(r || '').replace(/\\/g, '/').replace(/^\.?\//, '').replace(/\/+$/, '');
|
|
31
|
+
return (!s || s === '.' || s.includes('..')) ? '' : s;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 产物根(相对任务目录,'' = 任务根)。
|
|
36
|
+
* marker.root 是显式声明,最优先 —— 哪怕入口还没构建出来(agent 刚写完源、
|
|
37
|
+
* 还没跑 build 的窗口期),寻址也该指向将要出现的地方而不是源目录。
|
|
38
|
+
*/
|
|
39
|
+
async function artifactRoot(taskDir, marker) {
|
|
40
|
+
const declared = normRoot(marker?.root);
|
|
41
|
+
if (declared) return declared;
|
|
42
|
+
if (await exists(path.join(taskDir, ENTRY))) return '';
|
|
43
|
+
for (const d of OUTPUT_DIRS) {
|
|
44
|
+
if (await exists(path.join(taskDir, d, ENTRY))) return d;
|
|
45
|
+
}
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 站点产物实例发现(2026-07-29 多产物平权):
|
|
51
|
+
* - 任务根有 index.html(或声明根 / 约定构建目录)→ 整个任务是**一个**站点
|
|
52
|
+
* 实例,子目录是它的页面(about/index.html 是 pretty-URL,这是 web 事实,
|
|
53
|
+
* 不能把每个带 index.html 的子目录都拆成独立站)
|
|
54
|
+
* - 任务根没有站点证据 → 带 index.html 的**一级子目录**各算一个站点实例
|
|
55
|
+
* (两个平行版本 v1/ v2/ 就这么放)
|
|
56
|
+
* - `_drafts/<名>.html` → 各自一个**单页**实例(single:true),平等产物,
|
|
57
|
+
* 不再叫"试作"
|
|
58
|
+
*/
|
|
59
|
+
async function discoverInstances(taskDir, marker) {
|
|
60
|
+
const out = [];
|
|
61
|
+
const declared = normRoot(marker?.root);
|
|
62
|
+
const root = await artifactRoot(taskDir, marker);
|
|
63
|
+
// 根站成立条件:算出的产物根下有 index.html;或 marker 显式声明了 root
|
|
64
|
+
// (声明即意图 —— agent 刚写完源还没 build 的窗口期,站已经"在了")
|
|
65
|
+
const rootSite = declared ? true : await exists(path.join(taskDir, root || '.', ENTRY));
|
|
66
|
+
|
|
67
|
+
if (rootSite) {
|
|
68
|
+
out.push({ srcRoot: '', root, single: false });
|
|
69
|
+
} else {
|
|
70
|
+
// 无根站:一级子目录各自为站
|
|
71
|
+
let entries = [];
|
|
72
|
+
try { entries = await fs.readdir(taskDir, { withFileTypes: true }); } catch { /* */ }
|
|
73
|
+
const ignore = await loadIgnore(taskDir);
|
|
74
|
+
for (const e of entries) {
|
|
75
|
+
if (!e.isDirectory() || e.name.startsWith('.') || e.name === DRAFTS_DIR) continue;
|
|
76
|
+
if (RESERVED_DIRS.has(e.name)) continue;
|
|
77
|
+
if (ignore(e.name, true)) continue;
|
|
78
|
+
if (await exists(path.join(taskDir, e.name, ENTRY))) {
|
|
79
|
+
out.push({ srcRoot: e.name, root: e.name, single: false });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 单页实例(原"试作",平权后就是普通的单文件页面产物)
|
|
85
|
+
try {
|
|
86
|
+
const d = await fs.readdir(path.join(taskDir, DRAFTS_DIR), { withFileTypes: true });
|
|
87
|
+
for (const e of d) {
|
|
88
|
+
if (e.isFile() && /\.html?$/i.test(e.name) && !e.name.startsWith('.')) {
|
|
89
|
+
out.push({ srcRoot: '', root: '', single: true, file: `${DRAFTS_DIR}/${e.name}` });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} catch { /* 没有 _drafts/:正常 */ }
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default {
|
|
97
|
+
id: 'site',
|
|
98
|
+
capabilities: ['browsable'], // 入口是 html,能塞进 iframe / playwright
|
|
99
|
+
entryFile: ENTRY,
|
|
100
|
+
view: 'site',
|
|
101
|
+
injectFit: false, // 整屏翻页脚本会把长页改造成翻页器;stripFitScripts 的启发式也会误删站点动画
|
|
102
|
+
exportFormats: ['site', 'html', 'handoff'],
|
|
103
|
+
referenceDoc: { file: 'site-reference', title: '站点技术参考' },
|
|
104
|
+
// 目录型实例判据(卡即文件夹):单页(_drafts)是一个文件,其余站都是一棵树。
|
|
105
|
+
// 「这张卡是不是文件夹」全仓只问 isDirArtifact(kinds/index.js),别在调用点自判
|
|
106
|
+
directory: (a) => !a.single,
|
|
107
|
+
discoverInstances,
|
|
108
|
+
|
|
109
|
+
artifactRoot,
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* 单个站点实例的完整条目。pages 相对**实例产物根**(预览窗直接拼 URL)。
|
|
113
|
+
* 单页实例(原 _drafts)pages 就是它自己那一页。
|
|
114
|
+
*/
|
|
115
|
+
async instanceManifest(taskDir, marker, inst) {
|
|
116
|
+
if (inst.single) {
|
|
117
|
+
return {
|
|
118
|
+
kind: 'site',
|
|
119
|
+
root: '',
|
|
120
|
+
srcRoot: '',
|
|
121
|
+
entry: inst.file,
|
|
122
|
+
entryRel: inst.file,
|
|
123
|
+
file: inst.file,
|
|
124
|
+
pages: [inst.file],
|
|
125
|
+
single: true,
|
|
126
|
+
title: inst.file.replace(new RegExp(`^${DRAFTS_DIR}/`), '').replace(/\.html?$/i, ''),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const root = inst.root || '';
|
|
130
|
+
const rootAbs = root ? path.join(taskDir, root) : taskDir;
|
|
131
|
+
const ignore = await loadIgnore(taskDir);
|
|
132
|
+
const files = await walkTaskFiles(rootAbs, { maxDepth: PAGE_DEPTH, ignore, ignoreBase: taskDir });
|
|
133
|
+
const pages = files
|
|
134
|
+
.filter(f => /\.html?$/i.test(f.name))
|
|
135
|
+
// 根站(root='')时 canvas.html 是 deck 的保留名,不算站点页面。
|
|
136
|
+
// root 非空(dist/ 或子目录站)时目录即边界,不用滤
|
|
137
|
+
.filter(f => root || (f.rel !== 'canvas.html'))
|
|
138
|
+
.map(f => f.rel)
|
|
139
|
+
.sort((a, b) => (a === ENTRY ? -1 : b === ENTRY ? 1 : a.localeCompare(b)));
|
|
140
|
+
return {
|
|
141
|
+
kind: 'site',
|
|
142
|
+
root,
|
|
143
|
+
srcRoot: inst.srcRoot || '',
|
|
144
|
+
entry: ENTRY,
|
|
145
|
+
entryRel: root ? `${root}/${ENTRY}` : ENTRY,
|
|
146
|
+
file: null,
|
|
147
|
+
pages,
|
|
148
|
+
single: false,
|
|
149
|
+
title: inst.srcRoot || null, // 根站 null(前端用任务名),子目录站用目录名
|
|
150
|
+
};
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
async describe(taskDir, artifact) {
|
|
154
|
+
if (artifact.single) return `单页 ${artifact.entryRel}`;
|
|
155
|
+
const parts = [`站点${artifact.srcRoot ? ` ${artifact.srcRoot}/` : ''} · ${artifact.pages.length} 个页面:${artifact.pages.slice(0, 6).join(' / ')}${artifact.pages.length > 6 ? ' …' : ''}`];
|
|
156
|
+
if (artifact.root && artifact.root !== artifact.srcRoot) {
|
|
157
|
+
parts.push(`产物根 ${artifact.root}/(预览与导出看这里,改完源记得重新构建)`);
|
|
158
|
+
}
|
|
159
|
+
return parts.join(' · ');
|
|
160
|
+
},
|
|
161
|
+
};
|