owndesign 0.1.4 → 0.1.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 (157) hide show
  1. package/dist/index.js +1 -2
  2. package/dist/server/index.js +656 -1474
  3. package/dist/server/prompts/agents/component-audit.md +24 -0
  4. package/dist/server/prompts/agents/design-page.md +83 -55
  5. package/dist/server/prompts/index.ts +9 -16
  6. package/dist/server/templates/html/page-shell.html +65 -0
  7. package/dist/server/templates/index.ts +39 -0
  8. package/dist/web/assets/{angular-html-jQS2hAnh.js → angular-html-DCGj6j9k.js} +1 -1
  9. package/dist/web/assets/{angular-ts-DXCIRK8J.js → angular-ts-_jS5FvKQ.js} +1 -1
  10. package/dist/web/assets/{apl-uLTVM0SV.js → apl-COUiMcRO.js} +1 -1
  11. package/dist/web/assets/{arc-C6K-4bbh.js → arc-DO65316F.js} +1 -1
  12. package/dist/web/assets/architecture-7EHR7CIX-D10X4FnP.js +1 -0
  13. package/dist/web/assets/{architectureDiagram-3BPJPVTR-Nra6nVfI.js → architectureDiagram-3BPJPVTR-4QTI95py.js} +1 -1
  14. package/dist/web/assets/{astro-QydxzDKL.js → astro-DBm7xN0H.js} +1 -1
  15. package/dist/web/assets/{blade-g81cs1jW.js → blade-BrcGRjeD.js} +1 -1
  16. package/dist/web/assets/{blockDiagram-GPEHLZMM-Ei76DVKM.js → blockDiagram-GPEHLZMM-DyW2gTLi.js} +1 -1
  17. package/dist/web/assets/{c-CV8Fejge.js → c-DAqlvgYb.js} +1 -1
  18. package/dist/web/assets/{c4Diagram-AAUBKEIU-oIOIFFc0.js → c4Diagram-AAUBKEIU-D7VLn3UY.js} +1 -1
  19. package/dist/web/assets/channel-D4A7yvhc.js +1 -0
  20. package/dist/web/assets/{chunk-2J33WTMH-BokYjerh.js → chunk-2J33WTMH-ZI7iZmam.js} +1 -1
  21. package/dist/web/assets/{chunk-4BX2VUAB-CW80cEPP.js → chunk-4BX2VUAB-BXZ1tYtp.js} +1 -1
  22. package/dist/web/assets/{chunk-55IACEB6-cqUqIGMR.js → chunk-55IACEB6-BruFuyzh.js} +1 -1
  23. package/dist/web/assets/{chunk-727SXJPM-en_nLLtx.js → chunk-727SXJPM-WfltwAyi.js} +1 -1
  24. package/dist/web/assets/{chunk-AQP2D5EJ-E7hL18Vm.js → chunk-AQP2D5EJ-m-6Oqccg.js} +1 -1
  25. package/dist/web/assets/{chunk-FMBD7UC4-D2Mn_3Ze.js → chunk-FMBD7UC4-CTpop8z0.js} +1 -1
  26. package/dist/web/assets/{chunk-ND2GUHAM-BxpfbjDb.js → chunk-ND2GUHAM-DXbYu7j4.js} +1 -1
  27. package/dist/web/assets/chunk-QZHKN3VN-JoHxC1mG.js +1 -0
  28. package/dist/web/assets/classDiagram-4FO5ZUOK-D2XS2ai4.js +1 -0
  29. package/dist/web/assets/classDiagram-v2-Q7XG4LA2-D2XS2ai4.js +1 -0
  30. package/dist/web/assets/{cobol-CMtH7Q60.js → cobol-CV8XIe3g.js} +1 -1
  31. package/dist/web/assets/{coffee-Dd2oge0M.js → coffee-CjS-bqem.js} +1 -1
  32. package/dist/web/assets/{cose-bilkent-S5V4N54A-DlIl_zlL.js → cose-bilkent-S5V4N54A-qeF_aTDs.js} +1 -1
  33. package/dist/web/assets/{cpp-BxOkDHcj.js → cpp-BWTrby03.js} +1 -1
  34. package/dist/web/assets/{crystal-CP-EFrvi.js → crystal-B4jSurfu.js} +1 -1
  35. package/dist/web/assets/{css-56wYI_pE.js → css-CXDKo6HN.js} +1 -1
  36. package/dist/web/assets/{dagre-BM42HDAG-DPLYcjid.js → dagre-BM42HDAG-mtpIrAd-.js} +1 -1
  37. package/dist/web/assets/{diagram-2AECGRRQ-BCMoYOQK.js → diagram-2AECGRRQ-C2daXqRz.js} +1 -1
  38. package/dist/web/assets/{diagram-5GNKFQAL-CLKyT-zH.js → diagram-5GNKFQAL-Yn3Kkd1e.js} +1 -1
  39. package/dist/web/assets/{diagram-KO2AKTUF-aunZGxvu.js → diagram-KO2AKTUF-BSZBlF1_.js} +1 -1
  40. package/dist/web/assets/{diagram-LMA3HP47-BYp0cf90.js → diagram-LMA3HP47-C-o1wwy4.js} +1 -1
  41. package/dist/web/assets/{diagram-OG6HWLK6-M3iTfH5g.js → diagram-OG6HWLK6-BadUuiav.js} +1 -1
  42. package/dist/web/assets/{edge-DpKZbKMc.js → edge-IP3F2dQ8.js} +1 -1
  43. package/dist/web/assets/{elixir-CzSMdKdn.js → elixir-DNv4dzV2.js} +1 -1
  44. package/dist/web/assets/{elm-CPk9x22f.js → elm-DGlxwCzv.js} +1 -1
  45. package/dist/web/assets/{erDiagram-TEJ5UH35-BX0VkvGe.js → erDiagram-TEJ5UH35-uLYwqFWM.js} +1 -1
  46. package/dist/web/assets/{erb-TQAaZlNV.js → erb-Cx4MSh15.js} +1 -1
  47. package/dist/web/assets/eventmodeling-FCH6USID-CHKTnhfi.js +1 -0
  48. package/dist/web/assets/{flowDiagram-I6XJVG4X-Bvl7AO6M.js → flowDiagram-I6XJVG4X-C-JzpWfx.js} +1 -1
  49. package/dist/web/assets/{ganttDiagram-6RSMTGT7-Bcc6aiz3.js → ganttDiagram-6RSMTGT7-Be3WGPbk.js} +1 -1
  50. package/dist/web/assets/{git-rebase-C36akiNM.js → git-rebase-C_0JGP0g.js} +1 -1
  51. package/dist/web/assets/gitGraph-WXDBUCRP-aSGYLvSW.js +1 -0
  52. package/dist/web/assets/{gitGraphDiagram-PVQCEYII-DwIrICP0.js → gitGraphDiagram-PVQCEYII-DqJ0yj3L.js} +1 -1
  53. package/dist/web/assets/{glimmer-js-ZijKRLl6.js → glimmer-js-B7LRT5BS.js} +1 -1
  54. package/dist/web/assets/{glimmer-ts-DngRkkD1.js → glimmer-ts-CojGZlfZ.js} +1 -1
  55. package/dist/web/assets/{glsl-BaB0p3pF.js → glsl-CbmtoTb8.js} +1 -1
  56. package/dist/web/assets/{graphql-Betoeq1K.js → graphql-qYEIhTt7.js} +1 -1
  57. package/dist/web/assets/{hack-CE6PSHKG.js → hack-DxHXCCdq.js} +1 -1
  58. package/dist/web/assets/{haml-SuyMrzSD.js → haml-CiwrkJ8T.js} +1 -1
  59. package/dist/web/assets/{handlebars-JDrZp4fR.js → handlebars-Cn5UPYUQ.js} +1 -1
  60. package/dist/web/assets/{highlighted-body-OFNGDK62-CgC4ngzm.js → highlighted-body-OFNGDK62-ehiuxKzz.js} +1 -1
  61. package/dist/web/assets/{html-DC1Ltqmc.js → html-Cst8cPsk.js} +1 -1
  62. package/dist/web/assets/{html-derivative-iG8t5u5_.js → html-derivative-lnPAgvry.js} +1 -1
  63. package/dist/web/assets/{http-C40sH4NS.js → http-C5VDSUzW.js} +1 -1
  64. package/dist/web/assets/{hurl-CmUE8H3s.js → hurl-BFlMJrL0.js} +1 -1
  65. package/dist/web/assets/{index-Bz6PFGaR.js → index-Ai406YO8.js} +179 -179
  66. package/dist/web/assets/index-Cy3xCQ0m.css +2 -0
  67. package/dist/web/assets/info-J43DQDTF-C945CGZU.js +1 -0
  68. package/dist/web/assets/{infoDiagram-5YYISTIA-D92ErDLp.js → infoDiagram-5YYISTIA-CIanyf0S.js} +1 -1
  69. package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-COfhdNCZ.js → ishikawaDiagram-YF4QCWOH--1RL7uDF.js} +1 -1
  70. package/dist/web/assets/{java-Jk3c_AWT.js → java-BGahCCe1.js} +1 -1
  71. package/dist/web/assets/{javascript-D7j9lLtB.js → javascript-D5uMI8kB.js} +1 -1
  72. package/dist/web/assets/{jinja-KOP4LHnr.js → jinja-B4krr1-l.js} +1 -1
  73. package/dist/web/assets/{jison-CsByL99k.js → jison-BMMSlNZQ.js} +1 -1
  74. package/dist/web/assets/{journeyDiagram-JHISSGLW-BAaoSHcS.js → journeyDiagram-JHISSGLW-lkcDMGR5.js} +1 -1
  75. package/dist/web/assets/{json-bLUVjOwA.js → json-DXJMlo78.js} +1 -1
  76. package/dist/web/assets/{jsx-B-ZJ5sYl.js → jsx-C_bl3hBI.js} +1 -1
  77. package/dist/web/assets/{julia-DLZVVunR.js → julia-Sc_vYT5t.js} +1 -1
  78. package/dist/web/assets/{just-CTVFuTGe.js → just-CILkVz1n.js} +1 -1
  79. package/dist/web/assets/{kanban-definition-UN3LZRKU-txA385kR.js → kanban-definition-UN3LZRKU-Bi_hSUym.js} +1 -1
  80. package/dist/web/assets/{latex-BQMKb74T.js → latex-DUmFelG8.js} +1 -1
  81. package/dist/web/assets/{linear-D7ZS4umN.js → linear-Dpnh4Vbv.js} +1 -1
  82. package/dist/web/assets/{liquid-CG92kJNU.js → liquid-BnB6JKD1.js} +1 -1
  83. package/dist/web/assets/{lua-BVSZYGx9.js → lua-DhxAQE0G.js} +1 -1
  84. package/dist/web/assets/{marko-DEmKFKi0.js → marko-CyzzY1hm.js} +1 -1
  85. package/dist/web/assets/{mdc-Do8EO_lC.js → mdc-BptK7sXI.js} +1 -1
  86. package/dist/web/assets/mermaid-GHXKKRXX-DOJ_Dro4.js +1 -0
  87. package/dist/web/assets/{mermaid-parser.core-BaLOFMn_.js → mermaid-parser.core-DShH95hL.js} +2 -2
  88. package/dist/web/assets/{mindmap-definition-RKZ34NQL-D-yV4PLs.js → mindmap-definition-RKZ34NQL-LqYVQ-zW.js} +1 -1
  89. package/dist/web/assets/{nginx-BKSyUILj.js → nginx-CR9TIk16.js} +1 -1
  90. package/dist/web/assets/{nim-DnJRnIIP.js → nim-DzWiwbcf.js} +1 -1
  91. package/dist/web/assets/packet-YPE3B663-DuGIOCMW.js +1 -0
  92. package/dist/web/assets/{perl-3bt2qR9m.js → perl-CgrDNBt5.js} +1 -1
  93. package/dist/web/assets/{php-BzdDALOQ.js → php-DtuJQDgr.js} +1 -1
  94. package/dist/web/assets/pie-LRSECV5Y-BYeTdyfo.js +1 -0
  95. package/dist/web/assets/{pieDiagram-4H26LBE5-Bd114I4X.js → pieDiagram-4H26LBE5-B7_i9XPe.js} +1 -1
  96. package/dist/web/assets/{pug-DdwUkMw4.js → pug--9BJCC2D.js} +1 -1
  97. package/dist/web/assets/{qml-DP4is7YC.js → qml-C3iH27Ik.js} +1 -1
  98. package/dist/web/assets/{quadrantDiagram-W4KKPZXB-bREh_EBC.js → quadrantDiagram-W4KKPZXB-C2tSamWU.js} +1 -1
  99. package/dist/web/assets/{r-BF_jXTwW.js → r-CqKTpWRo.js} +1 -1
  100. package/dist/web/assets/radar-GUYGQ44K-Cqw7n4df.js +1 -0
  101. package/dist/web/assets/{razor-3soncf9G.js → razor-BNavdAx6.js} +1 -1
  102. package/dist/web/assets/{regexp-EYBuLXF9.js → regexp-zwVenqvj.js} +1 -1
  103. package/dist/web/assets/{requirementDiagram-4Y6WPE33-VtNiXWaP.js → requirementDiagram-4Y6WPE33-iIbtgBoU.js} +1 -1
  104. package/dist/web/assets/{rst-C71o5usK.js → rst-BHNeA4na.js} +1 -1
  105. package/dist/web/assets/{ruby-w0pHTyII.js → ruby-CkHs-w2j.js} +1 -1
  106. package/dist/web/assets/{sankeyDiagram-5OEKKPKP-CPbj-Xb-.js → sankeyDiagram-5OEKKPKP-B05ECsV0.js} +1 -1
  107. package/dist/web/assets/{sas-DXIh1KZ4.js → sas-COQs-ItK.js} +1 -1
  108. package/dist/web/assets/{scss-C4aItG-0.js → scss-DVsO7q0E.js} +1 -1
  109. package/dist/web/assets/{sequenceDiagram-3UESZ5HK-DwH_u_zz.js → sequenceDiagram-3UESZ5HK-CHtmtTcK.js} +1 -1
  110. package/dist/web/assets/{shellscript-BV5fN4m0.js → shellscript-DoNwQ9Oz.js} +1 -1
  111. package/dist/web/assets/{shellsession-COL1kA2z.js → shellsession-DvIRWuae.js} +1 -1
  112. package/dist/web/assets/{soy-Ccpzfdcl.js → soy--AEUdRNF.js} +1 -1
  113. package/dist/web/assets/{sql-DCE87e5l.js → sql-BVv9DbTm.js} +1 -1
  114. package/dist/web/assets/{stata-CEWTedB6.js → stata-Bzv07ePm.js} +1 -1
  115. package/dist/web/assets/{stateDiagram-AJRCARHV-B3VdYVO-.js → stateDiagram-AJRCARHV-COllcCkO.js} +1 -1
  116. package/dist/web/assets/stateDiagram-v2-BHNVJYJU-D9SuEs4r.js +1 -0
  117. package/dist/web/assets/{surrealql-CUKNwDpQ.js → surrealql-ChG0u1_y.js} +1 -1
  118. package/dist/web/assets/{svelte-DONiSH1x.js → svelte-CTk-krYk.js} +1 -1
  119. package/dist/web/assets/{templ-B_johG2O.js → templ-CIOvt0Gn.js} +1 -1
  120. package/dist/web/assets/{tex-Dh316BCp.js → tex-BLbgub0O.js} +1 -1
  121. package/dist/web/assets/{timeline-definition-PNZ67QCA-Blixs7cX.js → timeline-definition-PNZ67QCA-Dj0aLVCY.js} +1 -1
  122. package/dist/web/assets/treeView-BLDUP644-Bsp-7xCN.js +1 -0
  123. package/dist/web/assets/treemap-LRROVOQU-BsTzhV2b.js +1 -0
  124. package/dist/web/assets/{ts-tags-CWDOkWop.js → ts-tags-Cjp84wCR.js} +1 -1
  125. package/dist/web/assets/{tsx-STHGXrTg.js → tsx-BYXwwi4R.js} +1 -1
  126. package/dist/web/assets/{twig-DeYz4MvR.js → twig-CJXgadPB.js} +1 -1
  127. package/dist/web/assets/{typescript-BkMW9mh2.js → typescript-SkJO5gXX.js} +1 -1
  128. package/dist/web/assets/{vennDiagram-CIIHVFJN-OUEEmD5m.js → vennDiagram-CIIHVFJN-DotTRq6i.js} +1 -1
  129. package/dist/web/assets/{vue-BhG5XYAd.js → vue-DnNvWsLF.js} +1 -1
  130. package/dist/web/assets/{vue-html-Cw0OXIMV.js → vue-html-BtDfjhCy.js} +1 -1
  131. package/dist/web/assets/{vue-vine-DxYQGujI.js → vue-vine-87qc9dJ9.js} +1 -1
  132. package/dist/web/assets/wardley-L42UT6IY-BtXwfgcx.js +1 -0
  133. package/dist/web/assets/{wardleyDiagram-YWT4CUSO-CbSRnjwb.js → wardleyDiagram-YWT4CUSO-C4mIx6nt.js} +1 -1
  134. package/dist/web/assets/{xml-Co1Haim6.js → xml-BwxGZI3_.js} +1 -1
  135. package/dist/web/assets/{xsl-Dyxp9ECK.js → xsl-V11HisfD.js} +1 -1
  136. package/dist/web/assets/{xychartDiagram-2RQKCTM6-PQFpoSrS.js → xychartDiagram-2RQKCTM6-CR2seeGg.js} +1 -1
  137. package/dist/web/assets/{yaml-DfBwtMv_.js → yaml-DoTdQeS-.js} +1 -1
  138. package/dist/web/index.html +2 -2
  139. package/package.json +6 -7
  140. package/dist/server/prompts/agents/turn-prompt-rewriter.md +0 -15
  141. package/dist/web/assets/architecture-7EHR7CIX-BUVdwEbF.js +0 -1
  142. package/dist/web/assets/channel-DC7LeJJp.js +0 -1
  143. package/dist/web/assets/chunk-QZHKN3VN-DTMt_eZs.js +0 -1
  144. package/dist/web/assets/classDiagram-4FO5ZUOK-jfKwbhh5.js +0 -1
  145. package/dist/web/assets/classDiagram-v2-Q7XG4LA2-jfKwbhh5.js +0 -1
  146. package/dist/web/assets/eventmodeling-FCH6USID-CelVcBa9.js +0 -1
  147. package/dist/web/assets/gitGraph-WXDBUCRP-D9mlI27B.js +0 -1
  148. package/dist/web/assets/index-BFZtO7ji.css +0 -2
  149. package/dist/web/assets/info-J43DQDTF-DmWxkfWc.js +0 -1
  150. package/dist/web/assets/mermaid-GHXKKRXX-L1cLS261.js +0 -1
  151. package/dist/web/assets/packet-YPE3B663-CrP5sMYi.js +0 -1
  152. package/dist/web/assets/pie-LRSECV5Y-Di4KiVyS.js +0 -1
  153. package/dist/web/assets/radar-GUYGQ44K-BWc_UrwT.js +0 -1
  154. package/dist/web/assets/stateDiagram-v2-BHNVJYJU-C7wmfqKn.js +0 -1
  155. package/dist/web/assets/treeView-BLDUP644-DRpNUAD5.js +0 -1
  156. package/dist/web/assets/treemap-LRROVOQU-CRkuQjMq.js +0 -1
  157. package/dist/web/assets/wardley-L42UT6IY-AM6lsfHi.js +0 -1
@@ -8037,10 +8037,10 @@ var init_chunkify = __esm({
8037
8037
  // ../../node_modules/.pnpm/trash@10.1.1/node_modules/trash/lib/chunked-exec.js
8038
8038
  import { promisify as promisify2 } from "node:util";
8039
8039
  import { execFile } from "node:child_process";
8040
- import { fileURLToPath as fileURLToPath3 } from "node:url";
8040
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
8041
8041
  async function chunkedExec(binary3, paths, maxPaths) {
8042
8042
  for (const chunk of chunkify(paths, maxPaths)) {
8043
- await pExecFile(fileURLToPath3(binary3), chunk);
8043
+ await pExecFile(fileURLToPath4(binary3), chunk);
8044
8044
  }
8045
8045
  }
8046
8046
  var pExecFile;
@@ -11133,7 +11133,7 @@ var require_utils6 = __commonJS({
11133
11133
  openSync,
11134
11134
  readSync,
11135
11135
  closeSync,
11136
- readFileSync: readFileSync2,
11136
+ readFileSync: readFileSync3,
11137
11137
  existsSync: existsSync2
11138
11138
  } = __require("fs");
11139
11139
  var tmpBufMinLen = 4096 * 2;
@@ -11168,7 +11168,7 @@ var require_utils6 = __commonJS({
11168
11168
  }
11169
11169
  return ls;
11170
11170
  };
11171
- var readBuffer = readFileSync2;
11171
+ var readBuffer = readFileSync3;
11172
11172
  var exists = existsSync2;
11173
11173
  var readdir2 = readdirSync;
11174
11174
  var devIdGetMinor = (devId) => devId >> 20 << 8 | devId & 255;
@@ -56264,10 +56264,7 @@ import { randomUUID } from "node:crypto";
56264
56264
  // ../core/src/settings/model-utils.ts
56265
56265
  var DEEPSEEK_CONTEXT_SIZE_K = 1e3;
56266
56266
  var DEFAULT_OPENAI_COMPATIBLE_CONTEXT_SIZE_K = 200;
56267
- var DEEPSEEK_MODELS = [
56268
- "deepseek-v4-flash",
56269
- "deepseek-v4-pro"
56270
- ];
56267
+ var DEEPSEEK_MODELS = ["deepseek-v4-flash", "deepseek-v4-pro"];
56271
56268
  var DEFAULT_DEEPSEEK_MODEL = DEEPSEEK_MODELS[0];
56272
56269
 
56273
56270
  // ../core/src/settings/settings-service.ts
@@ -56355,12 +56352,8 @@ var SettingsService = class _SettingsService {
56355
56352
  }
56356
56353
  async writeSettings(settings) {
56357
56354
  await mkdir(path.dirname(this.settingsPath), { recursive: true });
56358
- await writeFile(
56359
- this.settingsPath,
56360
- `${JSON.stringify(settings, null, 2)}
56361
- `,
56362
- "utf8"
56363
- );
56355
+ await writeFile(this.settingsPath, `${JSON.stringify(settings, null, 2)}
56356
+ `, "utf8");
56364
56357
  try {
56365
56358
  const fileStats = await stat(this.settingsPath);
56366
56359
  _SettingsService.cache.set(this.settingsPath, {
@@ -56399,9 +56392,7 @@ function parseStoredSettings(value) {
56399
56392
  return {
56400
56393
  defaultModelId: typeof value.defaultModelId === "string" ? value.defaultModelId : null,
56401
56394
  interfaceLanguage: parseInterfaceLanguage(value.interfaceLanguage),
56402
- modelConfigurations: Array.isArray(value.modelConfigurations) ? value.modelConfigurations.map((configuration) => parseStoredModelConfiguration(configuration)).filter(
56403
- (configuration) => Boolean(configuration)
56404
- ) : [],
56395
+ modelConfigurations: Array.isArray(value.modelConfigurations) ? value.modelConfigurations.map((configuration) => parseStoredModelConfiguration(configuration)).filter((configuration) => Boolean(configuration)) : [],
56405
56396
  resources: parseStoredResourceSettings(value.resources)
56406
56397
  };
56407
56398
  }
@@ -56486,10 +56477,7 @@ function normalizeSettings(settings) {
56486
56477
  baseUrl: configuration.baseUrl.trim(),
56487
56478
  model: normalizeModelId(configuration.model.trim(), configuration.provider),
56488
56479
  apiKey: configuration.apiKey.trim(),
56489
- contextSizeK: normalizeContextSizeK(
56490
- configuration.contextSizeK,
56491
- configuration.provider
56492
- ),
56480
+ contextSizeK: normalizeContextSizeK(configuration.contextSizeK, configuration.provider),
56493
56481
  ...providerOptions ? { providerOptions } : {}
56494
56482
  };
56495
56483
  });
@@ -56687,61 +56675,8 @@ function elapsedWallTime(startedAt) {
56687
56675
  return Math.max(0, Math.round((performance.now() - startedAt) * 100) / 100);
56688
56676
  }
56689
56677
 
56690
- // ../core/src/agent/page-edit-mode.ts
56691
- import path3 from "node:path";
56692
-
56693
56678
  // ../core/src/agent/tools/cdn-guard.ts
56694
56679
  import path2 from "node:path";
56695
- async function writeProjectWorkspaceFileWithCdnGuard(workspaceStore, projectId, relativePath, content, approvedCdnUrls = []) {
56696
- const guardedContent = await guardIndexHtmlCdnChanges(
56697
- workspaceStore,
56698
- projectId,
56699
- relativePath,
56700
- content,
56701
- approvedCdnUrls
56702
- );
56703
- return workspaceStore.writeProjectWorkspaceFile(
56704
- projectId,
56705
- relativePath,
56706
- guardedContent
56707
- );
56708
- }
56709
- async function editProjectWorkspaceFileWithCdnGuard(workspaceStore, projectId, relativePath, oldString, newString, replaceAll = false, approvedCdnUrls = []) {
56710
- const content = await workspaceStore.readProjectWorkspaceFile(
56711
- projectId,
56712
- relativePath
56713
- );
56714
- const updatedContent = replaceInContent(
56715
- content,
56716
- oldString,
56717
- newString,
56718
- replaceAll,
56719
- relativePath
56720
- );
56721
- const writeResult = await writeProjectWorkspaceFileWithCdnGuard(
56722
- workspaceStore,
56723
- projectId,
56724
- relativePath,
56725
- updatedContent,
56726
- approvedCdnUrls
56727
- );
56728
- return {
56729
- diff: writeResult.diff,
56730
- path: normalizeToolPath(relativePath),
56731
- replacements: replaceAll ? countOccurrences(content, normalizeEditNeedle(content, oldString)) : 1
56732
- };
56733
- }
56734
- async function applyProjectWorkspacePatchWithCdnGuard(workspaceStore, projectId, changes, approvedCdnUrls = []) {
56735
- return workspaceStore.applyProjectWorkspacePatch(projectId, changes, {
56736
- transformContent: async (relativePath, content) => guardIndexHtmlCdnChanges(
56737
- workspaceStore,
56738
- projectId,
56739
- relativePath,
56740
- content,
56741
- approvedCdnUrls
56742
- )
56743
- });
56744
- }
56745
56680
  async function readProjectWorkspaceFileIfExists(workspaceStore, projectId, relativePath) {
56746
56681
  try {
56747
56682
  return await workspaceStore.readProjectWorkspaceFile(projectId, relativePath);
@@ -56752,364 +56687,15 @@ async function readProjectWorkspaceFileIfExists(workspaceStore, projectId, relat
56752
56687
  throw error51;
56753
56688
  }
56754
56689
  }
56755
- function buildCdnTag(input) {
56756
- const attributes = [
56757
- 'data-owndesign-approved-cdn="true"',
56758
- input.integrity ? `integrity="${escapeHtmlAttribute(input.integrity)}"` : "",
56759
- input.crossorigin ? `crossorigin="${escapeHtmlAttribute(input.crossorigin)}"` : ""
56760
- ].filter(Boolean);
56761
- const suffix = attributes.length ? ` ${attributes.join(" ")}` : "";
56762
- const url2 = escapeHtmlAttribute(input.url);
56763
- if (input.resourceType === "stylesheet") {
56764
- return `<link rel="stylesheet" href="${url2}"${suffix}>`;
56765
- }
56766
- if (input.resourceType === "style-import") {
56767
- return `<style${suffix}>
56768
- @import url('${url2}');
56769
- </style>`;
56770
- }
56771
- return `<script src="${url2}"${suffix}></script>`;
56772
- }
56773
- async function guardIndexHtmlCdnChanges(workspaceStore, projectId, relativePath, content, approvedCdnUrls) {
56774
- if (!isHtmlPath(relativePath)) {
56775
- return content;
56776
- }
56777
- const normalizedContent = normalizeConfiguredResourceCdnRefs(
56778
- content,
56779
- approvedCdnUrls
56780
- );
56781
- const approvedUrls = new Set(
56782
- approvedCdnUrls.map(normalizeHttpsUrl).filter(Boolean)
56783
- );
56784
- const unapprovedRefs = extractExternalCdnRefs(normalizedContent).filter(
56785
- ({ url: url2 }) => !approvedUrls.has(url2)
56786
- );
56787
- if (unapprovedRefs.length > 0) {
56788
- throw new Error(
56789
- `HTML files can only use CDN resources configured in settings: ${unapprovedRefs.map(({ url: url2 }) => url2).join(", ")}`
56790
- );
56791
- }
56792
- return normalizedContent;
56793
- }
56794
- function replaceInContent(content, oldString, newString, replaceAll, relativePath) {
56795
- if (!oldString) {
56796
- throw new Error("oldString must not be empty.");
56797
- }
56798
- if (oldString === newString) {
56799
- throw new Error("No changes to apply: oldString and newString are identical.");
56800
- }
56801
- const needle = normalizeEditNeedle(content, oldString);
56802
- const replacement = convertToLineEnding(newString, detectLineEnding(content));
56803
- const firstIndex = content.indexOf(needle);
56804
- if (firstIndex === -1) {
56805
- throw new Error(`oldString was not found in Project Workspace file: ${relativePath}`);
56806
- }
56807
- if (replaceAll) {
56808
- return content.split(needle).join(replacement);
56809
- }
56810
- if (content.indexOf(needle, firstIndex + needle.length) !== -1) {
56811
- throw new Error(
56812
- `oldString appears more than once in Project Workspace file: ${relativePath}`
56813
- );
56814
- }
56815
- return content.slice(0, firstIndex) + replacement + content.slice(firstIndex + needle.length);
56816
- }
56817
- function normalizeEditNeedle(content, oldString) {
56818
- return convertToLineEnding(oldString, detectLineEnding(content));
56819
- }
56820
- function detectLineEnding(text2) {
56821
- return text2.includes("\r\n") ? "\r\n" : "\n";
56822
- }
56823
- function convertToLineEnding(text2, ending) {
56824
- const normalized = text2.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
56825
- return ending === "\n" ? normalized : normalized.replaceAll("\n", "\r\n");
56826
- }
56827
- function countOccurrences(content, needle) {
56828
- if (!needle) {
56829
- return 0;
56830
- }
56831
- return content.split(needle).length - 1;
56832
- }
56833
- function extractExternalCdnRefs(html) {
56834
- return extractCdnTags(html).map(({ resourceType, url: url2 }) => ({
56835
- resourceType,
56836
- url: url2
56837
- }));
56838
- }
56839
- function extractCdnTags(html) {
56840
- const refs = [];
56841
- for (const match2 of html.matchAll(/<script\b[^>]*>[\s\S]*?<\/script>/gi)) {
56842
- const tag = match2[0];
56843
- const src = getHtmlAttribute(tag, "src");
56844
- if (src && isHttpsUrl(src)) {
56845
- refs.push({
56846
- rawUrl: src,
56847
- resourceType: "script",
56848
- tag,
56849
- url: normalizeUrl(src)
56850
- });
56851
- }
56852
- }
56853
- for (const match2 of html.matchAll(/<link\b[^>]*>/gi)) {
56854
- const tag = match2[0];
56855
- const href = getHtmlAttribute(tag, "href");
56856
- const rel = getHtmlAttribute(tag, "rel");
56857
- if (href && isHttpsUrl(href) && rel?.toLowerCase().split(/\s+/).includes("stylesheet")) {
56858
- refs.push({
56859
- rawUrl: href,
56860
- resourceType: "stylesheet",
56861
- tag,
56862
- url: normalizeUrl(href)
56863
- });
56864
- }
56865
- }
56866
- for (const match2 of html.matchAll(/<style\b[^>]*>[\s\S]*?<\/style>/gi)) {
56867
- const tag = match2[0];
56868
- for (const importMatch of tag.matchAll(
56869
- /@import\s+(?:url\(\s*)?(?:"([^"]+)"|'([^']+)'|([^"')\s;]+))\s*\)?/gi
56870
- )) {
56871
- const url2 = importMatch[1] ?? importMatch[2] ?? importMatch[3];
56872
- if (url2 && isHttpsUrl(url2)) {
56873
- refs.push({
56874
- rawUrl: url2,
56875
- resourceType: "style-import",
56876
- tag,
56877
- url: normalizeUrl(url2)
56878
- });
56879
- }
56880
- }
56881
- }
56882
- return refs;
56883
- }
56884
- function normalizeConfiguredResourceCdnRefs(html, approvedCdnUrls) {
56885
- const approvedUrls = approvedCdnUrls.map(normalizeHttpsUrl).filter((url2) => Boolean(url2));
56886
- const configuredFontCdn = approvedUrls.find(isGoogleFontsCss2Url);
56887
- let updatedHtml = html;
56888
- for (const ref of extractCdnTags(html)) {
56889
- const replacementUrl = getConfiguredReplacementUrl(ref.url, {
56890
- configuredFontCdn
56891
- });
56892
- if (!replacementUrl || ref.url === replacementUrl) {
56893
- continue;
56894
- }
56895
- if (ref.resourceType === "stylesheet" && configuredFontCdn && ref.url !== configuredFontCdn && isGoogleFontsCss2Url(ref.url)) {
56896
- updatedHtml = updatedHtml.replace(
56897
- ref.tag,
56898
- buildCdnTag({ resourceType: "style-import", url: configuredFontCdn })
56899
- );
56900
- continue;
56901
- }
56902
- updatedHtml = updatedHtml.split(ref.rawUrl).join(replacementUrl);
56903
- }
56904
- return updatedHtml;
56905
- }
56906
- function getConfiguredReplacementUrl(url2, replacements) {
56907
- if (replacements.configuredFontCdn && isGoogleFontsCss2Url(url2)) {
56908
- return replacements.configuredFontCdn;
56909
- }
56910
- return void 0;
56911
- }
56912
- function isGoogleFontsCss2Url(value) {
56913
- try {
56914
- const url2 = new URL(value);
56915
- return url2.hostname === "fonts.googleapis.com" && url2.pathname === "/css2";
56916
- } catch {
56917
- return false;
56918
- }
56919
- }
56920
- function getHtmlAttribute(tag, name21) {
56921
- const pattern = new RegExp(
56922
- `\\s${name21}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))`,
56923
- "i"
56924
- );
56925
- const match2 = tag.match(pattern);
56926
- return match2?.[1] ?? match2?.[2] ?? match2?.[3];
56927
- }
56928
- function isHttpsUrl(value) {
56929
- try {
56930
- return new URL(value).protocol === "https:";
56931
- } catch {
56932
- return false;
56933
- }
56934
- }
56935
- function normalizeUrl(value) {
56936
- return new URL(value).href;
56937
- }
56938
56690
  function isHtmlPath(relativePath) {
56939
56691
  return normalizeToolPath(relativePath).toLowerCase().endsWith(".html");
56940
56692
  }
56941
56693
  function normalizeToolPath(relativePath) {
56942
56694
  return path2.posix.normalize(relativePath.replaceAll("\\", "/"));
56943
56695
  }
56944
- function normalizeHttpsUrl(value) {
56945
- try {
56946
- const url2 = new URL(value);
56947
- return url2.protocol === "https:" ? url2.href : void 0;
56948
- } catch {
56949
- return void 0;
56950
- }
56951
- }
56952
56696
  function isMissingPathError2(error51) {
56953
56697
  return error51 instanceof Error && "code" in error51 && error51.code === "ENOENT";
56954
56698
  }
56955
- function escapeHtmlAttribute(value) {
56956
- return value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
56957
- }
56958
-
56959
- // ../core/src/agent/page-edit-mode.ts
56960
- var PAGE_EDIT_MODES = [
56961
- "auto",
56962
- "new_page",
56963
- "direct_edit",
56964
- "duplicate_edit"
56965
- ];
56966
- function parsePageEditMode(value) {
56967
- if (value === void 0 || value === null || value === "") {
56968
- return "auto";
56969
- }
56970
- return PAGE_EDIT_MODES.includes(value) ? value : void 0;
56971
- }
56972
- async function buildPageEditModePolicy({
56973
- currentPreviewPath,
56974
- mode = "auto",
56975
- projectId,
56976
- workspaceStore
56977
- }) {
56978
- if (mode === "auto") {
56979
- return { mode };
56980
- }
56981
- if (mode === "new_page") {
56982
- return {
56983
- currentPreviewPath: currentPreviewPath ? normalizeRequiredHtmlPath(currentPreviewPath, mode) : void 0,
56984
- mode
56985
- };
56986
- }
56987
- const sourcePath = normalizeRequiredHtmlPath(currentPreviewPath, mode);
56988
- await readRequiredHtmlFile(workspaceStore, projectId, sourcePath, mode);
56989
- if (mode === "direct_edit") {
56990
- return {
56991
- mode,
56992
- targetPath: sourcePath
56993
- };
56994
- }
56995
- const targetPath = await resolveUniqueCopyPath(
56996
- workspaceStore,
56997
- projectId,
56998
- sourcePath
56999
- );
57000
- return {
57001
- mode,
57002
- sourcePath,
57003
- targetPath
57004
- };
57005
- }
57006
- function assertCreateHtmlAllowed(policy, relativePath) {
57007
- assertHtmlPathOperationAllowed(policy, "create", relativePath);
57008
- }
57009
- function markCreatedHtmlPath(policy, relativePath) {
57010
- if (policy?.mode === "new_page") {
57011
- policy.createdHtmlPath = normalizeToolPath(relativePath);
57012
- }
57013
- }
57014
- function assertCopyFileAllowed(policy, sourcePath, targetPath) {
57015
- if (!policy || policy.mode !== "duplicate_edit") {
57016
- return;
57017
- }
57018
- const normalizedSourcePath = normalizeToolPath(sourcePath);
57019
- const normalizedTargetPath = normalizeToolPath(targetPath);
57020
- if (normalizedSourcePath !== policy.sourcePath || normalizedTargetPath !== policy.targetPath) {
57021
- throw new Error(
57022
- `Page edit mode "duplicate_edit" can only copy ${policy.sourcePath} to ${policy.targetPath}; attempted ${normalizedSourcePath} to ${normalizedTargetPath}.`
57023
- );
57024
- }
57025
- }
57026
- function resolveHtmlOperationPathForPageEditModePolicy(policy, operation, relativePath) {
57027
- if (!policy || policy.mode === "auto") {
57028
- return relativePath;
57029
- }
57030
- const targetPath = normalizeToolPath(relativePath);
57031
- if (!isHtmlPath(targetPath)) {
57032
- return relativePath;
57033
- }
57034
- assertHtmlPathOperationAllowed(policy, operation, targetPath);
57035
- return targetPath;
57036
- }
57037
- function assertHtmlPathOperationAllowed(policy, operation, relativePath) {
57038
- if (!policy || policy.mode !== "duplicate_edit") {
57039
- return;
57040
- }
57041
- const targetPath = normalizeToolPath(relativePath);
57042
- if (!isHtmlPath(targetPath)) {
57043
- return;
57044
- }
57045
- if (operation === "read") {
57046
- return;
57047
- }
57048
- if (operation === "copy" && targetPath === policy.targetPath) {
57049
- return;
57050
- }
57051
- if (targetPath !== policy.targetPath) {
57052
- throw new Error(
57053
- `Page edit mode "duplicate_edit" can only ${formatHtmlOperation(operation)} ${policy.targetPath}; attempted ${targetPath}.`
57054
- );
57055
- }
57056
- }
57057
- function formatHtmlOperation(operation) {
57058
- switch (operation) {
57059
- case "copy":
57060
- return "copy";
57061
- case "create":
57062
- return "create";
57063
- case "delete":
57064
- return "delete";
57065
- case "mutate":
57066
- return "edit";
57067
- case "preview":
57068
- return "preview";
57069
- case "read":
57070
- return "read";
57071
- }
57072
- }
57073
- function normalizeRequiredHtmlPath(relativePath, mode) {
57074
- if (!relativePath) {
57075
- throw new Error(`Page edit mode "${mode}" requires a current preview page.`);
57076
- }
57077
- const normalizedPath = normalizeToolPath(relativePath);
57078
- if (!isHtmlPath(normalizedPath)) {
57079
- throw new Error(
57080
- `Page edit mode "${mode}" requires an HTML preview page: ${normalizedPath}`
57081
- );
57082
- }
57083
- return normalizedPath;
57084
- }
57085
- async function readRequiredHtmlFile(workspaceStore, projectId, relativePath, mode) {
57086
- try {
57087
- return await workspaceStore.readProjectWorkspaceFile(projectId, relativePath);
57088
- } catch {
57089
- throw new Error(
57090
- `Page edit mode "${mode}" requires an existing current preview page: ${relativePath}`
57091
- );
57092
- }
57093
- }
57094
- async function resolveUniqueCopyPath(workspaceStore, projectId, sourcePath) {
57095
- const parsed = path3.posix.parse(sourcePath);
57096
- const directory = parsed.dir ? `${parsed.dir}/` : "";
57097
- const baseName = normalizeCopyBaseName(parsed.name);
57098
- const extension = parsed.ext || ".html";
57099
- for (let index = 1; index < 1e3; index += 1) {
57100
- const suffix = index === 1 ? "copy" : `copy-${index}`;
57101
- const candidatePath = `${directory}${baseName}.${suffix}${extension}`;
57102
- try {
57103
- await workspaceStore.readProjectWorkspaceFile(projectId, candidatePath);
57104
- } catch {
57105
- return candidatePath;
57106
- }
57107
- }
57108
- throw new Error(`Could not create a unique copy path for ${sourcePath}.`);
57109
- }
57110
- function normalizeCopyBaseName(baseName) {
57111
- return baseName.replace(/\.copy(?:-\d+)?$/, "");
57112
- }
57113
56699
 
57114
56700
  // ../core/src/agent/tools/copy-file.ts
57115
56701
  function createCopyFileToolDefinition() {
@@ -57117,67 +56703,84 @@ function createCopyFileToolDefinition() {
57117
56703
  description: "Copy one UTF-8 text file inside the current Project Workspace to a new path. Never overwrites existing files.",
57118
56704
  inputSchema: external_exports.object({
57119
56705
  sourcePath: external_exports.string().describe("Relative source file path inside the Project Workspace."),
57120
- targetPath: external_exports.string().describe("Relative destination file path inside the Project Workspace. Must not already exist.")
56706
+ targetPath: external_exports.string().describe(
56707
+ "Relative destination file path inside the Project Workspace. Must not already exist."
56708
+ )
57121
56709
  }).strict(),
57122
56710
  name: "copyFile",
57123
56711
  parallelSafe: false,
57124
- execute: async ({ sourcePath, targetPath }, { approvedCdnUrls, pageEditModePolicy, projectId, workspaceStore }) => {
56712
+ execute: async ({ sourcePath, targetPath }, { projectId, workspaceStore }) => {
57125
56713
  const normalizedSourcePath = normalizeToolPath(sourcePath);
57126
56714
  const normalizedTargetPath = normalizeToolPath(targetPath);
57127
- assertCopyFileAllowed(
57128
- pageEditModePolicy,
57129
- normalizedSourcePath,
57130
- normalizedTargetPath
57131
- );
57132
56715
  const existingTarget = await readProjectWorkspaceFileIfExists(
57133
56716
  workspaceStore,
57134
56717
  projectId,
57135
56718
  normalizedTargetPath
57136
56719
  );
57137
56720
  if (existingTarget !== void 0) {
57138
- throw new Error(
57139
- `Project Workspace file already exists: ${normalizedTargetPath}`
57140
- );
56721
+ throw new Error(`Project Workspace file already exists: ${normalizedTargetPath}`);
57141
56722
  }
57142
56723
  const sourceContent = await workspaceStore.readProjectWorkspaceFile(
57143
56724
  projectId,
57144
56725
  normalizedSourcePath
57145
56726
  );
57146
- return writeProjectWorkspaceFileWithCdnGuard(
57147
- workspaceStore,
57148
- projectId,
57149
- normalizedTargetPath,
57150
- sourceContent,
57151
- approvedCdnUrls
57152
- );
56727
+ return workspaceStore.writeProjectWorkspaceFile(projectId, normalizedTargetPath, sourceContent);
57153
56728
  }
57154
56729
  };
57155
56730
  }
57156
56731
 
56732
+ // ../core/src/templates/index.ts
56733
+ import { readFileSync } from "node:fs";
56734
+ import path3 from "node:path";
56735
+ import { fileURLToPath } from "node:url";
56736
+ var TEMPLATE_FILES = {
56737
+ "html/page-shell": "html/page-shell.html"
56738
+ };
56739
+ function loadTemplate(name21) {
56740
+ const templateFile = TEMPLATE_FILES[name21];
56741
+ if (!templateFile) {
56742
+ throw new Error(`Unsupported template: ${String(name21)}`);
56743
+ }
56744
+ const currentDir = path3.dirname(fileURLToPath(import.meta.url));
56745
+ const candidatePaths = [
56746
+ path3.join(currentDir, "templates", templateFile),
56747
+ path3.join(currentDir, templateFile)
56748
+ ];
56749
+ for (const candidatePath of candidatePaths) {
56750
+ try {
56751
+ return readFileSync(candidatePath, "utf8").trim();
56752
+ } catch (error51) {
56753
+ if (!isNotFoundError(error51)) {
56754
+ throw error51;
56755
+ }
56756
+ }
56757
+ }
56758
+ throw new Error(`Template "${name21}" was not found. Searched: ${candidatePaths.join(", ")}`);
56759
+ }
56760
+ function isNotFoundError(error51) {
56761
+ return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
56762
+ }
56763
+
57157
56764
  // ../core/src/agent/tools/create-html.ts
57158
56765
  var DEFAULT_TITLE = "OwnDesign Preview";
56766
+ var SINGLE_HTML_PATH = "index.html";
57159
56767
  function createCreateHtmlToolDefinition() {
57160
56768
  return {
57161
- description: "Create a new previewable HTML file from the configured resource template before designing a missing target HTML page. Never overwrites existing files.",
56769
+ description: "Create the single previewable index.html file from the configured template before designing a missing Single HTML project. Never overwrites existing files.",
57162
56770
  inputSchema: external_exports.object({
57163
- fontLibraryName: external_exports.string().describe("Optional configured font library name. Omit to use the default font library. Pass an empty string to disable font resources.").optional(),
57164
- iconLibraryName: external_exports.string().describe("Optional configured icon library name. Omit to use the default icon library. Pass an empty string to disable icon resources.").optional(),
57165
- path: external_exports.string().describe("Relative HTML file path inside the Project Workspace, such as index.html or pages/detail.html."),
56771
+ path: external_exports.string().describe("Relative HTML file path inside the Project Workspace. Must be index.html."),
57166
56772
  title: external_exports.string().describe("Optional document title. Defaults to OwnDesign Preview.").optional()
57167
56773
  }).strict(),
57168
56774
  name: "createHtml",
57169
56775
  parallelSafe: false,
57170
- execute: async (input, {
57171
- pageEditModePolicy,
57172
- projectId,
57173
- resources,
57174
- workspaceStore
57175
- }) => {
56776
+ execute: async (input, { projectId, workspaceStore }) => {
57176
56777
  const targetPath = normalizeToolPath(input.path);
57177
- assertCreateHtmlAllowed(pageEditModePolicy, targetPath);
57178
56778
  if (!isHtmlPath(targetPath)) {
57179
56779
  throw new Error(`HTML initialization target must end with .html: ${targetPath}`);
57180
56780
  }
56781
+ if (targetPath !== SINGLE_HTML_PATH) {
56782
+ throw new Error(`Single HTML projects can only create ${SINGLE_HTML_PATH}: ${targetPath}`);
56783
+ }
57181
56784
  const existingHtml = await readProjectWorkspaceFileIfExists(
57182
56785
  workspaceStore,
57183
56786
  projectId,
@@ -57186,94 +56789,28 @@ function createCreateHtmlToolDefinition() {
57186
56789
  if (existingHtml !== void 0) {
57187
56790
  throw new Error(`Project Workspace HTML file already exists: ${targetPath}`);
57188
56791
  }
57189
- const fontLibrary = selectLibrary(
57190
- resources.fontLibraries,
57191
- input.fontLibraryName,
57192
- "font"
57193
- );
57194
- const iconLibrary = selectLibrary(
57195
- resources.iconLibraries,
57196
- input.iconLibraryName,
57197
- "icon"
56792
+ const title = input.title?.trim() || DEFAULT_TITLE;
56793
+ await workspaceStore.writeProjectWorkspaceFile(
56794
+ projectId,
56795
+ targetPath,
56796
+ buildSingleHtmlTemplate({ title })
57198
56797
  );
57199
- const html = buildHtmlTemplate({
57200
- fontLibrary,
57201
- iconLibrary,
57202
- title: input.title?.trim() || DEFAULT_TITLE
57203
- });
57204
- await workspaceStore.writeProjectWorkspaceFile(projectId, targetPath, html);
57205
- markCreatedHtmlPath(pageEditModePolicy, targetPath);
57206
56798
  return {
57207
- fontLibrary: formatSelectedLibrary(fontLibrary),
57208
- iconLibrary: formatSelectedLibrary(iconLibrary),
57209
56799
  path: targetPath,
57210
- title: input.title?.trim() || DEFAULT_TITLE
56800
+ title
57211
56801
  };
57212
56802
  }
57213
56803
  };
57214
56804
  }
57215
- function selectLibrary(libraries, name21, kind) {
57216
- if (name21 === "") {
57217
- return void 0;
57218
- }
57219
- if (name21 !== void 0) {
57220
- const library = libraries.find((item) => item.name === name21);
57221
- if (!library) {
57222
- throw new Error(`Configured ${kind} library was not found: ${name21}`);
57223
- }
57224
- return library;
57225
- }
57226
- return libraries.find((library) => library.isDefault) ?? libraries[0];
57227
- }
57228
- function buildHtmlTemplate({
57229
- fontLibrary,
57230
- iconLibrary,
57231
- title
57232
- }) {
57233
- const headTags = [
57234
- fontLibrary?.cdn ? buildCdnTag({ resourceType: "style-import", url: fontLibrary.cdn }) : "",
57235
- iconLibrary?.cdn && inferIconLibraryResourceType(iconLibrary.cdn) === "stylesheet" ? buildCdnTag({ resourceType: "stylesheet", url: iconLibrary.cdn }) : ""
57236
- ].filter(Boolean);
57237
- const bodyScripts = [
57238
- iconLibrary?.cdn && inferIconLibraryResourceType(iconLibrary.cdn) === "script" ? buildCdnTag({ resourceType: "script", url: iconLibrary.cdn }) : "",
57239
- isLucideLibrary(iconLibrary) ? " <script>window.lucide?.createIcons?.();</script>" : ""
57240
- ].filter(Boolean);
57241
- return [
57242
- "<!doctype html>",
57243
- '<html lang="zh-CN">',
57244
- "<head>",
57245
- ' <meta charset="utf-8">',
57246
- ' <meta name="viewport" content="width=device-width, initial-scale=1">',
57247
- ` <title>${escapeHtmlText(title)}</title>`,
57248
- ...headTags.map((tag) => indentMultiline(tag, " ")),
57249
- "</head>",
57250
- "<body>",
57251
- ' <main id="app"></main>',
57252
- ...bodyScripts.map((tag) => indentMultiline(tag, " ")),
57253
- "</body>",
57254
- "</html>",
57255
- ""
57256
- ].join("\n");
57257
- }
57258
- function inferIconLibraryResourceType(cdn) {
57259
- const normalized = cdn.toLowerCase();
57260
- return normalized.includes(".css") || normalized.includes("/css/") || normalized.includes("font-awesome") ? "stylesheet" : "script";
57261
- }
57262
- function isLucideLibrary(library) {
57263
- if (!library?.cdn) {
57264
- return false;
57265
- }
57266
- const value = `${library.name} ${library.cdn}`.toLowerCase();
57267
- return value.includes("lucide");
57268
- }
57269
- function formatSelectedLibrary(library) {
57270
- return library ? {
57271
- cdn: library.cdn,
57272
- name: library.name
57273
- } : void 0;
56805
+ function buildSingleHtmlTemplate({ title }) {
56806
+ return renderTemplate(loadTemplate("html/page-shell"), {
56807
+ lang: "zh-CN",
56808
+ title: escapeHtmlText(title)
56809
+ });
57274
56810
  }
57275
- function indentMultiline(value, indent) {
57276
- return value.split("\n").map((line) => `${indent}${line}`).join("\n");
56811
+ function renderTemplate(template, values) {
56812
+ return `${template.replace(/\{\{([a-zA-Z0-9]+)\}\}/g, (_match, key) => values[key] ?? "")}
56813
+ `;
57277
56814
  }
57278
56815
  function escapeHtmlText(value) {
57279
56816
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
@@ -57288,14 +56825,7 @@ function createDeleteToolDefinition() {
57288
56825
  }).strict(),
57289
56826
  name: "delete",
57290
56827
  parallelSafe: false,
57291
- execute: async ({ path: path16 }, {
57292
- pageEditModePolicy,
57293
- projectId,
57294
- workspaceStore
57295
- }) => {
57296
- assertHtmlPathOperationAllowed(pageEditModePolicy, "delete", path16);
57297
- return workspaceStore.deleteProjectWorkspacePath(projectId, path16);
57298
- }
56828
+ execute: async ({ path: path16 }, { projectId, workspaceStore }) => workspaceStore.deleteProjectWorkspacePath(projectId, path16)
57299
56829
  };
57300
56830
  }
57301
56831
 
@@ -57311,27 +56841,7 @@ function createEditToolDefinition() {
57311
56841
  }).strict(),
57312
56842
  name: "edit",
57313
56843
  parallelSafe: false,
57314
- execute: async ({ newString, oldString, path: path16, replaceAll }, {
57315
- approvedCdnUrls,
57316
- pageEditModePolicy,
57317
- projectId,
57318
- workspaceStore
57319
- }) => {
57320
- const editPath = resolveHtmlOperationPathForPageEditModePolicy(
57321
- pageEditModePolicy,
57322
- "mutate",
57323
- path16
57324
- );
57325
- return editProjectWorkspaceFileWithCdnGuard(
57326
- workspaceStore,
57327
- projectId,
57328
- editPath,
57329
- oldString,
57330
- newString,
57331
- replaceAll,
57332
- approvedCdnUrls
57333
- );
57334
- }
56844
+ execute: async ({ newString, oldString, path: path16, replaceAll }, { projectId, workspaceStore }) => workspaceStore.editProjectWorkspaceFile(projectId, path16, oldString, newString, replaceAll)
57335
56845
  };
57336
56846
  }
57337
56847
 
@@ -57345,15 +56855,8 @@ function createGlobToolDefinition() {
57345
56855
  }).strict(),
57346
56856
  name: "glob",
57347
56857
  parallelSafe: true,
57348
- execute: async ({ path: path16, pattern }, {
57349
- projectId,
57350
- workspaceStore
57351
- }) => {
57352
- return workspaceStore.globProjectWorkspace(
57353
- projectId,
57354
- pattern,
57355
- path16
57356
- );
56858
+ execute: async ({ path: path16, pattern }, { projectId, workspaceStore }) => {
56859
+ return workspaceStore.globProjectWorkspace(projectId, pattern, path16);
57357
56860
  }
57358
56861
  };
57359
56862
  }
@@ -57364,15 +56867,14 @@ function createGrepToolDefinition() {
57364
56867
  description: "Search UTF-8 text files in the current Project Workspace using a JavaScript regular expression.",
57365
56868
  inputSchema: external_exports.object({
57366
56869
  include: external_exports.string().describe('Optional file glob to include, such as "*.html" or "**/*.{css,js}".').optional(),
57367
- path: external_exports.string().describe("Optional relative file or directory path inside the Project Workspace to search.").optional(),
56870
+ path: external_exports.string().describe(
56871
+ "Optional relative file or directory path inside the Project Workspace to search."
56872
+ ).optional(),
57368
56873
  pattern: external_exports.string().describe("JavaScript regular expression pattern to search for.")
57369
56874
  }).strict(),
57370
56875
  name: "grep",
57371
56876
  parallelSafe: true,
57372
- execute: async ({ include, path: path16, pattern }, {
57373
- projectId,
57374
- workspaceStore
57375
- }) => {
56877
+ execute: async ({ include, path: path16, pattern }, { projectId, workspaceStore }) => {
57376
56878
  return workspaceStore.grepProjectWorkspace(projectId, pattern, {
57377
56879
  include,
57378
56880
  path: path16
@@ -57386,48 +56888,35 @@ function createPatchToolDefinition() {
57386
56888
  return {
57387
56889
  description: "Apply coordinated UTF-8 file changes inside the current Project Workspace. Supports add/write, edit, and delete changes.",
57388
56890
  inputSchema: external_exports.object({
57389
- changes: external_exports.array(external_exports.discriminatedUnion("operation", [
57390
- external_exports.object({
57391
- content: external_exports.string().describe("Complete file content for add operations."),
57392
- operation: external_exports.literal("add"),
57393
- path: external_exports.string().describe("Relative file or directory path inside the Project Workspace.")
57394
- }).strict(),
57395
- external_exports.object({
57396
- content: external_exports.string().describe("Complete file content for write operations."),
57397
- operation: external_exports.literal("write"),
57398
- path: external_exports.string().describe("Relative file or directory path inside the Project Workspace.")
57399
- }).strict(),
57400
- external_exports.object({
57401
- newString: external_exports.string().describe("Replacement text for edit operations."),
57402
- oldString: external_exports.string().describe("Text to replace for edit operations."),
57403
- operation: external_exports.literal("edit"),
57404
- path: external_exports.string().describe("Relative file or directory path inside the Project Workspace."),
57405
- replaceAll: external_exports.boolean().describe("For edit operations, replace every occurrence of oldString.").optional()
57406
- }).strict(),
57407
- external_exports.object({
57408
- operation: external_exports.literal("delete"),
57409
- path: external_exports.string().describe("Relative file or directory path inside the Project Workspace.")
57410
- }).strict()
57411
- ])).min(1)
56891
+ changes: external_exports.array(
56892
+ external_exports.discriminatedUnion("operation", [
56893
+ external_exports.object({
56894
+ content: external_exports.string().describe("Complete file content for add operations."),
56895
+ operation: external_exports.literal("add"),
56896
+ path: external_exports.string().describe("Relative file or directory path inside the Project Workspace.")
56897
+ }).strict(),
56898
+ external_exports.object({
56899
+ content: external_exports.string().describe("Complete file content for write operations."),
56900
+ operation: external_exports.literal("write"),
56901
+ path: external_exports.string().describe("Relative file or directory path inside the Project Workspace.")
56902
+ }).strict(),
56903
+ external_exports.object({
56904
+ newString: external_exports.string().describe("Replacement text for edit operations."),
56905
+ oldString: external_exports.string().describe("Text to replace for edit operations."),
56906
+ operation: external_exports.literal("edit"),
56907
+ path: external_exports.string().describe("Relative file or directory path inside the Project Workspace."),
56908
+ replaceAll: external_exports.boolean().describe("For edit operations, replace every occurrence of oldString.").optional()
56909
+ }).strict(),
56910
+ external_exports.object({
56911
+ operation: external_exports.literal("delete"),
56912
+ path: external_exports.string().describe("Relative file or directory path inside the Project Workspace.")
56913
+ }).strict()
56914
+ ])
56915
+ ).min(1)
57412
56916
  }).strict(),
57413
56917
  name: "patch",
57414
56918
  parallelSafe: false,
57415
- execute: async ({ changes }, { approvedCdnUrls, pageEditModePolicy, projectId, workspaceStore }) => {
57416
- const resolvedChanges = changes.map((change) => ({
57417
- ...change,
57418
- path: resolveHtmlOperationPathForPageEditModePolicy(
57419
- pageEditModePolicy,
57420
- change.operation === "delete" ? "delete" : "mutate",
57421
- change.path
57422
- )
57423
- }));
57424
- return applyProjectWorkspacePatchWithCdnGuard(
57425
- workspaceStore,
57426
- projectId,
57427
- resolvedChanges,
57428
- approvedCdnUrls
57429
- );
57430
- }
56919
+ execute: async ({ changes }, { projectId, workspaceStore }) => workspaceStore.applyProjectWorkspacePatch(projectId, changes)
57431
56920
  };
57432
56921
  }
57433
56922
 
@@ -57435,11 +56924,7 @@ function createPatchToolDefinition() {
57435
56924
  var KEEPALIVE_INTERVAL_MS = 15e3;
57436
56925
  var FrontendCommandBus = class {
57437
56926
  connections = /* @__PURE__ */ new Map();
57438
- registerConnection({
57439
- frontendTabId,
57440
- projectId,
57441
- signal
57442
- }) {
56927
+ registerConnection({ frontendTabId, projectId, signal }) {
57443
56928
  const key = buildConnectionKey(projectId, frontendTabId);
57444
56929
  const encoder = new TextEncoder();
57445
56930
  let connection;
@@ -57476,9 +56961,7 @@ var FrontendCommandBus = class {
57476
56961
  payload,
57477
56962
  projectId
57478
56963
  }) {
57479
- const connection = this.connections.get(
57480
- buildConnectionKey(projectId, frontendTabId)
57481
- );
56964
+ const connection = this.connections.get(buildConnectionKey(projectId, frontendTabId));
57482
56965
  if (!connection) {
57483
56966
  return {
57484
56967
  delivered: false,
@@ -57490,12 +56973,7 @@ var FrontendCommandBus = class {
57490
56973
  id: createCommandId(),
57491
56974
  payload
57492
56975
  };
57493
- enqueueSseEvent(
57494
- connection.controller,
57495
- connection.encoder,
57496
- "frontend-command",
57497
- command
57498
- );
56976
+ enqueueSseEvent(connection.controller, connection.encoder, "frontend-command", command);
57499
56977
  return {
57500
56978
  command,
57501
56979
  delivered: true
@@ -57535,12 +57013,10 @@ function sendFrontendCommand(input) {
57535
57013
  return getFrontendCommandBus().sendCommand(input);
57536
57014
  }
57537
57015
  function enqueueSseEvent(controller, encoder, event, data) {
57538
- controller.enqueue(
57539
- encoder.encode(`event: ${event}
57016
+ controller.enqueue(encoder.encode(`event: ${event}
57540
57017
  data: ${JSON.stringify(data)}
57541
57018
 
57542
- `)
57543
- );
57019
+ `));
57544
57020
  }
57545
57021
  function enqueueSseComment(controller, encoder, comment) {
57546
57022
  controller.enqueue(encoder.encode(`: ${comment}
@@ -57585,76 +57061,24 @@ function createPreviewRefreshToolDefinition() {
57585
57061
  };
57586
57062
  }
57587
57063
 
57588
- // ../core/src/agent/tools/preview-switch-html.ts
57589
- function createPreviewSwitchHtmlToolDefinition() {
57590
- return {
57591
- description: "Switch the Preview Pane to an existing HTML file after successful previewable HTML changes.",
57592
- inputSchema: external_exports.object({
57593
- path: external_exports.string().describe("Relative HTML file path inside the Project Workspace to show in the Preview Pane.")
57594
- }).strict(),
57595
- name: "previewSwitchHtml",
57596
- parallelSafe: false,
57597
- execute: async ({ path: path16 }, context2) => {
57598
- if (!context2.frontendTabId) {
57599
- throw new Error("Frontend tab id is required to switch the preview.");
57600
- }
57601
- const targetPath = normalizeToolPath(path16);
57602
- if (!targetPath || targetPath === ".") {
57603
- throw new Error("Preview switch target path must not be empty.");
57604
- }
57605
- if (!isHtmlPath(targetPath)) {
57606
- throw new Error(`Preview switch target must end with .html: ${targetPath}`);
57607
- }
57608
- const previewPath = resolveHtmlOperationPathForPageEditModePolicy(
57609
- context2.pageEditModePolicy,
57610
- "preview",
57611
- targetPath
57612
- );
57613
- const htmlFiles = await context2.workspaceStore.listProjectHtmlFiles(
57614
- context2.projectId
57615
- );
57616
- if (!htmlFiles.includes(previewPath)) {
57617
- throw new Error(`Project Workspace HTML file was not found: ${previewPath}`);
57618
- }
57619
- const payload = { path: previewPath };
57620
- const result = sendFrontendCommand({
57621
- capability: "preview.switchHtml",
57622
- frontendTabId: context2.frontendTabId,
57623
- payload,
57624
- projectId: context2.projectId
57625
- });
57626
- return {
57627
- capability: "preview.switchHtml",
57628
- delivered: result?.delivered ?? false,
57629
- payload
57630
- };
57631
- }
57632
- };
57633
- }
57634
-
57635
57064
  // ../core/src/agent/tools/read.ts
57636
57065
  function createReadToolDefinition() {
57637
57066
  return {
57638
57067
  description: "Read one UTF-8 file or directory from the current Project Workspace. Files are returned with 1-indexed line numbers.",
57639
57068
  inputSchema: external_exports.object({
57640
57069
  limit: external_exports.number().describe("Maximum number of lines or directory entries to read. Defaults to 2000.").optional(),
57641
- offset: external_exports.number().describe("1-indexed line or directory-entry offset to start reading from. Defaults to 1.").optional(),
57070
+ offset: external_exports.number().describe(
57071
+ "1-indexed line or directory-entry offset to start reading from. Defaults to 1."
57072
+ ).optional(),
57642
57073
  path: external_exports.string().describe("Relative file or directory path inside the Project Workspace.")
57643
57074
  }).strict(),
57644
57075
  name: "read",
57645
57076
  parallelSafe: true,
57646
- execute: async ({ limit, offset, path: path16 }, {
57647
- projectId,
57648
- workspaceStore
57649
- }) => {
57650
- return workspaceStore.readProjectWorkspaceEntry(
57651
- projectId,
57652
- path16,
57653
- {
57654
- limit,
57655
- offset
57656
- }
57657
- );
57077
+ execute: async ({ limit, offset, path: path16 }, { projectId, workspaceStore }) => {
57078
+ return workspaceStore.readProjectWorkspaceEntry(projectId, path16, {
57079
+ limit,
57080
+ offset
57081
+ });
57658
57082
  }
57659
57083
  };
57660
57084
  }
@@ -57669,25 +57093,7 @@ function createWriteToolDefinition() {
57669
57093
  }).strict(),
57670
57094
  name: "write",
57671
57095
  parallelSafe: false,
57672
- execute: async ({ content, path: path16 }, {
57673
- approvedCdnUrls,
57674
- pageEditModePolicy,
57675
- projectId,
57676
- workspaceStore
57677
- }) => {
57678
- const writePath = resolveHtmlOperationPathForPageEditModePolicy(
57679
- pageEditModePolicy,
57680
- "mutate",
57681
- path16
57682
- );
57683
- return writeProjectWorkspaceFileWithCdnGuard(
57684
- workspaceStore,
57685
- projectId,
57686
- writePath,
57687
- content,
57688
- approvedCdnUrls
57689
- );
57690
- }
57096
+ execute: async ({ content, path: path16 }, { projectId, workspaceStore }) => workspaceStore.writeProjectWorkspaceFile(projectId, path16, content)
57691
57097
  };
57692
57098
  }
57693
57099
 
@@ -57702,50 +57108,41 @@ function createProjectWorkspaceToolDefinitions() {
57702
57108
  createGrepToolDefinition(),
57703
57109
  createPatchToolDefinition(),
57704
57110
  createPreviewRefreshToolDefinition(),
57705
- createPreviewSwitchHtmlToolDefinition(),
57706
57111
  createReadToolDefinition(),
57707
57112
  createWriteToolDefinition()
57708
57113
  ];
57709
57114
  }
57710
- function createProjectWorkspaceTools(context2) {
57711
- return createWorkspaceToolRegistry(
57712
- createProjectWorkspaceToolDefinitions(),
57713
- context2
57714
- );
57715
- }
57716
57115
 
57717
57116
  // ../core/src/prompts/index.ts
57718
- import { readFileSync } from "node:fs";
57117
+ import { readFileSync as readFileSync2 } from "node:fs";
57719
57118
  import path4 from "node:path";
57720
- import { fileURLToPath } from "node:url";
57119
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
57721
57120
  var PROMPT_FILES = {
57722
- "agents/design-page": "agents/design-page.md",
57723
- "agents/turn-prompt-rewriter": "agents/turn-prompt-rewriter.md"
57121
+ "agents/component-audit": "agents/component-audit.md",
57122
+ "agents/design-page": "agents/design-page.md"
57724
57123
  };
57725
57124
  function loadPrompt(name21) {
57726
57125
  const promptFile = PROMPT_FILES[name21];
57727
57126
  if (!promptFile) {
57728
57127
  throw new Error(`Unsupported prompt: ${String(name21)}`);
57729
57128
  }
57730
- const currentDir = path4.dirname(fileURLToPath(import.meta.url));
57129
+ const currentDir = path4.dirname(fileURLToPath2(import.meta.url));
57731
57130
  const candidatePaths = [
57732
57131
  path4.join(currentDir, "prompts", promptFile),
57733
57132
  path4.join(currentDir, promptFile)
57734
57133
  ];
57735
57134
  for (const candidatePath of candidatePaths) {
57736
57135
  try {
57737
- return readFileSync(candidatePath, "utf8").trim();
57136
+ return readFileSync2(candidatePath, "utf8").trim();
57738
57137
  } catch (error51) {
57739
- if (!isNotFoundError(error51)) {
57138
+ if (!isNotFoundError2(error51)) {
57740
57139
  throw error51;
57741
57140
  }
57742
57141
  }
57743
57142
  }
57744
- throw new Error(
57745
- `Prompt "${name21}" was not found. Searched: ${candidatePaths.join(", ")}`
57746
- );
57143
+ throw new Error(`Prompt "${name21}" was not found. Searched: ${candidatePaths.join(", ")}`);
57747
57144
  }
57748
- function isNotFoundError(error51) {
57145
+ function isNotFoundError2(error51) {
57749
57146
  return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
57750
57147
  }
57751
57148
 
@@ -57758,40 +57155,20 @@ var FRONTEND_CAPABILITIES = {
57758
57155
  properties: {},
57759
57156
  additionalProperties: false
57760
57157
  }
57761
- },
57762
- "preview.switchHtml": {
57763
- description: "Switch the Preview Pane to an existing HTML file.",
57764
- payloadSchema: {
57765
- type: "object",
57766
- properties: {
57767
- path: {
57768
- type: "string",
57769
- description: "Relative HTML file path inside the Project Workspace to show in the Preview Pane."
57770
- }
57771
- },
57772
- required: ["path"],
57773
- additionalProperties: false
57774
- }
57775
57158
  }
57776
57159
  };
57777
- var FRONTEND_CAPABILITY_IDS = Object.keys(
57778
- FRONTEND_CAPABILITIES
57779
- );
57160
+ var FRONTEND_CAPABILITY_IDS = Object.keys(FRONTEND_CAPABILITIES);
57780
57161
  function buildFrontendCapabilityPrompt() {
57781
57162
  return [
57782
57163
  "## Frontend Capabilities",
57783
57164
  "Use preview tools only to notify the browser UI after Project Workspace file changes are complete. They do not create, edit, or validate files.",
57784
- "After successful previewable HTML changes, call exactly one preview tool before the final user-facing summary.",
57165
+ "After successful previewable HTML changes, call exactly one `previewRefresh` before the final user-facing summary.",
57785
57166
  "Do not call a preview tool when no previewable HTML file changed or the file operation failed.",
57786
- "Use the current user request to decide which preview tool is needed:",
57787
- "- Use `previewSwitchHtml` only when the Preview Pane should move to a different existing relative `.html` file inside the Project Workspace.",
57788
- "- Use `previewRefresh` when the Preview Pane is already showing the correct page and only needs to reload changed HTML.",
57167
+ "Use `previewRefresh` when `index.html` changed and should reload.",
57789
57168
  "Do not use workspace file tools to simulate preview switching or refreshing.",
57790
57169
  "Available capabilities:",
57791
57170
  ...FRONTEND_CAPABILITY_IDS.map((capability) => {
57792
- const schema = JSON.stringify(
57793
- FRONTEND_CAPABILITIES[capability].payloadSchema
57794
- );
57171
+ const schema = JSON.stringify(FRONTEND_CAPABILITIES[capability].payloadSchema);
57795
57172
  return `- ${capability}: ${FRONTEND_CAPABILITIES[capability].description} Payload schema: ${schema}`;
57796
57173
  })
57797
57174
  ].join("\n");
@@ -57805,11 +57182,12 @@ var AiSdkDesignPageAgent = class {
57805
57182
  }
57806
57183
  workspaceStore;
57807
57184
  async generateProjectOutput(input) {
57808
- if (input.outputType !== "html") {
57809
- throw new Error(`Unsupported Project Output Type: ${input.outputType}`);
57185
+ const projectType = normalizeProjectType(input.projectType, input.outputType);
57186
+ if (projectType !== "single_html") {
57187
+ throw new Error(`Unsupported Project Type: ${projectType}`);
57810
57188
  }
57811
57189
  const context2 = await createDesignPageAgentContext({
57812
- outputType: input.outputType,
57190
+ projectType,
57813
57191
  projectId: input.projectId,
57814
57192
  workspaceStore: this.workspaceStore
57815
57193
  });
@@ -57827,36 +57205,27 @@ async function createDesignPageAgentContext({
57827
57205
  frontendTabId,
57828
57206
  modelConfigurationId,
57829
57207
  outputType,
57830
- pageEditMode = "auto",
57208
+ projectType,
57831
57209
  projectId,
57832
57210
  providerOptionsSelection,
57211
+ settingsPath,
57833
57212
  workspaceStore
57834
57213
  }) {
57835
- if (outputType !== "html") {
57836
- throw new Error(`Unsupported Project Output Type: ${outputType}`);
57214
+ projectType = normalizeProjectType(projectType, outputType);
57215
+ if (projectType !== "single_html") {
57216
+ throw new Error(`Unsupported Project Type: ${projectType}`);
57837
57217
  }
57838
- const settingsService = createSettingsService();
57218
+ const settingsService = createSettingsService({ settingsPath });
57839
57219
  const [settings, modelConfiguration] = await Promise.all([
57840
57220
  settingsService.getSettings(),
57841
57221
  settingsService.resolveModelConfiguration(modelConfigurationId)
57842
57222
  ]);
57843
- const pageEditModePolicy = await buildPageEditModePolicy({
57844
- currentPreviewPath,
57845
- mode: pageEditMode,
57846
- projectId,
57847
- workspaceStore
57848
- });
57849
57223
  return {
57850
57224
  currentPreviewPath,
57851
57225
  frontendTabId,
57852
57226
  model: buildLanguageModel(modelConfiguration),
57853
- outputType,
57854
- pageEditMode,
57855
- pageEditModePolicy,
57856
- providerOptions: buildProviderOptions(
57857
- modelConfiguration,
57858
- providerOptionsSelection
57859
- ),
57227
+ projectType,
57228
+ providerOptions: buildProviderOptions(modelConfiguration, providerOptionsSelection),
57860
57229
  projectId,
57861
57230
  resources: settings.resources,
57862
57231
  workspaceStore
@@ -57868,30 +57237,25 @@ function createDesignPageAgent(context2) {
57868
57237
  allowSystemInMessages: true,
57869
57238
  model,
57870
57239
  instructions: agentInstructions ?? buildDesignPageInstructions(context2),
57240
+ maxRetries: 5,
57871
57241
  providerOptions,
57872
57242
  stopWhen: stepCountIs(50),
57873
57243
  tools: createDesignPageWorkspaceTools(context2)
57874
57244
  });
57875
57245
  }
57876
- function createDesignPageWorkspaceTools({
57877
- projectId,
57878
- resources,
57879
- workspaceStore,
57880
- frontendTabId,
57881
- pageEditModePolicy
57882
- }) {
57883
- return createProjectWorkspaceTools({
57246
+ function createDesignPageWorkspaceTools(context2) {
57247
+ const { frontendTabId, projectId, resources, workspaceStore } = context2;
57248
+ return createWorkspaceToolRegistry(createProjectWorkspaceToolDefinitions(), {
57884
57249
  approvedCdnUrls: buildApprovedCdnUrls(resources),
57885
57250
  frontendTabId,
57886
- pageEditModePolicy,
57251
+ model: context2.model,
57887
57252
  projectId,
57253
+ providerOptions: context2.providerOptions,
57888
57254
  resources,
57889
57255
  workspaceStore
57890
57256
  });
57891
57257
  }
57892
- function buildDesignPageInstructions({
57893
- resources
57894
- }) {
57258
+ function buildDesignPageInstructions({ resources }) {
57895
57259
  return buildDesignPageConversationInstructions(resources);
57896
57260
  }
57897
57261
  function buildLanguageModel(configuration) {
@@ -57943,6 +57307,15 @@ function buildProviderOptions(configuration, selection) {
57943
57307
  }
57944
57308
  };
57945
57309
  }
57310
+ function normalizeProjectType(projectType, outputType) {
57311
+ if (projectType) {
57312
+ return projectType;
57313
+ }
57314
+ if (outputType === "html") {
57315
+ return "single_html";
57316
+ }
57317
+ return "single_html";
57318
+ }
57946
57319
  function buildDesignPageConversationInstructions(resources) {
57947
57320
  const sections = [
57948
57321
  {
@@ -57983,58 +57356,56 @@ function loadDesignPageAgentCorePrompt() {
57983
57356
  }
57984
57357
  function buildPageTargetProtocolPrompt() {
57985
57358
  return [
57986
- "## Page Target Protocol",
57987
- "",
57988
- "Resolve the target HTML page before creating or updating previewable output.",
57359
+ "## Single HTML Target Protocol",
57989
57360
  "",
57990
- "Target resolution:",
57991
- "- All previewable pages must be relative workspace paths ending in `.html`.",
57992
- "- If the user names a file, path, or page type, use that explicit target.",
57993
- "- Each turn's user message has already been rewritten with the current preview page and page edit mode when those matter.",
57994
- '- If the user uses a relative page reference such as "this page", "current page", "here", "top", or "bottom", use the target page stated in the current user message when available.',
57995
- "- If the user asks for a home, main, landing, or first page, use `index.html`.",
57996
- "- If the user asks for a new named page, choose a semantic filename such as `login.html`, `settings.html`, or `pages/detail.html`.",
57997
- "- If no target is specified and no multi-page structure is evident, default to `index.html`.",
57998
- "- If multiple HTML files exist, no current preview page is available, and the target remains ambiguous after inspection, ask one concise follow-up question.",
57999
- "- Do not ask a follow-up question just because the request is brief; act when the target can be resolved from the current user message, an explicit filename, or the `index.html` default.",
57361
+ "The project has one previewable file: `index.html`.",
58000
57362
  "",
58001
- "- Do not overwrite `index.html` for a new page unless the user intent points to the home or main page."
57363
+ "Rules:",
57364
+ "- Always target `index.html` for previewable output.",
57365
+ "- Do not create `login.html`, `detail.html`, versioned HTML files, or any other HTML page.",
57366
+ "- If the user asks for multiple pages, screens, or routes, implement them as internal views inside `index.html`.",
57367
+ "- Use ordinary HTML, CSS, and browser JavaScript in the file.",
57368
+ "- Do not create custom elements, component module folders, or page/component reuse metadata files.",
57369
+ '- If `index.html` is missing, call `createHtml({ path: "index.html" })` before editing.',
57370
+ "- If `index.html` exists, read it before editing and continue from the current design."
58002
57371
  ].join("\n");
58003
57372
  }
58004
57373
  function buildToolWorkflowPrompt() {
58005
57374
  return [
58006
57375
  "## Tool Workflow",
58007
- "Use the narrowest reliable tool for each task; inspect before writing, recover before retrying.",
57376
+ "Use the narrowest reliable tool for each task.",
58008
57377
  "",
58009
- "Inspect before changing files:",
57378
+ "Inspect before editing:",
58010
57379
  "- Use `glob` to discover files.",
58011
57380
  "- Use `grep` to locate relevant code or markup.",
58012
- "- Use `read` before editing any existing target file.",
58013
- "- Always inspect before coordinated edits across existing files.",
57381
+ "- Use `read` before editing an existing file.",
58014
57382
  "",
58015
- "Choose the write tool by intent:",
57383
+ "Choose tools by intent:",
57384
+ "- Use `createHtml` only to create a missing `index.html` file.",
58016
57385
  "- Use `edit` for small, focused replacements in one existing file.",
58017
57386
  "- Use `patch` for coordinated changes, repeated replacements, or multi-file edits.",
58018
- "- Use `write` only for non-HTML files or deliberate full-file overwrites; never use it to create initial HTML pages.",
58019
- "- Do not use `write` for ordinary HTML page edits. Use it for HTML only when the user explicitly asks to rebuild the whole file.",
58020
- "- Use `delete` only after using `grep` or direct inspection to confirm the file is no longer referenced by any page or build target.",
58021
- "- Use `copyFile` when the current user message asks you to duplicate an existing file before editing the duplicate.",
57387
+ "- Use `write` only for deliberate full-file replacement or non-preview support files.",
57388
+ "- Do not use `write` to create the initial `index.html`; use `createHtml` first.",
57389
+ "- Use `copyFile` only when the current user message explicitly asks you to duplicate an existing file.",
57390
+ "- Use `delete` only after confirming the file is not referenced.",
58022
57391
  "",
58023
- "For HTML pages:",
58024
- "- Do not modify unrelated HTML files unless the requested change requires coordinated edits.",
58025
- "- Use `createHtml` when the target HTML file does not exist; do not create initial HTML with `write`.",
58026
- "- For `createHtml`, pass the resolved target path. Pass `fontLibraryName` or `iconLibraryName` only when the user explicitly names a font or icon library; otherwise omit them so the tool reads configured defaults.",
58027
- "- After `createHtml`, immediately use `read` on that file before editing it.",
58028
- "- If the target HTML file exists, use `read` before editing it; do not call `createHtml`.",
58029
- "- Use `edit` or `patch` for HTML changes after reading the file.",
57392
+ "For Single HTML creation:",
57393
+ '- Call `createHtml({ path: "index.html" })` when `index.html` is missing.',
57394
+ "- After `createHtml`, read `index.html`.",
57395
+ "- Replace the default placeholder markup, CSS, and script with a complete designed prototype.",
58030
57396
  "",
58031
- "Recover from tool failures:",
58032
- "- If `edit` cannot find the expected text, `read` the file again and retry with a smaller replacement or `patch`.",
58033
- "- If a write tool returns an error or produces unexpected output, read the file again before retrying.",
57397
+ "For Single HTML updates:",
57398
+ "- If `index.html` exists, read it before editing and do not call `createHtml`.",
57399
+ "- Keep visible page structure, styling, and local interactions in `index.html`.",
57400
+ "- Use internal views for multi-screen workflows instead of additional HTML files.",
58034
57401
  "",
58035
- "Resource constraints:",
58036
- "- Only use CDNs already listed in resource settings; do not add others.",
58037
- "- `write`, `edit`, and `patch` will reject HTML with unlisted CDN tags - if rejected, fall back to configured libraries, system fonts, inline SVG, or local CSS."
57402
+ "After page changes:",
57403
+ "- Use `previewRefresh` when the current preview page changed and should reload.",
57404
+ "- Call exactly one `previewRefresh` after successful previewable changes.",
57405
+ "",
57406
+ "Recover from tool failures:",
57407
+ "- If an edit fails, read the file again and retry with a smaller edit or patch.",
57408
+ "- If a generated prototype becomes too large or brittle, simplify the file while preserving visible quality."
58038
57409
  ].join("\n");
58039
57410
  }
58040
57411
  function buildResourcePolicyPrompt(resources) {
@@ -58047,17 +57418,22 @@ function buildResourcePolicyPrompt(resources) {
58047
57418
  "Use these global resource settings when designing HTML preview pages.",
58048
57419
  defaultFontLibrary ? `Default font library: ${defaultFontLibrary.name}.` : "Default font library: none configured.",
58049
57420
  defaultIconLibrary ? `Default icon library: ${defaultIconLibrary.name}.` : "Default icon library: none configured.",
58050
- "If the user prompt explicitly names a configured font or icon library, use that named library; otherwise use the default library.",
58051
- "Only use configured font libraries or system fonts. Do not reference any unconfigured external font service or font CDN.",
58052
- "Prefer configured icon libraries for icons. Use inline SVG only when no icon library is configured or when the configured libraries cannot provide a suitable icon.",
58053
- "Do not reference any unconfigured external icon service or icon CDN.",
58054
- "When a configured library has no CDN, follow the library choice in CSS naming only and do not add a CDN tag for it.",
57421
+ "The default HTML template already configures Inter and Noto Sans SC on the `html` element.",
57422
+ "Unless the user explicitly asks for a different typeface, do not change `font-family`; adjust typography with size, weight, line-height, spacing, and hierarchy.",
57423
+ "Lucide icons are already configured by the default HTML template.",
57424
+ 'Use Lucide icons with `<i data-lucide="menu"></i>` syntax, replacing `menu` with the appropriate Lucide icon name.',
57425
+ "Do not use other icon systems, inline SVG icons, emoji icons, or decorative emoji as UI icons.",
57426
+ "When styling Lucide icons, do not target `i`, `i[data-lucide]`, or tag selectors because Lucide replaces the placeholder with inline `svg` elements.",
57427
+ "Give icons a semantic class or wrap them in a classed element, then style the class and child `svg`, such as `.nav-icon svg { width: 18px; height: 18px; stroke-width: 2; }`.",
57428
+ "If JavaScript dynamically inserts markup that contains Lucide placeholders, call `lucide.createIcons()` after updating the DOM.",
57429
+ "Prefer configured resources and local CSS before adding any external resource.",
57430
+ "Add an extra external resource only when the user explicitly requests it or when it is necessary for the prototype quality.",
57431
+ "When a configured library has no CDN, follow the library choice in CSS naming only.",
58055
57432
  "Configured font libraries:",
58056
57433
  fontLines.length ? fontLines.join("\n") : "- none",
58057
57434
  "Configured icon libraries:",
58058
57435
  iconLines.length ? iconLines.join("\n") : "- none",
58059
- "Use regular inline CSS as the primary styling method.",
58060
- "Do not add new CDN resources. If a needed resource is not configured, use system fonts, inline SVG, local CSS, or explain the limitation."
57436
+ "Use regular inline CSS as the primary styling method."
58061
57437
  ].join("\n");
58062
57438
  }
58063
57439
  function buildApprovedCdnUrls(resources) {
@@ -58075,75 +57451,6 @@ function formatResourceLibraryList(libraries) {
58075
57451
  );
58076
57452
  }
58077
57453
 
58078
- // ../core/src/agent/turn-prompt-rewriter.ts
58079
- async function rewriteTurnPrompt({
58080
- model,
58081
- originalUserPrompt,
58082
- pageEditMode,
58083
- pageEditModePolicy,
58084
- previewPath,
58085
- providerOptions
58086
- }) {
58087
- const result = await generateText({
58088
- model,
58089
- providerOptions,
58090
- system: buildTurnPromptRewriterSystemPrompt(),
58091
- prompt: buildTurnPromptRewriterPrompt({
58092
- originalUserPrompt,
58093
- pageEditMode,
58094
- pageEditModePolicy,
58095
- previewPath
58096
- })
58097
- });
58098
- const rewrittenPrompt = cleanRewrittenPrompt(result.text);
58099
- if (!rewrittenPrompt) {
58100
- throw new Error("TurnPromptRewriter returned an empty prompt.");
58101
- }
58102
- return { rewrittenPrompt };
58103
- }
58104
- function buildTurnPromptRewriterSystemPrompt() {
58105
- return loadPrompt("agents/turn-prompt-rewriter");
58106
- }
58107
- function buildTurnPromptRewriterPrompt({
58108
- originalUserPrompt,
58109
- pageEditMode,
58110
- pageEditModePolicy,
58111
- previewPath
58112
- }) {
58113
- return [
58114
- `currentPreviewFile: ${previewPath ?? "none"}`,
58115
- `pageEditMode: ${pageEditMode}`,
58116
- formatPolicyLine(pageEditModePolicy),
58117
- "",
58118
- "Rewrite rules:",
58119
- "- Keep the user's requested change unchanged in meaning.",
58120
- "- Make the target page/action explicit when the edit mode provides one.",
58121
- "- For duplicate_edit, tell the agent to use copyFile to copy the source page to the target page, then modify only the target HTML page.",
58122
- "- For duplicate_edit, allow inspection of other files but forbid modifying any HTML page except the target page.",
58123
- "- For new_page, tell the agent to create a new page, but do not forbid related edits such as navigation links.",
58124
- "- For direct_edit, tell the agent to edit the current preview page directly.",
58125
- "",
58126
- "Original user request:",
58127
- originalUserPrompt
58128
- ].join("\n");
58129
- }
58130
- function formatPolicyLine(policy) {
58131
- if (policy.mode === "duplicate_edit") {
58132
- return `duplicateSourcePath: ${policy.sourcePath}
58133
- duplicateTargetPath: ${policy.targetPath}`;
58134
- }
58135
- if (policy.mode === "direct_edit") {
58136
- return `targetPath: ${policy.targetPath}`;
58137
- }
58138
- if (policy.mode === "new_page") {
58139
- return `currentPreviewFile: ${policy.currentPreviewPath ?? "none"}`;
58140
- }
58141
- return "targetPath: none";
58142
- }
58143
- function cleanRewrittenPrompt(text2) {
58144
- return text2.trim().replace(/^```(?:text)?\s*/i, "").replace(/\s*```$/i, "").trim();
58145
- }
58146
-
58147
57454
  // ../core/src/preview/preview-server-manager.ts
58148
57455
  import { access, readFile as readFile2 } from "node:fs/promises";
58149
57456
  import path5 from "node:path";
@@ -58167,9 +57474,13 @@ var PreviewServerManager = class {
58167
57474
  }
58168
57475
  async ensure(projectId, clientId, previewPath) {
58169
57476
  const entry = await this.getOrStartEntry(projectId, clientId);
58170
- const files = await this.workspaceStore.listProjectHtmlFiles(projectId);
57477
+ const [files, pageManifest] = await Promise.all([
57478
+ this.workspaceStore.listProjectHtmlFiles(projectId),
57479
+ this.workspaceStore.readProjectHtmlPageManifest(projectId)
57480
+ ]);
58171
57481
  const activePath = resolveActivePreviewPath(
58172
57482
  files,
57483
+ pageManifest,
58173
57484
  previewPath ?? entry.activePath
58174
57485
  );
58175
57486
  entry.activePath = activePath;
@@ -58178,6 +57489,7 @@ var PreviewServerManager = class {
58178
57489
  return {
58179
57490
  ...activePath ? { activePath } : {},
58180
57491
  files,
57492
+ pageManifest,
58181
57493
  url: buildPreviewUrl(entry.baseUrl, activePath)
58182
57494
  };
58183
57495
  }
@@ -58211,9 +57523,7 @@ var PreviewServerManager = class {
58211
57523
  return this.entries.size;
58212
57524
  }
58213
57525
  getLeaseCount(projectId) {
58214
- return Array.from(this.entries.values()).filter(
58215
- (entry) => entry.projectId === projectId
58216
- ).length;
57526
+ return Array.from(this.entries.values()).filter((entry) => entry.projectId === projectId).length;
58217
57527
  }
58218
57528
  async cleanupExpiredLeases() {
58219
57529
  const now2 = this.now();
@@ -58253,24 +57563,21 @@ var PreviewServerManager = class {
58253
57563
  key,
58254
57564
  projectId
58255
57565
  };
58256
- app2.get(
58257
- "/",
58258
- async () => htmlResponse(await readIndexHtmlOrEmptyPreview(workspaceDirectory, entry))
58259
- );
58260
- app2.get(
58261
- "/index.html",
58262
- async () => htmlResponse(await readIndexHtmlOrEmptyPreview(workspaceDirectory, entry))
58263
- );
57566
+ app2.get("/", async () => readIndexHtmlOrNotFound(workspaceDirectory, entry));
57567
+ app2.get("/index.html", async () => readIndexHtmlOrNotFound(workspaceDirectory, entry));
58264
57568
  app2.use("*", async (context2, next) => {
58265
57569
  context2.header("Cache-Control", PREVIEW_CACHE_CONTROL);
58266
57570
  await next();
58267
57571
  });
58268
- app2.use("*", serveStatic({
58269
- onFound: (filePath) => {
58270
- recordServedStaticPath(entry, workspaceDirectory, filePath);
58271
- },
58272
- root: workspaceDirectory
58273
- }));
57572
+ app2.use(
57573
+ "*",
57574
+ serveStatic({
57575
+ onFound: (filePath) => {
57576
+ recordServedStaticPath(entry, workspaceDirectory, filePath);
57577
+ },
57578
+ root: workspaceDirectory
57579
+ })
57580
+ );
58274
57581
  const server2 = await listenOnRandomPort(app2);
58275
57582
  entry.server = server2.server;
58276
57583
  entry.baseUrl = server2.baseUrl;
@@ -58349,123 +57656,32 @@ function getPreviewServerManager(workspaceStore) {
58349
57656
  });
58350
57657
  return globalThis.__owndesignPreviewServerManager;
58351
57658
  }
58352
- async function readIndexHtmlOrEmptyPreview(workspaceDirectory, entry) {
57659
+ async function readIndexHtmlOrNotFound(workspaceDirectory, entry) {
58353
57660
  const indexPath = path5.join(workspaceDirectory, "index.html");
58354
57661
  try {
58355
57662
  await access(indexPath);
58356
57663
  entry.activePath = "index.html";
58357
- return readFile2(indexPath, "utf8");
57664
+ return htmlResponse(await readFile2(indexPath, "utf8"));
58358
57665
  } catch {
58359
- return buildEmptyPreviewHtml();
58360
- }
58361
- }
58362
- function buildEmptyPreviewHtml() {
58363
- return `<!doctype html>
58364
- <html lang="zh-CN">
58365
- <head>
58366
- <meta charset="utf-8" />
58367
- <meta name="viewport" content="width=device-width, initial-scale=1" />
58368
- <title>OwnDesign Preview</title>
58369
- <style>
58370
- :root {
58371
- color-scheme: dark;
58372
- --bg-base: #0a0a0b;
58373
- --bg-surface: #141416;
58374
- --bg-elevated: #1c1c1f;
58375
- --border-color: #2a2a2e;
58376
- --border-light: #38383d;
58377
- --text-primary: #f0f0f2;
58378
- --text-secondary: #a0a0ab;
58379
- --text-tertiary: #6b6b76;
58380
- --shadow-lg: 0 18px 48px rgba(0, 0, 0, 0.45);
58381
- }
58382
- * {
58383
- box-sizing: border-box;
58384
- }
58385
- html {
58386
- height: 100%;
58387
- background: var(--bg-base);
58388
- }
58389
- body {
58390
- margin: 0;
58391
- min-height: 100vh;
58392
- display: flex;
58393
- align-items: center;
58394
- justify-content: center;
58395
- padding: 24px;
58396
- background:
58397
- radial-gradient(circle at top, rgba(255,255,255,0.04), transparent 34%),
58398
- var(--bg-base);
58399
- color: var(--text-primary);
58400
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans SC", sans-serif;
58401
- }
58402
- main {
58403
- width: min(100%, 28rem);
58404
- padding: 2rem;
58405
- border: 1px solid var(--border-color);
58406
- border-radius: 24px;
58407
- background: var(--bg-surface);
58408
- text-align: center;
58409
- box-shadow: var(--shadow-lg);
58410
- }
58411
- .icon {
58412
- width: 56px;
58413
- height: 56px;
58414
- margin: 0 auto;
58415
- display: flex;
58416
- align-items: center;
58417
- justify-content: center;
58418
- border-radius: 18px;
58419
- border: 1px solid var(--border-light);
58420
- background: var(--bg-elevated);
58421
- color: var(--text-secondary);
58422
- box-shadow: inset 0 1px 0 rgba(255,255,255,0.03);
58423
- }
58424
- .badge {
58425
- margin-top: 20px;
58426
- color: var(--text-tertiary);
58427
- font-size: 11px;
58428
- font-weight: 600;
58429
- letter-spacing: 0.24em;
58430
- text-transform: uppercase;
58431
- }
58432
- h1 {
58433
- margin: 8px 0 0;
58434
- color: var(--text-primary);
58435
- font-size: 1.125rem;
58436
- font-weight: 600;
58437
- letter-spacing: -0.02em;
58438
- }
58439
- p {
58440
- margin: 10px auto 0;
58441
- max-width: 22rem;
58442
- color: var(--text-secondary);
58443
- font-size: 14px;
58444
- line-height: 1.7;
58445
- }
58446
- </style>
58447
- </head>
58448
- <body>
58449
- <main>
58450
- <div class="icon" aria-hidden="true">
58451
- <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
58452
- <path d="M4 7.5A2.5 2.5 0 0 1 6.5 5H10l2 2h5.5A2.5 2.5 0 0 1 20 9.5v7A2.5 2.5 0 0 1 17.5 19h-11A2.5 2.5 0 0 1 4 16.5z" />
58453
- </svg>
58454
- </div>
58455
- <div class="badge">Preview</div>
58456
- <h1>\u7B49\u5F85\u751F\u6210 HTML</h1>
58457
- <p>\u5728\u5DE6\u4FA7\u8F93\u5165\u201C\u8BBE\u8BA1\u4E00\u4E2A XXX \u7684\u754C\u9762\u201D\uFF0C\u751F\u6210\u7ED3\u679C\u4F1A\u663E\u793A\u5728\u8FD9\u91CC\u3002</p>
58458
- </main>
58459
- </body>
58460
- </html>`;
58461
- }
58462
- function resolveActivePreviewPath(files, previewPath) {
57666
+ return new Response(null, {
57667
+ headers: {
57668
+ "Cache-Control": PREVIEW_CACHE_CONTROL
57669
+ },
57670
+ status: 404
57671
+ });
57672
+ }
57673
+ }
57674
+ function resolveActivePreviewPath(files, pageManifest, previewPath) {
58463
57675
  if (previewPath && files.includes(previewPath)) {
58464
57676
  return previewPath;
58465
57677
  }
58466
57678
  if (files.includes("index.html")) {
58467
57679
  return "index.html";
58468
57680
  }
57681
+ const manifestPath = pageManifest.pages.find((page) => files.includes(page.htmlPath))?.htmlPath;
57682
+ if (manifestPath) {
57683
+ return manifestPath;
57684
+ }
58469
57685
  return files[0];
58470
57686
  }
58471
57687
  function buildPreviewUrl(baseUrl, previewPath) {
@@ -58493,11 +57709,7 @@ function recordServedStaticPath(entry, workspaceDirectory, servedPath) {
58493
57709
  }
58494
57710
 
58495
57711
  // ../core/src/navigation.ts
58496
- function buildWorkspaceHref({
58497
- conversationId,
58498
- previewPath,
58499
- projectId
58500
- }) {
57712
+ function buildWorkspaceHref({ conversationId, previewPath, projectId }) {
58501
57713
  if (!projectId) {
58502
57714
  return "/";
58503
57715
  }
@@ -58548,11 +57760,7 @@ var ConversationService = class {
58548
57760
  titleManuallySet: true,
58549
57761
  updatedAt: this.now()
58550
57762
  };
58551
- return this.workspaceStore.updateConversation(
58552
- projectId,
58553
- conversationId,
58554
- updatedConversation
58555
- );
57763
+ return this.workspaceStore.updateConversation(projectId, conversationId, updatedConversation);
58556
57764
  }
58557
57765
  async switchConversation(projectId, conversationId) {
58558
57766
  return this.workspaceStore.getConversation(projectId, conversationId);
@@ -58571,7 +57779,7 @@ var ConversationService = class {
58571
57779
  };
58572
57780
  const agentResult = await this.generateProjectOutputSafely({
58573
57781
  content,
58574
- outputType: project.outputType,
57782
+ projectType: project.projectType ?? "single_html",
58575
57783
  projectId
58576
57784
  });
58577
57785
  const mockReply = {
@@ -58579,11 +57787,7 @@ var ConversationService = class {
58579
57787
  content: agentResult.content,
58580
57788
  createdAt: timestamp
58581
57789
  };
58582
- const messages = [
58583
- ...existingConversation.messages,
58584
- userMessage,
58585
- mockReply
58586
- ];
57790
+ const messages = [...existingConversation.messages, userMessage, mockReply];
58587
57791
  const updatedConversation = {
58588
57792
  ...existingConversation,
58589
57793
  title: !existingConversation.titleManuallySet && existingConversation.title === DEFAULT_CONVERSATION_TITLE && existingConversation.messages.length === 0 ? summarizeConversationTitle(content) : existingConversation.title,
@@ -58596,11 +57800,7 @@ var ConversationService = class {
58596
57800
  updatedAt: timestamp
58597
57801
  };
58598
57802
  await this.workspaceStore.updateProject(projectId, updatedProject);
58599
- return this.workspaceStore.updateConversation(
58600
- projectId,
58601
- conversationId,
58602
- updatedConversation
58603
- );
57803
+ return this.workspaceStore.updateConversation(projectId, conversationId, updatedConversation);
58604
57804
  }
58605
57805
  async saveUIMessageStream(projectId, conversationId, messages) {
58606
57806
  const existingConversation = await this.workspaceStore.getConversation(
@@ -58622,22 +57822,13 @@ var ConversationService = class {
58622
57822
  updatedAt: timestamp
58623
57823
  };
58624
57824
  await this.workspaceStore.updateProject(projectId, updatedProject);
58625
- return this.workspaceStore.updateConversation(
58626
- projectId,
58627
- conversationId,
58628
- updatedConversation
58629
- );
57825
+ return this.workspaceStore.updateConversation(projectId, conversationId, updatedConversation);
58630
57826
  }
58631
57827
  async deleteConversation(projectId, conversationId, defaultTitle) {
58632
57828
  await this.workspaceStore.deleteConversation(projectId, conversationId);
58633
- let remainingConversations = await this.workspaceStore.listConversations(
58634
- projectId
58635
- );
57829
+ let remainingConversations = await this.workspaceStore.listConversations(projectId);
58636
57830
  if (remainingConversations.length === 0) {
58637
- const replacementConversation = await this.createConversation(
58638
- projectId,
58639
- defaultTitle
58640
- );
57831
+ const replacementConversation = await this.createConversation(projectId, defaultTitle);
58641
57832
  remainingConversations = [replacementConversation];
58642
57833
  }
58643
57834
  return remainingConversations;
@@ -58676,11 +57867,16 @@ var ProjectService = class {
58676
57867
  this.previewServerManager = options.previewServerManager;
58677
57868
  }
58678
57869
  async createProject(input) {
57870
+ const projectType = input.projectType ?? "single_html";
57871
+ if (projectType === "react") {
57872
+ throw new Error("React project type is reserved but not supported yet.");
57873
+ }
58679
57874
  const timestamp = this.now();
58680
57875
  const project = {
58681
57876
  id: this.createId(),
58682
57877
  name: input.name,
58683
57878
  description: input.description,
57879
+ projectType,
58684
57880
  outputType: "html",
58685
57881
  createdAt: timestamp,
58686
57882
  updatedAt: timestamp
@@ -58694,6 +57890,11 @@ var ProjectService = class {
58694
57890
  messages: []
58695
57891
  };
58696
57892
  await this.workspaceStore.createProject(project);
57893
+ await this.workspaceStore.writeProjectWorkspaceFile(
57894
+ project.id,
57895
+ "index.html",
57896
+ buildSingleHtmlTemplate({ title: input.name })
57897
+ );
58697
57898
  await this.workspaceStore.createConversation(conversation);
58698
57899
  return { conversation, project };
58699
57900
  }
@@ -58720,15 +57921,7 @@ var ProjectService = class {
58720
57921
  };
58721
57922
 
58722
57923
  // ../core/src/workspace-store/store.ts
58723
- import {
58724
- lstat,
58725
- mkdir as mkdir2,
58726
- readdir,
58727
- readFile as readFile4,
58728
- rm,
58729
- stat as stat3,
58730
- writeFile as writeFile2
58731
- } from "node:fs/promises";
57924
+ import { copyFile, lstat, mkdir as mkdir2, readdir, readFile as readFile4, rm, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
58732
57925
  import os5 from "node:os";
58733
57926
  import path13 from "node:path";
58734
57927
  import process12 from "node:process";
@@ -58744,7 +57937,7 @@ function normalizePositiveInteger(value, fallback, name21) {
58744
57937
  }
58745
57938
  return value;
58746
57939
  }
58747
- function countOccurrences2(content, needle) {
57940
+ function countOccurrences(content, needle) {
58748
57941
  if (!needle) {
58749
57942
  return 0;
58750
57943
  }
@@ -58757,27 +57950,25 @@ function applyTextEdit(content, oldText, newText, replaceAll = false, relativePa
58757
57950
  if (oldText === newText) {
58758
57951
  throw new Error("No changes to apply: oldText and newText are identical.");
58759
57952
  }
58760
- const normalizedOldText = convertToLineEnding2(oldText, detectLineEnding2(content));
58761
- const normalizedNewText = convertToLineEnding2(newText, detectLineEnding2(content));
57953
+ const normalizedOldText = convertToLineEnding(oldText, detectLineEnding(content));
57954
+ const normalizedNewText = convertToLineEnding(newText, detectLineEnding(content));
58762
57955
  const firstIndex = content.indexOf(normalizedOldText);
58763
57956
  if (firstIndex === -1) {
58764
57957
  throw new Error(`oldText was not found in Project Workspace file: ${relativePath}`);
58765
57958
  }
58766
- const replacements = countOccurrences2(content, normalizedOldText);
57959
+ const replacements = countOccurrences(content, normalizedOldText);
58767
57960
  if (!replaceAll && replacements > 1) {
58768
- throw new Error(
58769
- `oldText appears more than once in Project Workspace file: ${relativePath}`
58770
- );
57961
+ throw new Error(`oldText appears more than once in Project Workspace file: ${relativePath}`);
58771
57962
  }
58772
57963
  return {
58773
57964
  content: replaceAll ? content.split(normalizedOldText).join(normalizedNewText) : content.slice(0, firstIndex) + normalizedNewText + content.slice(firstIndex + normalizedOldText.length),
58774
57965
  replacements: replaceAll ? replacements : 1
58775
57966
  };
58776
57967
  }
58777
- function detectLineEnding2(text2) {
57968
+ function detectLineEnding(text2) {
58778
57969
  return text2.includes("\r\n") ? "\r\n" : "\n";
58779
57970
  }
58780
- function convertToLineEnding2(text2, ending) {
57971
+ function convertToLineEnding(text2, ending) {
58781
57972
  const normalized = text2.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
58782
57973
  return ending === "\n" ? normalized : normalized.replaceAll("\n", "\r\n");
58783
57974
  }
@@ -58832,6 +58023,39 @@ function buildUnifiedDiff(oldContent, newContent, relativePath) {
58832
58023
  return lines.join("\n");
58833
58024
  }
58834
58025
 
58026
+ // ../core/src/html-page-manifest.ts
58027
+ var HTML_PAGE_MANIFEST_PATH = ".owndesign-pages.json";
58028
+ var EMPTY_HTML_PAGE_MANIFEST = {
58029
+ pages: []
58030
+ };
58031
+ function parseHtmlPageManifest(content) {
58032
+ if (!content) {
58033
+ return EMPTY_HTML_PAGE_MANIFEST;
58034
+ }
58035
+ let value;
58036
+ try {
58037
+ value = JSON.parse(content);
58038
+ } catch {
58039
+ return EMPTY_HTML_PAGE_MANIFEST;
58040
+ }
58041
+ if (!isRecord2(value) || !Array.isArray(value.pages)) {
58042
+ return EMPTY_HTML_PAGE_MANIFEST;
58043
+ }
58044
+ const pages = value.pages.filter(isRecord2).map((page) => ({
58045
+ componentSource: typeof page.componentSource === "string" ? page.componentSource.trim() : "",
58046
+ componentTag: typeof page.componentTag === "string" ? page.componentTag.trim() : "",
58047
+ displayName: typeof page.displayName === "string" ? page.displayName.trim() : "",
58048
+ htmlPath: typeof page.htmlPath === "string" ? page.htmlPath.trim() : "",
58049
+ slug: typeof page.slug === "string" ? page.slug.trim() : ""
58050
+ })).filter(
58051
+ (page) => page.slug && page.displayName && page.htmlPath && page.componentTag && page.componentSource
58052
+ );
58053
+ return { pages };
58054
+ }
58055
+ function isRecord2(value) {
58056
+ return typeof value === "object" && value !== null;
58057
+ }
58058
+
58835
58059
  // ../core/src/workspace-store/files.ts
58836
58060
  import { open, readFile as readFile3, stat as stat2 } from "node:fs/promises";
58837
58061
 
@@ -59153,10 +58377,10 @@ var isSymlinkSync = isTypeSync.bind(void 0, "lstatSync", "isSymbolicLink");
59153
58377
  // ../../node_modules/.pnpm/unicorn-magic@0.3.0/node_modules/unicorn-magic/node.js
59154
58378
  import { promisify } from "node:util";
59155
58379
  import { execFile as execFileCallback, execFileSync as execFileSyncOriginal } from "node:child_process";
59156
- import { fileURLToPath as fileURLToPath2 } from "node:url";
58380
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
59157
58381
  var execFileOriginal = promisify(execFileCallback);
59158
58382
  function toPath(urlOrPath) {
59159
- return urlOrPath instanceof URL ? fileURLToPath2(urlOrPath) : urlOrPath;
58383
+ return urlOrPath instanceof URL ? fileURLToPath3(urlOrPath) : urlOrPath;
59160
58384
  }
59161
58385
  var TEN_MEGABYTES_IN_BYTES = 10 * 1024 * 1024;
59162
58386
 
@@ -59543,14 +58767,7 @@ if ($item.PSIsContainer) {
59543
58767
  `;
59544
58768
  await execFileAsync(
59545
58769
  "powershell.exe",
59546
- [
59547
- "-NoProfile",
59548
- "-NonInteractive",
59549
- "-ExecutionPolicy",
59550
- "Bypass",
59551
- "-Command",
59552
- recycleScript
59553
- ],
58770
+ ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", recycleScript],
59554
58771
  {
59555
58772
  env: {
59556
58773
  ...process11.env,
@@ -59604,7 +58821,7 @@ var WorkspaceStore = class {
59604
58821
  path13.join(projectsRoot, entry.name, "project.json"),
59605
58822
  "utf8"
59606
58823
  );
59607
- return JSON.parse(projectJson);
58824
+ return normalizeProjectRecord(JSON.parse(projectJson));
59608
58825
  })
59609
58826
  );
59610
58827
  return projects.sort(
@@ -59622,7 +58839,7 @@ var WorkspaceStore = class {
59622
58839
  path13.join(this.getProjectDirectory(projectId), "project.json"),
59623
58840
  "utf8"
59624
58841
  );
59625
- return JSON.parse(projectJson);
58842
+ return normalizeProjectRecord(JSON.parse(projectJson));
59626
58843
  }
59627
58844
  async updateProject(projectId, project) {
59628
58845
  await writeFile2(
@@ -59685,6 +58902,70 @@ var WorkspaceStore = class {
59685
58902
  );
59686
58903
  return conversation;
59687
58904
  }
58905
+ async createCheckpoint(input) {
58906
+ assertSafeCheckpointId(input.id);
58907
+ const record2 = {
58908
+ ...input,
58909
+ files: ["index.html"]
58910
+ };
58911
+ const checkpointDirectory = this.getCheckpointDirectory(input.projectId, input.id);
58912
+ const filesDirectory = path13.join(checkpointDirectory, "files");
58913
+ const sourceFilePath = await this.resolveProjectWorkspacePath(input.projectId, "index.html", {
58914
+ checkTargetSymlink: true
58915
+ });
58916
+ await mkdir2(filesDirectory, { recursive: true });
58917
+ await copyFile(sourceFilePath, path13.join(filesDirectory, "index.html"));
58918
+ await writeFile2(
58919
+ path13.join(checkpointDirectory, "meta.json"),
58920
+ `${JSON.stringify(record2, null, 2)}
58921
+ `,
58922
+ "utf8"
58923
+ );
58924
+ return record2;
58925
+ }
58926
+ async listCheckpoints(projectId) {
58927
+ const checkpointsDirectory = this.getCheckpointsDirectory(projectId);
58928
+ try {
58929
+ const entries = await readdir(checkpointsDirectory, { withFileTypes: true });
58930
+ const checkpoints = await Promise.all(
58931
+ entries.filter((entry) => entry.isDirectory()).map(async (entry) => this.readCheckpoint(projectId, entry.name))
58932
+ );
58933
+ return checkpoints.sort(
58934
+ (left, right) => new Date(right.createdAt).getTime() - new Date(left.createdAt).getTime()
58935
+ );
58936
+ } catch (error51) {
58937
+ if (isMissingPathError3(error51)) {
58938
+ return [];
58939
+ }
58940
+ throw error51;
58941
+ }
58942
+ }
58943
+ async readCheckpoint(projectId, checkpointId) {
58944
+ assertSafeCheckpointId(checkpointId);
58945
+ const checkpointJson = await readFile4(
58946
+ path13.join(this.getCheckpointDirectory(projectId, checkpointId), "meta.json"),
58947
+ "utf8"
58948
+ );
58949
+ const checkpoint = JSON.parse(checkpointJson);
58950
+ if (checkpoint.projectId !== projectId || checkpoint.id !== checkpointId) {
58951
+ throw new Error("Checkpoint metadata does not match the requested project.");
58952
+ }
58953
+ return checkpoint;
58954
+ }
58955
+ async restoreCheckpointFiles(projectId, checkpointId) {
58956
+ const checkpoint = await this.readCheckpoint(projectId, checkpointId);
58957
+ const checkpointDirectory = this.getCheckpointDirectory(projectId, checkpointId);
58958
+ for (const file2 of checkpoint.files) {
58959
+ const sourcePath = path13.join(checkpointDirectory, "files", file2);
58960
+ const targetPath = await this.resolveProjectWorkspacePath(projectId, file2, {
58961
+ checkTargetSymlink: true,
58962
+ targetMayBeMissing: true
58963
+ });
58964
+ await mkdir2(path13.dirname(targetPath), { recursive: true });
58965
+ await copyFile(sourcePath, targetPath);
58966
+ }
58967
+ return checkpoint;
58968
+ }
59688
58969
  async writeProjectOutput(projectId, outputType, content) {
59689
58970
  const outputPath = this.getProjectOutputFilePath(projectId, outputType);
59690
58971
  await mkdir2(path13.dirname(outputPath), { recursive: true });
@@ -59721,18 +59002,24 @@ var WorkspaceStore = class {
59721
59002
  return left.localeCompare(right);
59722
59003
  });
59723
59004
  }
59005
+ async readProjectHtmlPageManifest(projectId) {
59006
+ try {
59007
+ return parseHtmlPageManifest(
59008
+ await this.readProjectWorkspaceFile(projectId, HTML_PAGE_MANIFEST_PATH)
59009
+ );
59010
+ } catch (error51) {
59011
+ if (isMissingPathError3(error51)) {
59012
+ return parseHtmlPageManifest(void 0);
59013
+ }
59014
+ throw error51;
59015
+ }
59016
+ }
59724
59017
  async readProjectWorkspaceEntry(projectId, relativePath, options = {}) {
59725
59018
  const offset = normalizePositiveInteger(options.offset, 1, "offset");
59726
- const limit = normalizePositiveInteger(
59727
- options.limit,
59728
- DEFAULT_READ_LIMIT,
59729
- "limit"
59730
- );
59731
- const targetPath = relativePath === "." ? this.getProjectWorkspaceDirectory(projectId) : await this.resolveProjectWorkspacePath(
59732
- projectId,
59733
- relativePath,
59734
- { checkTargetSymlink: true }
59735
- );
59019
+ const limit = normalizePositiveInteger(options.limit, DEFAULT_READ_LIMIT, "limit");
59020
+ const targetPath = relativePath === "." ? this.getProjectWorkspaceDirectory(projectId) : await this.resolveProjectWorkspacePath(projectId, relativePath, {
59021
+ checkTargetSymlink: true
59022
+ });
59736
59023
  const targetStats = await lstat(targetPath);
59737
59024
  const normalizedPath = normalizeWorkspaceRelativePath(relativePath);
59738
59025
  if (targetStats.isSymbolicLink()) {
@@ -59752,15 +59039,11 @@ var WorkspaceStore = class {
59752
59039
  return void 0;
59753
59040
  }
59754
59041
  return {
59755
- path: normalizeWorkspaceRelativePath(
59756
- path13.relative(rootPath, absolutePath)
59757
- ),
59042
+ path: normalizeWorkspaceRelativePath(path13.relative(rootPath, absolutePath)),
59758
59043
  type: entryStats.isDirectory() ? "directory" : "file"
59759
59044
  };
59760
59045
  })
59761
- )).filter(
59762
- (entry) => Boolean(entry)
59763
- ).sort((left, right) => left.path.localeCompare(right.path));
59046
+ )).filter((entry) => Boolean(entry)).sort((left, right) => left.path.localeCompare(right.path));
59764
59047
  const start2 = offset - 1;
59765
59048
  const sliced = visibleEntries.slice(start2, start2 + limit);
59766
59049
  return {
@@ -59833,20 +59116,16 @@ var WorkspaceStore = class {
59833
59116
  const startPath = relativePath && relativePath !== "." ? relativePath : "";
59834
59117
  const matcher = globToRegExp(pattern);
59835
59118
  const matches = [];
59836
- await this.walkProjectWorkspace(
59837
- projectId,
59838
- startPath,
59839
- async (entry) => {
59840
- const pathFromStart = startPath ? normalizeWorkspaceRelativePath(path13.relative(startPath, entry.path)) : entry.path;
59841
- if (matcher.test(pathFromStart) || matcher.test(path13.basename(entry.path))) {
59842
- matches.push({
59843
- path: entry.path,
59844
- type: entry.type,
59845
- updatedAt: entry.updatedAt
59846
- });
59847
- }
59119
+ await this.walkProjectWorkspace(projectId, startPath, async (entry) => {
59120
+ const pathFromStart = startPath ? normalizeWorkspaceRelativePath(path13.relative(startPath, entry.path)) : entry.path;
59121
+ if (matcher.test(pathFromStart) || matcher.test(path13.basename(entry.path))) {
59122
+ matches.push({
59123
+ path: entry.path,
59124
+ type: entry.type,
59125
+ updatedAt: entry.updatedAt
59126
+ });
59848
59127
  }
59849
- );
59128
+ });
59850
59129
  const sortedMatches = matches.sort(
59851
59130
  (left, right) => new Date(right.updatedAt).getTime() - new Date(left.updatedAt).getTime()
59852
59131
  );
@@ -59944,11 +59223,7 @@ var WorkspaceStore = class {
59944
59223
  if (!changes.length) {
59945
59224
  throw new Error("Project Workspace patch must include at least one change.");
59946
59225
  }
59947
- const prepared = await this.prepareProjectWorkspacePatch(
59948
- projectId,
59949
- changes,
59950
- options
59951
- );
59226
+ const prepared = await this.prepareProjectWorkspacePatch(projectId, changes, options);
59952
59227
  for (const change of prepared) {
59953
59228
  if (change.operation === "delete") {
59954
59229
  await rm(change.absolutePath, { force: false, recursive: true });
@@ -59978,26 +59253,14 @@ var WorkspaceStore = class {
59978
59253
  }) : this.getProjectWorkspaceDirectory(projectId);
59979
59254
  const startStats = await stat3(startPath);
59980
59255
  if (startStats.isFile()) {
59981
- await this.searchWorkspaceFile(
59982
- projectId,
59983
- startPath,
59984
- query,
59985
- matches,
59986
- skippedFiles
59987
- );
59256
+ await this.searchWorkspaceFile(projectId, startPath, query, matches, skippedFiles);
59988
59257
  } else if (startStats.isDirectory()) {
59989
59258
  await this.walkProjectWorkspace(
59990
59259
  projectId,
59991
59260
  relativePath === "." ? "" : relativePath,
59992
59261
  async (entry, absolutePath) => {
59993
59262
  if (entry.type === "file") {
59994
- await this.searchWorkspaceFile(
59995
- projectId,
59996
- absolutePath,
59997
- query,
59998
- matches,
59999
- skippedFiles
60000
- );
59263
+ await this.searchWorkspaceFile(projectId, absolutePath, query, matches, skippedFiles);
60001
59264
  }
60002
59265
  }
60003
59266
  );
@@ -60011,11 +59274,9 @@ var WorkspaceStore = class {
60011
59274
  };
60012
59275
  }
60013
59276
  async readProjectWorkspaceFile(projectId, relativePath) {
60014
- const filePath = await this.resolveProjectWorkspacePath(
60015
- projectId,
60016
- relativePath,
60017
- { checkTargetSymlink: true }
60018
- );
59277
+ const filePath = await this.resolveProjectWorkspacePath(projectId, relativePath, {
59278
+ checkTargetSymlink: true
59279
+ });
60019
59280
  const fileStats = await stat3(filePath);
60020
59281
  if (!fileStats.isFile()) {
60021
59282
  throw new Error(`Project Workspace path is not a file: ${relativePath}`);
@@ -60023,11 +59284,9 @@ var WorkspaceStore = class {
60023
59284
  return readFile4(filePath, "utf8");
60024
59285
  }
60025
59286
  async readProjectWorkspaceFileBuffer(projectId, relativePath) {
60026
- const filePath = await this.resolveProjectWorkspacePath(
60027
- projectId,
60028
- relativePath,
60029
- { checkTargetSymlink: true }
60030
- );
59287
+ const filePath = await this.resolveProjectWorkspacePath(projectId, relativePath, {
59288
+ checkTargetSymlink: true
59289
+ });
60031
59290
  const fileStats = await stat3(filePath);
60032
59291
  if (!fileStats.isFile()) {
60033
59292
  throw new Error(`Project Workspace path is not a file: ${relativePath}`);
@@ -60035,11 +59294,10 @@ var WorkspaceStore = class {
60035
59294
  return readFile4(filePath);
60036
59295
  }
60037
59296
  async writeProjectWorkspaceFile(projectId, relativePath, content) {
60038
- const filePath = await this.resolveProjectWorkspacePath(
60039
- projectId,
60040
- relativePath,
60041
- { checkTargetSymlink: true, targetMayBeMissing: true }
60042
- );
59297
+ const filePath = await this.resolveProjectWorkspacePath(projectId, relativePath, {
59298
+ checkTargetSymlink: true,
59299
+ targetMayBeMissing: true
59300
+ });
60043
59301
  const previousContent = await readTextFileIfExists(filePath);
60044
59302
  await mkdir2(path13.dirname(filePath), { recursive: true });
60045
59303
  await writeFile2(filePath, content, "utf8");
@@ -60074,21 +59332,15 @@ var WorkspaceStore = class {
60074
59332
  );
60075
59333
  await this.writeProjectWorkspaceFile(projectId, relativePath, updatedContent);
60076
59334
  return {
60077
- diff: buildUnifiedDiff(
60078
- content,
60079
- updatedContent,
60080
- normalizeWorkspaceRelativePath(relativePath)
60081
- ),
59335
+ diff: buildUnifiedDiff(content, updatedContent, normalizeWorkspaceRelativePath(relativePath)),
60082
59336
  path: normalizeWorkspaceRelativePath(relativePath),
60083
59337
  replacements: replaceAll ? replacements : 1
60084
59338
  };
60085
59339
  }
60086
59340
  async deleteProjectWorkspacePath(projectId, relativePath) {
60087
- const targetPath = await this.resolveProjectWorkspacePath(
60088
- projectId,
60089
- relativePath,
60090
- { checkTargetSymlink: true }
60091
- );
59341
+ const targetPath = await this.resolveProjectWorkspacePath(projectId, relativePath, {
59342
+ checkTargetSymlink: true
59343
+ });
60092
59344
  await rm(targetPath, { force: false, recursive: true });
60093
59345
  return {
60094
59346
  deleted: true,
@@ -60096,9 +59348,7 @@ var WorkspaceStore = class {
60096
59348
  };
60097
59349
  }
60098
59350
  async deleteConversation(projectId, conversationId) {
60099
- await this.moveToTrash(
60100
- this.getConversationFilePath(projectId, conversationId)
60101
- );
59351
+ await this.moveToTrash(this.getConversationFilePath(projectId, conversationId));
60102
59352
  }
60103
59353
  async deleteProject(projectId) {
60104
59354
  await this.moveToTrash(this.getProjectDirectory(projectId));
@@ -60112,17 +59362,17 @@ var WorkspaceStore = class {
60112
59362
  getConversationsDirectory(projectId) {
60113
59363
  return path13.join(this.getProjectDirectory(projectId), "conversations");
60114
59364
  }
59365
+ getCheckpointsDirectory(projectId) {
59366
+ return path13.join(this.getProjectDirectory(projectId), "checkpoints");
59367
+ }
59368
+ getCheckpointDirectory(projectId, checkpointId) {
59369
+ return path13.join(this.getCheckpointsDirectory(projectId), checkpointId);
59370
+ }
60115
59371
  getProjectOutputFilePath(projectId, outputType) {
60116
- return path13.join(
60117
- this.getProjectWorkspaceDirectory(projectId),
60118
- `index.${outputType}`
60119
- );
59372
+ return path13.join(this.getProjectWorkspaceDirectory(projectId), `index.${outputType}`);
60120
59373
  }
60121
59374
  getConversationFilePath(projectId, conversationId) {
60122
- return path13.join(
60123
- this.getConversationsDirectory(projectId),
60124
- `${conversationId}.json`
60125
- );
59375
+ return path13.join(this.getConversationsDirectory(projectId), `${conversationId}.json`);
60126
59376
  }
60127
59377
  async walkProjectWorkspace(projectId, relativePath, visit2) {
60128
59378
  const rootPath = this.getProjectWorkspaceDirectory(projectId);
@@ -60224,25 +59474,20 @@ var WorkspaceStore = class {
60224
59474
  if (state.has(relativePath)) {
60225
59475
  return state.get(relativePath);
60226
59476
  }
60227
- const absolutePath = await this.resolveProjectWorkspacePath(
60228
- projectId,
60229
- relativePath,
60230
- { checkTargetSymlink: true, targetMayBeMissing: true }
60231
- );
59477
+ const absolutePath = await this.resolveProjectWorkspacePath(projectId, relativePath, {
59478
+ checkTargetSymlink: true,
59479
+ targetMayBeMissing: true
59480
+ });
60232
59481
  const content = await readTextFileIfExists(absolutePath);
60233
59482
  state.set(relativePath, content);
60234
59483
  return content;
60235
59484
  };
60236
59485
  for (const change of changes) {
60237
59486
  const relativePath = normalizeWorkspaceRelativePath(change.path);
60238
- const absolutePath = await this.resolveProjectWorkspacePath(
60239
- projectId,
60240
- relativePath,
60241
- {
60242
- checkTargetSymlink: true,
60243
- targetMayBeMissing: change.operation !== "delete"
60244
- }
60245
- );
59487
+ const absolutePath = await this.resolveProjectWorkspacePath(projectId, relativePath, {
59488
+ checkTargetSymlink: true,
59489
+ targetMayBeMissing: change.operation !== "delete"
59490
+ });
60246
59491
  if (change.operation === "delete") {
60247
59492
  const targetStats = await lstat(absolutePath);
60248
59493
  const previousContent2 = targetStats.isFile() ? await readCurrentContent(relativePath) : void 0;
@@ -60313,11 +59558,7 @@ var WorkspaceStore = class {
60313
59558
  if (!relativeFromWorkspace || relativeFromWorkspace.startsWith("..") || path13.isAbsolute(relativeFromWorkspace)) {
60314
59559
  throw new Error(`Project Workspace path escapes workspace: ${relativePath}`);
60315
59560
  }
60316
- await this.assertNoWorkspaceSymlinkPath(
60317
- workspaceDirectory,
60318
- relativeFromWorkspace,
60319
- options
60320
- );
59561
+ await this.assertNoWorkspaceSymlinkPath(workspaceDirectory, relativeFromWorkspace, options);
60321
59562
  return targetPath;
60322
59563
  }
60323
59564
  async assertNoWorkspaceSymlinkPath(workspaceDirectory, relativeFromWorkspace, options) {
@@ -60341,6 +59582,17 @@ var WorkspaceStore = class {
60341
59582
  }
60342
59583
  }
60343
59584
  };
59585
+ function normalizeProjectRecord(project) {
59586
+ return {
59587
+ ...project,
59588
+ projectType: project.projectType ?? "single_html"
59589
+ };
59590
+ }
59591
+ function assertSafeCheckpointId(checkpointId) {
59592
+ if (!/^[A-Za-z0-9_-]+$/.test(checkpointId)) {
59593
+ throw new Error(`Invalid checkpoint id: ${checkpointId}`);
59594
+ }
59595
+ }
60344
59596
 
60345
59597
  // src/services.ts
60346
59598
  function createWorkspaceStore(options = {}) {
@@ -60371,9 +59623,7 @@ function createOwnDesignServices(options = {}) {
60371
59623
  function sanitizePublicConversation(conversation) {
60372
59624
  return {
60373
59625
  ...conversation,
60374
- messages: Array.isArray(conversation.messages) ? conversation.messages.map(
60375
- (message) => sanitizePublicUIMessage(message)
60376
- ) : conversation.messages
59626
+ messages: Array.isArray(conversation.messages) ? conversation.messages.map((message) => sanitizePublicUIMessage(message)) : conversation.messages
60377
59627
  };
60378
59628
  }
60379
59629
  function sanitizePublicUIMessage(message) {
@@ -60592,10 +59842,14 @@ var ChatRunManager = class {
60592
59842
  } else {
60593
59843
  run.status = "failed";
60594
59844
  this.flushPendingLiveChunks(run);
60595
- this.publish(run, {
60596
- errorText: error51 instanceof Error ? error51.message : "\u751F\u6210\u5931\u8D25\uFF1AUnknown error",
60597
- type: "error"
60598
- }, { immediate: true });
59845
+ this.publish(
59846
+ run,
59847
+ {
59848
+ errorText: error51 instanceof Error ? error51.message : "\u751F\u6210\u5931\u8D25\uFF1AUnknown error",
59849
+ type: "error"
59850
+ },
59851
+ { immediate: true }
59852
+ );
60599
59853
  }
60600
59854
  } finally {
60601
59855
  run.completedAt = run.completedAt ?? (/* @__PURE__ */ new Date()).toISOString();
@@ -60615,10 +59869,7 @@ var ChatRunManager = class {
60615
59869
  message: getLastAssistantMessage(run.initialMessages),
60616
59870
  stream: stream2
60617
59871
  })) {
60618
- run.latestMessages = mergeResponseMessage(
60619
- run.initialMessages,
60620
- responseMessage
60621
- );
59872
+ run.latestMessages = mergeResponseMessage(run.initialMessages, responseMessage);
60622
59873
  if (isRunAtResumableBoundary(run)) {
60623
59874
  run.safeMessages = run.latestMessages;
60624
59875
  run.safeSnapshotChunkIndex = run.safeResumeChunkIndex;
@@ -60812,25 +60063,19 @@ function createOwnDesignApp(options = {}) {
60812
60063
  activeConversationId: activeConversation?.id,
60813
60064
  activeProject,
60814
60065
  activeRun: activeProject ? chatRunManager.getActiveRun(activeProject.id) : void 0,
60815
- conversations: conversationState.conversations.map(
60816
- sanitizePublicConversation
60817
- ),
60066
+ conversations: conversationState.conversations.map(sanitizePublicConversation),
60818
60067
  projects: projectState.projects,
60819
60068
  settings
60820
60069
  });
60821
60070
  });
60822
60071
  app2.get("/api/settings", async (context2) => {
60823
- return context2.json(
60824
- await createOwnDesignServices(options).settingsService.getPublicSettings()
60825
- );
60072
+ return context2.json(await createOwnDesignServices(options).settingsService.getPublicSettings());
60826
60073
  });
60827
60074
  app2.put("/api/settings", async (context2) => {
60828
60075
  try {
60829
60076
  const body = await context2.req.json();
60830
60077
  return context2.json(
60831
- await createOwnDesignServices(options).settingsService.updatePublicSettings(
60832
- body
60833
- )
60078
+ await createOwnDesignServices(options).settingsService.updatePublicSettings(body)
60834
60079
  );
60835
60080
  } catch (error51) {
60836
60081
  return context2.text(
@@ -60872,17 +60117,20 @@ function createOwnDesignApp(options = {}) {
60872
60117
  app2.post("/api/projects", async (context2) => {
60873
60118
  const body = await context2.req.json();
60874
60119
  const trimmedName = asNonEmptyString(body.name);
60875
- if (!trimmedName) {
60120
+ const projectType = parseProjectType(body.projectType);
60121
+ if (!trimmedName || !projectType) {
60876
60122
  return context2.json({}, 400);
60877
60123
  }
60124
+ if (projectType === "react") {
60125
+ return context2.text("React project type is reserved but not supported yet.", 400);
60126
+ }
60878
60127
  const services = createOwnDesignServices(options);
60879
60128
  const settings = await services.settingsService.getSettings();
60880
60129
  const result = await services.projectService.createProject({
60881
- defaultConversationTitle: getDefaultConversationTitle(
60882
- settings.interfaceLanguage
60883
- ),
60130
+ defaultConversationTitle: getDefaultConversationTitle(settings.interfaceLanguage),
60884
60131
  name: trimmedName,
60885
- description: asNonEmptyString(body.description)
60132
+ description: asNonEmptyString(body.description),
60133
+ projectType
60886
60134
  });
60887
60135
  return context2.json({
60888
60136
  href: buildWorkspaceHref({
@@ -60909,9 +60157,7 @@ function createOwnDesignApp(options = {}) {
60909
60157
  await createProjectService(options).deleteProject(projectId);
60910
60158
  const projectState = await createProjectService(options).getProjectState();
60911
60159
  const fallbackProject = projectState.projects[0];
60912
- const fallbackConversation = fallbackProject ? (await createConversationService(options).getConversationState(
60913
- fallbackProject.id
60914
- )).conversations[0] : void 0;
60160
+ const fallbackConversation = fallbackProject ? (await createConversationService(options).getConversationState(fallbackProject.id)).conversations[0] : void 0;
60915
60161
  return context2.json({
60916
60162
  href: buildWorkspaceHref({
60917
60163
  conversationId: fallbackConversation?.id,
@@ -60934,76 +60180,58 @@ function createOwnDesignApp(options = {}) {
60934
60180
  })
60935
60181
  });
60936
60182
  });
60937
- app2.post(
60938
- "/api/projects/:projectId/conversations/:conversationId/select",
60939
- async (context2) => {
60940
- const projectId = context2.req.param("projectId");
60941
- const conversationId = context2.req.param("conversationId");
60942
- await createConversationService(options).switchConversation(
60943
- projectId,
60944
- conversationId
60945
- );
60946
- return context2.json({
60947
- href: buildWorkspaceHref({
60948
- conversationId,
60949
- projectId
60950
- })
60951
- });
60952
- }
60953
- );
60954
- app2.patch(
60955
- "/api/projects/:projectId/conversations/:conversationId",
60956
- async (context2) => {
60957
- const projectId = context2.req.param("projectId");
60958
- const conversationId = context2.req.param("conversationId");
60959
- const body = await context2.req.json();
60960
- const trimmedTitle = asNonEmptyString(body.title);
60961
- if (!trimmedTitle) {
60962
- return context2.json({}, 400);
60963
- }
60964
- await createConversationService(options).renameConversation(
60965
- projectId,
60966
- conversationId,
60967
- { title: trimmedTitle }
60968
- );
60969
- return context2.json({});
60970
- }
60971
- );
60972
- app2.delete(
60973
- "/api/projects/:projectId/conversations/:conversationId",
60974
- async (context2) => {
60975
- const projectId = context2.req.param("projectId");
60976
- const conversationId = context2.req.param("conversationId");
60977
- const currentConversationId = context2.req.query("currentConversationId");
60978
- const services = createOwnDesignServices(options);
60979
- const settings = await services.settingsService.getSettings();
60980
- const remainingConversations = await services.conversationService.deleteConversation(
60981
- projectId,
60183
+ app2.post("/api/projects/:projectId/conversations/:conversationId/select", async (context2) => {
60184
+ const projectId = context2.req.param("projectId");
60185
+ const conversationId = context2.req.param("conversationId");
60186
+ await createConversationService(options).switchConversation(projectId, conversationId);
60187
+ return context2.json({
60188
+ href: buildWorkspaceHref({
60982
60189
  conversationId,
60983
- getDefaultConversationTitle(settings.interfaceLanguage)
60984
- );
60985
- const nextConversationId = currentConversationId === conversationId ? remainingConversations[0]?.id : currentConversationId;
60986
- return context2.json({
60987
- href: buildWorkspaceHref({
60988
- conversationId: nextConversationId,
60989
- projectId
60990
- })
60991
- });
60190
+ projectId
60191
+ })
60192
+ });
60193
+ });
60194
+ app2.patch("/api/projects/:projectId/conversations/:conversationId", async (context2) => {
60195
+ const projectId = context2.req.param("projectId");
60196
+ const conversationId = context2.req.param("conversationId");
60197
+ const body = await context2.req.json();
60198
+ const trimmedTitle = asNonEmptyString(body.title);
60199
+ if (!trimmedTitle) {
60200
+ return context2.json({}, 400);
60992
60201
  }
60993
- );
60202
+ await createConversationService(options).renameConversation(projectId, conversationId, {
60203
+ title: trimmedTitle
60204
+ });
60205
+ return context2.json({});
60206
+ });
60207
+ app2.delete("/api/projects/:projectId/conversations/:conversationId", async (context2) => {
60208
+ const projectId = context2.req.param("projectId");
60209
+ const conversationId = context2.req.param("conversationId");
60210
+ const currentConversationId = context2.req.query("currentConversationId");
60211
+ const services = createOwnDesignServices(options);
60212
+ const settings = await services.settingsService.getSettings();
60213
+ const remainingConversations = await services.conversationService.deleteConversation(
60214
+ projectId,
60215
+ conversationId,
60216
+ getDefaultConversationTitle(settings.interfaceLanguage)
60217
+ );
60218
+ const nextConversationId = currentConversationId === conversationId ? remainingConversations[0]?.id : currentConversationId;
60219
+ return context2.json({
60220
+ href: buildWorkspaceHref({
60221
+ conversationId: nextConversationId,
60222
+ projectId
60223
+ })
60224
+ });
60225
+ });
60994
60226
  app2.post("/api/chat", async (context2) => {
60995
60227
  const body = await context2.req.json();
60996
60228
  const projectId = asNonEmptyString(body.projectId);
60997
60229
  const conversationId = asNonEmptyString(body.conversationId);
60998
60230
  const requestedPreviewPath = asNonEmptyString(body.previewPath);
60999
- const pageEditMode = parsePageEditMode(body.pageEditMode);
61000
60231
  const currentUserMessage = createCurrentUserMessage(body.message);
61001
60232
  if (!projectId || !conversationId || !currentUserMessage) {
61002
60233
  return context2.text("Invalid chat request.", 400);
61003
60234
  }
61004
- if (!pageEditMode) {
61005
- return context2.text("Invalid page edit mode.", 400);
61006
- }
61007
60235
  const workspaceStore = createWorkspaceStore(options);
61008
60236
  const project = await workspaceStore.getProject(projectId);
61009
60237
  const previewPath = await resolveExistingPreviewPath(
@@ -61014,29 +60242,29 @@ function createOwnDesignApp(options = {}) {
61014
60242
  if (chatRunManager.getActiveRun(projectId)) {
61015
60243
  return context2.text("\u5F53\u524D\u9879\u76EE\u5DF2\u6709\u4EFB\u52A1\u6B63\u5728\u6267\u884C\u3002", 409);
61016
60244
  }
61017
- let conversation = await workspaceStore.getConversation(
60245
+ await workspaceStore.createCheckpoint({
60246
+ id: createCheckpointId(),
60247
+ conversationId,
60248
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
61018
60249
  projectId,
61019
- conversationId
61020
- );
60250
+ userMessageId: currentUserMessage.id,
60251
+ userPrompt: getUIMessageText(currentUserMessage)
60252
+ });
60253
+ let conversation = await workspaceStore.getConversation(projectId, conversationId);
61021
60254
  const storedMessages = normalizeConversationMessages(
61022
60255
  conversation.messages
61023
60256
  );
61024
- let messages = [
61025
- ...storedMessages,
61026
- currentUserMessage
61027
- ];
60257
+ let messages = [...storedMessages, currentUserMessage];
61028
60258
  let agentContext;
61029
60259
  try {
61030
60260
  agentContext = await createDesignPageAgentContext({
61031
60261
  currentPreviewPath: previewPath,
61032
60262
  frontendTabId: asNonEmptyString(body.frontendTabId),
61033
60263
  modelConfigurationId: asNonEmptyString(body.modelConfigurationId),
61034
- outputType: project.outputType,
61035
- pageEditMode,
60264
+ projectType: project.projectType ?? "single_html",
61036
60265
  projectId,
61037
- providerOptionsSelection: parseProviderOptionsSelection(
61038
- body.providerOptionsSelection
61039
- ),
60266
+ providerOptionsSelection: parseProviderOptionsSelection(body.providerOptionsSelection),
60267
+ settingsPath: options.settingsPath,
61040
60268
  workspaceStore
61041
60269
  });
61042
60270
  } catch (error51) {
@@ -61046,33 +60274,13 @@ function createOwnDesignApp(options = {}) {
61046
60274
  );
61047
60275
  }
61048
60276
  if (!conversation.agentInstructions) {
61049
- conversation = await workspaceStore.updateConversation(
61050
- projectId,
61051
- conversationId,
61052
- {
61053
- ...conversation,
61054
- agentInstructions: buildDesignPageConversationInstructions(
61055
- agentContext.resources
61056
- ),
61057
- agentPromptVersion: DESIGN_PAGE_AGENT_PROMPT_VERSION
61058
- }
61059
- );
61060
- }
61061
- agentContext.agentInstructions = conversation.agentInstructions;
61062
- try {
61063
- messages = await rewriteLastUserMessageForDesignAgent({
61064
- messages,
61065
- pageEditMode,
61066
- pageEditModePolicy: agentContext.pageEditModePolicy ?? { mode: "auto" },
61067
- previewPath,
61068
- agentContext
60277
+ conversation = await workspaceStore.updateConversation(projectId, conversationId, {
60278
+ ...conversation,
60279
+ agentInstructions: buildDesignPageConversationInstructions(agentContext.resources),
60280
+ agentPromptVersion: DESIGN_PAGE_AGENT_PROMPT_VERSION
61069
60281
  });
61070
- } catch (error51) {
61071
- return context2.text(
61072
- error51 instanceof Error ? error51.message : "Failed to rewrite user prompt.",
61073
- 500
61074
- );
61075
60282
  }
60283
+ agentContext.agentInstructions = conversation.agentInstructions;
61076
60284
  const agent = createDesignPageAgent(agentContext);
61077
60285
  let latestStepUsage;
61078
60286
  const run = chatRunManager.startRun({
@@ -61124,6 +60332,49 @@ function createOwnDesignApp(options = {}) {
61124
60332
  }
61125
60333
  return createChatRunStreamResponse(run.stream);
61126
60334
  });
60335
+ app2.get("/api/projects/:projectId/checkpoints", async (context2) => {
60336
+ const workspaceStore = createWorkspaceStore(options);
60337
+ const projectId = context2.req.param("projectId");
60338
+ await workspaceStore.getProject(projectId);
60339
+ return context2.json(await workspaceStore.listCheckpoints(projectId));
60340
+ });
60341
+ app2.post("/api/projects/:projectId/checkpoints/:checkpointId/restore", async (context2) => {
60342
+ const workspaceStore = createWorkspaceStore(options);
60343
+ const projectId = context2.req.param("projectId");
60344
+ const checkpointId = context2.req.param("checkpointId");
60345
+ const body = await readJson(context2.req.raw);
60346
+ const mode = parseCheckpointRestoreMode(body?.mode);
60347
+ if (!mode) {
60348
+ return context2.text("Invalid checkpoint restore request.", 400);
60349
+ }
60350
+ if (chatRunManager.getActiveRun(projectId)) {
60351
+ return context2.text("\u5F53\u524D\u9879\u76EE\u5DF2\u6709\u4EFB\u52A1\u6B63\u5728\u6267\u884C\u3002", 409);
60352
+ }
60353
+ try {
60354
+ const checkpoint = mode === "conversation" ? await workspaceStore.readCheckpoint(projectId, checkpointId) : await workspaceStore.restoreCheckpointFiles(projectId, checkpointId);
60355
+ if (mode === "conversation" || mode === "both") {
60356
+ await restoreCheckpointConversation({
60357
+ options,
60358
+ workspaceStore,
60359
+ checkpoint
60360
+ });
60361
+ }
60362
+ return context2.json({
60363
+ href: buildWorkspaceHref({
60364
+ conversationId: checkpoint.conversationId,
60365
+ projectId
60366
+ })
60367
+ });
60368
+ } catch (error51) {
60369
+ if (isMissingPathError4(error51)) {
60370
+ return context2.text("Checkpoint not found.", 404);
60371
+ }
60372
+ return context2.text(
60373
+ error51 instanceof Error ? error51.message : "Checkpoint restore failed.",
60374
+ 400
60375
+ );
60376
+ }
60377
+ });
61127
60378
  app2.get("/api/projects/:projectId/runs/active", async (context2) => {
61128
60379
  const activeRun = chatRunManager.getActiveRun(context2.req.param("projectId"));
61129
60380
  if (!activeRun) {
@@ -61132,9 +60383,7 @@ function createOwnDesignApp(options = {}) {
61132
60383
  return context2.json(activeRun);
61133
60384
  });
61134
60385
  app2.delete("/api/projects/:projectId/runs/active", async (context2) => {
61135
- const activeRun = chatRunManager.cancelActiveRun(
61136
- context2.req.param("projectId")
61137
- );
60386
+ const activeRun = chatRunManager.cancelActiveRun(context2.req.param("projectId"));
61138
60387
  if (!activeRun) {
61139
60388
  return new Response(null, { status: 204 });
61140
60389
  }
@@ -61143,10 +60392,7 @@ function createOwnDesignApp(options = {}) {
61143
60392
  app2.get(
61144
60393
  "/api/projects/:projectId/conversations/:conversationId/runs/active/stream",
61145
60394
  async (context2) => {
61146
- const afterChunkIndex = Number.parseInt(
61147
- context2.req.query("after") ?? "0",
61148
- 10
61149
- );
60395
+ const afterChunkIndex = Number.parseInt(context2.req.query("after") ?? "0", 10);
61150
60396
  const stream2 = chatRunManager.subscribeActiveRun(
61151
60397
  context2.req.param("projectId"),
61152
60398
  context2.req.param("conversationId"),
@@ -61197,57 +60443,51 @@ function createOwnDesignApp(options = {}) {
61197
60443
  await manager.release(context2.req.param("projectId"), clientId);
61198
60444
  return new Response(null, { status: 204 });
61199
60445
  });
61200
- app2.post(
61201
- "/api/projects/:projectId/preview-session/heartbeat",
61202
- async (context2) => {
61203
- const body = await readJson(context2.req.raw);
61204
- const clientId = asNonEmptyString(body?.clientId);
61205
- if (!clientId) {
61206
- return context2.text("Invalid preview heartbeat request.", 400);
61207
- }
61208
- const workspaceStore = createWorkspaceStore(options);
61209
- const manager = getPreviewServerManager(workspaceStore);
61210
- const session = await manager.heartbeat(
61211
- context2.req.param("projectId"),
61212
- clientId,
61213
- asNonEmptyString(body?.previewPath)
61214
- );
61215
- return context2.json(session);
60446
+ app2.post("/api/projects/:projectId/preview-session/heartbeat", async (context2) => {
60447
+ const body = await readJson(context2.req.raw);
60448
+ const clientId = asNonEmptyString(body?.clientId);
60449
+ if (!clientId) {
60450
+ return context2.text("Invalid preview heartbeat request.", 400);
61216
60451
  }
61217
- );
61218
- app2.get(
61219
- "/api/projects/:projectId/frontend-capabilities/stream",
61220
- async (context2) => {
61221
- const projectId = context2.req.param("projectId");
61222
- const tabId = context2.req.query("tabId")?.trim();
61223
- if (!tabId) {
61224
- return context2.text("Invalid frontend capability stream request.", 400);
61225
- }
61226
- await createWorkspaceStore(options).getProject(projectId);
61227
- context2.header("Cache-Control", "no-cache, no-transform");
61228
- context2.header("Connection", "keep-alive");
61229
- context2.header("Content-Type", "text/event-stream; charset=utf-8");
61230
- return stream(context2, async (streamApi) => {
61231
- const commandStream = registerFrontendConnection({
61232
- frontendTabId: tabId,
61233
- projectId,
61234
- signal: context2.req.raw.signal
61235
- });
61236
- const reader = commandStream.getReader();
61237
- try {
61238
- while (true) {
61239
- const { done, value } = await reader.read();
61240
- if (done) {
61241
- break;
61242
- }
61243
- await streamApi.write(value);
60452
+ const workspaceStore = createWorkspaceStore(options);
60453
+ const manager = getPreviewServerManager(workspaceStore);
60454
+ const session = await manager.heartbeat(
60455
+ context2.req.param("projectId"),
60456
+ clientId,
60457
+ asNonEmptyString(body?.previewPath)
60458
+ );
60459
+ return context2.json(session);
60460
+ });
60461
+ app2.get("/api/projects/:projectId/frontend-capabilities/stream", async (context2) => {
60462
+ const projectId = context2.req.param("projectId");
60463
+ const tabId = context2.req.query("tabId")?.trim();
60464
+ if (!tabId) {
60465
+ return context2.text("Invalid frontend capability stream request.", 400);
60466
+ }
60467
+ await createWorkspaceStore(options).getProject(projectId);
60468
+ context2.header("Cache-Control", "no-cache, no-transform");
60469
+ context2.header("Connection", "keep-alive");
60470
+ context2.header("Content-Type", "text/event-stream; charset=utf-8");
60471
+ return stream(context2, async (streamApi) => {
60472
+ const commandStream = registerFrontendConnection({
60473
+ frontendTabId: tabId,
60474
+ projectId,
60475
+ signal: context2.req.raw.signal
60476
+ });
60477
+ const reader = commandStream.getReader();
60478
+ try {
60479
+ while (true) {
60480
+ const { done, value } = await reader.read();
60481
+ if (done) {
60482
+ break;
61244
60483
  }
61245
- } finally {
61246
- reader.releaseLock();
60484
+ await streamApi.write(value);
61247
60485
  }
61248
- });
61249
- }
61250
- );
60486
+ } finally {
60487
+ reader.releaseLock();
60488
+ }
60489
+ });
60490
+ });
61251
60491
  app2.get("/api/projects/:projectId/download", async (context2) => {
61252
60492
  const workspaceStore = createWorkspaceStore(options);
61253
60493
  const projectId = context2.req.param("projectId");
@@ -61327,15 +60567,10 @@ async function downloadCurrentHtml(workspaceStore, projectId, previewPath) {
61327
60567
  return new Response("Invalid download request.", { status: 400 });
61328
60568
  }
61329
60569
  try {
61330
- const content = await workspaceStore.readProjectWorkspaceFileBuffer(
61331
- projectId,
61332
- previewPath
61333
- );
60570
+ const content = await workspaceStore.readProjectWorkspaceFileBuffer(projectId, previewPath);
61334
60571
  return new Response(content, {
61335
60572
  headers: {
61336
- "Content-Disposition": createAttachmentDisposition(
61337
- path14.basename(previewPath)
61338
- ),
60573
+ "Content-Disposition": createAttachmentDisposition(path14.basename(previewPath)),
61339
60574
  "Content-Type": "text/html; charset=utf-8"
61340
60575
  },
61341
60576
  status: 200
@@ -61348,15 +60583,10 @@ async function downloadWorkspaceZip(workspaceStore, projectId) {
61348
60583
  let tempDirectory;
61349
60584
  try {
61350
60585
  const project = await workspaceStore.getProject(projectId);
61351
- tempDirectory = await mkdtemp(
61352
- path14.join(os6.tmpdir(), "owndesign-project-download-")
61353
- );
60586
+ tempDirectory = await mkdtemp(path14.join(os6.tmpdir(), "owndesign-project-download-"));
61354
60587
  const zipPath = path14.join(tempDirectory, "workspace.zip");
61355
60588
  await writeWorkspaceZip(workspaceStore, projectId, zipPath);
61356
- const [zipStats, zipBuffer] = await Promise.all([
61357
- stat4(zipPath),
61358
- readFile5(zipPath)
61359
- ]);
60589
+ const [zipStats, zipBuffer] = await Promise.all([stat4(zipPath), readFile5(zipPath)]);
61360
60590
  await rm2(tempDirectory, { force: true, recursive: true });
61361
60591
  tempDirectory = void 0;
61362
60592
  return new Response(zipBuffer, {
@@ -61391,10 +60621,7 @@ async function writeWorkspaceZip(workspaceStore, projectId, zipPath) {
61391
60621
  if (entry.type !== "file") {
61392
60622
  continue;
61393
60623
  }
61394
- const content = await workspaceStore.readProjectWorkspaceFileBuffer(
61395
- projectId,
61396
- entry.path
61397
- );
60624
+ const content = await workspaceStore.readProjectWorkspaceFileBuffer(projectId, entry.path);
61398
60625
  zipFile.addBuffer(content, entry.path);
61399
60626
  }
61400
60627
  zipFile.end();
@@ -61448,16 +60675,13 @@ async function readJson(request) {
61448
60675
  }
61449
60676
  }
61450
60677
  function createCurrentUserMessage(value) {
61451
- if (!isRecord2(value)) {
60678
+ if (!isRecord3(value)) {
61452
60679
  return void 0;
61453
60680
  }
61454
60681
  const id = asNonEmptyString(value.id);
61455
60682
  const text2 = typeof value.text === "string" ? value.text : "";
61456
60683
  const files = Array.isArray(value.files) ? value.files.filter(isFileUIPart2) : [];
61457
- const parts = [
61458
- ...text2 ? [{ text: text2, type: "text" }] : [],
61459
- ...files
61460
- ];
60684
+ const parts = [...text2 ? [{ text: text2, type: "text" }] : [], ...files];
61461
60685
  if (!id || parts.length === 0) {
61462
60686
  return void 0;
61463
60687
  }
@@ -61468,93 +60692,9 @@ function createCurrentUserMessage(value) {
61468
60692
  };
61469
60693
  }
61470
60694
  function isFileUIPart2(value) {
61471
- return isRecord2(value) && value.type === "file";
61472
- }
61473
- async function rewriteLastUserMessageForDesignAgent({
61474
- agentContext,
61475
- messages,
61476
- pageEditMode,
61477
- pageEditModePolicy,
61478
- previewPath
61479
- }) {
61480
- const lastUserMessageIndex = findLastUserMessageIndex(messages);
61481
- if (lastUserMessageIndex < 0) {
61482
- return messages;
61483
- }
61484
- const lastUserMessage = messages[lastUserMessageIndex];
61485
- if (!lastUserMessage || hasPromptRewriteMetadata(lastUserMessage)) {
61486
- return messages;
61487
- }
61488
- const originalUserPrompt = getUIMessageText(lastUserMessage);
61489
- const { rewrittenPrompt } = await rewriteTurnPrompt({
61490
- model: agentContext.model,
61491
- originalUserPrompt,
61492
- pageEditMode,
61493
- pageEditModePolicy,
61494
- previewPath,
61495
- providerOptions: agentContext.providerOptions
61496
- });
61497
- const metadata = createTurnPromptRewriteMetadata({
61498
- originalUserPrompt,
61499
- pageEditMode,
61500
- pageEditModePolicy,
61501
- previewPath
61502
- });
61503
- const rewrittenMessage = {
61504
- ...lastUserMessage,
61505
- metadata: {
61506
- ...isRecord2(lastUserMessage.metadata) ? lastUserMessage.metadata : {},
61507
- ...metadata
61508
- },
61509
- parts: [
61510
- {
61511
- text: rewrittenPrompt,
61512
- type: "text"
61513
- },
61514
- ...lastUserMessage.parts.filter((part) => part.type !== "text")
61515
- ]
61516
- };
61517
- return [
61518
- ...messages.slice(0, lastUserMessageIndex),
61519
- rewrittenMessage,
61520
- ...messages.slice(lastUserMessageIndex + 1)
61521
- ];
61522
- }
61523
- function findLastUserMessageIndex(messages) {
61524
- for (let index = messages.length - 1; index >= 0; index -= 1) {
61525
- const message = messages[index];
61526
- if (message?.role === "user") {
61527
- return index;
61528
- }
61529
- }
61530
- return -1;
61531
- }
61532
- function createTurnPromptRewriteMetadata({
61533
- originalUserPrompt,
61534
- pageEditMode,
61535
- pageEditModePolicy,
61536
- previewPath
61537
- }) {
61538
- return {
61539
- originalUserPrompt,
61540
- promptRewrite: {
61541
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
61542
- kind: "turn-prompt-rewriter",
61543
- pageEditMode,
61544
- previewFileExists: Boolean(previewPath),
61545
- ...previewPath ? { previewPath } : {},
61546
- ...pageEditModePolicy.mode === "duplicate_edit" ? {
61547
- duplicateSourcePath: pageEditModePolicy.sourcePath,
61548
- duplicateTargetPath: pageEditModePolicy.targetPath
61549
- } : {}
61550
- }
61551
- };
61552
- }
61553
- function hasPromptRewriteMetadata(message) {
61554
- const metadata = message.metadata;
61555
- return isRecord2(metadata) && isRecord2(metadata.promptRewrite) && metadata.promptRewrite.kind === "turn-prompt-rewriter";
60695
+ return isRecord3(value) && value.type === "file";
61556
60696
  }
61557
- function isRecord2(value) {
60697
+ function isRecord3(value) {
61558
60698
  return typeof value === "object" && value !== null;
61559
60699
  }
61560
60700
  async function resolveExistingPreviewPath(workspaceStore, projectId, previewPath) {
@@ -61567,8 +60707,20 @@ async function resolveExistingPreviewPath(workspaceStore, projectId, previewPath
61567
60707
  function asNonEmptyString(value) {
61568
60708
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
61569
60709
  }
60710
+ function parseProjectType(value) {
60711
+ if (value === void 0 || value === null || value === "") {
60712
+ return "single_html";
60713
+ }
60714
+ if (value === "single_html" || value === "react") {
60715
+ return value;
60716
+ }
60717
+ return void 0;
60718
+ }
60719
+ function parseCheckpointRestoreMode(value) {
60720
+ return value === "files" || value === "conversation" || value === "both" ? value : void 0;
60721
+ }
61570
60722
  function parseProviderOptionsSelection(value) {
61571
- if (!isRecord2(value)) {
60723
+ if (!isRecord3(value)) {
61572
60724
  return void 0;
61573
60725
  }
61574
60726
  const deepseek2 = parseDeepSeekThinkingMode(value.deepseek);
@@ -61581,6 +60733,36 @@ function parseProviderOptionsSelection(value) {
61581
60733
  ...deepseek2 ? { deepseek: deepseek2 } : {}
61582
60734
  };
61583
60735
  }
60736
+ function createCheckpointId() {
60737
+ const id = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2)}`;
60738
+ return `cp_${id.replace(/[^A-Za-z0-9_-]/g, "_")}`;
60739
+ }
60740
+ async function restoreCheckpointConversation({
60741
+ checkpoint,
60742
+ options,
60743
+ workspaceStore
60744
+ }) {
60745
+ const conversation = await workspaceStore.getConversation(
60746
+ checkpoint.projectId,
60747
+ checkpoint.conversationId
60748
+ );
60749
+ const messages = normalizeConversationMessages(conversation.messages);
60750
+ const targetIndex = messages.findIndex((message) => message.id === checkpoint.userMessageId);
60751
+ if (targetIndex < 0) {
60752
+ throw new Error("Checkpoint user message was not found in the conversation.");
60753
+ }
60754
+ const nextMessages = messages.slice(0, targetIndex);
60755
+ const settings = await createOwnDesignServices(options).settingsService.getSettings();
60756
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
60757
+ const lastMessage = nextMessages.at(-1);
60758
+ await workspaceStore.updateConversation(checkpoint.projectId, checkpoint.conversationId, {
60759
+ ...conversation,
60760
+ lastMessageAt: lastMessage ? timestamp : void 0,
60761
+ messages: nextMessages,
60762
+ title: !conversation.titleManuallySet && nextMessages.length === 0 ? getDefaultConversationTitle(settings.interfaceLanguage) : conversation.title,
60763
+ updatedAt: timestamp
60764
+ });
60765
+ }
61584
60766
 
61585
60767
  // src/index.ts
61586
60768
  var DEFAULT_HOST = "127.0.0.1";