@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.
Files changed (353) hide show
  1. package/README.md +266 -79
  2. package/README.zh-CN.md +254 -67
  3. package/package.json +2 -2
  4. package/server/api/admin.js +7 -0
  5. package/server/api/assets/docx-page.js +5 -4
  6. package/server/api/assets/helpers.js +8 -0
  7. package/server/api/assets.js +21 -33
  8. package/server/api/board.js +29 -1
  9. package/server/api/browse.js +3 -2
  10. package/server/api/chatai.js +9 -8
  11. package/server/api/exports/cards.js +7 -6
  12. package/server/api/exports/docx-pdf.js +4 -1
  13. package/server/api/local.js +10 -8
  14. package/server/api/projects.js +19 -3
  15. package/server/api/publish.js +3 -2
  16. package/server/api/sessions-rewind.js +220 -0
  17. package/server/api/sessions.js +58 -194
  18. package/server/api/stage.js +35 -0
  19. package/server/api/turn-model-switch.js +109 -0
  20. package/server/api/turn.js +52 -88
  21. package/server/auth/middleware.js +29 -6
  22. package/server/auth/tier.js +27 -7
  23. package/server/auth/users-store.js +14 -1
  24. package/server/engine/agent/actor-trail.js +110 -0
  25. package/server/engine/agent/agent-shared.js +22 -52
  26. package/server/engine/agent/beat-state.js +61 -0
  27. package/server/engine/agent/cast.js +322 -0
  28. package/server/engine/agent/events.js +9 -2
  29. package/server/engine/agent/hooks/beat-gate.js +73 -0
  30. package/server/engine/agent/hooks/lifecycle.js +6 -2
  31. package/server/engine/agent/hooks/post-trim.js +1 -0
  32. package/server/engine/agent/hooks/pre-board-dirty.js +29 -0
  33. package/server/engine/agent/hooks/pre-defaults.js +206 -2
  34. package/server/engine/agent/hooks/pre-injectors.js +19 -2
  35. package/server/engine/agent/hooks/pre-peer-guard.js +88 -0
  36. package/server/engine/agent/hooks/pre-unknown-params.js +82 -0
  37. package/server/engine/agent/hooks/pre-workspace-scope-guard.js +33 -0
  38. package/server/engine/agent/hooks/resident-role-lifecycle.js +100 -0
  39. package/server/engine/agent/hooks/slot-alias.js +30 -0
  40. package/server/engine/agent/hooks/user-prompt-submit.js +197 -7
  41. package/server/engine/agent/hooks.js +76 -6
  42. package/server/engine/agent/isolation.js +25 -2
  43. package/server/engine/agent/memory-config.js +27 -1
  44. package/server/engine/agent/model-context.js +102 -18
  45. package/server/engine/agent/model-table.js +361 -58
  46. package/server/engine/agent/prompts/nodesign-prelude.md +566 -59
  47. package/server/engine/agent/prompts/tools/direct-edit-protocol.md +1 -1
  48. package/server/engine/agent/prompts/tools/generate-image-cookbook.md +2 -2
  49. package/server/engine/agent/prompts/tools/paint-still-cookbook.md +1 -1
  50. package/server/engine/agent/prompts/tools/site-reference.md +2 -1
  51. package/server/engine/agent/role-card.js +175 -0
  52. package/server/engine/agent/rp-mode.js +17 -0
  53. package/server/engine/agent/session-loop.js +26 -14
  54. package/server/engine/agent/stage-status.js +73 -0
  55. package/server/engine/agent/subagent-flight.js +73 -0
  56. package/server/engine/agent/system-prompts.js +41 -0
  57. package/server/engine/agent/tool-input-stream.js +165 -0
  58. package/server/engine/mcp/actor.js +59 -0
  59. package/server/engine/mcp/capability-gate.js +21 -2
  60. package/server/engine/mcp/index.js +116 -24
  61. package/server/engine/mcp/mode-profile.js +139 -0
  62. package/server/engine/mcp/server-name.js +19 -0
  63. package/server/engine/mcp/tools/artifact-session.js +2 -1
  64. package/server/engine/mcp/tools/browse-find-batch.js +76 -14
  65. package/server/engine/mcp/tools/browse.js +1 -1
  66. package/server/engine/mcp/tools/cast-role.js +167 -0
  67. package/server/engine/mcp/tools/draw-trend.js +104 -0
  68. package/server/engine/mcp/tools/edit-board-schema.js +56 -0
  69. package/server/engine/mcp/tools/edit-board.js +569 -0
  70. package/server/engine/mcp/tools/look-at-board.js +4 -3
  71. package/server/engine/mcp/tools/open-sheet.js +242 -0
  72. package/server/engine/mcp/tools/organize-board.js +19 -5
  73. package/server/engine/mcp/tools/paint-still.js +1 -1
  74. package/server/engine/mcp/tools/pin-to-board.js +78 -3
  75. package/server/engine/mcp/tools/read-board.js +109 -16
  76. package/server/engine/mcp/tools/read-tavern-json.js +87 -19
  77. package/server/engine/mcp/tools/report-issue.js +1 -0
  78. package/server/engine/mcp/tools/role-memory.js +83 -0
  79. package/server/engine/mcp/tools/roll-dice.js +82 -0
  80. package/server/engine/mcp/tools/roll-film.js +1 -1
  81. package/server/engine/mcp/tools/screenshot.js +2 -2
  82. package/server/engine/mcp/tools/set-vars.js +150 -0
  83. package/server/engine/mcp/tools/sheet-replan.js +46 -0
  84. package/server/engine/mcp/tools/web-search.js +1 -1
  85. package/server/engine/mcp/tools/write-on-board-flow.js +108 -0
  86. package/server/engine/mcp/tools/write-on-board-place.js +211 -0
  87. package/server/engine/mcp/tools/write-on-board-role-anchor.js +48 -0
  88. package/server/engine/mcp/tools/write-on-board-schema.js +125 -0
  89. package/server/engine/mcp/tools/write-on-board.js +567 -107
  90. package/server/engine/plugins/nodesign/skills/blackboard-rp/SKILL.md +215 -0
  91. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +11 -3
  92. package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +34 -4
  93. package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +20 -4
  94. package/server/engine/plugins/nodesign/skills/story-craft/SKILL.md +94 -0
  95. package/server/engine/plugins/nodesign/skills/story-import/SKILL.md +112 -0
  96. package/server/engine/plugins/nodesign/skills/story-intimacy/SKILL.md +72 -0
  97. package/server/engine/plugins/nodesign/skills/story-voice/SKILL.md +66 -0
  98. package/server/engine/plugins/nodesign/skills/story-voice/voices//345/206/267/347/241/254.md +33 -0
  99. package/server/engine/plugins/nodesign/skills/story-voice/voices//346/203/205/346/204/237/347/233/264/347/273/231.md +42 -0
  100. package/server/engine/plugins/nodesign/skills/story-voice/voices//346/204/217/350/257/206/346/265/201.md +42 -0
  101. package/server/engine/plugins/nodesign/skills/story-voice/voices//346/255/246/344/276/240/347/231/275/350/257/235.md +34 -0
  102. package/server/engine/plugins/nodesign/skills/story-voice/voices//347/224/237/346/264/273/346/265/201.md +35 -0
  103. package/server/engine/plugins/nodesign/skills/story-voice/voices//347/231/275/346/217/217.md +37 -0
  104. package/server/engine/plugins/nodesign/skills/story-voice/voices//350/210/236/345/217/260/345/211/247.md +32 -0
  105. package/server/engine/plugins/nodesign/skills/story-voice/voices//351/230/266/346/256/265/345/210/207/346/215/242.md +42 -0
  106. package/server/engine/runs/board-seater.js +272 -0
  107. package/server/engine/runs/board-tasklist.js +90 -48
  108. package/server/engine/runs/turn-relay.js +24 -2
  109. package/server/engine/runs/user-chalk-echo.js +44 -0
  110. package/server/index.js +2 -0
  111. package/server/lib/board-anchor.js +49 -0
  112. package/server/lib/board-dirty.js +93 -0
  113. package/server/lib/board-excerpt.js +80 -0
  114. package/server/lib/board-follow.js +111 -0
  115. package/server/lib/board-groups.js +1 -5
  116. package/server/lib/board-hero.js +3 -1
  117. package/server/lib/board-kind-sizes.js +36 -0
  118. package/server/lib/board-lanes.js +58 -0
  119. package/server/lib/board-obstacles.js +80 -0
  120. package/server/lib/board-relations.js +5 -1
  121. package/server/lib/board-sheets.js +467 -0
  122. package/server/lib/board-shelf.js +86 -0
  123. package/server/lib/board-transform.js +62 -0
  124. package/server/lib/canvas-id.js +18 -1
  125. package/server/lib/chalk-flow.js +88 -0
  126. package/server/lib/chalk-rewrite.js +63 -0
  127. package/server/lib/chalk-size-pref.js +48 -0
  128. package/server/lib/chalk.js +42 -3
  129. package/server/lib/ingress/forward-openai-chat.js +16 -4
  130. package/server/lib/ingress/openai-chat.js +23 -12
  131. package/server/lib/ingress/session-routes.js +3 -3
  132. package/server/lib/ingress/stream-watchdog.js +53 -0
  133. package/server/lib/ingress/upstream-billing.js +15 -0
  134. package/server/lib/ingress/upstream-error-hints.js +36 -0
  135. package/server/lib/json-preview.js +58 -0
  136. package/server/lib/model-ingress.js +26 -1
  137. package/server/lib/moderation.js +10 -5
  138. package/server/lib/quota.js +26 -6
  139. package/server/lib/rect.js +50 -0
  140. package/server/lib/screen.js +36 -0
  141. package/server/lib/sheet-state.js +19 -0
  142. package/server/lib/sketch-array.js +118 -0
  143. package/server/lib/sketch-hatch.js +99 -0
  144. package/server/lib/sketch-layout.js +269 -69
  145. package/server/lib/sketch-shapes.js +152 -0
  146. package/server/lib/sketch-stencils.js +170 -0
  147. package/server/lib/state-table.js +237 -0
  148. package/server/lib/state-trend.js +94 -0
  149. package/server/lib/state-triggers.js +199 -0
  150. package/server/lib/tavern-json.js +48 -0
  151. package/server/lib/user-uploads.js +88 -0
  152. package/server/projects/board-limits.js +5 -3
  153. package/server/projects/board-sanitize.js +220 -10
  154. package/server/projects/board-store.js +95 -6
  155. package/server/projects/session-jsonl.js +110 -0
  156. package/server/projects/store.js +56 -3
  157. package/server/projects/viewpoint-store.js +35 -3
  158. package/server/projects/workspace-slots.js +30 -0
  159. package/server/projects/workspace-templates.js +29 -0
  160. package/server/projects/workspace.js +16 -17
  161. package/server/runtime/capabilities.js +6 -2
  162. package/server/shared/locales.js +70 -0
  163. package/server/shared/messages-en.js +69 -0
  164. package/server/shared/messages.js +65 -0
  165. package/server/ws/broker.js +3 -0
  166. package/server/ws/index.js +43 -3
  167. package/web/README.md +1 -1
  168. package/web/dist/assets/{BrowserWindow-BoOBaIyz.js → BrowserWindow-CcaGRWz5.js} +1 -1
  169. package/web/dist/assets/DeckWindow-BIr2KGFT.js +6 -0
  170. package/web/dist/assets/{DocxWindow-B_u6aX9o.js → DocxWindow-B9KZmU5z.js} +2 -2
  171. package/web/dist/assets/{MermaidBlock-BO-46Du5.js → MermaidBlock-DrfRnz8E.js} +43 -43
  172. package/web/dist/assets/{SiteWindow-ConPmzdM.js → SiteWindow-CJOjCF7d.js} +6 -6
  173. package/web/dist/assets/{abnfDiagram-VCTEODGH-FCO9yf-n.js → abnfDiagram-VCTEODGH-CUZbQGIg.js} +1 -1
  174. package/web/dist/assets/{arc-BLcEOSRX.js → arc-C1-HSUny.js} +1 -1
  175. package/web/dist/assets/{architectureDiagram-5GKGNRK7-kxiX4TeN.js → architectureDiagram-5GKGNRK7-B5nwvu60.js} +2 -2
  176. package/web/dist/assets/{blockDiagram-NRAW4CY4-CptFksix.js → blockDiagram-NRAW4CY4-uPPuFLwR.js} +1 -1
  177. package/web/dist/assets/{c4Diagram-UCG6FXSJ-Dxmxj34F.js → c4Diagram-UCG6FXSJ-C0mKBFlz.js} +1 -1
  178. package/web/dist/assets/channel-BxpWNOGU.js +1 -0
  179. package/web/dist/assets/{chunk-2Q5K7J3B-D9inx5oo.js → chunk-2Q5K7J3B-Cs0rlZ4q.js} +1 -1
  180. package/web/dist/assets/{chunk-5VM5RSS4-DydfHlEc.js → chunk-5VM5RSS4-DtR63oYb.js} +1 -1
  181. package/web/dist/assets/{chunk-F27PBJKO-BO7GxhSv.js → chunk-F27PBJKO-B3MRdezJ.js} +1 -1
  182. package/web/dist/assets/{chunk-G27WJ6UU-BzP6YiQ_.js → chunk-G27WJ6UU-BtwntwpS.js} +1 -1
  183. package/web/dist/assets/{chunk-JWPE2WC7-CHcgcd0s.js → chunk-JWPE2WC7-BHsTe5PD.js} +1 -1
  184. package/web/dist/assets/{chunk-LCL6LL3I-D2Wg5JtX.js → chunk-LCL6LL3I-knY15Qdm.js} +1 -1
  185. package/web/dist/assets/{chunk-POPQ4Y6H-B2fCRfpM.js → chunk-POPQ4Y6H-C51tgK_I.js} +1 -1
  186. package/web/dist/assets/{chunk-SVP7TREG-JSLcsRlZ.js → chunk-SVP7TREG-CnwRZmF_.js} +1 -1
  187. package/web/dist/assets/{chunk-XXDRQBXY-BCjvaeHm.js → chunk-XXDRQBXY-BtXzVF-_.js} +1 -1
  188. package/web/dist/assets/classDiagram-DTDB5LWJ-DUEKmDfo.js +1 -0
  189. package/web/dist/assets/classDiagram-v2-JRS7N3AN-DUEKmDfo.js +1 -0
  190. package/web/dist/assets/{cose-bilkent-JH36ORCC-0uKv6oJ7.js → cose-bilkent-JH36ORCC-CzWlGP6i.js} +1 -1
  191. package/web/dist/assets/{cynefin-OW5HDTMX-Bvt-gEmA.js → cynefin-OW5HDTMX-vK64EGSv.js} +1 -1
  192. package/web/dist/assets/{cynefinDiagram-5FMLGOSQ-C9Ht02qC.js → cynefinDiagram-5FMLGOSQ-Bs3g1Gvh.js} +1 -1
  193. package/web/dist/assets/{dagre-3AP2YEHR-BfxZny4s.js → dagre-3AP2YEHR-BhgkYe8V.js} +1 -1
  194. package/web/dist/assets/{diagram-S7CK7UJ4-CrE0ZETE.js → diagram-S7CK7UJ4-C_6mvD2V.js} +1 -1
  195. package/web/dist/assets/{diagram-UQ7AKVKN-BO8VQ4Fx.js → diagram-UQ7AKVKN-qW7tssUW.js} +1 -1
  196. package/web/dist/assets/{diagram-VSXAHHWV-CHUZnR9F.js → diagram-VSXAHHWV-txJPHpvG.js} +1 -1
  197. package/web/dist/assets/{diagram-VX7I27RA-DYQAz1Vu.js → diagram-VX7I27RA-CQTMsCnL.js} +1 -1
  198. package/web/dist/assets/{diagram-Z3DM3KII-BDjivgLr.js → diagram-Z3DM3KII--cb1hoE6.js} +1 -1
  199. package/web/dist/assets/{ebnfDiagram-PWID7BFC-Bpe_QVbH.js → ebnfDiagram-PWID7BFC-Da6lDU77.js} +1 -1
  200. package/web/dist/assets/{erDiagram-SSCWMZ5O-BxhwuAu1.js → erDiagram-SSCWMZ5O-DGCMrsQn.js} +1 -1
  201. package/web/dist/assets/{flowDiagram-A5DVABFB-CCw2H08-.js → flowDiagram-A5DVABFB-GdOL_qno.js} +1 -1
  202. package/web/dist/assets/{ganttDiagram-EL5Y4UJY-DtTc7MyI.js → ganttDiagram-EL5Y4UJY-Cx1F_jBK.js} +4 -4
  203. package/web/dist/assets/{gitGraphDiagram-WWUBYQGX-BvORxMd2.js → gitGraphDiagram-WWUBYQGX-B5_6JhR3.js} +1 -1
  204. package/web/dist/assets/hand-CuvZZQGW.webp +0 -0
  205. package/web/dist/assets/index-BYKfMnmv.js +2759 -0
  206. package/web/dist/assets/index-CW7VsrVg.css +1 -0
  207. package/web/dist/assets/{infoDiagram-RXCK75RN-DMcrkypD.js → infoDiagram-RXCK75RN-CjvKpSPA.js} +1 -1
  208. package/web/dist/assets/{ishikawaDiagram-5VMMS53U-CZkTIbe-.js → ishikawaDiagram-5VMMS53U-CxLoAPhx.js} +1 -1
  209. package/web/dist/assets/{journeyDiagram-EYS64GPL-DR0XazdE.js → journeyDiagram-EYS64GPL-BfvS1upJ.js} +1 -1
  210. package/web/dist/assets/{kanban-definition-3QL26DDD-DN8VbwVu.js → kanban-definition-3QL26DDD-DKPPu0w4.js} +1 -1
  211. package/web/dist/assets/{layout-9_paCbyt.js → layout-Dhgck-Zo.js} +1 -1
  212. package/web/dist/assets/{linear-g6Ct012e.js → linear-DMSQ-aUw.js} +1 -1
  213. package/web/dist/assets/lxgw-nd-app-bold-D1pKA1bR.woff2 +0 -0
  214. package/web/dist/assets/lxgw-nd-app-regular-BDe3d26v.woff2 +0 -0
  215. package/web/dist/assets/lxgw-nd-bold-Bufvlg03.woff2 +0 -0
  216. package/web/dist/assets/lxgw-nd-regular-DQwoI4OP.woff2 +0 -0
  217. package/web/dist/assets/lxgwwenkaiscreen-subset-100-DLPe7tEt.woff2 +0 -0
  218. package/web/dist/assets/lxgwwenkaiscreen-subset-101-d3VsZKWT.woff2 +0 -0
  219. package/web/dist/assets/lxgwwenkaiscreen-subset-102-ClzJuLq6.woff2 +0 -0
  220. package/web/dist/assets/lxgwwenkaiscreen-subset-103-DuDnQ6K-.woff2 +0 -0
  221. package/web/dist/assets/lxgwwenkaiscreen-subset-104-kPGW3Eem.woff2 +0 -0
  222. package/web/dist/assets/lxgwwenkaiscreen-subset-105-BzD54h-0.woff2 +0 -0
  223. package/web/dist/assets/lxgwwenkaiscreen-subset-106-BQ7fIzlx.woff2 +0 -0
  224. package/web/dist/assets/lxgwwenkaiscreen-subset-107-D8tDfFHI.woff2 +0 -0
  225. package/web/dist/assets/lxgwwenkaiscreen-subset-108-tGhkpwep.woff2 +0 -0
  226. package/web/dist/assets/lxgwwenkaiscreen-subset-109-7SMVSrly.woff2 +0 -0
  227. package/web/dist/assets/lxgwwenkaiscreen-subset-110-_uDKrFed.woff2 +0 -0
  228. package/web/dist/assets/lxgwwenkaiscreen-subset-111-Bio825RE.woff2 +0 -0
  229. package/web/dist/assets/lxgwwenkaiscreen-subset-112-D2ee0i-C.woff2 +0 -0
  230. package/web/dist/assets/lxgwwenkaiscreen-subset-113-JPFVDrga.woff2 +0 -0
  231. package/web/dist/assets/lxgwwenkaiscreen-subset-114-BcfxYA4w.woff2 +0 -0
  232. package/web/dist/assets/lxgwwenkaiscreen-subset-115-B1HeeOsd.woff2 +0 -0
  233. package/web/dist/assets/lxgwwenkaiscreen-subset-116-hY12tXpq.woff2 +0 -0
  234. package/web/dist/assets/lxgwwenkaiscreen-subset-117-BvuZwmre.woff2 +0 -0
  235. package/web/dist/assets/lxgwwenkaiscreen-subset-118-tzvs_H7j.woff2 +0 -0
  236. package/web/dist/assets/lxgwwenkaiscreen-subset-119-J2DWuYDN.woff2 +0 -0
  237. package/web/dist/assets/lxgwwenkaiscreen-subset-21-pDubCJV5.woff2 +0 -0
  238. package/web/dist/assets/lxgwwenkaiscreen-subset-22-ChAPMpqp.woff2 +0 -0
  239. package/web/dist/assets/lxgwwenkaiscreen-subset-23-BBx3oNpT.woff2 +0 -0
  240. package/web/dist/assets/lxgwwenkaiscreen-subset-24-CYt-Oagk.woff2 +0 -0
  241. package/web/dist/assets/lxgwwenkaiscreen-subset-25-Cx3j-ZUv.woff2 +0 -0
  242. package/web/dist/assets/lxgwwenkaiscreen-subset-26-B9AfZ3LL.woff2 +0 -0
  243. package/web/dist/assets/lxgwwenkaiscreen-subset-27-Bh2s9MNE.woff2 +0 -0
  244. package/web/dist/assets/lxgwwenkaiscreen-subset-28-C02JPoKP.woff2 +0 -0
  245. package/web/dist/assets/lxgwwenkaiscreen-subset-29-T_tbUGhk.woff2 +0 -0
  246. package/web/dist/assets/lxgwwenkaiscreen-subset-30-DMfyNzhz.woff2 +0 -0
  247. package/web/dist/assets/lxgwwenkaiscreen-subset-31-DFfdzbkg.woff2 +0 -0
  248. package/web/dist/assets/lxgwwenkaiscreen-subset-32-Bs1fz2iW.woff2 +0 -0
  249. package/web/dist/assets/lxgwwenkaiscreen-subset-33-cpB96OkZ.woff2 +0 -0
  250. package/web/dist/assets/lxgwwenkaiscreen-subset-34-BhinPYWX.woff2 +0 -0
  251. package/web/dist/assets/lxgwwenkaiscreen-subset-35-D_njRQqA.woff2 +0 -0
  252. package/web/dist/assets/lxgwwenkaiscreen-subset-36-DqXlLE3t.woff2 +0 -0
  253. package/web/dist/assets/lxgwwenkaiscreen-subset-37-j5LbvMph.woff2 +0 -0
  254. package/web/dist/assets/lxgwwenkaiscreen-subset-38-oO4jWdKm.woff2 +0 -0
  255. package/web/dist/assets/lxgwwenkaiscreen-subset-39-CabsX4rw.woff2 +0 -0
  256. package/web/dist/assets/lxgwwenkaiscreen-subset-4-PatS3YHh.woff2 +0 -0
  257. package/web/dist/assets/lxgwwenkaiscreen-subset-40-yrLg3C3e.woff2 +0 -0
  258. package/web/dist/assets/lxgwwenkaiscreen-subset-41-DBXmCQhc.woff2 +0 -0
  259. package/web/dist/assets/lxgwwenkaiscreen-subset-42-Bqij_UiQ.woff2 +0 -0
  260. package/web/dist/assets/lxgwwenkaiscreen-subset-43-D3kp0Ntc.woff2 +0 -0
  261. package/web/dist/assets/lxgwwenkaiscreen-subset-44-B_BNGvK4.woff2 +0 -0
  262. package/web/dist/assets/lxgwwenkaiscreen-subset-45-CZc0cQWs.woff2 +0 -0
  263. package/web/dist/assets/lxgwwenkaiscreen-subset-46-CHQX5Kd9.woff2 +0 -0
  264. package/web/dist/assets/lxgwwenkaiscreen-subset-47-Ci8gLrHA.woff2 +0 -0
  265. package/web/dist/assets/lxgwwenkaiscreen-subset-48-run3ugD3.woff2 +0 -0
  266. package/web/dist/assets/lxgwwenkaiscreen-subset-49-DgfiUb6u.woff2 +0 -0
  267. package/web/dist/assets/lxgwwenkaiscreen-subset-5-B0CtGbX_.woff2 +0 -0
  268. package/web/dist/assets/lxgwwenkaiscreen-subset-50-CdCG-POr.woff2 +0 -0
  269. package/web/dist/assets/lxgwwenkaiscreen-subset-51-L2okeQZ2.woff2 +0 -0
  270. package/web/dist/assets/lxgwwenkaiscreen-subset-52-BZAqyGJW.woff2 +0 -0
  271. package/web/dist/assets/lxgwwenkaiscreen-subset-53-B0xWqLeU.woff2 +0 -0
  272. package/web/dist/assets/lxgwwenkaiscreen-subset-54-BZVQX-EA.woff2 +0 -0
  273. package/web/dist/assets/lxgwwenkaiscreen-subset-55-093mkIUJ.woff2 +0 -0
  274. package/web/dist/assets/lxgwwenkaiscreen-subset-56-CbhWG8G-.woff2 +0 -0
  275. package/web/dist/assets/lxgwwenkaiscreen-subset-57-pLQuvo6c.woff2 +0 -0
  276. package/web/dist/assets/lxgwwenkaiscreen-subset-58-BHhoY50x.woff2 +0 -0
  277. package/web/dist/assets/lxgwwenkaiscreen-subset-59-D1_6DuIQ.woff2 +0 -0
  278. package/web/dist/assets/lxgwwenkaiscreen-subset-6-BnAelmYU.woff2 +0 -0
  279. package/web/dist/assets/lxgwwenkaiscreen-subset-60-HDBIuLTZ.woff2 +0 -0
  280. package/web/dist/assets/lxgwwenkaiscreen-subset-61-C-c13SeU.woff2 +0 -0
  281. package/web/dist/assets/lxgwwenkaiscreen-subset-62-kHRVOhZ2.woff2 +0 -0
  282. package/web/dist/assets/lxgwwenkaiscreen-subset-63-C0MCsvFv.woff2 +0 -0
  283. package/web/dist/assets/lxgwwenkaiscreen-subset-64-DZekguCj.woff2 +0 -0
  284. package/web/dist/assets/lxgwwenkaiscreen-subset-65-CtP6c3IU.woff2 +0 -0
  285. package/web/dist/assets/lxgwwenkaiscreen-subset-66-DLVWoMSf.woff2 +0 -0
  286. package/web/dist/assets/lxgwwenkaiscreen-subset-67-BVuOnayH.woff2 +0 -0
  287. package/web/dist/assets/lxgwwenkaiscreen-subset-68-DpxQ6Mnx.woff2 +0 -0
  288. package/web/dist/assets/lxgwwenkaiscreen-subset-69-qesBxLmC.woff2 +0 -0
  289. package/web/dist/assets/lxgwwenkaiscreen-subset-70-BBt1xN7h.woff2 +0 -0
  290. package/web/dist/assets/lxgwwenkaiscreen-subset-71-BWqZacwB.woff2 +0 -0
  291. package/web/dist/assets/lxgwwenkaiscreen-subset-72-B1wP_Yst.woff2 +0 -0
  292. package/web/dist/assets/lxgwwenkaiscreen-subset-73-C99T7jNZ.woff2 +0 -0
  293. package/web/dist/assets/lxgwwenkaiscreen-subset-74-DrUTsWBe.woff2 +0 -0
  294. package/web/dist/assets/lxgwwenkaiscreen-subset-75-C4M5XXQe.woff2 +0 -0
  295. package/web/dist/assets/lxgwwenkaiscreen-subset-76-Bq8spVWF.woff2 +0 -0
  296. package/web/dist/assets/lxgwwenkaiscreen-subset-77-D9vNupE2.woff2 +0 -0
  297. package/web/dist/assets/lxgwwenkaiscreen-subset-78-D6th7Gnu.woff2 +0 -0
  298. package/web/dist/assets/lxgwwenkaiscreen-subset-79-bezM85cS.woff2 +0 -0
  299. package/web/dist/assets/lxgwwenkaiscreen-subset-80-DD8pnhG2.woff2 +0 -0
  300. package/web/dist/assets/lxgwwenkaiscreen-subset-81-N6jtSyBs.woff2 +0 -0
  301. package/web/dist/assets/lxgwwenkaiscreen-subset-82-D6c180bZ.woff2 +0 -0
  302. package/web/dist/assets/lxgwwenkaiscreen-subset-83-em17AadM.woff2 +0 -0
  303. package/web/dist/assets/lxgwwenkaiscreen-subset-84-CJ8gdrMK.woff2 +0 -0
  304. package/web/dist/assets/lxgwwenkaiscreen-subset-85-9PPGO1eB.woff2 +0 -0
  305. package/web/dist/assets/lxgwwenkaiscreen-subset-86-CjWPf-u5.woff2 +0 -0
  306. package/web/dist/assets/lxgwwenkaiscreen-subset-87-8D_X_yOC.woff2 +0 -0
  307. package/web/dist/assets/lxgwwenkaiscreen-subset-88-9vtKFPBx.woff2 +0 -0
  308. package/web/dist/assets/lxgwwenkaiscreen-subset-89-BhE5jAKX.woff2 +0 -0
  309. package/web/dist/assets/lxgwwenkaiscreen-subset-90-D2Npfm5E.woff2 +0 -0
  310. package/web/dist/assets/lxgwwenkaiscreen-subset-91-C6Ft1-Eu.woff2 +0 -0
  311. package/web/dist/assets/lxgwwenkaiscreen-subset-97-CaExyQP9.woff2 +0 -0
  312. package/web/dist/assets/lxgwwenkaiscreen-subset-98-CPoVbsUd.woff2 +0 -0
  313. package/web/dist/assets/lxgwwenkaiscreen-subset-99-ZUQZGVgH.woff2 +0 -0
  314. package/web/dist/assets/{mindmap-definition-FBJOCRG2-BGt4omYC.js → mindmap-definition-FBJOCRG2-CalfL_Kn.js} +1 -1
  315. package/web/dist/assets/{pegDiagram-XKGWAZYB-uSfS-PBE.js → pegDiagram-XKGWAZYB-D9mVWz7l.js} +1 -1
  316. package/web/dist/assets/pending-edit-apply-kwMFWsUe.js +31 -0
  317. package/web/dist/assets/{pieDiagram-E7YTZNPT-DpeI935H.js → pieDiagram-E7YTZNPT-DELzbZNS.js} +1 -1
  318. package/web/dist/assets/{quadrantDiagram-AXDQQJYC-B7IuNvWa.js → quadrantDiagram-AXDQQJYC-TZqp4mfA.js} +1 -1
  319. package/web/dist/assets/{railroadDiagram-O6MQD6OU-ARzYpPQI.js → railroadDiagram-O6MQD6OU-DxX59f0i.js} +1 -1
  320. package/web/dist/assets/{requirementDiagram-EFPCY7ZU-Cj4upkAo.js → requirementDiagram-EFPCY7ZU-DFdsJLIe.js} +1 -1
  321. package/web/dist/assets/{rotate-cw-CXJcyHWk.js → rotate-cw-62liz7LX.js} +1 -1
  322. package/web/dist/assets/{sankeyDiagram-P5KCCOFB-OaVwSnLP.js → sankeyDiagram-P5KCCOFB-ByYcd3fG.js} +1 -1
  323. package/web/dist/assets/{sequenceDiagram-WJ2MYXX4-1yrUbq4t.js → sequenceDiagram-WJ2MYXX4-Bj-ml1jS.js} +1 -1
  324. package/web/dist/assets/{sizeCapture-X5ZJPWSS-nYH5p9D6.js → sizeCapture-X5ZJPWSS-KhsXThyD.js} +1 -1
  325. package/web/dist/assets/{square-dashed-mouse-pointer-CTtTVKoH.js → square-dashed-mouse-pointer-D_VrqVpy.js} +1 -1
  326. package/web/dist/assets/{stateDiagram-HBIQ2CUA-Dj5HvyNq.js → stateDiagram-HBIQ2CUA-BJlpHfWU.js} +1 -1
  327. package/web/dist/assets/stateDiagram-v2-4QOOHH4V-CR2kYAH2.js +1 -0
  328. package/web/dist/assets/{swimlanes-XN3QIQJK-umeT0HpB.js → swimlanes-XN3QIQJK-DUB9OJVs.js} +1 -1
  329. package/web/dist/assets/swimlanesDiagram-VK2B7HYN-BitOxnG2.js +8 -0
  330. package/web/dist/assets/{timeline-definition-24CTP7MA-MqnQfHKB.js → timeline-definition-24CTP7MA-C03NX82A.js} +1 -1
  331. package/web/dist/assets/{vennDiagram-4TSXK5OY-b2liyZra.js → vennDiagram-4TSXK5OY-B9pjzngp.js} +1 -1
  332. package/web/dist/assets/{wardleyDiagram-VM6X3IG4-QMK-zNh1.js → wardleyDiagram-VM6X3IG4-UqWq_mcT.js} +1 -1
  333. package/web/dist/assets/{xychartDiagram-S5SC5T6Z-D5Mc6tfA.js → xychartDiagram-S5SC5T6Z-D2pDKtOv.js} +1 -1
  334. package/web/dist/index.html +2 -2
  335. package/server/engine/mcp/tools/arrange-on-board.js +0 -126
  336. package/server/engine/mcp/tools/create-on-board.js +0 -152
  337. package/server/engine/mcp/tools/edit-sketch.js +0 -167
  338. package/server/engine/mcp/tools/relate-on-board.js +0 -150
  339. package/server/engine/mcp/tools/sketch-on-board.js +0 -345
  340. package/server/engine/plugins/nodesign/skills/rp-craft/SKILL.md +0 -235
  341. 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
  342. package/server/engine/plugins/nodesign/skills/rp-craft//346/274/224/345/207/272.template.js +0 -268
  343. package/web/dist/assets/DeckWindow-Bg3rgkvi.js +0 -11
  344. package/web/dist/assets/channel-DFyiuGL0.js +0 -1
  345. package/web/dist/assets/classDiagram-DTDB5LWJ-Aw1f8z-b.js +0 -1
  346. package/web/dist/assets/classDiagram-v2-JRS7N3AN-Aw1f8z-b.js +0 -1
  347. package/web/dist/assets/index-C0KUf6Wd.css +0 -1
  348. package/web/dist/assets/index-DGG5wIZs.js +0 -2118
  349. package/web/dist/assets/lxgw-nd-bold-DbRfiTgo.woff2 +0 -0
  350. package/web/dist/assets/lxgw-nd-regular-CpMw9Ehg.woff2 +0 -0
  351. package/web/dist/assets/pending-edit-apply-uP4uvrOD.js +0 -31
  352. package/web/dist/assets/stateDiagram-v2-4QOOHH4V-kJ1m_zJT.js +0 -1
  353. package/web/dist/assets/swimlanesDiagram-VK2B7HYN-DPSkMbbl.js +0 -8
@@ -0,0 +1,70 @@
1
+ /**
2
+ * server/shared/locales.js — 支持的界面语言(服务端真相源,2026-08-26)
3
+ *
4
+ * 服务端要这份表做三件事:校验 `users.locale` 的写入、按语言挑用户可见的报错、
5
+ * 给 agent 注语言指令。前端另有一份带显示名的 `web/src/lib/i18n.js` —— 那份要
6
+ * 「中文 / English」这种给人看的标签,塞进服务端没意义。
7
+ *
8
+ * 双份表的宿命是漂移,所以配了 `web/src/lib/i18n-locales.parity.test.js` 逐项对账
9
+ * (跟 board-kind-sizes 那对表同款纪律)。加语言要同时改两处,忘了就红。
10
+ */
11
+
12
+ /** 语言 id。第一项是默认值 —— 这个产品是中文优先的,英文是第二语言 */
13
+ export const LOCALES = Object.freeze(['zh-CN', 'en']);
14
+
15
+ export const DEFAULT_LOCALE = LOCALES[0];
16
+
17
+ export function isLocale(id) {
18
+ return LOCALES.includes(id);
19
+ }
20
+
21
+ /**
22
+ * 把语言 tag 归一到支持的语言,认不出返回 null。
23
+ * `zh` / `zh-Hans` / `zh-TW` 全落 zh-CN(繁体没单独做,先给中文别给英文)。
24
+ */
25
+ export function normalizeLocale(tag) {
26
+ if (!tag) return null;
27
+ const s = String(tag).toLowerCase();
28
+ if (s.startsWith('zh')) return 'zh-CN';
29
+ if (s.startsWith('en')) return 'en';
30
+ return null;
31
+ }
32
+
33
+ /**
34
+ * 解析 HTTP `Accept-Language`,按 q 值排序取第一个认识的。
35
+ * 拿不到就返回 null —— 由调用方决定落什么默认,这里不替它做主。
36
+ *
37
+ * 例:`en-US,en;q=0.9,zh-CN;q=0.8` → 'en'
38
+ */
39
+ export function localeFromAcceptLanguage(header) {
40
+ if (!header) return null;
41
+ const ranked = String(header).split(',')
42
+ .map((part) => {
43
+ const [tag, ...params] = part.trim().split(';');
44
+ const q = params.find((p) => p.trim().startsWith('q='));
45
+ return { tag: tag.trim(), q: q ? parseFloat(q.split('=')[1]) || 0 : 1 };
46
+ })
47
+ .filter((x) => x.tag)
48
+ .sort((a, b) => b.q - a.q);
49
+ for (const { tag } of ranked) {
50
+ const hit = normalizeLocale(tag);
51
+ if (hit) return hit;
52
+ }
53
+ return null;
54
+ }
55
+
56
+ /**
57
+ * Accept-Language 里有没有**至少一个像语言 tag 的东西**(2026-08-29)。
58
+ *
59
+ * 用来区分两种"认不出":
60
+ * `ja,ko;q=0.9` —— 人家明确说了自己读日语韩语,只是我们没有这两门 → 给英文
61
+ * `???` / `;;;` / `*` / 空 —— 什么都没说清 → 中文优先照旧
62
+ * 前一种落回中文等于递给他一页看不懂的字;后一种没有任何证据,不该乱猜。
63
+ */
64
+ export function statesAnyLanguage(header) {
65
+ if (!header) return false;
66
+ return String(header).split(',').some((part) => {
67
+ const tag = part.trim().split(';')[0].trim();
68
+ return tag !== '*' && /^[a-z]{2,3}(-[a-z0-9]{2,8})*$/i.test(tag);
69
+ });
70
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * 服务端消息的英文词表(2026-08-26)。key = 源码里的中文原文,见 messages.js。
3
+ *
4
+ * 只收「用户操作的自然结果」那一类。内部错(dir 跑出工作区 / canvas.html 缺 section /
5
+ * admin 后台的参数校验)不收 —— 那些用户不该看到,翻了也没人读。
6
+ *
7
+ * 口径:跟中文原文一样直接,不加 "Sorry" 和 "Please try again later" 这种缓冲垫。
8
+ * 中文原文说「太快了,歇几秒再发」,英文就是 "Too fast. Give it a few seconds."
9
+ */
10
+ export default {
11
+ // ── 登录 / 注册(auth/middleware.js)──
12
+ // 英文用户的第一站。这几条说不对,后面做得再好也没机会被看到。
13
+ '用户名或密码错误': 'Incorrect username or password',
14
+ '尝试次数过多,{waitMin} 分钟后再试': 'Too many attempts. Try again in {waitMin} minutes.',
15
+ '这个网络今天开的号太多了,明天再来': 'Too many accounts created from this network today. Try again tomorrow.',
16
+ '没登录,语言只记在这台机器上': 'Not signed in. The language is only remembered on this device.',
17
+ 'locale 需为 {allowed} 或 null': 'locale must be one of {allowed}, or null',
18
+
19
+ // ── 模型与配额(turn.js / sessions.js / turn-model-switch.js / chatai.js)──
20
+ '这个模型仅限 Pro 档,暂未对外开放': 'This model is Pro-only and not yet publicly available',
21
+ '这个模型({model})仅限 Pro 档,暂未对外开放。换成免费模型继续':
22
+ 'This model ({model}) is Pro-only and not yet publicly available. Switch to a free model to continue.',
23
+ '这个会话指向的模型({model})现在不可用,请在模型选择器里换一个':
24
+ 'The model this session points to ({model}) is unavailable. Pick another one in the model selector.',
25
+ '还没有可用的模型:到「设置」填 API Key(或本机 claude login),或者配一个模型插槽':
26
+ 'No models available yet. Add an API key under Settings (or run claude login locally), or configure a model slot.',
27
+ '今天的免费轮次用完了({used} / {limit}),明天零点刷新':
28
+ "You're out of free turns for today ({used} / {limit}). Resets at midnight.",
29
+ '{word}用完了({used} / {limit})': '{word} exhausted ({used} / {limit})',
30
+ '太快了,歇几秒再发': 'Too fast. Give it a few seconds.',
31
+ '试用额度': 'Trial credit',
32
+ '今日额度': "Today's credit",
33
+ '这场演出正有一轮在跑,等它回完': 'This session already has a turn running. Wait for it to finish.',
34
+ '演出模式仅限 Pro 档,暂未对外开放;当前档位请用设计会话':
35
+ 'Roleplay mode is Pro-only and not yet publicly available. Use a design session on your current plan.',
36
+ '演出通路尚未对这个账号开放': 'Roleplay mode is not enabled for this account',
37
+ 'input 是空的': 'Input is empty',
38
+ 'input 超长(上限 {max} 字符)': 'Input too long (limit {max} characters)',
39
+
40
+ // ── 本地 / BYOK 配置(local.js)。npx 本地版是国际化的主战场,这几条要准 ──
41
+ '配置必须是一个对象 { upstreams, models }': 'Config must be an object: { upstreams, models }',
42
+ '写配置失败:{err}': 'Failed to write config: {err}',
43
+ '模型 {id} 不在可选清单里(没配钥匙的行不体检)':
44
+ 'Model {id} is not in the available list (rows without a key are not checked)',
45
+ '这一行正在体检,等它完': 'This row is already being checked. Wait for it to finish.',
46
+ '体检出错:{err}': 'Check failed: {err}',
47
+
48
+ // ── 文档渲染(assets/docx-page.js)──
49
+ // ⚠️ assets.js 的五条(新建/改名/重名)**没做**:那个文件正好卡在行数棘轮的
50
+ // 冻结上限 896 上,加一行 import 就超标,仓库规矩是「先拆再加」。见 README 缺口一节。
51
+ '找不到这份文档': 'Document not found',
52
+ '渲染失败': 'Rendering failed',
53
+
54
+ // ── 导出与发布(exports/cards.js、docx-pdf.js、publish.js)──
55
+ '要导出哪几张卡(cardIds 不能为空)': 'Which cards? (cardIds cannot be empty)',
56
+ '一次最多导出 {max} 张卡': 'At most {max} cards per export',
57
+ '不认识的导出格式:{format}': 'Unknown export format: {format}',
58
+ '导出排队中(这台机器一次只打两个包),过几秒再点':
59
+ 'Export queued (this machine packs two at a time). Try again in a few seconds.',
60
+ '这个项目已经有一个导出在跑了,等它完事': 'This project already has an export running. Wait for it to finish.',
61
+ '一张都没收到': 'Received no cards',
62
+ 'LibreOffice 转换失败': 'LibreOffice conversion failed',
63
+ '发布失败:Cloudflare 部署没成功,稍后再试': 'Publish failed: the Cloudflare deploy did not succeed. Try again later.',
64
+ '下线失败,稍后再试': 'Unpublish failed. Try again later.',
65
+
66
+ // ── 浏览器(browse.js)──
67
+ '没有可打开的地址(这个项目还没逛过任何站)': 'No address to open (this project has not visited any site yet)',
68
+ '网络闸拒了这个地址:{reason}': 'The network gate rejected this address: {reason}',
69
+ };
@@ -0,0 +1,65 @@
1
+ /**
2
+ * server/shared/messages.js — 服务端吐给用户的话(2026-08-26 i18n)
3
+ *
4
+ * 跟前端 `web/src/lib/i18n.js` 同一个约定:**key 就是中文原文**,查不到落回原文。
5
+ * 所以把 `error: '用户名或密码错误'` 改成 `error: msg(req, '用户名或密码错误')`
6
+ * 对中文用户是空操作 —— zh-CN 没有词表,msg() 恒等返回。
7
+ *
8
+ * ## 只翻用户真会撞上的
9
+ *
10
+ * 服务端的中文报错有两类:用户操作的自然结果(密码错、配额满、模型不开放),
11
+ * 和用户根本不该看到的内部错(`dir 跑出了项目工作区`、`canvas.html 没有 <section>`)。
12
+ * 只翻第一类。第二类翻了也没人读,还会让词表看起来比实际覆盖得多。
13
+ *
14
+ * ## 语言从哪来
15
+ *
16
+ * `req.user.locale` 优先,没有就读 `Accept-Language`。**登录失败时压根没有
17
+ * req.user**(那正是最需要说对语言的时刻),所以 header 兜底不是可选项。
18
+ */
19
+
20
+ import { DEFAULT_LOCALE, isLocale, localeFromAcceptLanguage, statesAnyLanguage } from './locales.js';
21
+ import en from './messages-en.js';
22
+
23
+ const CATALOGS = { 'zh-CN': null, en };
24
+
25
+ /**
26
+ * 这个请求该用哪种语言。
27
+ * 顺序:账号上记的 > Accept-Language > 中文。
28
+ */
29
+ export function localeOf(req) {
30
+ const fromUser = req?.user?.locale;
31
+ if (isLocale(fromUser)) return fromUser;
32
+ const header = req?.headers?.['accept-language'];
33
+ const fromHeader = localeFromAcceptLanguage(header);
34
+ if (fromHeader) return fromHeader;
35
+ // 报了 Accept-Language 但一门都不是中英(日/韩/德…):读英文的可能远大于读中文。
36
+ // 中文优先只对"没说自己读什么"的情况成立(2026-08-29,跟前端 localeFromTags 同一条规矩)。
37
+ // 畸形头 / `*` 不算"说了" —— 那是没证据,不是别的语言。
38
+ return statesAnyLanguage(header) ? 'en' : DEFAULT_LOCALE;
39
+ }
40
+
41
+ /** `{name}` 占位符。参数缺了留占位符,别把 undefined 印给用户 */
42
+ function interpolate(str, params) {
43
+ return str.replace(/\{(\w+)\}/g, (whole, k) => (
44
+ params[k] === undefined || params[k] === null ? whole : String(params[k])
45
+ ));
46
+ }
47
+
48
+ /**
49
+ * 按语言取一句话。
50
+ *
51
+ * msg(req, '用户名或密码错误')
52
+ * msg(req, '今天的免费轮次用完了({used} / {limit}),明天零点刷新', { used, limit })
53
+ *
54
+ * 查不到词条返回中文原文 —— 这是设计。
55
+ */
56
+ export function msg(req, key, params) {
57
+ return withLocale(localeOf(req), key, params);
58
+ }
59
+
60
+ /** 手上只有 locale 没有 req 时用(比如后台任务、WS 推送) */
61
+ export function withLocale(locale, key, params) {
62
+ const dict = CATALOGS[isLocale(locale) ? locale : DEFAULT_LOCALE];
63
+ const v = (dict && dict[key] !== undefined) ? dict[key] : key;
64
+ return params ? interpolate(v, params) : v;
65
+ }
@@ -11,6 +11,7 @@
11
11
  import { EventBus } from '../engine/agent/events.js';
12
12
  import { attachLiveTurnTracker } from '../engine/runs/live-turn.js';
13
13
  import { attachBoardTasklist } from '../engine/runs/board-tasklist.js';
14
+ import { attachBoardSeater } from '../engine/runs/board-seater.js';
14
15
 
15
16
  /** @type {Map<string, EventBus>} */
16
17
  const projectBuses = new Map();
@@ -25,6 +26,8 @@ export function getProjectBus(projectId) {
25
26
  attachLiveTurnTracker(bus);
26
27
  // 步骤清单镜像成板书 + 每步产物连线(2026-08-23 黑板文化,harness 做不靠 agent 记得)
27
28
  attachBoardTasklist(bus, projectId);
29
+ // 服务端入座(2026-08-25 范式重做④):本轮新产物 run 收尾一批排座
30
+ attachBoardSeater(bus, projectId);
28
31
  projectBuses.set(projectId, bus);
29
32
  }
30
33
  return bus;
@@ -31,6 +31,7 @@ import {
31
31
  closeQuerySession,
32
32
  markSessionActivity,
33
33
  } from '../engine/runs/active-runs.js';
34
+ import { workingSubagents } from '../engine/agent/subagent-flight.js';
34
35
  import { getLiveTurnSnapshot } from '../engine/runs/live-turn.js';
35
36
 
36
37
  const HEARTBEAT_INTERVAL_MS = 30_000;
@@ -46,6 +47,18 @@ const GLOBAL_CLAUDE_CONFIG_DIR = platform.claudeConfigDir;
46
47
  */
47
48
  const WS_GRACE_MS = Number(process.env.NODESIGN_WS_GRACE_MS) || 60_000;
48
49
 
50
+ /**
51
+ * 后台角色在飞时最多续多少个 grace 周期(默认 10 × 60s = 10 分钟)。
52
+ *
53
+ * 封顶不是性能优化,是**这道闸自己的保险**:SubagentStop 漏一次(hook 抛异常、
54
+ * SDK 换版本改字段名、进程被 kill -9),在飞台账里就永远躺着一个不落地的条目,
55
+ * 会话再也关不掉 —— 一个 SDK 进程 ~250MB 且 RSS 单调不减,这台盒子 swap=0。
56
+ * 封到期照常关,日志喊一声,最坏后果回到「腰斩」这个原状,不会更差。
57
+ *
58
+ * 上限按实测选:turn 中位 57s,角色写一拍 700 字实测 ~2.5min,10 分钟够宽。
59
+ */
60
+ const MAX_SUBAGENT_DEFERS = Number(process.env.NODESIGN_WS_SUBAGENT_DEFER_MAX) || 10;
61
+
49
62
  /**
50
63
  * sid → { count: 当前活跃 WS 连接数, graceTimer: 0 引用时启动的关闭定时器 }
51
64
  *
@@ -56,13 +69,15 @@ const WS_GRACE_MS = Number(process.env.NODESIGN_WS_GRACE_MS) || 60_000;
56
69
  */
57
70
  const sessionRefs = new Map();
58
71
 
59
- function refSession(sid) {
72
+ function refSession(sid, projectId = null) {
60
73
  if (!sid) return;
61
74
  let entry = sessionRefs.get(sid);
62
75
  if (!entry) {
63
- entry = { count: 0, graceTimer: null };
76
+ entry = { count: 0, graceTimer: null, projectId, subagentDefers: 0 };
64
77
  sessionRefs.set(sid, entry);
65
78
  }
79
+ if (projectId) entry.projectId = projectId; // 事件与状态按项目分桶
80
+ entry.subagentDefers = 0; // 人回来了,续命额度重新计
66
81
  entry.count += 1;
67
82
  if (entry.graceTimer) {
68
83
  clearTimeout(entry.graceTimer);
@@ -93,6 +108,31 @@ function unrefSession(sid) {
93
108
  cur.graceTimer.unref?.();
94
109
  return;
95
110
  }
111
+ // 后台角色在写也不杀(2026-08-28):主持人的一拍几秒钟就结束,角色还要写好几分钟
112
+ // —— 上面那道闸只认 turn,认不出这种在飞工作。08-28 实录:角色派出 110s 后 grace
113
+ // 到期,人被腰斩在第三个 Read 上,板上永远没有第二段。
114
+ // (08-29:判据从「在飞 − 候场」简化成「在飞」—— 角色写完就结束这一轮,
115
+ // 没有挂着不收回合的形态了。见 subagent-flight.js 头注。)
116
+ const working = workingSubagents(sid, cur.projectId);
117
+ if (working.length > 0) {
118
+ if (cur.subagentDefers < MAX_SUBAGENT_DEFERS) {
119
+ cur.subagentDefers += 1;
120
+ const who = working.map((w) => w.name || w.agentType || w.agentId.slice(0, 8)).join(', ');
121
+ console.info(
122
+ `[ws] grace expired for sid=${sid.slice(0, 8)} but ${working.length} subagent(s) still working `
123
+ + `[${who}] — deferring close (${cur.subagentDefers}/${MAX_SUBAGENT_DEFERS})`,
124
+ );
125
+ cur.graceTimer = setTimeout(onGraceExpired, WS_GRACE_MS);
126
+ cur.graceTimer.unref?.();
127
+ return;
128
+ }
129
+ // 封顶:台账没落地不代表人还活着,不能拿它无限抵押一个 250MB 的进程
130
+ console.warn(
131
+ `[ws] sid=${sid.slice(0, 8)} 续命已封顶(${MAX_SUBAGENT_DEFERS} × ${WS_GRACE_MS}ms),`
132
+ + `台账里仍有 ${working.length} 个子代理在飞 —— 照常关闭回收,它们没写完的活会丢。`
133
+ + `若这条常出现,先查 SubagentStop 是不是漏了盖章(在飞台账只进不出)。`,
134
+ );
135
+ }
96
136
  sessionRefs.delete(sid);
97
137
  if (hasActiveQuerySession(sid)) {
98
138
  console.info(`[ws] grace expired for sid=${sid.slice(0, 8)}, closing session (no_active_subscriber)`);
@@ -277,7 +317,7 @@ async function handleProjectWS(ws, pid, since = 0, sid = null) {
277
317
 
278
318
  // sid lifecycle ref:带 sid 的 WS 连上即 ref++,close/error 时 unref。0 ref 触发
279
319
  // grace timer N ms 后 closeQuerySession 让 SDK subprocess 自然退出。
280
- refSession(sid);
320
+ refSession(sid, pid);
281
321
 
282
322
  // ── 连接协议(2026-07-27 重构:快照 + 尾随,每次连接都全量重建)──
283
323
  //
package/web/README.md CHANGED
@@ -47,7 +47,7 @@ src/
47
47
  │ ├── DesignSystemList.jsx # 占位
48
48
  │ └── SkillList.jsx # 占位
49
49
  ├── components/
50
- │ ├── layout/ # AppShell / TopBar / ThreeColumnLayout
50
+ │ ├── layout/ # AppShell / TopBar / ChatDock / FloatingPanel
51
51
  │ ├── chat/ # ChatPanel / MessageList / Message / ChatComposer
52
52
  │ ├── canvas/ # CanvasFrame / Toolbar / HtmlIframe / EditOverlay / DirectEditBridge / CodeCanvas
53
53
  │ ├── context-panel/ # ContextPanel / InputsTab / SystemTab
@@ -1,4 +1,4 @@
1
- import{c as G,r as i,B as z,H as ee,j as t,I as te,F as y,a as $,G as u,b as ne,A as ie,C as N,d as c,e as j,L as W,f as se,g as re,h as oe}from"./index-DGG5wIZs.js";import{A as ae,R as le}from"./rotate-cw-CXJcyHWk.js";/**
1
+ import{c as G,r as i,B as z,H as ee,j as t,I as te,F as y,a as $,G as u,b as ne,A as ie,C as N,d as c,e as j,L as W,f as se,g as re,h as oe}from"./index-BYKfMnmv.js";import{A as ae,R as le}from"./rotate-cw-62liz7LX.js";/**
2
2
  * @license lucide-react v0.468.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -0,0 +1,6 @@
1
+ import{c as Mt,r as a,j as e,d as r,G as s,$ as Rt,R as z,F as m,a as F,a0 as Ce,u as D,X as ue,w as Ot,e as E,a1 as et,a2 as Bt,a3 as Ht,a4 as ke,a5 as _t,a6 as tt,P as nt,a7 as Dt,a8 as st,a9 as rt,aa as qt,ab as It,ac as Nt,ad as it,o as lt,ae as Gt,af as ot,k as Ut,ag as ve,m as Xt,ah as at,ai as Kt,S as Vt,Z as dt,aj as Yt,ak as Ue,al as Zt,Q as Qt,am as Jt,an as Ct,ao as en,M as tn,ap as nn,aq as sn,n as rn,A as ln}from"./index-BYKfMnmv.js";import{d as Xe,c as Ke,M as on,o as an,t as dn,E as cn,C as un,G as xn,p as pn,D as fn,b as gn,a as hn,P as yn,I as mn,R as bn,e as wn}from"./pending-edit-apply-kwMFWsUe.js";import{E as kn,S as vn}from"./square-dashed-mouse-pointer-D_VrqVpy.js";/**
2
+ * @license lucide-react v0.468.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const jn=Mt("ShieldCheck",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]]),Sn=1920,zn=1080;function En({src:n,srcDoc:t,mode:i="edit",onSelect:d,onTextEdit:c,onIframeReady:h,zoom:y=1,deckW:u=Sn,deckH:b=zn}){const o=a.useRef(null),p=a.useRef(!1),w=a.useRef(0),[g,v]=a.useState(!1);a.useEffect(()=>{v(!0)},[n,t]),a.useEffect(()=>{const l=o.current;if(!(!l||!p.current))return Xe(l),i==="edit"&&Ke(l,{onSelect:d,onTextEdit:c}),()=>Xe(l)},[i,d,c]),a.useEffect(()=>()=>{var l;try{const S=(l=o.current)==null?void 0:l.contentWindow;S&&typeof S.scrollY=="number"&&(w.current=S.scrollY)}catch{}},[n,t]);const k=()=>{p.current=!0;const l=o.current;if(l){if(h==null||h(l),i==="edit"&&Ke(l,{onSelect:d,onTextEdit:c}),w.current>0)try{requestAnimationFrame(()=>{var S;try{(S=l.contentWindow)==null||S.scrollTo(0,w.current)}catch{}})}catch{}requestAnimationFrame(()=>{requestAnimationFrame(()=>v(!1))})}};return e.jsx("div",{style:{flex:1,minHeight:0,overflow:"hidden",background:r.bgWhite,position:"relative",display:"flex",alignItems:"center",justifyContent:"center"},children:e.jsx("iframe",{ref:o,src:n,srcDoc:t,onLoad:k,sandbox:"allow-scripts allow-same-origin",style:{width:`${u}px`,height:`${b}px`,flexShrink:0,border:0,background:r.bgWhite,transform:y===1?"none":`scale(${y})`,transformOrigin:"center center",opacity:g?.5:1,transition:"opacity 180ms ease-out"}})})}function $n({candidates:n,activeId:t,onSelect:i,onAdd:d,onRemove:c,onRename:h}){if(!n||n.length===0)return null;const y=n.length===1;return e.jsxs("div",{style:{flexShrink:0,height:36,borderBottom:`1px solid ${r.border}`,background:r.bgWhite,padding:`0 ${s.lg}px`,display:"flex",alignItems:"center",gap:s.xs,overflowX:"auto",overflowY:"hidden"},children:[e.jsx(Rt,{size:11,color:r.sub,style:{flexShrink:0,marginRight:s.xs}}),n.map(u=>{const b=u.id===t;return e.jsx(Ln,{candidate:u,active:b,onSelect:()=>i==null?void 0:i(u.id),onRemove:y?null:()=>c==null?void 0:c(u.id),onRename:h},u.id)}),e.jsxs("button",{onClick:d,title:"新建候选(mock 复制当前)",style:{flexShrink:0,padding:`${s.xs}px ${s.md}px`,display:"inline-flex",alignItems:"center",gap:s.xs,fontFamily:F,fontSize:m.xs,color:r.sub,background:"transparent",border:`1px dashed ${r.borderMd}`,borderRadius:z.sm,cursor:"pointer"},onMouseEnter:u=>{u.currentTarget.style.color=r.text2,u.currentTarget.style.background="rgba(43,33,23,0.03)"},onMouseLeave:u=>{u.currentTarget.style.color=r.sub,u.currentTarget.style.background="transparent"},children:[e.jsx(Ce,{size:11})," 新候选"]}),e.jsx("div",{style:{flex:1}}),e.jsxs("span",{style:{fontFamily:F,fontSize:m.xs,color:r.sub,opacity:y?0:.7,flexShrink:0},children:[n.length," 个候选"]})]})}function Ln({candidate:n,active:t,onSelect:i,onRemove:d,onRename:c}){const[h,y]=a.useState(!1),u=D(w=>w.prompt),b=D(w=>w.confirm),o=async w=>{if(w.stopPropagation(),!c)return;const g=await u({title:"重命名候选",initialValue:n.label,placeholder:"候选名"});g&&g.trim()&&g!==n.label&&c(n.id,g.trim())},p=async w=>{w.stopPropagation(),await b({title:"删除候选",message:`删除候选「${n.label}」?`,confirmLabel:"删除",danger:!0})&&d()};return e.jsxs("div",{onMouseEnter:()=>y(!0),onMouseLeave:()=>y(!1),style:{flexShrink:0,position:"relative",display:"inline-flex",alignItems:"center",background:t?"rgba(43,33,23,0.06)":"transparent",borderRadius:z.sm,transition:"background 0.15s"},children:[e.jsx("button",{onClick:i,onDoubleClick:o,title:"双击重命名",style:{padding:`${s.xs}px ${d&&h?s.lg:s.md}px ${s.xs}px ${s.md}px`,fontFamily:F,fontSize:m.xs,fontWeight:t?500:400,color:t?r.text:r.text4,background:"transparent",borderRadius:z.sm,cursor:"pointer",transition:"all 0.15s"},children:n.label}),d&&h&&e.jsx("button",{onClick:p,style:{position:"absolute",right:2,top:"50%",transform:"translateY(-50%)",width:14,height:14,borderRadius:z.xs,background:"rgba(43,33,23,0.06)",color:r.sub,display:"flex",alignItems:"center",justifyContent:"center",cursor:"pointer"},onMouseEnter:w=>{w.currentTarget.style.background="rgba(184,58,42,0.15)",w.currentTarget.style.color=r.error},onMouseLeave:w=>{w.currentTarget.style.background="rgba(43,33,23,0.06)",w.currentTarget.style.color=r.sub},title:"删除候选",children:e.jsx(ue,{size:9})})]})}const de=12,Fn=8;function ze(n,t){const[i,d]=a.useState(null);return a.useLayoutEffect(()=>{const c=n==null?void 0:n.current;if(!c){d(null);return}const h=()=>{const y=c.getBoundingClientRect();if(!y.width&&!y.height){d(null);return}const u=window.innerWidth,b=window.innerHeight,o=Math.max(de,Math.min(y.left,u-t-de)),p=Math.min(y.bottom+Fn,b-de-120);d({position:"fixed",left:o,top:p,maxHeight:b-p-de})};return h(),window.addEventListener("resize",h),()=>window.removeEventListener("resize",h)},[n,t]),i||{position:"absolute",top:78,right:16}}function An({anchorRef:n,onClose:t,iframeDoc:i}){const d=a.useRef(null),c=ze(n,320),[h,y]=a.useState(!1),[u,b]=a.useState(null);a.useEffect(()=>{i&&o()},[i]),a.useEffect(()=>{const p=w=>{var g;d.current&&!d.current.contains(w.target)&&!((g=n==null?void 0:n.current)!=null&&g.contains(w.target))&&(t==null||t())};return window.addEventListener("mousedown",p),()=>window.removeEventListener("mousedown",p)},[t,n]);const o=()=>{i&&(y(!0),setTimeout(()=>{b(Tn(i)),y(!1)},400))};return e.jsxs("div",{ref:d,style:{...c,width:320,background:r.bgWhite,borderRadius:2,boxShadow:"0 2px 4px rgba(43,33,23,0.04), 0 8px 20px rgba(43,33,23,0.08), 0 24px 48px rgba(43,33,23,0.10), inset 0 1px 0 rgba(255,254,246,0.8)",zIndex:60,overflow:"hidden"},children:[e.jsxs("div",{style:{padding:`${s.md}px ${s.lg}px`,display:"flex",alignItems:"center",justifyContent:"space-between",borderBottom:`1px solid ${r.borderLt}`},children:[e.jsx("span",{style:{fontFamily:F,fontSize:m.sm,fontWeight:500,color:r.text},children:"Accessibility Review"}),e.jsx("button",{onClick:o,disabled:h,style:{padding:s.xs,color:r.sub,borderRadius:z.xs,opacity:h?.5:1},title:"重新扫描",children:e.jsx(Ot,{size:11,className:h?"spin":""})})]}),e.jsxs("div",{style:{padding:s.lg,maxHeight:360,overflowY:"auto"},children:[h&&e.jsx("div",{style:{fontFamily:E,fontSize:m.sm,color:r.sub,textAlign:"center",padding:s.lg},children:"扫描中…"}),!h&&u&&e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:s.sm},children:u.map((p,w)=>e.jsx(Pn,{...p},w))})]}),e.jsx("div",{style:{padding:`${s.sm}px ${s.lg}px`,background:r.bgCard,borderTop:`1px solid ${r.borderLt}`,fontFamily:E,fontSize:m.xs,color:r.sub,lineHeight:1.5},children:"P5 接 LLM 真做 contrast / hierarchy / usability 审查;当前是启发式扫描。"})]})}function Pn({kind:n,label:t,hint:i,count:d}){const c=n==="ok",h=n==="warn",y=c?et:h?Bt:ue,u=c?r.success:h?r.warn:r.error;return e.jsxs("div",{style:{display:"flex",alignItems:"flex-start",gap:s.sm,padding:`${s.xs}px ${s.sm}px`,background:c?"transparent":"rgba(43,33,23,0.02)",borderRadius:z.sm},children:[e.jsx(y,{size:12,color:u,style:{marginTop:s.xxs,flexShrink:0}}),e.jsxs("div",{style:{flex:1},children:[e.jsxs("div",{style:{fontFamily:E,fontSize:m.sm,color:r.text2,lineHeight:1.4},children:[t," ",d!=null&&e.jsxs("span",{style:{color:r.sub,fontSize:m.xs},children:["(",d,")"]})]}),i&&e.jsx("div",{style:{fontFamily:E,fontSize:m.xs,color:r.sub,marginTop:1,lineHeight:1.4},children:i})]})]})}function Tn(n){const t=[],i=Array.from(n.querySelectorAll("img")),d=i.filter(o=>!o.hasAttribute("alt"));i.length===0?t.push({kind:"ok",label:"无图片元素需要 alt",hint:"没有 <img> 标签"}):d.length===0?t.push({kind:"ok",label:"图片 alt 完整",count:i.length}):t.push({kind:"warn",label:"部分图片缺 alt",count:d.length,hint:"加 alt 才能让屏幕阅读器识别"});const c=n.querySelectorAll("h1").length,h=n.querySelectorAll("h2").length;c===0&&h===0?t.push({kind:"warn",label:"没有 h1/h2 标题",hint:"层级缺失,screen reader 难导航"}):c>5?t.push({kind:"warn",label:"h1 过多",count:c,hint:"一般每页只 1 个 h1(每 section 1 个也可)"}):t.push({kind:"ok",label:"标题层级合理",hint:`h1 × ${c}, h2 × ${h}`});const y=Array.from(n.querySelectorAll("button")),u=y.filter(o=>!(o.textContent||"").trim()&&!o.getAttribute("aria-label"));y.length===0?t.push({kind:"ok",label:"无 button 需要文本",hint:"页面没有 <button>"}):u.length===0?t.push({kind:"ok",label:"按钮文本完整",count:y.length}):t.push({kind:"warn",label:"部分按钮缺文本/aria-label",count:u.length});const b=n.documentElement;return b.hasAttribute("lang")?t.push({kind:"ok",label:`<html> 标记了 lang="${b.getAttribute("lang")}"`}):t.push({kind:"warn",label:"<html> 缺 lang 属性",hint:"影响 screen reader 发音"}),t.push({kind:"ok",label:"色彩对比度(抽样)",hint:"主色块 WCAG AA pass(mock)"}),t.push({kind:"ok",label:"焦点顺序(mock)",hint:"Tab 键 traversal — P5 真模拟"}),t}function Wn({project:n,deckSpec:t,projectId:i}){var H,I,N;const d=Ht(f=>{var P;return((P=f.contextByProject[i])==null?void 0:P.systemInfo)||null}),c=D(f=>f.showToast),h=D(f=>f.confirm),[y,u]=a.useState([]),[b,o]=a.useState(!1),[p,w]=a.useState(!1),g=a.useRef(null),v=a.useCallback(async()=>{if(i){o(!0);try{const{plugins:f}=await ke.listProject(i);u(f||[])}catch(f){console.error("[SystemTab] listProject plugins failed:",f)}finally{o(!1)}}},[i]);a.useEffect(()=>{v()},[v]);const k=a.useCallback(async(f,P=!1)=>{var T,ee,X,K,B;if(!(!f||!i)){w(!0);try{const L=await ke.installProject(i,f,{force:P}),G=(T=L==null?void 0:L.installed)==null?void 0:T.name;c(`已装 plugin \`${G}\`(新会话生效)`,"success"),(ee=L==null?void 0:L.warnings)!=null&&ee.length&&c(`警告:${L.warnings.join(";")}`,"warn"),await v()}catch(L){if(L.status===409&&((X=L.body)!=null&&X.existing)){await h({title:"覆盖已装 plugin?",message:`已装 \`${L.body.existing.name}@${L.body.existing.version}\`,将覆盖为 \`${L.body.incoming.name}@${L.body.incoming.version}\`。`,confirmLabel:"覆盖",cancelLabel:"取消",danger:!0})&&await k(f,!0);return}const G=(B=(K=L.body)==null?void 0:K.errors)!=null&&B.length?`:${L.body.errors.join(";")}`:"";c(`安装失败${G||`:${L.message}`}`,"error")}finally{w(!1),g.current&&(g.current.value="")}}},[i,v,c,h]),l=a.useCallback(async f=>{if(await h({title:"卸载 plugin?",message:`从 project 移除 \`${f}\`(新会话生效)。文件会被删除。`,confirmLabel:"卸载",cancelLabel:"取消",danger:!0}))try{await ke.removeProject(i,f),c(`已卸载 \`${f}\``,"success"),await v()}catch(T){c(`卸载失败:${T.message}`,"error")}},[i,v,c,h]),S=Array.isArray(d==null?void 0:d.skills)?d.skills.map(f=>typeof f=="string"?f:f.name||f.id).filter(Boolean):[],A=Array.isArray(d==null?void 0:d.plugins)?d.plugins.map(f=>typeof f=="string"?f:f.name||f.id).filter(Boolean):[];return e.jsxs("div",{style:{padding:s.lg,display:"flex",flexDirection:"column",gap:s.xl},children:[e.jsx(C,{label:"本会话的 Skills",children:S.length===0?e.jsx("span",{style:{fontFamily:E,fontSize:m.sm,color:r.sub},children:"(等会话起动后从 SDK system_init 拉取)"}):e.jsx("div",{style:{display:"flex",flexWrap:"wrap",gap:s.xs},children:S.map(f=>e.jsx(Ve,{icon:_t,label:f},f))})}),e.jsx(C,{label:"本会话的 Plugins",children:A.length===0?e.jsx("span",{style:{fontFamily:E,fontSize:m.sm,color:r.sub},children:"—"}):e.jsx("div",{style:{display:"flex",flexWrap:"wrap",gap:s.xs},children:A.map(f=>e.jsx(Ve,{icon:tt,label:f},f))})}),e.jsxs(C,{label:"Project 级 Plugins",children:[e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:s.xs,marginBottom:s.sm},children:b&&y.length===0?e.jsx("span",{style:{fontFamily:E,fontSize:m.sm,color:r.sub},children:"加载中…"}):y.length===0?e.jsx("span",{style:{fontFamily:E,fontSize:m.sm,color:r.sub},children:"暂无(上传 zip 安装到本 project)"}):y.map(f=>e.jsx(Mn,{plugin:f,onUninstall:()=>l(f.name)},f.name))}),e.jsx("input",{ref:g,type:"file",accept:".md,.zip,application/zip,text/markdown,text/plain",style:{display:"none"},onChange:f=>{var T;const P=(T=f.target.files)==null?void 0:T[0];P&&k(P)}}),e.jsxs("button",{onClick:()=>{var f;return(f=g.current)==null?void 0:f.click()},disabled:p,style:{display:"inline-flex",alignItems:"center",gap:s.xs,padding:`${s.xs+1}px ${s.md}px`,fontFamily:E,fontSize:m.xs,color:r.text2,background:r.bgWhite,boxShadow:nt.far,borderRadius:z.md,cursor:p?"not-allowed":"pointer",opacity:p?.6:1},children:[e.jsx(Dt,{size:11})," ",p?"安装中…":"上传 skill / plugin"]}),e.jsx("div",{style:{marginTop:s.xs,fontFamily:E,fontSize:m.xs,color:r.sub,lineHeight:1.5},children:"支持单 .md / skill zip / 完整 plugin zip。仅装到本 project;全局可见的走右上角 Skill 页面。新会话生效。"})]}),e.jsxs(C,{label:"模型",children:[e.jsx(ce,{k:"LLM",v:(d==null?void 0:d.model)||"—"}),(d==null?void 0:d.cwd)&&e.jsx(ce,{k:"cwd",v:d.cwd})]}),t&&e.jsxs(C,{label:"设计意图(spec)",children:[e.jsx(ce,{k:"metaphor",v:((H=t.meta)==null?void 0:H.metaphor)||"—"}),e.jsx(ce,{k:"audience",v:((I=t.meta)==null?void 0:I.audience)||"—"}),((N=t.meta)==null?void 0:N.intent)&&e.jsx("div",{style:{marginTop:s.sm,padding:s.md,background:r.bgCard,border:`1px solid ${r.borderLt}`,borderRadius:z.md,fontFamily:E,fontSize:m.xs,color:r.text2,lineHeight:1.6},children:t.meta.intent}),t.outline&&t.outline.length>0&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{style:{marginTop:s.lg,fontFamily:F,fontSize:m.xs,color:r.sub,textTransform:"uppercase",letterSpacing:"0.05em",marginBottom:s.sm},children:["页面 outline (",t.outline.length,")"]}),e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:s.xs},children:t.outline.map(f=>e.jsxs("div",{style:{padding:`${s.xs+1}px ${s.md}px`,fontFamily:E,fontSize:m.xs,background:"rgba(43,33,23,0.025)",borderRadius:z.sm,display:"flex",gap:s.sm,alignItems:"baseline"},children:[e.jsx("span",{style:{fontFamily:F,color:r.sub,minWidth:24},children:String(f.index).padStart(2,"0")}),e.jsx("span",{style:{color:r.text3,minWidth:64,fontFamily:F,fontSize:m.xs,textTransform:"uppercase"},children:f.layout}),e.jsx("span",{style:{color:r.text2,flex:1,lineHeight:1.4},children:f.intent})]},f.id))})]})]})]})}function Mn({plugin:n,onUninstall:t}){var i;return e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:s.sm,padding:`${s.xs+1}px ${s.sm}px`,background:"rgba(43,33,23,0.025)",borderRadius:z.sm},children:[e.jsx(tt,{size:11,color:r.text4}),e.jsxs("div",{style:{flex:1,minWidth:0},children:[e.jsxs("div",{style:{display:"flex",alignItems:"baseline",gap:s.xs},children:[e.jsx("span",{style:{fontFamily:F,fontSize:m.sm,color:r.text2},children:n.name}),e.jsx("span",{style:{fontFamily:F,fontSize:m.xs,color:r.sub},children:n.version}),e.jsxs("span",{style:{fontFamily:E,fontSize:m.xs,color:r.sub},children:["· ",((i=n.skills)==null?void 0:i.length)||0," skill"]})]}),n.description&&e.jsx("div",{style:{fontFamily:E,fontSize:m.xs,color:r.sub,lineHeight:1.4,marginTop:s.xxs,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},title:n.description,children:n.description})]}),e.jsx("button",{onClick:t,title:"卸载",style:{background:"transparent",border:"none",color:r.sub,cursor:"pointer",padding:s.xxs},children:e.jsx(st,{size:11})})]})}function Ve({icon:n,label:t}){return e.jsxs("span",{style:{display:"inline-flex",alignItems:"center",gap:s.xs,padding:`${s.xxs}px ${s.sm}px`,fontFamily:F,fontSize:m.xs,color:r.text2,background:"rgba(43,33,23,0.04)",borderRadius:z.pill},children:[e.jsx(n,{size:10,color:r.text4}),t]})}function C({label:n,children:t}){return e.jsxs("div",{children:[e.jsx("div",{style:{fontFamily:F,fontSize:m.xs,color:r.sub,textTransform:"uppercase",letterSpacing:"0.05em",marginBottom:s.sm},children:n}),e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:s.xs},children:t})]})}function ce({k:n,v:t}){return e.jsxs("div",{style:{display:"flex",gap:s.md,fontFamily:E,fontSize:m.sm},children:[e.jsx("span",{style:{color:r.sub,minWidth:70},children:n}),e.jsx("span",{style:{color:r.text2,flex:1,wordBreak:"break-word"},children:t})]})}function Rn({anchorRef:n,onClose:t,project:i,deckSpec:d,projectId:c,sessionId:h,onA11yClick:y,tweaksEnabled:u=null,onTweaksEnabledChange:b=null}){const o=D(l=>l.canvasFont),p=D(l=>l.followAgent),w=D(l=>l.setFollowAgent),g=D(l=>l.setCanvasFont),v=a.useRef(null),k=ze(n,360);return a.useEffect(()=>{const l=S=>{var A;v.current&&!v.current.contains(S.target)&&!((A=n==null?void 0:n.current)!=null&&A.contains(S.target))&&(t==null||t())};return window.addEventListener("mousedown",l),()=>window.removeEventListener("mousedown",l)},[t,n]),a.useEffect(()=>{const l=S=>{S.key==="Escape"&&(t==null||t())};return window.addEventListener("keydown",l),()=>window.removeEventListener("keydown",l)},[t]),e.jsxs("div",{ref:v,style:{...k,width:360,background:r.bgWhite,borderRadius:2,boxShadow:"0 2px 4px rgba(43,33,23,0.04), 0 8px 20px rgba(43,33,23,0.08), 0 24px 48px rgba(43,33,23,0.10), inset 0 1px 0 rgba(255,254,246,0.8)",zIndex:60,display:"flex",flexDirection:"column",overflow:"hidden"},children:[e.jsxs("div",{style:{padding:`${s.md}px ${s.lg}px`,display:"flex",alignItems:"center",gap:s.sm,borderBottom:`1px solid ${r.borderLt}`,flexShrink:0},children:[e.jsx(rt,{size:12,color:r.text4}),e.jsx("span",{style:{fontFamily:F,fontSize:m.sm,fontWeight:500,color:r.text},children:"System"})]}),e.jsxs("div",{style:{flex:1,minHeight:0,overflowY:"auto"},children:[y&&e.jsxs("div",{style:{padding:`${s.md}px ${s.lg}px`,borderBottom:`1px solid ${r.borderLt}`,display:"flex",flexDirection:"column",gap:s.sm},children:[e.jsx("div",{style:{fontFamily:F,fontSize:m.xs,color:r.sub,textTransform:"uppercase",letterSpacing:"0.05em"},children:"Canvas 工具"}),e.jsx("div",{style:{display:"flex",gap:s.sm},children:e.jsxs("button",{onClick:()=>{y(),t==null||t()},style:On,title:"无障碍审查(mock)",children:[e.jsx(jn,{size:11})," A11y"]})})]}),b&&e.jsxs("div",{style:{padding:`${s.md}px ${s.lg}px`,borderBottom:`1px solid ${r.borderLt}`,display:"flex",alignItems:"center",gap:s.sm},children:[e.jsxs("div",{style:{flex:1,minWidth:0},children:[e.jsx("div",{style:{fontFamily:E,fontSize:m.sm,color:r.text},children:"Tweaks 模式"}),e.jsx("div",{style:{fontFamily:E,fontSize:m.xs,color:r.sub,marginTop:2,lineHeight:1.5},children:u?"开:agent 会主动把核心参数做成控件让你拖":"关:不暴露控件,改样式走对话"})]}),e.jsx(Ye,{checked:!!u,onChange:b})]}),e.jsxs("div",{style:{padding:`${s.md}px ${s.lg}px`,borderBottom:`1px solid ${r.borderLt}`,display:"flex",alignItems:"center",gap:s.sm},children:[e.jsxs("div",{style:{flex:1,minWidth:0},children:[e.jsx("div",{style:{fontFamily:E,fontSize:m.sm,color:r.text},children:"镜头跟随 agent"}),e.jsx("div",{style:{fontFamily:E,fontSize:m.xs,color:r.sub,marginTop:2,lineHeight:1.5},children:p?"开:agent 换到哪个产物,镜头就飞过去(你操作后 8 秒内不抢)":"关:镜头只听你的;agent 在动什么看光圈"})]}),e.jsx(Ye,{checked:p,onChange:w})]}),e.jsxs("div",{style:{padding:`${s.md}px ${s.lg}px`,borderBottom:`1px solid ${r.borderLt}`},children:[e.jsx("div",{style:{fontFamily:E,fontSize:m.sm,color:r.text},children:"画布手写字体"}),e.jsx("div",{style:{fontFamily:E,fontSize:m.xs,color:r.sub,marginTop:2,marginBottom:s.sm},children:"用「文字」工具(T)在画布上写字时用这个"}),e.jsx("div",{style:{display:"flex",gap:s.xs,flexWrap:"wrap"},children:Object.entries(qt).map(([l,S])=>e.jsx("button",{onClick:()=>g({...o,font:l}),style:{padding:`${s.xs}px ${s.sm}px`,borderRadius:z.md,cursor:"pointer",fontFamily:It[l],fontSize:m.sm,border:`1px solid ${o.font===l?r.text:r.borderLt}`,background:o.font===l?r.text:"transparent",color:o.font===l?r.bg:r.text2},children:S},l))}),e.jsx("div",{style:{display:"flex",gap:s.xs,marginTop:s.sm},children:Object.entries(Nt).map(([l,S])=>e.jsx("button",{onClick:()=>g({...o,size:l}),style:{padding:`${s.xs}px ${s.sm}px`,borderRadius:z.md,cursor:"pointer",fontFamily:E,fontSize:m.xs,border:`1px solid ${o.size===l?r.text:r.borderLt}`,background:o.size===l?r.text:"transparent",color:o.size===l?r.bg:r.text2},children:S},l))})]}),e.jsx(Wn,{project:i,deckSpec:d,projectId:c})]}),e.jsx("div",{style:{padding:`${s.sm}px ${s.lg}px`,background:r.bgCard,borderTop:`1px solid ${r.borderLt}`,fontFamily:E,fontSize:m.xs,color:r.sub,lineHeight:1.5,flexShrink:0},children:"spec 不可在此编辑 — 改 spec 跟 agent 说,触发新 run。"})]})}function Ye({checked:n,onChange:t,title:i}){return e.jsx("button",{onClick:()=>t==null?void 0:t(!n),title:i,style:{width:28,height:16,padding:0,border:"none",borderRadius:z.lg,background:n?r.text:"rgba(43,33,23,0.18)",position:"relative",cursor:"pointer",transition:"background 0.15s",flexShrink:0},children:e.jsx("span",{style:{position:"absolute",top:2,left:n?14:2,width:12,height:12,borderRadius:z.round,background:r.bgWhite,boxShadow:it.crispSm,transition:"left 0.15s"}})})}const On={padding:`${s.xs+1}px ${s.md}px`,fontFamily:F,fontSize:m.xs,color:r.text4,background:"rgba(43,33,23,0.04)",border:"none",borderRadius:z.sm,display:"inline-flex",alignItems:"center",gap:s.xs,cursor:"pointer"};function Bn({comments:n=[],iframeDoc:t,iframeRef:i,anchorRef:d,onClose:c,onResolveComment:h,onDeleteComment:y,onSelectAnchor:u}){const b=a.useRef(null),o=ze(d,380);a.useEffect(()=>{const g=v=>{var k;b.current&&!b.current.contains(v.target)&&!((k=d==null?void 0:d.current)!=null&&k.contains(v.target))&&(c==null||c())};return window.addEventListener("mousedown",g),()=>window.removeEventListener("mousedown",g)},[c,d]),a.useEffect(()=>{const g=v=>{v.key==="Escape"&&(c==null||c())};return window.addEventListener("keydown",g),()=>window.removeEventListener("keydown",g)},[c]);const p=a.useMemo(()=>{const g=new Map;for(const k of n){const l=t&&k.anchor?lt(k.anchor,t.body):null,S=l?Gt(l):null,A=(S==null?void 0:S.index)??null;g.has(A)||g.set(A,[]),g.get(A).push({comment:k,el:l})}return[...g.entries()].sort((k,l)=>k[0]===null?1:l[0]===null?-1:k[0]-l[0])},[n,t]),w=g=>{if(g.el)try{g.el.scrollIntoView({behavior:"smooth",block:"center"})}catch{}u&&u(g.comment.anchor)};return e.jsxs("div",{ref:b,style:{...o,width:380,background:r.bgWhite,borderRadius:2,boxShadow:"0 2px 4px rgba(43,33,23,0.04), 0 8px 20px rgba(43,33,23,0.08), 0 24px 48px rgba(43,33,23,0.10), inset 0 1px 0 rgba(255,254,246,0.8)",zIndex:60,display:"flex",flexDirection:"column",overflow:"hidden"},children:[e.jsxs("div",{style:{padding:`${s.md}px ${s.lg}px`,display:"flex",alignItems:"center",gap:s.sm,borderBottom:`1px solid ${r.borderLt}`,flexShrink:0},children:[e.jsx(ot,{size:12,color:r.text4}),e.jsxs("span",{style:{fontFamily:F,fontSize:m.sm,fontWeight:500,color:r.text,flex:1},children:["全部评论 ",n.length>0&&`(${n.length})`]}),e.jsx("button",{onClick:c,title:"关闭",style:{padding:s.xs,color:r.text5,borderRadius:z.xs,background:"transparent",border:"none",cursor:"pointer"},children:e.jsx(ue,{size:12})})]}),e.jsx("div",{style:{flex:1,minHeight:0,overflowY:"auto"},children:n.length===0?e.jsxs("div",{style:{padding:`${s.lg+s.md}px ${s.lg}px`,fontFamily:E,fontSize:m.sm,color:r.sub,textAlign:"center",lineHeight:1.6},children:["还没有评论。",e.jsx("br",{}),"点击画布上任意元素 → 在弹出的浮卡里写评论。"]}):p.map(([g,v])=>e.jsxs("div",{style:{padding:`${s.md}px ${s.lg}px`,borderBottom:`1px solid ${r.borderLt}`},children:[e.jsxs("div",{style:{fontFamily:F,fontSize:m.xs,color:r.sub,textTransform:"uppercase",letterSpacing:"0.05em",marginBottom:s.sm},children:[g===null?"未定位":`第 ${g+1} 页`," · ",v.length," 条"]}),e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:s.xs},children:v.map(({comment:k,el:l})=>e.jsx(Hn,{comment:k,available:!!l,onClick:()=>w({comment:k,el:l}),onResolve:()=>h==null?void 0:h(k.id),onDelete:()=>y==null?void 0:y(k.id)},k.id))})]},g??"orphan"))}),e.jsx("div",{style:{padding:`${s.sm}px ${s.lg}px`,background:r.bgCard,borderTop:`1px solid ${r.borderLt}`,fontFamily:E,fontSize:m.xs,color:r.sub,lineHeight:1.5,flexShrink:0},children:"点击评论 → 跳到对应元素;evaluator 切到 edit 模式后可在浮卡续写。"})]})}function Hn({comment:n,available:t,onClick:i,onResolve:d,onDelete:c}){var y,u,b;const h=n.status==="resolved";return e.jsxs("div",{style:{padding:`${s.sm}px ${s.md}px`,background:r.bgWhite,boxShadow:nt.far,borderRadius:z.md,display:"flex",flexDirection:"column",gap:s.xs,opacity:h?.5:1,cursor:t?"pointer":"default",transition:"background 0.1s"},onClick:t?i:void 0,onMouseEnter:o=>{t&&(o.currentTarget.style.background="rgba(43,33,23,0.02)")},onMouseLeave:o=>{o.currentTarget.style.background=r.bgWhite},children:[e.jsx("div",{style:{fontFamily:E,fontSize:m.sm,color:r.text2,lineHeight:1.5,textDecoration:h?"line-through":"none",whiteSpace:"pre-wrap",wordBreak:"break-word"},children:n.text}),e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:s.xs,fontFamily:F,fontSize:m.xs,color:r.sub},children:[t?e.jsxs(e.Fragment,{children:[e.jsx(on,{size:9}),e.jsx("span",{children:((y=n.anchor)==null?void 0:y.dataId)||((b=(u=n.anchor)==null?void 0:u.path)==null?void 0:b.split(">").pop())||"元素"})]}):e.jsx("span",{style:{fontStyle:"italic"},children:"元素已不在"}),e.jsx("span",{style:{flex:1}}),e.jsx("button",{onClick:o=>{o.stopPropagation(),d()},title:h?"取消解决":"标记解决",style:{padding:s.xxs,color:r.sub,borderRadius:z.xs,background:"transparent",border:"none",cursor:"pointer"},children:e.jsx(et,{size:11})}),e.jsx("button",{onClick:o=>{o.stopPropagation(),c()},title:"删除",style:{padding:s.xxs,color:r.sub,borderRadius:z.xs,background:"transparent",border:"none",cursor:"pointer"},children:e.jsx(st,{size:11})})]})]})}function _n({iframeDoc:n,active:t}){const[i,d]=a.useState([]),[c,h]=a.useState(0);a.useEffect(()=>{if(!t||!n){d([]);return}let u=null;const b=()=>{const p=Array.from(n.querySelectorAll("section[data-page]"));if(d(p),u&&u.disconnect(),!!p.length)try{u=new(n.defaultView||window).IntersectionObserver(w=>{const g=w.filter(k=>k.isIntersecting).sort((k,l)=>l.intersectionRatio-k.intersectionRatio)[0];if(!g)return;const v=p.indexOf(g.target);v>=0&&h(v)},{threshold:[.35,.6]}),p.forEach(w=>u.observe(w))}catch{}};b();let o=null;try{o=new(n.defaultView||window).MutationObserver(()=>b()),n.body&&o.observe(n.body,{childList:!0,subtree:!0})}catch{}return()=>{u==null||u.disconnect(),o==null||o.disconnect()}},[n,t]);const y=a.useCallback(u=>{h(b=>{const o=Math.max(0,Math.min(i.length-1,b+u)),p=i[o];if(p)try{p.scrollIntoView({behavior:"smooth",block:"start"})}catch{}return o})},[i]);return a.useEffect(()=>{if(!t||i.length<2)return;const u=o=>{if(o.key!=="ArrowLeft"&&o.key!=="ArrowRight")return;const p=o.target;p&&(p.tagName==="INPUT"||p.tagName==="TEXTAREA"||p.isContentEditable)||(o.preventDefault(),y(o.key==="ArrowRight"?1:-1))};window.addEventListener("keydown",u);let b=null;try{b=n,b==null||b.addEventListener("keydown",u)}catch{}return()=>{window.removeEventListener("keydown",u);try{b==null||b.removeEventListener("keydown",u)}catch{}}},[t,i.length,y,n]),!t||i.length<2?null:e.jsxs("div",{style:{position:"absolute",bottom:16,left:"50%",transform:"translateX(-50%)",display:"flex",alignItems:"center",gap:s.xxs,background:"rgba(28,24,18,0.86)",borderRadius:z.pill,padding:s.xs,boxShadow:"0 6px 22px rgba(93,74,44,0.242)",zIndex:30,backdropFilter:"blur(6px)"},children:[e.jsx(Ze,{onClick:()=>y(-1),disabled:c===0,icon:Ut,title:"上一页(←)"}),e.jsxs("span",{style:{fontFamily:F,fontSize:m.sm,color:ve.ink,padding:`0 ${s.md}px`,minWidth:52,textAlign:"center",userSelect:"none"},children:[c+1," / ",i.length]}),e.jsx(Ze,{onClick:()=>y(1),disabled:c===i.length-1,icon:Xt,title:"下一页(→)"})]})}function Ze({onClick:n,disabled:t,icon:i,title:d}){return e.jsx("button",{onClick:n,disabled:t,title:d,style:{display:"flex",alignItems:"center",justifyContent:"center",width:26,height:26,borderRadius:z.round,border:"none",background:"transparent",color:t?at(ve.ink,.3):ve.ink,cursor:t?"default":"pointer",transition:"background 0.15s"},onMouseEnter:c=>{t||(c.currentTarget.style.background="rgba(255,254,246,0.12)")},onMouseLeave:c=>{c.currentTarget.style.background="transparent"},children:e.jsx(i,{size:15})})}function Dn({edits:n=[],onApply:t,onUndo:i,onClearAll:d,canUndo:c=!1,isRunning:h=!1}){const y=n.length;if(y===0)return null;const u=qn(n);return e.jsxs("div",{style:{position:"absolute",bottom:16,left:"50%",transform:"translateX(-50%)",background:"rgba(43,33,23, 0.94)",color:r.btnText,borderRadius:z.lg,padding:`${s.sm}px ${s.lg}px`,display:"flex",alignItems:"center",gap:s.md,fontFamily:F,fontSize:m.xs,boxShadow:"0 8px 32px rgba(93,74,44,0.275), 0 2px 8px rgba(93,74,44,0.198)",zIndex:50,backdropFilter:"blur(8px)",WebkitBackdropFilter:"blur(8px)"},children:[e.jsx("span",{style:{padding:`${s.xxs}px ${s.md}px`,background:"rgba(245, 240, 232, 0.15)",borderRadius:z.xl,fontWeight:600,minWidth:22,textAlign:"center"},children:y}),e.jsxs("span",{style:{color:"#dfd6c6"},children:[u," 待应用"]}),e.jsx("div",{style:{width:1,height:18,background:"rgba(245,240,232,0.2)"}}),e.jsxs("button",{onClick:i,disabled:!c,title:c?"撤销最近一次 (Cmd+Z)":"无可撤销操作",style:{...Qe,opacity:c?1:.4,cursor:c?"pointer":"not-allowed"},children:[e.jsx(Kt,{size:11})," 撤销"]}),e.jsxs("button",{onClick:d,title:"全部撤销 — 把所有 pending 编辑清掉",style:Qe,children:[e.jsx(ue,{size:11})," 全部撤销"]}),e.jsxs("button",{onClick:t,disabled:h,title:h?"agent 正在跑,等当前 run 结束":"让 agent 把这些调整落到源代码",style:{...In,opacity:h?.6:1,cursor:h?"wait":"pointer"},children:[e.jsx(Vt,{size:11})," 应用"]})]})}function qn(n){const t={move:0,style:0,dup:0,del:0};for(const d of n)d.kind==="pending-move"?t.move++:d.kind==="pending-style"?t.style++:d.kind==="pending-duplicate"?t.dup++:d.kind==="pending-delete"&&t.del++;const i=[];return t.move&&i.push(`${t.move} 拖动`),t.dup&&i.push(`${t.dup} 复制`),t.style&&i.push(`${t.style} 样式`),t.del&&i.push(`${t.del} 删除`),i.join(" · ")||"调整"}const Qe={display:"inline-flex",alignItems:"center",gap:s.xs,padding:`${s.xs}px ${s.base}px`,fontFamily:"inherit",fontSize:m.xs,color:r.btnText,background:"transparent",border:"1px solid rgba(245,240,232,0.25)",borderRadius:z.sm},In={display:"inline-flex",alignItems:"center",gap:s.xs,padding:"5px 14px",fontFamily:"inherit",fontSize:m.xs,fontWeight:600,color:r.btn,background:r.btnText,border:"none",borderRadius:z.sm,boxShadow:"0 1px 2px rgba(93,74,44,0.165)"},Je=dt.purple,Nn=at(dt.purple,.18);function Gn({edits:n=[],iframeRef:t,zoom:i=1}){const[,d]=a.useState(0);a.useEffect(()=>{if(n.length===0)return;const g=t==null?void 0:t.current;if(!g)return;const v=g.contentWindow;if(!v)return;let k=null;const l=()=>{k||(k=requestAnimationFrame(()=>{k=null,d(S=>S+1)}))};try{v.addEventListener("scroll",l,{passive:!0,capture:!0}),v.addEventListener("resize",l),window.addEventListener("resize",l)}catch{}return()=>{try{v.removeEventListener("scroll",l,{capture:!0}),v.removeEventListener("resize",l),window.removeEventListener("resize",l)}catch{}k&&cancelAnimationFrame(k)}},[n.length,t]);const c=a.useMemo(()=>n.filter(g=>(g.kind==="pending-move"||g.kind==="pending-duplicate")&&g.reactMount===!0),[n]);if(c.length===0||!(t!=null&&t.current))return null;const h=t.current,y=h.contentDocument;if(!y)return null;const u=h.getBoundingClientRect(),b=an(h);if(!b)return null;const o=h.contentWindow,p=(o==null?void 0:o.innerWidth)??u.width/i,w=(o==null?void 0:o.innerHeight)??u.height/i;return e.jsx(e.Fragment,{children:c.map((g,v)=>{var f;const k=lt(g.anchor,y.body);if(!k)return null;const l=k.getBoundingClientRect();if(l.bottom<=0||l.top>=w||l.right<=0||l.left>=p)return null;const{top:S,left:A}=dn(b,l.left,l.top,i),H=l.width*i,I=l.height*i,N=(f=g.aiContext)!=null&&f.targetContainerTag?`${g.aiContext.targetContainerTag}`:"目标容器";return e.jsxs("div",{style:{position:"absolute",top:0,left:0,pointerEvents:"none"},children:[e.jsx("div",{style:{position:"absolute",pointerEvents:"none",top:S-3,left:A-3,width:H+6,height:I+6,border:`2px dashed ${Je}`,borderRadius:z.sm,boxShadow:`0 0 0 3px ${Nn}`,zIndex:9}}),e.jsxs("div",{style:{position:"absolute",pointerEvents:"none",top:S-12,left:A+H-8,transform:"translateX(-100%)",padding:`${s.xxs}px ${s.sm}px`,fontFamily:F,fontSize:m.xs,lineHeight:"14px",fontWeight:500,color:r.bgWhite,background:Je,borderRadius:z.xs,whiteSpace:"nowrap",boxShadow:it.crisp,zIndex:11,maxWidth:Math.max(120,H),overflow:"hidden",textOverflow:"ellipsis"},children:[g.kind==="pending-duplicate"?"复制到":"移到"," <",N,">"]})]},v)})})}const je={"16:9":{w:1920,h:1080},"16:10":{w:1920,h:1200},"9:16":{w:1080,h:1920},"4:3":{w:1440,h:1080}},Se="16:9";function Un(n){if(!n||typeof n!="string")return Se;const t=n.match(/<div\b[^>]*class\s*=\s*['"][^'"]*__nd-deck-wrap[^'"]*['"][^>]*data-deck-aspect\s*=\s*['"]([^'"]+)['"]/i)||n.match(/<div\b[^>]*data-deck-aspect\s*=\s*['"]([^'"]+)['"][^>]*class\s*=\s*['"][^'"]*__nd-deck-wrap[^'"]*['"]/i);return t&&je[t[1]]?t[1]:Se}function Yn({tab:n,onTabChange:t,onClose:i,artifactExports:d=null,onExport:c=null,onToolbarGroups:h=null,title:y="幻灯",htmlSrc:u,htmlContent:b,selectedAnchor:o,onSelectChange:p,onTextEdit:w,onIframeReady:g,candidates:v,activeCandidateId:k,onSelectCandidate:l,onAddCandidate:S,onRemoveCandidate:A,onRenameCandidate:H,project:I,deckSpec:N,projectId:f,sessionId:P,comments:T=[],onAddComment:ee,onResolveComment:X,onDeleteComment:K,onRegionComment:B=null,tweaksAvailable:L=!1,pendingEdits:G=[],onCommitMove:q,onCommitFreePosition:xe,onSubmitDragNote:ct,lastPendingEditId:ut=null,onApplyPendingEdits:xt,onUndoPending:pt,onClearAllPending:ft,canUndoPending:gt=!1,isStreaming:U=!1}){var Me,Re,Oe,Be,He,_e,De,qe,Ie,Ne;const{panels:pe,setPanelVisible:ht}=Yt(),fe=!!((Me=pe==null?void 0:pe.tweaks)!=null&&Me.visible),yt=()=>ht("tweaks",!fe),[V,Ee]=a.useState(!1);a.useEffect(()=>{U&&n==="drag"&&(t==null||t("edit"))},[U,n,t]);const mt=a.useRef(null),[ge,bt]=a.useState(!1),[$e,wt]=a.useState(null),[kt,Le]=a.useState(!1),[Y,te]=a.useState("fit"),[Z,vt]=a.useState({width:0,height:0}),[Fe,jt]=a.useState(0),[he,ne]=a.useState(""),[ye,se]=a.useState(!1),[_,Ae]=a.useState(null),[me,re]=a.useState(!1),[Q,ie]=a.useState(!1),[J,le]=a.useState(!1),[oe,Pe]=a.useState(!0),O=a.useRef(null),be=a.useRef(null),Te=a.useRef(null);a.useEffect(()=>{if(!f||!P)return;let x=!1;return Ue.read(f).then(({config:j})=>{x||j&&typeof j.tweaks_mode_enabled=="boolean"&&Pe(j.tweaks_mode_enabled)}).catch(()=>{}),()=>{x=!0}},[f,P]);const St=a.useCallback(x=>{Pe(x),!(!f||!P)&&Ue.patch(f,{tweaks_mode_enabled:x}).catch(()=>{})},[f,P]);a.useEffect(()=>{const x=O.current;if(!x)return;const j=()=>{const M=x.getBoundingClientRect();vt(R=>R.width===M.width&&R.height===M.height?R:{width:M.width,height:M.height})};j();let $=null;try{$=new ResizeObserver(j),$.observe(x)}catch{}return window.addEventListener("resize",j),()=>{$&&$.disconnect(),window.removeEventListener("resize",j)}},[n]);const We=Un(he),ae=je[We]||je[Se],W=Y==="fit"?Z.width>0&&Z.height>0?Math.min(Z.width/ae.w,Z.height/ae.h):1:Y,zt=v&&v.length>=1;a.useEffect(()=>{se(!1),Ae(null)},[k]),a.useEffect(()=>{const x=$=>{var R;if($.key!=="Escape")return;const M=$.target;((R=M==null?void 0:M.getAttribute)==null?void 0:R.call(M,"contenteditable"))!=="true"&&(Q||me||J||(o?p==null||p(null):i==null||i()))};window.addEventListener("keydown",x);let j=null;try{j=_,j==null||j.addEventListener("keydown",x)}catch{}return()=>{window.removeEventListener("keydown",x);try{j==null||j.removeEventListener("keydown",x)}catch{}}},[o,_,p,i,Q,me,J]),a.useEffect(()=>{const x=j=>{const $=j==null?void 0:j.data;if(!(!$||typeof $!="object")&&$.type==="canvas-page-change"&&Number.isFinite($.page))try{window.dispatchEvent(new CustomEvent("nd-canvas-page-change",{detail:{page:$.page}}))}catch{}};return window.addEventListener("message",x),()=>window.removeEventListener("message",x)},[]),a.useEffect(()=>{if(!u){ne(b||""),se(!1);return}fetch(u).then(x=>x.text()).then(x=>{ne(x),se(!1)}).catch(()=>ne("<!-- 无法加载源码 -->"))},[u,b,Fe]);const Et=x=>{p==null||p((x==null?void 0:x.anchor)||null)},$t=x=>{w==null||w(x),console.log("[direct edit]",x)},Lt=a.useCallback(x=>{ne(x),se(!0)},[]),Ft=a.useCallback(x=>{try{Ae(x.contentDocument)}catch{}g==null||g(x)},[g]),At=()=>{jt(x=>x+1),p==null||p(null)},Pt=()=>{re(!1),ie(!1),le(x=>!x)},Tt=()=>{re(!1),le(!1),ie(x=>!x)},we=Array.isArray(T)?T.filter(x=>x.status!=="resolved").length:0,Wt=a.useMemo(()=>[rn({kind:"deck",exports:d,onExport:c}),{id:"mode",type:"mode",value:n,onChange:x=>{t==null||t(x),p==null||p(null)},items:[{id:"preview",icon:kn,label:"预览",title:"看成品(左右键翻页)"},{id:"edit",icon:Zt,label:"编辑",title:"双击文字直接改 · 单击元素弹评论卡"},{id:"drag",icon:Qt,label:"拖拽",disabled:U,title:U?"agent 正在跑,拖拽暂停以免和它抢同一份源码":"拖动元素调布局"},{id:"region",icon:vn,label:"圈选",disabled:!B,title:B?"框一块地方说事 —— 框住谁、当时长什么样、你想说什么,一起交给 agent":"要先开一个会话才能把圈选交给 agent"},{id:"code",icon:Jt,label:"源码",title:"看/改这一份 HTML"}]},n==="drag"&&{id:"dragmode",items:[{id:"free",icon:Ct,label:V?"自由":"嵌入",active:V,title:V?"自由摆放 · 松手落到像素位置(P 切回嵌入)":"嵌入 · 松手按 DOM 树插进容器(P 切自由摆放)",onClick:()=>Ee(x=>!x)}]},n!=="code"&&{id:"zoom",items:[{id:"zoomOut",icon:en,title:"缩小",onClick:()=>te(Math.max(.25,W-.1))},{id:"zoomLevel",label:`${Math.round(W*100)}%`,active:Y==="fit",title:Y==="fit"?"当前自适应窗口":"点一下自适应窗口",onClick:()=>te("fit")},{id:"zoomIn",icon:Ce,title:"放大",onClick:()=>te(Math.min(3,W+.1))},{id:"fit",icon:tn,title:"自适应窗口",onClick:()=>te("fit")}]},{id:"actions",items:[{id:"tweaks",icon:nn,active:fe,disabled:!oe,title:oe?L?"Tweaks:拖控件实时改样式":"Tweaks(agent 还没暴露参数 — 跟它说一句让它 expose_tweaks)":"Tweaks 模式已关(在 System 里开)",onClick:yt},{id:"comment",icon:ot,label:we>0?String(we):void 0,active:J,btnRef:Te,title:"这一份上已有的评论",onClick:Pt},{id:"reload",icon:sn,title:"重载(agent 改完没刷新时用)",onClick:At},{id:"system",icon:rt,active:Q,btnRef:be,title:"System:项目档案 / Tweaks 模式 / A11y",onClick:Tt}]}].filter(Boolean),[n,U,V,Y,W,fe,oe,L,J,Q,we,B,d,c]);return e.jsxs(ln,{kind:"deck",title:y,subtitle:We,onClose:i,escToClose:!1,groups:Wt,onToolbarGroups:h,headerExtra:zt?e.jsx($n,{candidates:v,activeId:k,onSelect:l,onAdd:S,onRemove:A,onRename:H}):null,children:[(n==="edit"||n==="preview"||n==="drag"||n==="region")&&e.jsxs("div",{ref:O,style:{flex:1,minHeight:0,position:"relative",display:"flex",flexDirection:"column"},children:[e.jsx(En,{src:ye?void 0:u,srcDoc:ye?he:u?void 0:b,mode:n==="drag"||n==="region"?"preview":n,onSelect:Et,onTextEdit:$t,onIframeReady:Ft,zoom:W,deckW:ae.w,deckH:ae.h},`${k||"default"}-${Fe}-${ye?"doc":"src"}`),n==="edit"&&o&&e.jsx(cn,{selectedAnchor:o,iframeRef:{current:(Re=O.current)==null?void 0:Re.querySelector("iframe")},zoom:W}),e.jsx(un,{comments:T,iframeRef:{current:(Oe=O.current)==null?void 0:Oe.querySelector("iframe")},zoom:W,onSelectAnchor:x=>{n!=="edit"&&(t==null||t("edit")),p==null||p(x)}}),e.jsx(Gn,{edits:G,iframeRef:{current:(Be=O.current)==null?void 0:Be.querySelector("iframe")},zoom:W}),e.jsx(_n,{iframeDoc:_,active:n==="preview"}),e.jsx(xn,{active:n==="drag"&&!ge,iframeRef:{current:(He=O.current)==null?void 0:He.querySelector("iframe")},zoom:W,pickDragSource:pn,isDragging:ge}),e.jsx(fn,{active:n==="drag",iframeRef:{current:(_e=O.current)==null?void 0:_e.querySelector("iframe")},zoom:W,freeMode:V,onFreeModeChange:Ee,apiRef:mt,onDraggingChange:bt,onSelectionChange:x=>{wt(x),x&&Le(!0)},onCommitMove:(x,j)=>{var M;if(j.duplicate){const R=(M=j.sourceEl)==null?void 0:M.cloneNode(!0);let Ge=()=>{};if(R&&j.targetContainer){try{R.removeAttribute("data-anchor")}catch{}j.beforeEl&&j.beforeEl.parentNode===j.targetContainer?j.targetContainer.insertBefore(R,j.beforeEl):j.targetContainer.appendChild(R),Ge=()=>{try{R.remove()}catch{}}}q==null||q(x,Ge);return}const $=hn({iframeDoc:_,sourceEl:j.sourceEl,targetContainer:j.targetContainer,beforeEl:j.beforeEl});q==null||q(x,$==null?void 0:$.revert)},onCommitFreePosition:(x,j)=>{const $=gn({sourceEl:j.sourceEl,parentEl:j.parentEl,styleDelta:x.styleDelta,runtimeLocks:x.runtimeLocks,parentNeedsRelative:x.parentNeedsRelative});xe==null||xe(x,$==null?void 0:$.revert)}}),e.jsx(yn,{active:n==="drag"&&kt&&!ge&&!!$e,iframeRef:{current:(De=O.current)==null?void 0:De.querySelector("iframe")},zoom:W,sourceEl:$e,hasPendingEditId:!!ut,onSubmit:ct,onDismiss:()=>Le(!1)}),n==="edit"&&o&&_&&e.jsx(mn,{selectedAnchor:o,iframeDoc:_,iframeRef:{current:(qe=O.current)==null?void 0:qe.querySelector("iframe")},iframeRect:Z,zoom:W,comments:T,onClose:()=>p==null?void 0:p(null),onAddComment:ee,onResolveComment:X,onDeleteComment:K}),e.jsx(bn,{active:n==="region"&&!!B,iframeRef:{current:(Ie=O.current)==null?void 0:Ie.querySelector("iframe")},zoom:W,onSubmit:x=>B==null?void 0:B(x),onExit:()=>t==null?void 0:t("preview")}),e.jsx(Dn,{edits:G,onApply:xt,onUndo:pt,onClearAll:ft,canUndo:gt,isRunning:U})]}),n==="code"&&e.jsx(wn,{value:he,onChange:Lt,readOnly:!1}),me&&e.jsx(An,{anchorRef:be,onClose:()=>re(!1),iframeDoc:_}),Q&&e.jsx(Rn,{anchorRef:be,onClose:()=>ie(!1),project:I,deckSpec:N,projectId:f,sessionId:P,onA11yClick:()=>{ie(!1),re(!0)},tweaksEnabled:oe,onTweaksEnabledChange:St}),J&&e.jsx(Bn,{comments:T,iframeDoc:_,iframeRef:{current:(Ne=O.current)==null?void 0:Ne.querySelector("iframe")},anchorRef:Te,onClose:()=>le(!1),onResolveComment:X,onDeleteComment:K,onSelectAnchor:x=>{le(!1),n!=="edit"&&(t==null||t("edit")),p==null||p(x)}})]})}export{Yn as default};
@@ -1,4 +1,4 @@
1
- import{c as q,r as n,j as e,R as N,d as c,G as g,P as Q,F as k,e as L,L as tt,S as ct,X as dt,a as K,v as ut,h as _,M as ht,I as G,T as pt,i as J,k as ft,l as xt,m as gt,n as yt,A as bt,C as vt}from"./index-DGG5wIZs.js";import{S as wt,E as kt}from"./square-dashed-mouse-pointer-CTtTVKoH.js";/**
1
+ import{c as q,r as n,j as e,R as N,d as c,G as g,P as Q,F as k,e as L,L as tt,S as ct,X as dt,a as K,v as ut,h as _,M as ht,I as G,T as pt,i as J,k as ft,l as xt,m as gt,n as yt,A as bt,C as vt}from"./index-BYKfMnmv.js";import{S as wt,E as kt}from"./square-dashed-mouse-pointer-D_VrqVpy.js";/**
2
2
  * @license lucide-react v0.468.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -13,4 +13,4 @@ import{c as q,r as n,j as e,R as N,d as c,G as g,P as Q,F as k,e as L,L as tt,S
13
13
  *
14
14
  * This source code is licensed under the ISC license.
15
15
  * See the LICENSE file in the root directory of this source tree.
16
- */const jt=q("Scan",[["path",{d:"M3 7V5a2 2 0 0 1 2-2h2",key:"aa7l1z"}],["path",{d:"M17 3h2a2 2 0 0 1 2 2v2",key:"4qcy5o"}],["path",{d:"M21 17v2a2 2 0 0 1-2 2h-2",key:"6vwrx8"}],["path",{d:"M7 21H5a2 2 0 0 1-2-2v-2",key:"ioqczr"}]]),Z=8,Y=280;function Mt({active:y,imgRef:h,onSubmit:m,onExit:b}){const[s,S]=n.useState(null),[r,U]=n.useState(null),[O,v]=n.useState(""),[P,u]=n.useState(!1),w=n.useCallback(()=>{S(null),U(null),v("")},[]);if(n.useEffect(()=>{y||w()},[y,w]),n.useEffect(()=>{if(!y)return;const t=a=>{a.key==="Escape"&&(a.stopPropagation(),r||s?w():b==null||b())};return window.addEventListener("keydown",t,!0),()=>window.removeEventListener("keydown",t,!0)},[y,r,s,w,b]),!y)return null;const f=t=>{const a=t.currentTarget.getBoundingClientRect();return{x:Math.min(Math.max(0,t.clientX-a.left),a.width),y:Math.min(Math.max(0,t.clientY-a.top),a.height)}},T=(t,a)=>({x:Math.min(t.x,a.x),y:Math.min(t.y,a.y),w:Math.abs(t.x-a.x),h:Math.abs(t.y-a.y)}),I=t=>{var l,C;if(t.button!==0||r)return;t.preventDefault(),t.stopPropagation(),(C=(l=t.currentTarget).setPointerCapture)==null||C.call(l,t.pointerId);const a=f(t);S({from:a,to:a})},W=t=>{if(!s)return;t.stopPropagation();const a=f(t);S(l=>l&&{...l,to:a})},R=()=>{if(!s)return;const t=T(s.from,s.to);S(null),!(t.w<Z||t.h<Z)&&U({box:t})},z=async()=>{if(!r||P)return;const t=h==null?void 0:h.current;if(!t||!t.naturalWidth||!t.clientWidth)return;const a=t.naturalWidth/t.clientWidth,l=r.box;u(!0);try{await(m==null?void 0:m({region:{x:Math.round(l.x*a),y:Math.round(l.y*a),w:Math.round(l.w*a),h:Math.round(l.h*a)},viewport:{width:t.naturalWidth,height:t.naturalHeight},text:O.trim()})),w(),b==null||b()}finally{u(!1)}},j=s?T(s.from,s.to):null,M=(r==null?void 0:r.box)||j;let p=0,$=0;if(r&&(h!=null&&h.current)){const t=r.box,a=h.current.clientHeight,l=h.current.clientWidth;p=t.y+t.h+10,p+170>a&&(p=Math.max(8,t.y-170-10)),$=Math.min(Math.max(8,t.x),Math.max(8,l-Y-8))}return e.jsxs("div",{onPointerDown:I,onPointerMove:W,onPointerUp:R,onPointerCancel:()=>S(null),style:{position:"absolute",inset:0,cursor:r?"default":"crosshair",background:r?"transparent":"rgba(32,26,14,0.10)",touchAction:"none",borderRadius:2},children:[M&&e.jsx("div",{style:{position:"absolute",left:M.x,top:M.y,width:M.w,height:M.h,border:`2px solid ${c.btn}`,borderRadius:N.sm,background:"rgba(255,255,255,0.06)",boxShadow:"0 0 0 9999px rgba(32,26,14,0.18)",pointerEvents:"none"}}),r&&e.jsxs("div",{onPointerDown:t=>t.stopPropagation(),style:{position:"absolute",left:$,top:p,width:Y,background:c.bgWhite,borderRadius:N.lg,boxShadow:Q.far,padding:g.md,display:"flex",flexDirection:"column",gap:g.sm},children:[e.jsx("textarea",{value:O,autoFocus:!0,onChange:t=>v(t.target.value),onKeyDown:t=>{t.key==="Enter"&&(t.metaKey||t.ctrlKey)&&(t.preventDefault(),z())},placeholder:"这一块想说什么…(可以不写,框本身就是话)",style:{width:"100%",boxSizing:"border-box",minHeight:56,resize:"vertical",fontFamily:L,fontSize:k.sm,color:c.text,lineHeight:1.6,border:`1px solid ${c.borderLt}`,borderRadius:N.md,padding:g.sm,outline:"none",background:c.bg}}),e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:g.sm},children:[e.jsxs("button",{onClick:z,disabled:P,style:{display:"inline-flex",alignItems:"center",gap:g.xs,padding:`5px ${g.lg}px`,borderRadius:N.pill,border:"none",background:c.btn,color:c.btnText,fontFamily:L,fontSize:k.sm,fontWeight:600,cursor:P?"default":"pointer",opacity:P?.7:1},children:[P?e.jsxs(e.Fragment,{children:[e.jsx(tt,{size:12,style:{animation:"spin 1s linear infinite"}})," 发送中…"]}):e.jsxs(e.Fragment,{children:[e.jsx(ct,{size:12})," 发给 agent"]}),e.jsx("style",{children:"@keyframes spin{to{transform:rotate(360deg)}}"})]}),e.jsxs("button",{onClick:w,style:{display:"inline-flex",alignItems:"center",gap:g.xs,padding:`5px ${g.md}px`,borderRadius:N.pill,border:`1px solid ${c.borderLt}`,background:"transparent",color:c.sub,fontFamily:L,fontSize:k.xs,cursor:"pointer"},children:[e.jsx(dt,{size:11})," 重画"]}),e.jsx("span",{style:{marginLeft:"auto",fontFamily:K,fontSize:k.xxs,color:c.dim},children:"⌘↵"})]})]})]})}const Pt=24;function Ct({projectId:y,file:h,title:m,sourceFile:b=null,members:s=null,exports:S,onExport:r,onClose:U,onToolbarGroups:O,onRegionComment:v=null,fileVersions:P=null}){const[u,w]=n.useState(1),[f,T]=n.useState(null),[I,W]=n.useState(!0),[R,z]=n.useState(null),[j,M]=n.useState("height"),[p,$]=n.useState("preview"),[t,a]=n.useState(!1),[l,C]=n.useState(null),[et,nt]=n.useState(null),at=n.useRef(null),B=n.useRef(null),[d,V]=n.useState(h);n.useEffect(()=>{V(h)},[h]);const A=(s==null?void 0:s.find(i=>i.file===d))||null,F=A?A.sourceFile:b,H=ut(P,d),X=n.useMemo(()=>_.docxPageUrl(y,d,u,{v:H}),[y,d,u,H]);n.useEffect(()=>{w(1),T(null),C(null)},[d]),n.useEffect(()=>{let i=!1,o=null;return W(!0),z(null),fetch(X).then(async x=>{if(!x.ok){const lt=await x.json().catch(()=>({}));throw new Error(lt.error||`渲染失败(${x.status})`)}const E=Number(x.headers.get("X-Docx-Pages")),rt=await x.blob();i||(E>0&&T(E),o=URL.createObjectURL(rt),nt(o),W(!1))}).catch(x=>{i||(z(x.message),W(!1))}),()=>{i=!0,o&&URL.revokeObjectURL(o)}},[X]),n.useEffect(()=>{p!=="source"||!F||l!=null||fetch(_.artifactFileUrl(y,F)).then(i=>i.text()).then(C).catch(()=>C("(读不到源文件)"))},[p,F,y,l]);const D=n.useCallback(i=>{w(o=>Math.min(Math.max(1,o+i),f||o+i))},[f]);n.useEffect(()=>{const i=o=>{var x,E;((x=o.target)==null?void 0:x.tagName)==="INPUT"||((E=o.target)==null?void 0:E.tagName)==="TEXTAREA"||((o.key==="ArrowRight"||o.key==="PageDown")&&(o.preventDefault(),D(1)),(o.key==="ArrowLeft"||o.key==="PageUp")&&(o.preventDefault(),D(-1)))};return window.addEventListener("keydown",i),()=>window.removeEventListener("keydown",i)},[D]);const it=n.useMemo(()=>[s&&s.length>1?{id:"member",value:d,node:e.jsx("select",{value:d,onChange:i=>V(i.target.value),title:"这个文件夹里的文档(多版本并排放,选一份看)",style:{maxWidth:180,height:24,padding:"0 4px",border:`1px solid ${G.hair}`,borderRadius:pt.radius,background:"transparent",color:G.text,fontFamily:L,fontSize:k.xs,cursor:"pointer"},children:s.map(i=>e.jsx("option",{value:i.file,style:{background:c.bgWhite,color:c.text},children:i.title||i.file},i.file))})}:null,{id:"pager",value:`${u}/${f??""}`,node:e.jsxs(e.Fragment,{children:[e.jsx(J,{dataId:"prev",icon:ft,title:"上一页(← / PageUp)",disabled:u<=1,onClick:()=>D(-1)}),e.jsx("span",{style:{...xt,fontFamily:K,fontVariantNumeric:"tabular-nums",padding:`0 ${g.xs}px`,minWidth:40,textAlign:"center",whiteSpace:"nowrap",userSelect:"none"},children:f?`${u} / ${f}`:u}),e.jsx(J,{dataId:"next",icon:gt,title:"下一页(→ / PageDown)",disabled:!!f&&u>=f,onClick:()=>D(1)})]})},v&&p==="preview"?{id:"regionmode",items:[{id:"region",icon:wt,label:"圈选",title:"在页面上框一块发给 agent",active:t,onClick:()=>a(i=>!i)}]}:null,{id:"fit",items:[{id:"fitH",icon:ht,title:"整页(铺满高度)",active:j==="height",onClick:()=>M("height")},{id:"fitW",icon:jt,title:"铺满宽度(看细节)",active:j==="width",onClick:()=>M("width")}]},{id:"tab",items:[{id:"preview",icon:kt,title:"看页面",active:p==="preview",onClick:()=>$("preview")},{id:"pdf",icon:St,title:"连续阅读(浏览器 PDF 视图,可搜索选字)",active:p==="pdf",onClick:()=>$("pdf")},...F?[{id:"source",icon:mt,title:`看源码(${F})—— 改这份再 build,别改 .docx`,active:p==="source",onClick:()=>$("source")}]:[]]},yt({kind:"docx",exports:S,onExport:r?i=>r(i,d):null})].filter(Boolean),[u,f,j,p,F,S,r,D,s,d,v,t]),ot=n.useCallback(async({region:i,viewport:o,text:x})=>{await(v==null?void 0:v({region:i,viewport:o,elements:[],container:null,text:x,path:d,docxPage:u}))},[v,d,u]),st=j==="height"?{height:"100%",width:"auto",maxWidth:"100%"}:{width:"100%",height:"auto"};return e.jsx(bt,{kind:"docx",title:A!=null&&A.title?`${m||h} · ${A.title}`:m||h,subtitle:f?`${f} 页`:null,onClose:U,groups:it,onToolbarGroups:O,banner:F?null:e.jsxs("span",{children:["这是一份",e.jsx("b",{children:"外来文档"}),"(没有 token 源)。现在能看、能导出,",e.jsx("b",{children:"改它要等编辑道上线"}),"—— 想现在就要一个改过的版本,让 agent 基于它的内容重做一份。"]}),contentStyle:{background:vt.paper},children:p==="pdf"?e.jsx("iframe",{title:`${m||d} PDF`,src:_.docxPdfUrl(y,d,{v:H}),style:{width:"100%",height:"100%",border:0,display:"block"}},`${d}?v=${H}`):p==="source"?e.jsx("pre",{style:{margin:0,padding:g.lg,height:"100%",overflow:"auto",fontFamily:K,fontSize:k.sm,lineHeight:1.6,color:c.text,background:c.bgWhite,whiteSpace:"pre-wrap"},children:l??"读取中…"}):e.jsx("div",{ref:at,style:{height:"100%",width:"100%",overflow:"auto",display:"flex",alignItems:j==="height"?"center":"flex-start",justifyContent:"center",padding:Pt,boxSizing:"border-box"},children:R?e.jsxs("div",{style:{fontFamily:L,fontSize:k.sm,color:c.text2,textAlign:"center",maxWidth:420,lineHeight:1.7},children:[R,e.jsx("div",{style:{marginTop:g.sm,color:c.sub,fontSize:k.xs},children:"渲染链路的问题跟文档本身无关。文档还在,导出照常能用。"})]}):e.jsxs("div",{style:{position:"relative",height:j==="height"?"100%":"auto",maxWidth:"100%"},children:[e.jsx("img",{ref:B,alt:`${m||d} 第 ${u} 页`,src:et||void 0,style:{...st,display:"block",background:"#fff",boxShadow:Q.mid,borderRadius:2}}),e.jsx(Mt,{active:t&&!I,imgRef:B,onSubmit:ot,onExit:()=>a(!1)},`${d}#${u}`),I&&e.jsxs("div",{style:{position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(255,255,255,0.65)",gap:g.sm,fontFamily:L,fontSize:k.sm,color:c.text2,borderRadius:2},children:[e.jsx("style",{children:"@keyframes spin{to{transform:rotate(360deg)}}"}),e.jsx(tt,{size:14,style:{animation:"spin 1s linear infinite"}}),"加载中"]})]})})})}export{Ct as default};
16
+ */const jt=q("Scan",[["path",{d:"M3 7V5a2 2 0 0 1 2-2h2",key:"aa7l1z"}],["path",{d:"M17 3h2a2 2 0 0 1 2 2v2",key:"4qcy5o"}],["path",{d:"M21 17v2a2 2 0 0 1-2 2h-2",key:"6vwrx8"}],["path",{d:"M7 21H5a2 2 0 0 1-2-2v-2",key:"ioqczr"}]]),Z=8,Y=280;function Mt({active:y,imgRef:h,onSubmit:m,onExit:b}){const[s,S]=n.useState(null),[r,U]=n.useState(null),[O,v]=n.useState(""),[P,u]=n.useState(!1),w=n.useCallback(()=>{S(null),U(null),v("")},[]);if(n.useEffect(()=>{y||w()},[y,w]),n.useEffect(()=>{if(!y)return;const t=a=>{a.key==="Escape"&&(a.stopPropagation(),r||s?w():b==null||b())};return window.addEventListener("keydown",t,!0),()=>window.removeEventListener("keydown",t,!0)},[y,r,s,w,b]),!y)return null;const f=t=>{const a=t.currentTarget.getBoundingClientRect();return{x:Math.min(Math.max(0,t.clientX-a.left),a.width),y:Math.min(Math.max(0,t.clientY-a.top),a.height)}},T=(t,a)=>({x:Math.min(t.x,a.x),y:Math.min(t.y,a.y),w:Math.abs(t.x-a.x),h:Math.abs(t.y-a.y)}),I=t=>{var l,C;if(t.button!==0||r)return;t.preventDefault(),t.stopPropagation(),(C=(l=t.currentTarget).setPointerCapture)==null||C.call(l,t.pointerId);const a=f(t);S({from:a,to:a})},W=t=>{if(!s)return;t.stopPropagation();const a=f(t);S(l=>l&&{...l,to:a})},R=()=>{if(!s)return;const t=T(s.from,s.to);S(null),!(t.w<Z||t.h<Z)&&U({box:t})},z=async()=>{if(!r||P)return;const t=h==null?void 0:h.current;if(!t||!t.naturalWidth||!t.clientWidth)return;const a=t.naturalWidth/t.clientWidth,l=r.box;u(!0);try{await(m==null?void 0:m({region:{x:Math.round(l.x*a),y:Math.round(l.y*a),w:Math.round(l.w*a),h:Math.round(l.h*a)},viewport:{width:t.naturalWidth,height:t.naturalHeight},text:O.trim()})),w(),b==null||b()}finally{u(!1)}},j=s?T(s.from,s.to):null,M=(r==null?void 0:r.box)||j;let p=0,$=0;if(r&&(h!=null&&h.current)){const t=r.box,a=h.current.clientHeight,l=h.current.clientWidth;p=t.y+t.h+10,p+170>a&&(p=Math.max(8,t.y-170-10)),$=Math.min(Math.max(8,t.x),Math.max(8,l-Y-8))}return e.jsxs("div",{onPointerDown:I,onPointerMove:W,onPointerUp:R,onPointerCancel:()=>S(null),style:{position:"absolute",inset:0,cursor:r?"default":"crosshair",background:r?"transparent":"rgba(32,26,14,0.10)",touchAction:"none",borderRadius:2},children:[M&&e.jsx("div",{style:{position:"absolute",left:M.x,top:M.y,width:M.w,height:M.h,border:`2px solid ${c.btn}`,borderRadius:N.sm,background:"rgba(255,254,246,0.06)",boxShadow:"0 0 0 9999px rgba(32,26,14,0.18)",pointerEvents:"none"}}),r&&e.jsxs("div",{onPointerDown:t=>t.stopPropagation(),style:{position:"absolute",left:$,top:p,width:Y,background:c.bgWhite,borderRadius:N.lg,boxShadow:Q.far,padding:g.md,display:"flex",flexDirection:"column",gap:g.sm},children:[e.jsx("textarea",{value:O,autoFocus:!0,onChange:t=>v(t.target.value),onKeyDown:t=>{t.key==="Enter"&&(t.metaKey||t.ctrlKey)&&(t.preventDefault(),z())},placeholder:"这一块想说什么…(可以不写,框本身就是话)",style:{width:"100%",boxSizing:"border-box",minHeight:56,resize:"vertical",fontFamily:L,fontSize:k.sm,color:c.text,lineHeight:1.6,border:`1px solid ${c.borderLt}`,borderRadius:N.md,padding:g.sm,outline:"none",background:c.bg}}),e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:g.sm},children:[e.jsxs("button",{onClick:z,disabled:P,style:{display:"inline-flex",alignItems:"center",gap:g.xs,padding:`5px ${g.lg}px`,borderRadius:N.pill,border:"none",background:c.btn,color:c.btnText,fontFamily:L,fontSize:k.sm,fontWeight:600,cursor:P?"default":"pointer",opacity:P?.7:1},children:[P?e.jsxs(e.Fragment,{children:[e.jsx(tt,{size:12,style:{animation:"spin 1s linear infinite"}})," 发送中…"]}):e.jsxs(e.Fragment,{children:[e.jsx(ct,{size:12})," 发给 agent"]}),e.jsx("style",{children:"@keyframes spin{to{transform:rotate(360deg)}}"})]}),e.jsxs("button",{onClick:w,style:{display:"inline-flex",alignItems:"center",gap:g.xs,padding:`5px ${g.md}px`,borderRadius:N.pill,border:`1px solid ${c.borderLt}`,background:"transparent",color:c.sub,fontFamily:L,fontSize:k.xs,cursor:"pointer"},children:[e.jsx(dt,{size:11})," 重画"]}),e.jsx("span",{style:{marginLeft:"auto",fontFamily:K,fontSize:k.xxs,color:c.dim},children:"⌘↵"})]})]})]})}const Pt=24;function Ct({projectId:y,file:h,title:m,sourceFile:b=null,members:s=null,exports:S,onExport:r,onClose:U,onToolbarGroups:O,onRegionComment:v=null,fileVersions:P=null}){const[u,w]=n.useState(1),[f,T]=n.useState(null),[I,W]=n.useState(!0),[R,z]=n.useState(null),[j,M]=n.useState("height"),[p,$]=n.useState("preview"),[t,a]=n.useState(!1),[l,C]=n.useState(null),[et,nt]=n.useState(null),at=n.useRef(null),B=n.useRef(null),[d,V]=n.useState(h);n.useEffect(()=>{V(h)},[h]);const A=(s==null?void 0:s.find(i=>i.file===d))||null,F=A?A.sourceFile:b,H=ut(P,d),X=n.useMemo(()=>_.docxPageUrl(y,d,u,{v:H}),[y,d,u,H]);n.useEffect(()=>{w(1),T(null),C(null)},[d]),n.useEffect(()=>{let i=!1,o=null;return W(!0),z(null),fetch(X).then(async x=>{if(!x.ok){const lt=await x.json().catch(()=>({}));throw new Error(lt.error||`渲染失败(${x.status})`)}const E=Number(x.headers.get("X-Docx-Pages")),rt=await x.blob();i||(E>0&&T(E),o=URL.createObjectURL(rt),nt(o),W(!1))}).catch(x=>{i||(z(x.message),W(!1))}),()=>{i=!0,o&&URL.revokeObjectURL(o)}},[X]),n.useEffect(()=>{p!=="source"||!F||l!=null||fetch(_.artifactFileUrl(y,F)).then(i=>i.text()).then(C).catch(()=>C("(读不到源文件)"))},[p,F,y,l]);const D=n.useCallback(i=>{w(o=>Math.min(Math.max(1,o+i),f||o+i))},[f]);n.useEffect(()=>{const i=o=>{var x,E;((x=o.target)==null?void 0:x.tagName)==="INPUT"||((E=o.target)==null?void 0:E.tagName)==="TEXTAREA"||((o.key==="ArrowRight"||o.key==="PageDown")&&(o.preventDefault(),D(1)),(o.key==="ArrowLeft"||o.key==="PageUp")&&(o.preventDefault(),D(-1)))};return window.addEventListener("keydown",i),()=>window.removeEventListener("keydown",i)},[D]);const it=n.useMemo(()=>[s&&s.length>1?{id:"member",value:d,node:e.jsx("select",{value:d,onChange:i=>V(i.target.value),title:"这个文件夹里的文档(多版本并排放,选一份看)",style:{maxWidth:180,height:24,padding:"0 4px",border:`1px solid ${G.hair}`,borderRadius:pt.radius,background:"transparent",color:G.text,fontFamily:L,fontSize:k.xs,cursor:"pointer"},children:s.map(i=>e.jsx("option",{value:i.file,style:{background:c.bgWhite,color:c.text},children:i.title||i.file},i.file))})}:null,{id:"pager",value:`${u}/${f??""}`,node:e.jsxs(e.Fragment,{children:[e.jsx(J,{dataId:"prev",icon:ft,title:"上一页(← / PageUp)",disabled:u<=1,onClick:()=>D(-1)}),e.jsx("span",{style:{...xt,fontFamily:K,fontVariantNumeric:"tabular-nums",padding:`0 ${g.xs}px`,minWidth:40,textAlign:"center",whiteSpace:"nowrap",userSelect:"none"},children:f?`${u} / ${f}`:u}),e.jsx(J,{dataId:"next",icon:gt,title:"下一页(→ / PageDown)",disabled:!!f&&u>=f,onClick:()=>D(1)})]})},v&&p==="preview"?{id:"regionmode",items:[{id:"region",icon:wt,label:"圈选",title:"在页面上框一块发给 agent",active:t,onClick:()=>a(i=>!i)}]}:null,{id:"fit",items:[{id:"fitH",icon:ht,title:"整页(铺满高度)",active:j==="height",onClick:()=>M("height")},{id:"fitW",icon:jt,title:"铺满宽度(看细节)",active:j==="width",onClick:()=>M("width")}]},{id:"tab",items:[{id:"preview",icon:kt,title:"看页面",active:p==="preview",onClick:()=>$("preview")},{id:"pdf",icon:St,title:"连续阅读(浏览器 PDF 视图,可搜索选字)",active:p==="pdf",onClick:()=>$("pdf")},...F?[{id:"source",icon:mt,title:`看源码(${F})—— 改这份再 build,别改 .docx`,active:p==="source",onClick:()=>$("source")}]:[]]},yt({kind:"docx",exports:S,onExport:r?i=>r(i,d):null})].filter(Boolean),[u,f,j,p,F,S,r,D,s,d,v,t]),ot=n.useCallback(async({region:i,viewport:o,text:x})=>{await(v==null?void 0:v({region:i,viewport:o,elements:[],container:null,text:x,path:d,docxPage:u}))},[v,d,u]),st=j==="height"?{height:"100%",width:"auto",maxWidth:"100%"}:{width:"100%",height:"auto"};return e.jsx(bt,{kind:"docx",title:A!=null&&A.title?`${m||h} · ${A.title}`:m||h,subtitle:f?`${f} 页`:null,onClose:U,groups:it,onToolbarGroups:O,banner:F?null:e.jsxs("span",{children:["这是一份",e.jsx("b",{children:"外来文档"}),"(没有 token 源)。现在能看、能导出,",e.jsx("b",{children:"改它要等编辑道上线"}),"—— 想现在就要一个改过的版本,让 agent 基于它的内容重做一份。"]}),contentStyle:{background:vt.paper},children:p==="pdf"?e.jsx("iframe",{title:`${m||d} PDF`,src:_.docxPdfUrl(y,d,{v:H}),style:{width:"100%",height:"100%",border:0,display:"block"}},`${d}?v=${H}`):p==="source"?e.jsx("pre",{style:{margin:0,padding:g.lg,height:"100%",overflow:"auto",fontFamily:K,fontSize:k.sm,lineHeight:1.6,color:c.text,background:c.bgWhite,whiteSpace:"pre-wrap"},children:l??"读取中…"}):e.jsx("div",{ref:at,style:{height:"100%",width:"100%",overflow:"auto",display:"flex",alignItems:j==="height"?"center":"flex-start",justifyContent:"center",padding:Pt,boxSizing:"border-box"},children:R?e.jsxs("div",{style:{fontFamily:L,fontSize:k.sm,color:c.text2,textAlign:"center",maxWidth:420,lineHeight:1.7},children:[R,e.jsx("div",{style:{marginTop:g.sm,color:c.sub,fontSize:k.xs},children:"渲染链路的问题跟文档本身无关。文档还在,导出照常能用。"})]}):e.jsxs("div",{style:{position:"relative",height:j==="height"?"100%":"auto",maxWidth:"100%"},children:[e.jsx("img",{ref:B,alt:`${m||d} 第 ${u} 页`,src:et||void 0,style:{...st,display:"block",background:"#fff",boxShadow:Q.mid,borderRadius:2}}),e.jsx(Mt,{active:t&&!I,imgRef:B,onSubmit:ot,onExit:()=>a(!1)},`${d}#${u}`),I&&e.jsxs("div",{style:{position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(255,254,246,0.65)",gap:g.sm,fontFamily:L,fontSize:k.sm,color:c.text2,borderRadius:2},children:[e.jsx("style",{children:"@keyframes spin{to{transform:rotate(360deg)}}"}),e.jsx(tt,{size:14,style:{animation:"spin 1s linear infinite"}}),"加载中"]})]})})})}export{Ct as default};