@xiaobuyu/nodesign 0.0.7 → 0.0.8
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 +266 -79
- package/README.zh-CN.md +254 -67
- package/package.json +2 -2
- package/server/api/admin.js +7 -0
- package/server/api/assets/docx-page.js +5 -4
- package/server/api/assets/helpers.js +8 -0
- package/server/api/assets.js +21 -33
- package/server/api/board.js +29 -1
- package/server/api/browse.js +3 -2
- package/server/api/chatai.js +9 -8
- package/server/api/exports/cards.js +7 -6
- package/server/api/exports/docx-pdf.js +4 -1
- package/server/api/local.js +10 -8
- package/server/api/projects.js +19 -3
- package/server/api/publish.js +3 -2
- package/server/api/sessions-rewind.js +220 -0
- package/server/api/sessions.js +58 -194
- package/server/api/stage.js +35 -0
- package/server/api/turn-model-switch.js +109 -0
- package/server/api/turn.js +52 -88
- package/server/auth/middleware.js +29 -6
- package/server/auth/tier.js +27 -7
- package/server/auth/users-store.js +14 -1
- package/server/engine/agent/actor-trail.js +110 -0
- package/server/engine/agent/agent-shared.js +22 -52
- package/server/engine/agent/beat-state.js +61 -0
- package/server/engine/agent/cast.js +322 -0
- package/server/engine/agent/events.js +9 -2
- package/server/engine/agent/hooks/beat-gate.js +73 -0
- package/server/engine/agent/hooks/lifecycle.js +6 -2
- package/server/engine/agent/hooks/post-trim.js +1 -0
- package/server/engine/agent/hooks/pre-board-dirty.js +29 -0
- package/server/engine/agent/hooks/pre-defaults.js +206 -2
- package/server/engine/agent/hooks/pre-injectors.js +19 -2
- package/server/engine/agent/hooks/pre-peer-guard.js +88 -0
- package/server/engine/agent/hooks/pre-unknown-params.js +82 -0
- package/server/engine/agent/hooks/pre-workspace-scope-guard.js +33 -0
- package/server/engine/agent/hooks/resident-role-lifecycle.js +100 -0
- package/server/engine/agent/hooks/slot-alias.js +30 -0
- package/server/engine/agent/hooks/user-prompt-submit.js +197 -7
- package/server/engine/agent/hooks.js +76 -6
- package/server/engine/agent/isolation.js +25 -2
- package/server/engine/agent/memory-config.js +27 -1
- package/server/engine/agent/model-context.js +102 -18
- package/server/engine/agent/model-table.js +361 -58
- package/server/engine/agent/prompts/nodesign-prelude.md +566 -59
- package/server/engine/agent/prompts/tools/direct-edit-protocol.md +1 -1
- package/server/engine/agent/prompts/tools/generate-image-cookbook.md +2 -2
- package/server/engine/agent/prompts/tools/paint-still-cookbook.md +1 -1
- package/server/engine/agent/prompts/tools/site-reference.md +2 -1
- package/server/engine/agent/role-card.js +175 -0
- package/server/engine/agent/rp-mode.js +17 -0
- package/server/engine/agent/session-loop.js +26 -14
- package/server/engine/agent/stage-status.js +73 -0
- package/server/engine/agent/subagent-flight.js +73 -0
- package/server/engine/agent/system-prompts.js +41 -0
- package/server/engine/agent/tool-input-stream.js +165 -0
- package/server/engine/mcp/actor.js +59 -0
- package/server/engine/mcp/capability-gate.js +21 -2
- package/server/engine/mcp/index.js +116 -24
- package/server/engine/mcp/mode-profile.js +139 -0
- package/server/engine/mcp/server-name.js +19 -0
- package/server/engine/mcp/tools/artifact-session.js +2 -1
- package/server/engine/mcp/tools/browse-find-batch.js +76 -14
- package/server/engine/mcp/tools/browse.js +1 -1
- package/server/engine/mcp/tools/cast-role.js +167 -0
- package/server/engine/mcp/tools/draw-trend.js +104 -0
- package/server/engine/mcp/tools/edit-board-schema.js +56 -0
- package/server/engine/mcp/tools/edit-board.js +569 -0
- package/server/engine/mcp/tools/look-at-board.js +4 -3
- package/server/engine/mcp/tools/open-sheet.js +242 -0
- package/server/engine/mcp/tools/organize-board.js +19 -5
- package/server/engine/mcp/tools/paint-still.js +1 -1
- package/server/engine/mcp/tools/pin-to-board.js +78 -3
- package/server/engine/mcp/tools/read-board.js +109 -16
- package/server/engine/mcp/tools/read-tavern-json.js +87 -19
- package/server/engine/mcp/tools/report-issue.js +1 -0
- package/server/engine/mcp/tools/role-memory.js +83 -0
- package/server/engine/mcp/tools/roll-dice.js +82 -0
- package/server/engine/mcp/tools/roll-film.js +1 -1
- package/server/engine/mcp/tools/screenshot.js +2 -2
- package/server/engine/mcp/tools/set-vars.js +150 -0
- package/server/engine/mcp/tools/sheet-replan.js +46 -0
- package/server/engine/mcp/tools/web-search.js +1 -1
- package/server/engine/mcp/tools/write-on-board-flow.js +108 -0
- package/server/engine/mcp/tools/write-on-board-place.js +211 -0
- package/server/engine/mcp/tools/write-on-board-role-anchor.js +48 -0
- package/server/engine/mcp/tools/write-on-board-schema.js +125 -0
- package/server/engine/mcp/tools/write-on-board.js +567 -107
- package/server/engine/plugins/nodesign/skills/blackboard-rp/SKILL.md +215 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +11 -3
- package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +34 -4
- package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +20 -4
- package/server/engine/plugins/nodesign/skills/story-craft/SKILL.md +94 -0
- package/server/engine/plugins/nodesign/skills/story-import/SKILL.md +112 -0
- package/server/engine/plugins/nodesign/skills/story-intimacy/SKILL.md +72 -0
- package/server/engine/plugins/nodesign/skills/story-voice/SKILL.md +66 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//345/206/267/347/241/254.md +33 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//346/203/205/346/204/237/347/233/264/347/273/231.md +42 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//346/204/217/350/257/206/346/265/201.md +42 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//346/255/246/344/276/240/347/231/275/350/257/235.md +34 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//347/224/237/346/264/273/346/265/201.md +35 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//347/231/275/346/217/217.md +37 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//350/210/236/345/217/260/345/211/247.md +32 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//351/230/266/346/256/265/345/210/207/346/215/242.md +42 -0
- package/server/engine/runs/board-seater.js +272 -0
- package/server/engine/runs/board-tasklist.js +90 -48
- package/server/engine/runs/turn-relay.js +24 -2
- package/server/engine/runs/user-chalk-echo.js +44 -0
- package/server/index.js +2 -0
- package/server/lib/board-anchor.js +49 -0
- package/server/lib/board-dirty.js +93 -0
- package/server/lib/board-excerpt.js +80 -0
- package/server/lib/board-follow.js +111 -0
- package/server/lib/board-groups.js +1 -5
- package/server/lib/board-hero.js +3 -1
- package/server/lib/board-kind-sizes.js +36 -0
- package/server/lib/board-lanes.js +58 -0
- package/server/lib/board-obstacles.js +80 -0
- package/server/lib/board-relations.js +5 -1
- package/server/lib/board-sheets.js +467 -0
- package/server/lib/board-shelf.js +86 -0
- package/server/lib/board-transform.js +62 -0
- package/server/lib/canvas-id.js +18 -1
- package/server/lib/chalk-flow.js +88 -0
- package/server/lib/chalk-rewrite.js +63 -0
- package/server/lib/chalk-size-pref.js +48 -0
- package/server/lib/chalk.js +42 -3
- package/server/lib/ingress/forward-openai-chat.js +16 -4
- package/server/lib/ingress/openai-chat.js +23 -12
- package/server/lib/ingress/session-routes.js +3 -3
- package/server/lib/ingress/stream-watchdog.js +53 -0
- package/server/lib/ingress/upstream-billing.js +15 -0
- package/server/lib/ingress/upstream-error-hints.js +36 -0
- package/server/lib/json-preview.js +58 -0
- package/server/lib/model-ingress.js +26 -1
- package/server/lib/moderation.js +10 -5
- package/server/lib/quota.js +26 -6
- package/server/lib/rect.js +50 -0
- package/server/lib/screen.js +36 -0
- package/server/lib/sheet-state.js +19 -0
- package/server/lib/sketch-array.js +118 -0
- package/server/lib/sketch-hatch.js +99 -0
- package/server/lib/sketch-layout.js +269 -69
- package/server/lib/sketch-shapes.js +152 -0
- package/server/lib/sketch-stencils.js +170 -0
- package/server/lib/state-table.js +237 -0
- package/server/lib/state-trend.js +94 -0
- package/server/lib/state-triggers.js +199 -0
- package/server/lib/tavern-json.js +48 -0
- package/server/lib/user-uploads.js +88 -0
- package/server/projects/board-limits.js +5 -3
- package/server/projects/board-sanitize.js +220 -10
- package/server/projects/board-store.js +95 -6
- package/server/projects/session-jsonl.js +110 -0
- package/server/projects/store.js +56 -3
- package/server/projects/viewpoint-store.js +35 -3
- package/server/projects/workspace-slots.js +30 -0
- package/server/projects/workspace-templates.js +29 -0
- package/server/projects/workspace.js +16 -17
- package/server/runtime/capabilities.js +6 -2
- package/server/shared/locales.js +70 -0
- package/server/shared/messages-en.js +69 -0
- package/server/shared/messages.js +65 -0
- package/server/ws/broker.js +3 -0
- package/server/ws/index.js +43 -3
- package/web/README.md +1 -1
- package/web/dist/assets/{BrowserWindow-BoOBaIyz.js → BrowserWindow-CcaGRWz5.js} +1 -1
- package/web/dist/assets/DeckWindow-BIr2KGFT.js +6 -0
- package/web/dist/assets/{DocxWindow-B_u6aX9o.js → DocxWindow-B9KZmU5z.js} +2 -2
- package/web/dist/assets/{MermaidBlock-BO-46Du5.js → MermaidBlock-DrfRnz8E.js} +43 -43
- package/web/dist/assets/{SiteWindow-ConPmzdM.js → SiteWindow-CJOjCF7d.js} +6 -6
- package/web/dist/assets/{abnfDiagram-VCTEODGH-FCO9yf-n.js → abnfDiagram-VCTEODGH-CUZbQGIg.js} +1 -1
- package/web/dist/assets/{arc-BLcEOSRX.js → arc-C1-HSUny.js} +1 -1
- package/web/dist/assets/{architectureDiagram-5GKGNRK7-kxiX4TeN.js → architectureDiagram-5GKGNRK7-B5nwvu60.js} +2 -2
- package/web/dist/assets/{blockDiagram-NRAW4CY4-CptFksix.js → blockDiagram-NRAW4CY4-uPPuFLwR.js} +1 -1
- package/web/dist/assets/{c4Diagram-UCG6FXSJ-Dxmxj34F.js → c4Diagram-UCG6FXSJ-C0mKBFlz.js} +1 -1
- package/web/dist/assets/channel-BxpWNOGU.js +1 -0
- package/web/dist/assets/{chunk-2Q5K7J3B-D9inx5oo.js → chunk-2Q5K7J3B-Cs0rlZ4q.js} +1 -1
- package/web/dist/assets/{chunk-5VM5RSS4-DydfHlEc.js → chunk-5VM5RSS4-DtR63oYb.js} +1 -1
- package/web/dist/assets/{chunk-F27PBJKO-BO7GxhSv.js → chunk-F27PBJKO-B3MRdezJ.js} +1 -1
- package/web/dist/assets/{chunk-G27WJ6UU-BzP6YiQ_.js → chunk-G27WJ6UU-BtwntwpS.js} +1 -1
- package/web/dist/assets/{chunk-JWPE2WC7-CHcgcd0s.js → chunk-JWPE2WC7-BHsTe5PD.js} +1 -1
- package/web/dist/assets/{chunk-LCL6LL3I-D2Wg5JtX.js → chunk-LCL6LL3I-knY15Qdm.js} +1 -1
- package/web/dist/assets/{chunk-POPQ4Y6H-B2fCRfpM.js → chunk-POPQ4Y6H-C51tgK_I.js} +1 -1
- package/web/dist/assets/{chunk-SVP7TREG-JSLcsRlZ.js → chunk-SVP7TREG-CnwRZmF_.js} +1 -1
- package/web/dist/assets/{chunk-XXDRQBXY-BCjvaeHm.js → chunk-XXDRQBXY-BtXzVF-_.js} +1 -1
- package/web/dist/assets/classDiagram-DTDB5LWJ-DUEKmDfo.js +1 -0
- package/web/dist/assets/classDiagram-v2-JRS7N3AN-DUEKmDfo.js +1 -0
- package/web/dist/assets/{cose-bilkent-JH36ORCC-0uKv6oJ7.js → cose-bilkent-JH36ORCC-CzWlGP6i.js} +1 -1
- package/web/dist/assets/{cynefin-OW5HDTMX-Bvt-gEmA.js → cynefin-OW5HDTMX-vK64EGSv.js} +1 -1
- package/web/dist/assets/{cynefinDiagram-5FMLGOSQ-C9Ht02qC.js → cynefinDiagram-5FMLGOSQ-Bs3g1Gvh.js} +1 -1
- package/web/dist/assets/{dagre-3AP2YEHR-BfxZny4s.js → dagre-3AP2YEHR-BhgkYe8V.js} +1 -1
- package/web/dist/assets/{diagram-S7CK7UJ4-CrE0ZETE.js → diagram-S7CK7UJ4-C_6mvD2V.js} +1 -1
- package/web/dist/assets/{diagram-UQ7AKVKN-BO8VQ4Fx.js → diagram-UQ7AKVKN-qW7tssUW.js} +1 -1
- package/web/dist/assets/{diagram-VSXAHHWV-CHUZnR9F.js → diagram-VSXAHHWV-txJPHpvG.js} +1 -1
- package/web/dist/assets/{diagram-VX7I27RA-DYQAz1Vu.js → diagram-VX7I27RA-CQTMsCnL.js} +1 -1
- package/web/dist/assets/{diagram-Z3DM3KII-BDjivgLr.js → diagram-Z3DM3KII--cb1hoE6.js} +1 -1
- package/web/dist/assets/{ebnfDiagram-PWID7BFC-Bpe_QVbH.js → ebnfDiagram-PWID7BFC-Da6lDU77.js} +1 -1
- package/web/dist/assets/{erDiagram-SSCWMZ5O-BxhwuAu1.js → erDiagram-SSCWMZ5O-DGCMrsQn.js} +1 -1
- package/web/dist/assets/{flowDiagram-A5DVABFB-CCw2H08-.js → flowDiagram-A5DVABFB-GdOL_qno.js} +1 -1
- package/web/dist/assets/{ganttDiagram-EL5Y4UJY-DtTc7MyI.js → ganttDiagram-EL5Y4UJY-Cx1F_jBK.js} +4 -4
- package/web/dist/assets/{gitGraphDiagram-WWUBYQGX-BvORxMd2.js → gitGraphDiagram-WWUBYQGX-B5_6JhR3.js} +1 -1
- package/web/dist/assets/hand-CuvZZQGW.webp +0 -0
- package/web/dist/assets/index-BYKfMnmv.js +2759 -0
- package/web/dist/assets/index-CW7VsrVg.css +1 -0
- package/web/dist/assets/{infoDiagram-RXCK75RN-DMcrkypD.js → infoDiagram-RXCK75RN-CjvKpSPA.js} +1 -1
- package/web/dist/assets/{ishikawaDiagram-5VMMS53U-CZkTIbe-.js → ishikawaDiagram-5VMMS53U-CxLoAPhx.js} +1 -1
- package/web/dist/assets/{journeyDiagram-EYS64GPL-DR0XazdE.js → journeyDiagram-EYS64GPL-BfvS1upJ.js} +1 -1
- package/web/dist/assets/{kanban-definition-3QL26DDD-DN8VbwVu.js → kanban-definition-3QL26DDD-DKPPu0w4.js} +1 -1
- package/web/dist/assets/{layout-9_paCbyt.js → layout-Dhgck-Zo.js} +1 -1
- package/web/dist/assets/{linear-g6Ct012e.js → linear-DMSQ-aUw.js} +1 -1
- package/web/dist/assets/lxgw-nd-app-bold-D1pKA1bR.woff2 +0 -0
- package/web/dist/assets/lxgw-nd-app-regular-BDe3d26v.woff2 +0 -0
- package/web/dist/assets/lxgw-nd-bold-Bufvlg03.woff2 +0 -0
- package/web/dist/assets/lxgw-nd-regular-DQwoI4OP.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-100-DLPe7tEt.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-101-d3VsZKWT.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-102-ClzJuLq6.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-103-DuDnQ6K-.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-104-kPGW3Eem.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-105-BzD54h-0.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-106-BQ7fIzlx.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-107-D8tDfFHI.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-108-tGhkpwep.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-109-7SMVSrly.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-110-_uDKrFed.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-111-Bio825RE.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-112-D2ee0i-C.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-113-JPFVDrga.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-114-BcfxYA4w.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-115-B1HeeOsd.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-116-hY12tXpq.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-117-BvuZwmre.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-118-tzvs_H7j.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-119-J2DWuYDN.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-21-pDubCJV5.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-22-ChAPMpqp.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-23-BBx3oNpT.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-24-CYt-Oagk.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-25-Cx3j-ZUv.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-26-B9AfZ3LL.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-27-Bh2s9MNE.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-28-C02JPoKP.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-29-T_tbUGhk.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-30-DMfyNzhz.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-31-DFfdzbkg.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-32-Bs1fz2iW.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-33-cpB96OkZ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-34-BhinPYWX.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-35-D_njRQqA.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-36-DqXlLE3t.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-37-j5LbvMph.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-38-oO4jWdKm.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-39-CabsX4rw.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-4-PatS3YHh.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-40-yrLg3C3e.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-41-DBXmCQhc.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-42-Bqij_UiQ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-43-D3kp0Ntc.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-44-B_BNGvK4.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-45-CZc0cQWs.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-46-CHQX5Kd9.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-47-Ci8gLrHA.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-48-run3ugD3.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-49-DgfiUb6u.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-5-B0CtGbX_.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-50-CdCG-POr.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-51-L2okeQZ2.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-52-BZAqyGJW.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-53-B0xWqLeU.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-54-BZVQX-EA.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-55-093mkIUJ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-56-CbhWG8G-.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-57-pLQuvo6c.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-58-BHhoY50x.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-59-D1_6DuIQ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-6-BnAelmYU.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-60-HDBIuLTZ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-61-C-c13SeU.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-62-kHRVOhZ2.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-63-C0MCsvFv.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-64-DZekguCj.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-65-CtP6c3IU.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-66-DLVWoMSf.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-67-BVuOnayH.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-68-DpxQ6Mnx.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-69-qesBxLmC.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-70-BBt1xN7h.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-71-BWqZacwB.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-72-B1wP_Yst.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-73-C99T7jNZ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-74-DrUTsWBe.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-75-C4M5XXQe.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-76-Bq8spVWF.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-77-D9vNupE2.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-78-D6th7Gnu.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-79-bezM85cS.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-80-DD8pnhG2.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-81-N6jtSyBs.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-82-D6c180bZ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-83-em17AadM.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-84-CJ8gdrMK.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-85-9PPGO1eB.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-86-CjWPf-u5.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-87-8D_X_yOC.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-88-9vtKFPBx.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-89-BhE5jAKX.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-90-D2Npfm5E.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-91-C6Ft1-Eu.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-97-CaExyQP9.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-98-CPoVbsUd.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-99-ZUQZGVgH.woff2 +0 -0
- package/web/dist/assets/{mindmap-definition-FBJOCRG2-BGt4omYC.js → mindmap-definition-FBJOCRG2-CalfL_Kn.js} +1 -1
- package/web/dist/assets/{pegDiagram-XKGWAZYB-uSfS-PBE.js → pegDiagram-XKGWAZYB-D9mVWz7l.js} +1 -1
- package/web/dist/assets/pending-edit-apply-kwMFWsUe.js +31 -0
- package/web/dist/assets/{pieDiagram-E7YTZNPT-DpeI935H.js → pieDiagram-E7YTZNPT-DELzbZNS.js} +1 -1
- package/web/dist/assets/{quadrantDiagram-AXDQQJYC-B7IuNvWa.js → quadrantDiagram-AXDQQJYC-TZqp4mfA.js} +1 -1
- package/web/dist/assets/{railroadDiagram-O6MQD6OU-ARzYpPQI.js → railroadDiagram-O6MQD6OU-DxX59f0i.js} +1 -1
- package/web/dist/assets/{requirementDiagram-EFPCY7ZU-Cj4upkAo.js → requirementDiagram-EFPCY7ZU-DFdsJLIe.js} +1 -1
- package/web/dist/assets/{rotate-cw-CXJcyHWk.js → rotate-cw-62liz7LX.js} +1 -1
- package/web/dist/assets/{sankeyDiagram-P5KCCOFB-OaVwSnLP.js → sankeyDiagram-P5KCCOFB-ByYcd3fG.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-WJ2MYXX4-1yrUbq4t.js → sequenceDiagram-WJ2MYXX4-Bj-ml1jS.js} +1 -1
- package/web/dist/assets/{sizeCapture-X5ZJPWSS-nYH5p9D6.js → sizeCapture-X5ZJPWSS-KhsXThyD.js} +1 -1
- package/web/dist/assets/{square-dashed-mouse-pointer-CTtTVKoH.js → square-dashed-mouse-pointer-D_VrqVpy.js} +1 -1
- package/web/dist/assets/{stateDiagram-HBIQ2CUA-Dj5HvyNq.js → stateDiagram-HBIQ2CUA-BJlpHfWU.js} +1 -1
- package/web/dist/assets/stateDiagram-v2-4QOOHH4V-CR2kYAH2.js +1 -0
- package/web/dist/assets/{swimlanes-XN3QIQJK-umeT0HpB.js → swimlanes-XN3QIQJK-DUB9OJVs.js} +1 -1
- package/web/dist/assets/swimlanesDiagram-VK2B7HYN-BitOxnG2.js +8 -0
- package/web/dist/assets/{timeline-definition-24CTP7MA-MqnQfHKB.js → timeline-definition-24CTP7MA-C03NX82A.js} +1 -1
- package/web/dist/assets/{vennDiagram-4TSXK5OY-b2liyZra.js → vennDiagram-4TSXK5OY-B9pjzngp.js} +1 -1
- package/web/dist/assets/{wardleyDiagram-VM6X3IG4-QMK-zNh1.js → wardleyDiagram-VM6X3IG4-UqWq_mcT.js} +1 -1
- package/web/dist/assets/{xychartDiagram-S5SC5T6Z-D5Mc6tfA.js → xychartDiagram-S5SC5T6Z-D2pDKtOv.js} +1 -1
- package/web/dist/index.html +2 -2
- package/server/engine/mcp/tools/arrange-on-board.js +0 -126
- package/server/engine/mcp/tools/create-on-board.js +0 -152
- package/server/engine/mcp/tools/edit-sketch.js +0 -167
- package/server/engine/mcp/tools/relate-on-board.js +0 -150
- package/server/engine/mcp/tools/sketch-on-board.js +0 -345
- package/server/engine/plugins/nodesign/skills/rp-craft/SKILL.md +0 -235
- 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 +0 -78
- package/server/engine/plugins/nodesign/skills/rp-craft//346/274/224/345/207/272.template.js +0 -268
- package/web/dist/assets/DeckWindow-Bg3rgkvi.js +0 -11
- package/web/dist/assets/channel-DFyiuGL0.js +0 -1
- package/web/dist/assets/classDiagram-DTDB5LWJ-Aw1f8z-b.js +0 -1
- package/web/dist/assets/classDiagram-v2-JRS7N3AN-Aw1f8z-b.js +0 -1
- package/web/dist/assets/index-C0KUf6Wd.css +0 -1
- package/web/dist/assets/index-DGG5wIZs.js +0 -2118
- package/web/dist/assets/lxgw-nd-bold-DbRfiTgo.woff2 +0 -0
- package/web/dist/assets/lxgw-nd-regular-CpMw9Ehg.woff2 +0 -0
- package/web/dist/assets/pending-edit-apply-uP4uvrOD.js +0 -31
- package/web/dist/assets/stateDiagram-v2-4QOOHH4V-kJ1m_zJT.js +0 -1
- package/web/dist/assets/swimlanesDiagram-VK2B7HYN-DPSkMbbl.js +0 -8
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* mcp/tools/sketch-on-board.js —— sketch_on_board / finish_sketch(2026-08-23 黑板)
|
|
3
|
-
*
|
|
4
|
-
* 让 agent 把画布当黑板:一次调用落一整张示意图(节点 + 形状 + 线),而不是
|
|
5
|
-
* 像 create_on_board 那样一条条写。设计要点(跟用户商定的形状):
|
|
6
|
-
* - **没有几何容器**:一张图 = 一批带同一 #tag 的普通物件和线。read_board 按
|
|
7
|
-
* 连通分量 + tag 分段读,用户可以整组选/整组擦,入座算法照旧绕着走
|
|
8
|
-
* - **两阶段**:落下来的默认是 staging(半透明,"正在画"),finish_sketch 或
|
|
9
|
-
* 回合结束自动落定;落定只清 staging 位,tag 留着当逻辑分组
|
|
10
|
-
* - **结构不是像素**:agent 给模板 + 网格坐标(1 格 = 24px),服务端算像素与落位
|
|
11
|
-
* - **形状是涂鸦**:rect/ellipse/arrow… 在服务端生成带手绘抖动的路径,落成
|
|
12
|
-
* 普通 scribble 对象,前端零改动;自由路径(path)也收,同一白名单
|
|
13
|
-
* - 线的材质缺省 pencil(手绘)—— 草图上的线是人顺手拉的;要丝线/墨线自己说
|
|
14
|
-
*
|
|
15
|
-
* 护栏:每回合 3 张图;每张 ≤ 40 节点 / 30 形状 / 60 线。
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { tool } from '@anthropic-ai/claude-agent-sdk';
|
|
19
|
-
import { z } from 'zod';
|
|
20
|
-
import { readBoard, patchBoard, commitStaging, removeByTag, TEXT_FONTS } from '../../../projects/board-store.js';
|
|
21
|
-
import { estimateSizeOn } from '../../../lib/board-kind-sizes.js';
|
|
22
|
-
import { layerOf, normalizeCanvasId, tagEnvelope } from '../../../lib/canvas-id.js';
|
|
23
|
-
import { BINDING_TYPE_IDS, BINDING_MATERIALS } from '../../../lib/binding-types.js';
|
|
24
|
-
import { UNIT, SKETCH_FIT, SKETCH_MAX, textBox, shapePath, layoutNodes, bboxOf, findSpot, fitFor } from '../../../lib/sketch-layout.js';
|
|
25
|
-
import { getViewpoint } from '../../../projects/viewpoint-store.js';
|
|
26
|
-
import { Events } from '../../agent/events.js';
|
|
27
|
-
|
|
28
|
-
const MAX_NODES = 40;
|
|
29
|
-
const MAX_SHAPES = 30;
|
|
30
|
-
const MAX_EDGES = 60;
|
|
31
|
-
|
|
32
|
-
let seq = 0;
|
|
33
|
-
const stamp = () => `${Date.now().toString(36)}${(seq++ % 1000).toString(36)}`;
|
|
34
|
-
const COLORS = ['ink', 'red', 'pencil', 'brass'];
|
|
35
|
-
|
|
36
|
-
const LOCAL_ID = z.string().regex(/^[A-Za-z0-9_-]{1,24}$/, 'local id: letters/digits/_/-');
|
|
37
|
-
const GRID_PT = z.object({ x: z.number().min(-400).max(400), y: z.number().min(-400).max(400) });
|
|
38
|
-
|
|
39
|
-
export function makeSketchOnBoardTool({ projectId, ctx }) {
|
|
40
|
-
const turnStamp = { turn: -1, count: 0 }; // 只记数(返回里不再报余额)
|
|
41
|
-
return tool(
|
|
42
|
-
'sketch_on_board',
|
|
43
|
-
`Draw a whole sketch on the canvas in ONE call — notes, shapes and lines, laid out
|
|
44
|
-
together: a comparison table, a flow, a mind map, a detective board linking real
|
|
45
|
-
artifacts. The canvas is the blackboard; use it when thinking out loud with the user
|
|
46
|
-
would be clearer as a picture than as prose.
|
|
47
|
-
|
|
48
|
-
How it works
|
|
49
|
-
- You describe STRUCTURE; the server does pixels. Pick a layout template (column /
|
|
50
|
-
row / grid / mindmap) or place nodes yourself on a grid (1 cell = ${UNIT}px, layout
|
|
51
|
-
'free', at:{x,y}). Whole sketch lands beside \`near\` (a canvas id from read_board)
|
|
52
|
-
or under the desktop's current content.
|
|
53
|
-
- nodes: plain handwriting or format:'md' (Markdown + KaTeX $…$ + \`\`\`mermaid fences).
|
|
54
|
-
Use md for lists/tables/formulas; mermaid only for dense formal diagrams
|
|
55
|
-
(sequence/state) — anything that should connect to real artifacts must be nodes+edges.
|
|
56
|
-
- shapes: rect / ellipse / circle / line / arrow / underline (hand-drawn look is
|
|
57
|
-
applied for you) or path (raw SVG M/L/Q in local px) — all in grid units.
|
|
58
|
-
- edges: between local node ids and/or real canvas ids. type = the relation
|
|
59
|
-
vocabulary (link/annotates/flow/contrast/derives-from/ref); material = ink (quiet
|
|
60
|
-
archive line) / pencil (hand-drawn, default here) / yarn (detective red string + pins
|
|
61
|
-
for hypotheses and evidence).
|
|
62
|
-
- Everything carries one #tag (a group): read_board {tag} reads just it, the user can
|
|
63
|
-
select/erase it as a whole, finish_sketch commits or erases it.
|
|
64
|
-
- Items land as STAGING (half-transparent = you are still drawing). Call finish_sketch
|
|
65
|
-
when done, or they commit automatically when the turn ends. Look at the result with
|
|
66
|
-
look_at_board before you call it done — agent scribbles only make sense with eyes.
|
|
67
|
-
- Readability rule: the user reads the board at 80–100% zoom. Body text stays md/lg
|
|
68
|
-
(sm only for captions ≤40 chars); one sketch ≤ ${SKETCH_FIT.w}x${SKETCH_FIT.h} world px (one screen at 80%),
|
|
69
|
-
hard limit ${SKETCH_MAX.w}x${SKETCH_MAX.h} — split into two tagged sketches instead of one huge one.
|
|
70
|
-
- To change an existing sketch use edit_sketch (move/retext/add/remove by id) — do NOT
|
|
71
|
-
erase and redraw the whole thing for a small change.
|
|
72
|
-
Per sketch: ≤${MAX_NODES} nodes, ${MAX_SHAPES} shapes, ${MAX_EDGES} edges (split big ones).`,
|
|
73
|
-
{
|
|
74
|
-
title: z.string().max(60).optional().describe('Optional heading written at the top of the sketch'),
|
|
75
|
-
tag: z.string().regex(/^[\w一-鿿-ヿ-]{1,40}$/).optional()
|
|
76
|
-
.describe('Group tag (letters/digits/_/-/CJK, ≤40). Default: auto "sk-<stamp>"'),
|
|
77
|
-
near: z.string().max(300).optional().describe('Canvas id to place the sketch beside (right side; falls back to below)'),
|
|
78
|
-
layout: z.enum(['auto', 'free', 'column', 'row', 'grid', 'mindmap']).optional()
|
|
79
|
-
.describe('auto = free if any node has at, else column (≤4) / grid'),
|
|
80
|
-
cols: z.number().int().min(1).max(8).optional().describe('grid columns'),
|
|
81
|
-
staging: z.boolean().optional().describe('Default true. false = land committed (solid) immediately'),
|
|
82
|
-
nodes: z.array(z.object({
|
|
83
|
-
id: LOCAL_ID.describe('Local id to reference from edges/shapes'),
|
|
84
|
-
text: z.string().min(1).max(4000),
|
|
85
|
-
format: z.enum(['plain', 'md']).optional(),
|
|
86
|
-
size: z.enum(['sm', 'md', 'lg', 'xl']).optional(),
|
|
87
|
-
font: z.enum(['pen', 'kai', 'sans', 'serif', 'mono']).optional(),
|
|
88
|
-
color: z.enum(['ink', 'red', 'pencil', 'brass']).optional(),
|
|
89
|
-
at: GRID_PT.optional().describe('Grid position (layout free); top-left of the node'),
|
|
90
|
-
w: z.number().min(3).max(22).optional().describe('Width in grid units (md nodes; default by content; ≤22 = 528px — paragraphs grow down, not wide)'),
|
|
91
|
-
})).max(MAX_NODES).optional(),
|
|
92
|
-
shapes: z.array(z.object({
|
|
93
|
-
id: LOCAL_ID.optional(),
|
|
94
|
-
kind: z.enum(['rect', 'ellipse', 'circle', 'line', 'arrow', 'underline', 'path']),
|
|
95
|
-
at: GRID_PT.optional().describe('Grid position (top-left for rect/ellipse; start point for line/arrow). Or use around:'),
|
|
96
|
-
around: LOCAL_ID.optional().describe('rect/ellipse/circle/underline: wrap this node (pad applied) instead of at/w/h'),
|
|
97
|
-
w: z.number().min(0).max(200).optional().describe('grid units'),
|
|
98
|
-
h: z.number().min(0).max(200).optional().describe('grid units'),
|
|
99
|
-
to: GRID_PT.optional().describe('line/arrow end point (grid)'),
|
|
100
|
-
toNode: LOCAL_ID.optional().describe('line/arrow: aim at this node\'s center instead of to'),
|
|
101
|
-
d: z.string().max(8000).optional().describe('path kind: SVG path (M/L/Q/Z only) in local px'),
|
|
102
|
-
color: z.enum(['ink', 'red', 'pencil', 'brass']).optional(),
|
|
103
|
-
width: z.number().min(1).max(12).optional(),
|
|
104
|
-
})).max(MAX_SHAPES).optional(),
|
|
105
|
-
edges: z.array(z.object({
|
|
106
|
-
from: z.string().min(1).max(300).describe('local node id or canvas id'),
|
|
107
|
-
to: z.string().min(1).max(300).describe('local node id or canvas id'),
|
|
108
|
-
type: z.enum(BINDING_TYPE_IDS).optional().describe('default link'),
|
|
109
|
-
material: z.enum(BINDING_MATERIALS).optional().describe('default pencil'),
|
|
110
|
-
label: z.string().max(60).optional(),
|
|
111
|
-
})).max(MAX_EDGES).optional(),
|
|
112
|
-
},
|
|
113
|
-
async (args) => {
|
|
114
|
-
if (!projectId) return { content: [{ type: 'text', text: 'No project bound.' }], isError: true };
|
|
115
|
-
const err = (t) => ({ content: [{ type: 'text', text: t }], isError: true });
|
|
116
|
-
// 回合闸 08-23 用户拍板先不设(「别设置限制吧先」):只记数不拦
|
|
117
|
-
const turn = ctx?.runId ?? -1;
|
|
118
|
-
if (turnStamp.turn !== turn) { turnStamp.turn = turn; turnStamp.count = 0; }
|
|
119
|
-
|
|
120
|
-
const nodesIn = args.nodes || [];
|
|
121
|
-
const shapesIn = args.shapes || [];
|
|
122
|
-
const edgesIn = args.edges || [];
|
|
123
|
-
if (!nodesIn.length && !shapesIn.length) return err('空图不落板:至少给一个节点或形状。');
|
|
124
|
-
const tag = args.tag || `sk-${stamp()}`;
|
|
125
|
-
const staging = args.staging !== false;
|
|
126
|
-
|
|
127
|
-
// ── 节点身位 ──
|
|
128
|
-
const localIds = new Set();
|
|
129
|
-
const nodes = [];
|
|
130
|
-
if (args.title) {
|
|
131
|
-
nodes.push({ key: '__title', text: `## ${args.title}`, format: 'md', size: 'md', font: 'kai', color: 'ink', at: null, w: null });
|
|
132
|
-
}
|
|
133
|
-
for (const n of nodesIn) {
|
|
134
|
-
if (localIds.has(n.id)) return err(`节点 id 重复:${n.id}`);
|
|
135
|
-
localIds.add(n.id);
|
|
136
|
-
// 可读性规范:正文不低于 md(0.8 倍下 12.8 屏幕像素是底线);sm 只给 ≤40 字的题注
|
|
137
|
-
const size = (n.size === 'sm' && n.text.length > 40) ? 'md' : (n.size || 'md');
|
|
138
|
-
nodes.push({ key: n.id, text: n.text, format: n.format || 'plain', size, font: n.font || 'pen', color: n.color || 'ink', at: n.at || null, w: n.w || null });
|
|
139
|
-
}
|
|
140
|
-
for (const n of nodes) {
|
|
141
|
-
const box = textBox(n.text, n.size, { md: n.format === 'md', wUnits: n.w });
|
|
142
|
-
n.w = box.w; n.h = box.h;
|
|
143
|
-
}
|
|
144
|
-
// 标题永远在最上面:free 模式给它 (0,-2),模板模式它就是第一个
|
|
145
|
-
const titleNode = nodes.find(n => n.key === '__title');
|
|
146
|
-
let tpl = args.layout || 'auto';
|
|
147
|
-
if (tpl === 'auto') tpl = nodes.some(n => n.at) ? 'free' : (nodes.length - (titleNode ? 1 : 0) <= 4 ? 'column' : 'grid');
|
|
148
|
-
const layoutInput = titleNode && tpl === 'mindmap' ? nodes.filter(n => n !== titleNode) : nodes;
|
|
149
|
-
const pos = layoutNodes(layoutInput, { template: tpl, cols: args.cols });
|
|
150
|
-
if (titleNode && !pos.has('__title')) {
|
|
151
|
-
const bb = bboxOf([...pos.entries()].map(([k, p]) => ({ x: p.x, y: p.y, ...nodes.find(n => n.key === k) })));
|
|
152
|
-
pos.set('__title', { x: bb.x, y: bb.y - titleNode.h - 12 });
|
|
153
|
-
} else if (titleNode && tpl === 'free' && !titleNode.at) {
|
|
154
|
-
const bb = bboxOf([...pos.entries()].filter(([k]) => k !== '__title').map(([k, p]) => ({ x: p.x, y: p.y, ...nodes.find(n => n.key === k) })));
|
|
155
|
-
pos.set('__title', { x: bb.x, y: bb.y - titleNode.h - 12 });
|
|
156
|
-
}
|
|
157
|
-
const rectOfNode = (key) => { const n = nodes.find(x => x.key === key); const p = pos.get(key); return n && p ? { x: p.x, y: p.y, w: n.w, h: n.h } : null; };
|
|
158
|
-
|
|
159
|
-
// ── 形状(局部像素)──
|
|
160
|
-
const shapes = [];
|
|
161
|
-
for (let i = 0; i < shapesIn.length; i += 1) {
|
|
162
|
-
const s = shapesIn[i];
|
|
163
|
-
const sid = s.id || `s${i + 1}`;
|
|
164
|
-
if (localIds.has(sid) || shapes.some(x => x.key === sid)) return err(`形状 id「${sid}」跟节点/别的形状重名(形状缺省叫 s1,s2…,节点别用这类名)`);
|
|
165
|
-
const seed = `${tag}:${sid}`;
|
|
166
|
-
const color = COLORS.includes(s.color) ? s.color : (s.kind === 'arrow' || s.kind === 'line' ? 'ink2' : 'ink');
|
|
167
|
-
const width = s.width || 2;
|
|
168
|
-
let rect; let d;
|
|
169
|
-
if (s.kind === 'path') {
|
|
170
|
-
if (!s.d || !/^[\dMLQCZ ,.\-eE]+$/.test(s.d)) return err(`形状 ${sid}:path 只收 M/L/Q/Z 与数字`);
|
|
171
|
-
// 自由路径:算它的包围盒,平移到 (at 或 0,0)
|
|
172
|
-
const nums = s.d.match(/-?\d*\.?\d+(?:[eE][-+]?\d+)?/g)?.map(Number) || [];
|
|
173
|
-
const xs = nums.filter((_, k) => k % 2 === 0); const ys = nums.filter((_, k) => k % 2 === 1);
|
|
174
|
-
const w = Math.max(4, Math.max(...xs) - Math.min(0, Math.min(...xs))); const h = Math.max(4, Math.max(...ys) - Math.min(0, Math.min(...ys)));
|
|
175
|
-
rect = { x: (s.at?.x || 0) * UNIT, y: (s.at?.y || 0) * UNIT, w: w + 6, h: h + 6 };
|
|
176
|
-
d = s.d;
|
|
177
|
-
} else if (s.kind === 'line' || s.kind === 'arrow' || s.kind === 'underline') {
|
|
178
|
-
let a; let b;
|
|
179
|
-
if (s.kind === 'underline' && s.around) {
|
|
180
|
-
const r = rectOfNode(s.around); if (!r) return err(`形状 ${sid}:around 指向不存在的节点 ${s.around}`);
|
|
181
|
-
a = { x: r.x + 2, y: r.y + r.h - 2 }; b = { x: r.x + r.w - 2, y: r.y + r.h - 2 };
|
|
182
|
-
} else {
|
|
183
|
-
if (!s.at) return err(`形状 ${sid}:${s.kind} 要 at 起点`);
|
|
184
|
-
a = { x: s.at.x * UNIT, y: s.at.y * UNIT };
|
|
185
|
-
if (s.toNode) {
|
|
186
|
-
const r = rectOfNode(s.toNode); if (!r) return err(`形状 ${sid}:toNode 指向不存在的节点 ${s.toNode}`);
|
|
187
|
-
b = { x: r.x + r.w / 2, y: r.y + r.h / 2 };
|
|
188
|
-
} else if (s.to) b = { x: s.to.x * UNIT, y: s.to.y * UNIT };
|
|
189
|
-
else if (s.kind === 'underline') b = { x: a.x + (s.w || 4) * UNIT, y: a.y };
|
|
190
|
-
else return err(`形状 ${sid}:${s.kind} 要 to 或 toNode`);
|
|
191
|
-
}
|
|
192
|
-
const sp = shapePath(s.kind, { to: { x: b.x - a.x, y: b.y - a.y } }, seed);
|
|
193
|
-
rect = { x: Math.min(a.x, b.x) - 6, y: Math.min(a.y, b.y) - 6, w: sp.w, h: sp.h };
|
|
194
|
-
d = sp.d;
|
|
195
|
-
} else {
|
|
196
|
-
let box;
|
|
197
|
-
if (s.around) {
|
|
198
|
-
const r = rectOfNode(s.around); if (!r) return err(`形状 ${sid}:around 指向不存在的节点 ${s.around}`);
|
|
199
|
-
const pad = s.kind === 'rect' ? 8 : 14;
|
|
200
|
-
box = { x: r.x - pad, y: r.y - pad, w: r.w + pad * 2, h: r.h + pad * 2 };
|
|
201
|
-
if (s.kind === 'circle') { const dmax = Math.max(box.w, box.h); box = { x: box.x + (box.w - dmax) / 2, y: box.y + (box.h - dmax) / 2, w: dmax, h: dmax }; }
|
|
202
|
-
} else {
|
|
203
|
-
if (!s.at || !s.w) return err(`形状 ${sid}:${s.kind} 要 at + w(+h)或 around`);
|
|
204
|
-
box = { x: s.at.x * UNIT, y: s.at.y * UNIT, w: s.w * UNIT, h: (s.h || s.w) * UNIT };
|
|
205
|
-
}
|
|
206
|
-
const sp = shapePath(s.kind, { w: box.w, h: box.h }, seed);
|
|
207
|
-
rect = { x: box.x - 6, y: box.y - 6, w: sp.w, h: sp.h };
|
|
208
|
-
d = sp.d;
|
|
209
|
-
}
|
|
210
|
-
shapes.push({ key: sid, rect, d, color: COLORS.includes(color) ? color : 'ink', width });
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// ── 先分配 id、解析线 ──
|
|
214
|
-
// 线要先于落位算:一条连到真实产物卡的线会改变**主角判断**(手画线 +0.5 焦点分),
|
|
215
|
-
// 那张卡可能因此放大 1.5 倍 —— 落位必须按"线画上去之后"的尺寸避让
|
|
216
|
-
// (08-23 首跑真踩:草图连了一条丝线到纸本站,纸本当场成主角,身体盖住半张图)。
|
|
217
|
-
const board = await readBoard(projectId);
|
|
218
|
-
const known = new Set(Object.keys(board.zones || {}));
|
|
219
|
-
const idOf = new Map(); // local → canvas id
|
|
220
|
-
for (const n of nodes) idOf.set(n.key, `text:a${stamp()}`);
|
|
221
|
-
for (const sh of shapes) idOf.set(sh.key, `scribble:a${stamp()}`);
|
|
222
|
-
const resolveEnd = (raw) => {
|
|
223
|
-
if (idOf.has(raw)) return idOf.get(raw);
|
|
224
|
-
const cid = normalizeCanvasId(raw);
|
|
225
|
-
if (cid && board.objects?.[cid]) return cid;
|
|
226
|
-
if (cid && board.zones?.[cid] !== undefined) return cid;
|
|
227
|
-
return null;
|
|
228
|
-
};
|
|
229
|
-
const bindings = {};
|
|
230
|
-
const badEdges = [];
|
|
231
|
-
for (const e of edgesIn) {
|
|
232
|
-
const from = resolveEnd(e.from); const to = resolveEnd(e.to);
|
|
233
|
-
if (!from || !to || from === to) { badEdges.push(`${e.from}→${e.to}`); continue; }
|
|
234
|
-
bindings[`b:a${stamp()}`] = {
|
|
235
|
-
type: e.type || 'link', from, to, by: 'agent', material: e.material || 'pencil', tag,
|
|
236
|
-
...(staging ? { staging: true } : {}), ...(e.label ? { label: e.label } : {}),
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
// 落位用"线画上之后"的板来估尺寸(主角可能换人)
|
|
240
|
-
const after = { ...board, bindings: { ...(board.bindings || {}), ...bindings } };
|
|
241
|
-
|
|
242
|
-
// ── 宏观落位 ──
|
|
243
|
-
const local = bboxOf([
|
|
244
|
-
...nodes.map(n => ({ ...pos.get(n.key), w: n.w, h: n.h })),
|
|
245
|
-
...shapes.map(sh => sh.rect),
|
|
246
|
-
]);
|
|
247
|
-
let zone = '';
|
|
248
|
-
let anchor = null;
|
|
249
|
-
if (args.near) {
|
|
250
|
-
const nid = normalizeCanvasId(args.near);
|
|
251
|
-
const e = nid ? board.objects?.[nid] : null;
|
|
252
|
-
if (e && Number.isFinite(e.x)) {
|
|
253
|
-
zone = layerOf(nid, e, known);
|
|
254
|
-
anchor = { x: e.x, y: e.y, ...estimateSizeOn(after, nid, e) };
|
|
255
|
-
} else {
|
|
256
|
-
// near 也认 tag(08-23 案,同 write_on_board)
|
|
257
|
-
const env = tagEnvelope(board, args.near, (id2, e2) => estimateSizeOn(after, id2, e2));
|
|
258
|
-
if (!env) return err(`锚点 ${args.near} 还没有座位,也不是板上任何 tag(read_board 里看不到就锚不上)。`);
|
|
259
|
-
zone = layerOf(env.anchorId, board.objects[env.anchorId], known);
|
|
260
|
-
anchor = { x: env.x, y: env.y, w: env.w, h: env.h };
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
const obstacles = [];
|
|
264
|
-
let contentBottom = 0;
|
|
265
|
-
for (const [id, e] of Object.entries(board.objects || {})) {
|
|
266
|
-
if (!Number.isFinite(e?.x) || layerOf(id, e, known) !== zone) continue;
|
|
267
|
-
const r = { x: e.x, y: e.y, ...estimateSizeOn(after, id, e) };
|
|
268
|
-
obstacles.push(r); contentBottom = Math.max(contentBottom, r.y + r.h);
|
|
269
|
-
}
|
|
270
|
-
if (!zone) for (const zz of Object.values(board.zones || {})) if (Number.isFinite(zz?.y)) contentBottom = Math.max(contentBottom, zz.y + 240);
|
|
271
|
-
// 尺寸规范:硬上限直接拒(一张图说一件事),推荐上限以上给提醒
|
|
272
|
-
if (local.w > SKETCH_MAX.w || local.h > SKETCH_MAX.h) {
|
|
273
|
-
return err(`这张图太大了(${Math.round(local.w)}x${Math.round(local.h)} 世界像素,上限 ${SKETCH_MAX.w}x${SKETCH_MAX.h}):用户在 80%~100% 缩放下看不全。拆成两张(各自一个 tag,之间用线连),或减节点/缩 w。`);
|
|
274
|
-
}
|
|
275
|
-
// 用户视口(同一层才算;黑板是主窗口时画在他眼前)+ 按他的屏幕算"一屏"
|
|
276
|
-
const vp = getViewpoint(projectId);
|
|
277
|
-
const vpRect = (vp && (vp.layer || '') === zone && vp.camera) ? vp.camera : null;
|
|
278
|
-
const fit = fitFor(vp);
|
|
279
|
-
const spot = findSpot({ w: local.w + 24, h: local.h + 24, near: anchor, obstacles, contentBottom, viewport: vpRect });
|
|
280
|
-
const ox = spot.x - local.x + 12; const oy = spot.y - local.y + 12;
|
|
281
|
-
|
|
282
|
-
// ── 落盘 ──
|
|
283
|
-
const objects = {};
|
|
284
|
-
const common = { z: 1, zone, by: 'agent', tag, ...(staging ? { staging: true } : {}) };
|
|
285
|
-
for (const n of nodes) {
|
|
286
|
-
const p = pos.get(n.key);
|
|
287
|
-
objects[idOf.get(n.key)] = {
|
|
288
|
-
x: Math.round(p.x + ox), y: Math.round(p.y + oy), w: n.w, h: n.h, kind: 'text',
|
|
289
|
-
data: { t: n.text, ...(n.format === 'md' ? { format: 'md' } : {}), font: TEXT_FONTS.includes(n.font) ? n.font : 'pen', size: n.size, color: n.color },
|
|
290
|
-
...common,
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
for (const sh of shapes) {
|
|
294
|
-
objects[idOf.get(sh.key)] = { x: Math.round(sh.rect.x + ox), y: Math.round(sh.rect.y + oy), w: Math.round(sh.rect.w), h: Math.round(sh.rect.h), kind: 'scribble', data: { d: sh.d, color: sh.color, width: sh.width }, ...common };
|
|
295
|
-
}
|
|
296
|
-
const saved = await patchBoard(projectId, { objects, bindings });
|
|
297
|
-
const landed = Object.keys(objects).filter(id => saved.objects?.[id]).length;
|
|
298
|
-
if (!landed) return err('草图被 board 拒了(内容或字段不合法)。');
|
|
299
|
-
turnStamp.count += 1;
|
|
300
|
-
const world = { x: Math.round(local.x + ox), y: Math.round(local.y + oy), w: Math.round(local.w), h: Math.round(local.h) };
|
|
301
|
-
try {
|
|
302
|
-
ctx?.emit?.({ type: 'board.updated', sessionId: null, summary: `画了一张草图 #${tag}` });
|
|
303
|
-
// 镜头提示(前端只在黑板模式跟随;不是劫持,是"黑板是主窗口"的约定)
|
|
304
|
-
ctx?.emit?.(Events.boardFocus(world, { tag, layer: zone }));
|
|
305
|
-
} catch { /* fail-soft */ }
|
|
306
|
-
const lines = [
|
|
307
|
-
`Sketch #${tag} landed${staging ? ' as STAGING (半透明)' : ''}: ${Object.keys(objects).length - shapes.length} nodes, ${shapes.length} shapes, ${Object.keys(bindings).length} lines; layout ${tpl}; at world (${world.x},${world.y}) ${world.w}x${world.h}${anchor ? ` (${spot.side} of ${args.near})` : ' (below current content)'}.`,
|
|
308
|
-
`ids: ${[...idOf].map(([k, v]) => `${k}=${v}`).join(', ')}`,
|
|
309
|
-
];
|
|
310
|
-
if (badEdges.length) lines.push(`Skipped ${badEdges.length} edge(s) with unknown endpoints: ${badEdges.slice(0, 6).join(', ')}`);
|
|
311
|
-
if (world.w > fit.w || world.h > fit.h) lines.push(`⚠ Bigger than one screen at 80% zoom${fit.screen ? ` (user's screen ${fit.screen.w}x${fit.screen.h}px → ${fit.w}x${fit.h} world px fits)` : ` (${fit.w}x${fit.h} fits)`} — the user will have to pan. Split into two tagged sketches (side by side or stacked) next time.`);
|
|
312
|
-
if (vp?.zoom && vp.zoom < 0.8) lines.push(`User's zoom is ${vp.zoom} (<0.8): text this size is small on their screen. Keep nodes md/lg and say in one line that there is a sketch on the board.`);
|
|
313
|
-
lines.push(spot.side === 'viewport' ? 'Placed inside the user\'s current viewport.' : `Placed ${spot.side === 'bottom' ? 'below current content' : spot.side + ' of the anchor'}${vpRect ? ' (no room in the user\'s viewport)' : ''}.`);
|
|
314
|
-
lines.push(staging
|
|
315
|
-
? 'Next: look_at_board {tag} to check it, then finish_sketch {tag} (or it commits at turn end). Mention the sketch to the user in one line.'
|
|
316
|
-
: 'Next: look_at_board {tag} to check it.');
|
|
317
|
-
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
318
|
-
},
|
|
319
|
-
);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
export function makeFinishSketchTool({ projectId, ctx }) {
|
|
323
|
-
return tool(
|
|
324
|
-
'finish_sketch',
|
|
325
|
-
`Commit a staging sketch (make it solid) — or erase it. Pass the #tag from
|
|
326
|
-
sketch_on_board; omit tag to commit everything still staging. erase:true deletes the
|
|
327
|
-
whole group (its notes/shapes/lines; real artifact cards only lose the tag).`,
|
|
328
|
-
{
|
|
329
|
-
tag: z.string().max(40).optional(),
|
|
330
|
-
erase: z.boolean().optional(),
|
|
331
|
-
},
|
|
332
|
-
async ({ tag, erase }) => {
|
|
333
|
-
if (!projectId) return { content: [{ type: 'text', text: 'No project bound.' }], isError: true };
|
|
334
|
-
if (erase) {
|
|
335
|
-
if (!tag) return { content: [{ type: 'text', text: 'erase needs a tag.' }], isError: true };
|
|
336
|
-
const { removed } = await removeByTag(projectId, tag);
|
|
337
|
-
try { ctx?.emit?.({ type: 'board.updated', sessionId: null, summary: `擦掉了草图 #${tag}` }); } catch { /* */ }
|
|
338
|
-
return { content: [{ type: 'text', text: `Erased #${tag}: ${removed} item(s)/line(s) removed.` }] };
|
|
339
|
-
}
|
|
340
|
-
const { committed } = await commitStaging(projectId, { tag: tag || null });
|
|
341
|
-
if (committed) { try { ctx?.emit?.({ type: 'board.updated', sessionId: null, summary: `草图落定${tag ? ` #${tag}` : ''}` }); } catch { /* */ } }
|
|
342
|
-
return { content: [{ type: 'text', text: committed ? `Committed ${committed} item(s)${tag ? ` of #${tag}` : ''}.` : 'Nothing was staging.' }] };
|
|
343
|
-
},
|
|
344
|
-
);
|
|
345
|
-
}
|
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: rp-craft
|
|
3
|
-
version: 0.1.1
|
|
4
|
-
description: NoDesign 互动演出(RP)方法论 —— 搭一场角色扮演:编排上下文(编排.yaml 三区模型)、写角色与世界、造演出页前端、串起 chatai 通路。只要用户想角色扮演就用本 skill —— 包括直接说「开一场RP」「我们来玩角色扮演」并当场给出人物设定的情况:你是搭台的场务,不是台上的演员,绝不在设计对话里亲自扮演角色,而是按本 skill 把演出搭成能反复进场的作品(角色在专门的演出页里开口,那条通路便宜得多、也不占设计会话)。也用于把酒馆(SillyTavern)角色卡搬进来。不要用于写小说(那是另一回事,别拿这套硬套)。
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# 一场演出是什么
|
|
8
|
-
|
|
9
|
-
三样东西住在**一个文件夹**里,缺一不可:
|
|
10
|
-
|
|
11
|
-
1. `编排.yaml` —— 上下文的播放单(进模型的一切按它拼)
|
|
12
|
-
2. 设定文件 —— 角色、世界、文风,自由组织,被播放单引用
|
|
13
|
-
3. `index.html` —— 演出页。有它这个文件夹就会被认成站点,用通用站点窗打开
|
|
14
|
-
|
|
15
|
-
对话记录 `对话.jsonl` 和摘要 `摘要.json` 演出中自动生成。**摘要是派生态,永远别手写。**
|
|
16
|
-
|
|
17
|
-
台上说话的是 **chatai**:一次纯补全调用,没有工具、没有会话、改不了任何文件。
|
|
18
|
-
你(agent)只负责搭台和调台,不在台上。演出的每一轮花费按平台单价表记入用户额度。
|
|
19
|
-
|
|
20
|
-
# 机制契约(这一节照抄不走样)
|
|
21
|
-
|
|
22
|
-
## 编排.yaml 全貌
|
|
23
|
-
|
|
24
|
-
```yaml
|
|
25
|
-
模型: gemini-3.7-flash # 可省 = 平台默认。摘要可单配便宜的(摘要.模型)
|
|
26
|
-
思考: 低 # 思考档,可省 = 该模型最省的那档。gemini 系 低/中/高、
|
|
27
|
-
# claude 系 关/高(开扩展思考);写了模型没有的档不报错,
|
|
28
|
-
# 按默认档跑并在回包里说明。摘要另有 摘要.思考,不写就走
|
|
29
|
-
# 默认档(压缩不是创作,别给摘要开高思考烧钱)
|
|
30
|
-
最大输出: 400 # RP 短回合是质量旋钮:300–600 合适,放大了戏会水
|
|
31
|
-
上下文预算: 60000 # 输入侧预算:系统层和尾部照给,历史是弹性区被从最旧挤压
|
|
32
|
-
|
|
33
|
-
系统层: # → system 参数。整区冻结,是缓存的本体
|
|
34
|
-
- 名字: 身份与规则
|
|
35
|
-
文件: 设定/老芦.md
|
|
36
|
-
- 名字: 世界观
|
|
37
|
-
文件: 设定/夜行集市.md
|
|
38
|
-
- 名字: 文风
|
|
39
|
-
内容: |
|
|
40
|
-
第二人称现在时,只写可感知的事,结尾留一个能接的动作或问题。
|
|
41
|
-
|
|
42
|
-
历史:
|
|
43
|
-
保留轮数: 40 # 硬上限;预算不够时还会再被挤
|
|
44
|
-
|
|
45
|
-
摘要: # 滚动前情提要:活历史攒到触发轮数就折叠一次
|
|
46
|
-
保留轮数: 12
|
|
47
|
-
触发轮数: 24 # 必须 > 保留轮数,差值是滞回区间
|
|
48
|
-
长度: 500
|
|
49
|
-
|
|
50
|
-
尾部: # → 拼进当轮 user 消息前部。唯一允许每轮变的区
|
|
51
|
-
- 名字: 当前场景
|
|
52
|
-
文件: 状态/场景.md # 谁改这个文件,下一轮就生效
|
|
53
|
-
- 名字: 交易提醒
|
|
54
|
-
内容: 涉及买卖时报价要具体到铜币数目。
|
|
55
|
-
触发: [买, 卖, 价钱] # 触发词命中最近几轮或当轮输入才插入
|
|
56
|
-
# 停用: true # 播放单开关
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
铁律三条:条目 `文件`/`内容` 二选一;**触发条目只能住尾部**(住系统层直接拒开演);
|
|
60
|
-
文件引用不许跑出演出文件夹。配置错了端点会拒演并报人话,照改就是。
|
|
61
|
-
|
|
62
|
-
## 两条 API(演出页用,cookie 同源自动带)
|
|
63
|
-
|
|
64
|
-
- `GET /api/projects/<pid>/chatai/log?dir=<文件夹>` → `{records, 摘要, 配置}`,开场铺台用
|
|
65
|
-
- `POST /api/projects/<pid>/chatai/turn` body `{dir, input}` → SSE 流:
|
|
66
|
-
`data:{delta}`… 最后 `data:{done, text, usage, costUsd}`;错误 `data:{error}`。
|
|
67
|
-
拒绝码:429 太快或额度尽、409 上一轮没回完、403 账号未开通演出通路
|
|
68
|
-
|
|
69
|
-
## 开场白(角色先开口)
|
|
70
|
-
|
|
71
|
-
往 `对话.jsonl` 种第一条记录:
|
|
72
|
-
|
|
73
|
-
```
|
|
74
|
-
{"seq":1,"role":"assistant","text":"<开场白>","at":"<ISO时间>"}
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
它不算轮数,够老了会随摘要折叠。用户视角:一进场角色已经在说话。
|
|
78
|
-
|
|
79
|
-
# 三区纪律 —— 为什么这么摆
|
|
80
|
-
|
|
81
|
-
顺序即上下文:**稳定的在前吃缓存,易变的在后换自由。** 缓存按字节精确匹配前缀,
|
|
82
|
-
前缀里任何一个字变了,它后面的一切全部重算。
|
|
83
|
-
|
|
84
|
-
反面教材是酒馆的世界书:几百个词条按关键词触发、插在前缀中段,每轮触发集一变
|
|
85
|
-
后面全废 —— 实测 573 条词库缓存命中率 **0%,一轮一美元**。所以这里触发条目被
|
|
86
|
-
结构性关进尾部:尾部本来就在最新对话之后、缓存必然失效的位置,触发集怎么变都
|
|
87
|
-
伤不到前缀。
|
|
88
|
-
|
|
89
|
-
「每轮允许多大范围变化」是用户的取舍旋钮,空间化了:一段内容放系统层 = 冻住换
|
|
90
|
-
缓存,挪尾部 = 放开换灵活。用户要"自由一点",就是把东西往尾部挪。
|
|
91
|
-
|
|
92
|
-
# 特化菜谱(参考,不当分类)
|
|
93
|
-
|
|
94
|
-
**身份与规则**(系统层第一条):演谁、对用户用什么人称、回合怎么接(只推进一拍,
|
|
95
|
-
别替用户行动)、回复长度、这场戏的禁区。这一条的质量决定八成体验。
|
|
96
|
-
|
|
97
|
-
**世界观:整本冻结,别切条。** 这条路是 1M 窗口 + 便宜的 flash,几万字设定冻在
|
|
98
|
-
系统层一次写进缓存,比切几百条按关键词捞又稳又便宜。酒馆玩家的切条肌肉记忆是
|
|
99
|
-
8k 窗口时代逼出来的,别带过来。
|
|
100
|
-
|
|
101
|
-
**文风锚**:三五条"怎么写"的约束 + 一小段范文,比一堆形容词管用。范文是最强的锚。
|
|
102
|
-
|
|
103
|
-
**状态文件当活道具**:`状态/场景.md` 这类尾部条目,演出中你或用户改文件就等于
|
|
104
|
-
改状态,不需要任何变量系统。要推进剧情就改状态文件——**不要去读 `对话.jsonl`**
|
|
105
|
-
(见下方隐私纪律;平台也会拦)。用户想跟你讨论某段戏,会自己粘贴过来。
|
|
106
|
-
|
|
107
|
-
**触发条目 = 导演小纸条**,一场十条以内:战斗时收紧节奏、某个名字出现时提醒一段
|
|
108
|
-
旧怨。它是"到了这一幕才递的条子",不是知识检索。
|
|
109
|
-
|
|
110
|
-
# 从酒馆搬东西进来
|
|
111
|
-
|
|
112
|
-
⚠️ **任何酒馆导出的 JSON 都先用 `read_tavern_json`,不要用 Read。** 真样本
|
|
113
|
-
(一份社区预设)464KB、210 条提示词而启用的只有 56 条 —— Read 一次就是十几万
|
|
114
|
-
token 进上下文,换回来的绝大部分是停用的备选。工具两步走:`mode="digest"` 看结构
|
|
115
|
-
(名字/角色/字数/一句引子),挑好了再 `mode="fetch"` + `entries=[…]` 拿正文。它只读不写,怎么分组
|
|
116
|
-
是你的判断。
|
|
117
|
-
|
|
118
|
-
酒馆导出的 JSON 有三种,别搞混:
|
|
119
|
-
|
|
120
|
-
## ① 预设(Chat Completion preset)—— `prompts[]` + `prompt_order[]`
|
|
121
|
-
|
|
122
|
-
**它跟 `编排.yaml` 是同一类东西**:管的是"每次请求怎么拼上下文 + 采样参数",
|
|
123
|
-
里面没有角色也没有剧情。搬法:
|
|
124
|
-
|
|
125
|
-
1. `read_tavern_json` 摘要 → 看清启用了哪些、顺序如何。
|
|
126
|
-
2. 跟用户过一遍**分歧点**(预设通常把同一功能备了好几版只开一个):文风、
|
|
127
|
-
思维链、人称、对白量、NSFW 档。问"这几个开关你要哪一档",不要自己替他选。
|
|
128
|
-
3. `mode="fetch"` 拿选中条目的正文,按功能并成两三份设定文件(例:
|
|
129
|
-
`设定/规则.md`、`设定/文风.md`、`设定/思维链.md`),**顺序照抄原顺序**,
|
|
130
|
-
全部进**系统层**(它们每轮不变,正是缓存本体)。
|
|
131
|
-
4. **丢掉**:marker 占位条(角色描述/性格/场景/Chat History —— 我们由
|
|
132
|
-
编排.yaml 自己管历史和角色)、0 字的分节标题、以及针对特定平台的破甲话术
|
|
133
|
-
(这条通路不过内容审查,尺度由账号档位定,那些绕过话术在这儿没有意义,
|
|
134
|
-
还白占前缀)。
|
|
135
|
-
5. 采样参数(temperature / top_p / 各种惩罚)**我们没有这些旋钮**,照实告诉
|
|
136
|
-
用户:这边能调的是 模型 / 思考档 / 最大输出 / 上下文预算。预设里的
|
|
137
|
-
`openai_max_tokens` 可以参考着填 `最大输出`,但 RP 短回合 300–600 更好。
|
|
138
|
-
|
|
139
|
-
一个真数字供你估:那份 56 条的预设,正文合计约 9.8k 字符 ≈ 4–5k token 常驻前缀。
|
|
140
|
-
搬进系统层没问题,但要跟用户说清这是每轮都在的开销。
|
|
141
|
-
|
|
142
|
-
## ② 角色卡(V2/V3)—— `spec: chara_card_v2` 或有 `first_mes`
|
|
143
|
-
|
|
144
|
-
`description/personality/scenario` 并成一份角色文件进系统层;`first_mes` 种成
|
|
145
|
-
开场白(往 `对话.jsonl` 写 assistant seq 1);`mes_example` 提炼进文风锚;
|
|
146
|
-
`alternate_greetings` 问用户要哪一条。`character_book` 见下。
|
|
147
|
-
|
|
148
|
-
## ③ 世界书(lorebook)—— `entries`
|
|
149
|
-
|
|
150
|
-
constant 的并进世界观正文(系统层);带 keys 的挑**十条以内**做成尾部触发条目
|
|
151
|
-
(`触发: [关键词…]`),其余并进世界观。别把整本世界书搬成触发条目 —— 触发条目
|
|
152
|
-
住尾部是为了缓存稳定,几十条一起塞进去就把尾部撑成第二个前缀了。
|
|
153
|
-
|
|
154
|
-
# 建场流程
|
|
155
|
-
|
|
156
|
-
1. **先问再动手**:题材与基调、角色是谁跟用户什么关系、文风参照(有没有喜欢的
|
|
157
|
-
作品口吻)、回复长短、禁区、谁先开口。两三个问题一起问,别拷问。
|
|
158
|
-
2. 落设定文件和 `编排.yaml`,要开场白就种上。
|
|
159
|
-
3. 造前端,两件事分开做:
|
|
160
|
-
- **管线不用自己写,也不用去找**:加载本 skill 时平台已经把 `演出.template.js`
|
|
161
|
-
拷进工作区根,直接 `cp 演出.template.js <演出文件夹>/演出.js`,**一个字都别改**。
|
|
162
|
-
URL 自省、铺场、SSE 跨分片解析、错误话术、中断、输入法回车、滚动锚定、
|
|
163
|
-
摘要提示、隐私声明自检、探针模式全在里面 —— 这些是协议不是设计,现写必错
|
|
164
|
-
(真页面统计:三张手写的里两张漏了隐私声明和探针分支,还有一张每来一片就
|
|
165
|
-
抢滚动,用户往上翻看前情会被拽回底部)。
|
|
166
|
-
- **皮肤自己长**:`index.html` 按题材重设计 —— 字体、色纸、气泡形态、台面装饰
|
|
167
|
-
都是戏的一部分,手机窄屏必须能玩。接管线只需要回答"一条消息长什么样":
|
|
168
|
-
|
|
169
|
-
```html
|
|
170
|
-
<!-- ⚠️ classic script,**不许写 type="module"**:你自检截图走 file://,
|
|
171
|
-
ES module 在那儿被当跨源拦掉,整页脚本一起死 -->
|
|
172
|
-
<script src="演出.js"></script>
|
|
173
|
-
<script>
|
|
174
|
-
const 台 = document.getElementById('台');
|
|
175
|
-
const 条 = (cls, 文) => { const el = document.createElement('div');
|
|
176
|
-
el.className = cls; el.textContent = 文; 台.appendChild(el); return el; };
|
|
177
|
-
ND演出.挂载({
|
|
178
|
-
台,
|
|
179
|
-
输入: document.getElementById('输入'),
|
|
180
|
-
发送钮: document.getElementById('发送'),
|
|
181
|
-
画: {
|
|
182
|
-
用户: (文) => 条('回合 用户', 文),
|
|
183
|
-
演出: () => { const el = 条('回合 演出 等', '');
|
|
184
|
-
return { 写: (全) => { el.classList.remove('等'); el.textContent = 全; },
|
|
185
|
-
完成: (全) => { el.classList.remove('等'); el.textContent = 全; },
|
|
186
|
-
撤: () => el.remove() }; },
|
|
187
|
-
提示: (文) => 条('提示', 文),
|
|
188
|
-
出错: (文) => 条('出错', 文),
|
|
189
|
-
},
|
|
190
|
-
});
|
|
191
|
-
</script>
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
`写(全文)` 每来一片调一次,给的是**到目前为止的全文**不是增量,直接
|
|
195
|
-
`textContent = 全文` 最省事。想看一整张能跑的皮肤范例:
|
|
196
|
-
`patterns/演出页-最小实现.html`(找不到就算了,上面这段就够接)。
|
|
197
|
-
|
|
198
|
-
4. 自检:screenshot 演出页,确认铺场正常、开场白在台上。第一轮留给用户开。
|
|
199
|
-
5. 告诉用户:点开这个文件夹的站点窗就能进场;想调口味(换文风、改场景、挪冻结
|
|
200
|
-
范围)随时叫你改 —— 你就是这场戏的场务。
|
|
201
|
-
|
|
202
|
-
# 价钱小抄(跟用户聊配置时用)
|
|
203
|
-
|
|
204
|
-
gemini-3.7-flash:$0.75/M 入、$3.75/M 出(促销价,思考 token 计入输出且关不掉,
|
|
205
|
-
实测约为正文的三倍半);claude-sonnet:$3/$15。一轮典型 RP(几千字上下文、几百字
|
|
206
|
-
回复)flash 约 $0.003–0.008。预算紧就守住:最大输出别超 600、上下文预算 4–6 万。
|
|
207
|
-
|
|
208
|
-
思考档不是免费的旋钮:思考 token 一律按输出价计。gemini 三档实测差异有限(别拿它
|
|
209
|
-
当质量保证),claude 的「高」是真开扩展思考、一轮更贵。用户没提就别自作主张往上调。
|
|
210
|
-
|
|
211
|
-
# 隐私纪律(跟机制契约同级,照办)
|
|
212
|
-
|
|
213
|
-
演出记录是用户的隐私:**台词只活在 chatai 通路里,永远不进你的上下文。**
|
|
214
|
-
|
|
215
|
-
- **不读 `对话.jsonl` / `摘要.json`**(Read/Grep 会被平台拦下)。调度剧情的接口
|
|
216
|
-
是 状态/ 文件;用户想给你看片段会自己粘贴——主动粘贴即知情同意。
|
|
217
|
-
- 这两道保险住在 `演出.js` 里,改皮时**不许删也不许改**:`<meta name="nd-privacy">`
|
|
218
|
-
(用户在演出页上标注元素时,平台据此剥文本只发结构给你;模块发现页面没有会
|
|
219
|
-
自己补一条);探针分支(显式带 `?nd_probe=1` 打开时渲样例台账——⚠️ 你的自检截图走 http、跟用户预览同源,**不会**自动进这个分支;你看到的就是用户看到的,别把真问题当样例——
|
|
220
|
-
screenshot 走的就是 file://,所以你看到的是样例,那是**正常的**,不是页面坏了,
|
|
221
|
-
别去"修")。
|
|
222
|
-
- 自检截图想看特定版面状态:`artifact_open` 开进会话真的点到那一步再量(量具带 `live:true`),或 `beforeShot` 塞假台账 —— 两条都行,别想办法弄真记录。
|
|
223
|
-
|
|
224
|
-
# 边界
|
|
225
|
-
|
|
226
|
-
- 页面里永远不出现任何密钥(不需要:端点靠 cookie)。
|
|
227
|
-
- 页面不做定时轮询、自动重发 —— 限频闸会拦,但根上就别写。
|
|
228
|
-
- 演出内容平台不审(chatai 无工具无副作用),尺度是用户和角色设定之间的事。
|
|
229
|
-
|
|
230
|
-
## 板书(交稿纪律)
|
|
231
|
-
做完这一轮的东西,在它旁边写一条板书(`write_on_board near=那张卡`):这版是什么 / 为什么这么做 /
|
|
232
|
-
看哪里。改了几处就写几条要点,一条说一件事。同一件东西出了新版本,画一条「改自」线
|
|
233
|
-
(`relate_on_board` derives-from);有参考来源画「取材」。用户在你的板书上标注了,用
|
|
234
|
-
`write_on_board reply_to=` 接在那条下面回。多步任务先 TodoWrite 列步骤 —— 步骤清单会自动上板、
|
|
235
|
-
每步的产物自动连过去。
|