owndesign 0.1.7 → 0.1.9

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 -1
  2. package/dist/server/index.js +647 -1475
  3. package/dist/server/prompts/agents/component-audit.md +24 -0
  4. package/dist/server/prompts/agents/design-page.md +87 -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-CoTKBCTn.js → angular-html-DCGj6j9k.js} +1 -1
  9. package/dist/web/assets/{angular-ts-B6lkmQt5.js → angular-ts-_jS5FvKQ.js} +1 -1
  10. package/dist/web/assets/{apl-DdUDDcTX.js → apl-COUiMcRO.js} +1 -1
  11. package/dist/web/assets/{arc-CpC_-kIU.js → arc-DO65316F.js} +1 -1
  12. package/dist/web/assets/architecture-7EHR7CIX-D10X4FnP.js +1 -0
  13. package/dist/web/assets/{architectureDiagram-3BPJPVTR-BFC9sw4f.js → architectureDiagram-3BPJPVTR-4QTI95py.js} +1 -1
  14. package/dist/web/assets/{astro-D8dj_1e3.js → astro-DBm7xN0H.js} +1 -1
  15. package/dist/web/assets/{blade-BOBPcO2h.js → blade-BrcGRjeD.js} +1 -1
  16. package/dist/web/assets/{blockDiagram-GPEHLZMM-Dm-IZ0OK.js → blockDiagram-GPEHLZMM-DyW2gTLi.js} +1 -1
  17. package/dist/web/assets/{c-BuJlpWo1.js → c-DAqlvgYb.js} +1 -1
  18. package/dist/web/assets/{c4Diagram-AAUBKEIU-D_naMOBh.js → c4Diagram-AAUBKEIU-D7VLn3UY.js} +1 -1
  19. package/dist/web/assets/channel-D4A7yvhc.js +1 -0
  20. package/dist/web/assets/{chunk-2J33WTMH-Br9TjHyg.js → chunk-2J33WTMH-ZI7iZmam.js} +1 -1
  21. package/dist/web/assets/{chunk-4BX2VUAB-BKXeBOxn.js → chunk-4BX2VUAB-BXZ1tYtp.js} +1 -1
  22. package/dist/web/assets/{chunk-55IACEB6-D3MxtENu.js → chunk-55IACEB6-BruFuyzh.js} +1 -1
  23. package/dist/web/assets/{chunk-727SXJPM-9i7OtYmm.js → chunk-727SXJPM-WfltwAyi.js} +1 -1
  24. package/dist/web/assets/{chunk-AQP2D5EJ-C1rNd_R7.js → chunk-AQP2D5EJ-m-6Oqccg.js} +1 -1
  25. package/dist/web/assets/{chunk-FMBD7UC4-fJyYpXV-.js → chunk-FMBD7UC4-CTpop8z0.js} +1 -1
  26. package/dist/web/assets/{chunk-ND2GUHAM-DNlEVqx2.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-ClEhNYgi.js → cobol-CV8XIe3g.js} +1 -1
  31. package/dist/web/assets/{coffee-DKH3A-3k.js → coffee-CjS-bqem.js} +1 -1
  32. package/dist/web/assets/{cose-bilkent-S5V4N54A-CKEXfMFa.js → cose-bilkent-S5V4N54A-qeF_aTDs.js} +1 -1
  33. package/dist/web/assets/{cpp-Dg0_NLWR.js → cpp-BWTrby03.js} +1 -1
  34. package/dist/web/assets/{crystal-B-pl8MQM.js → crystal-B4jSurfu.js} +1 -1
  35. package/dist/web/assets/{css-Q2XFe7KV.js → css-CXDKo6HN.js} +1 -1
  36. package/dist/web/assets/{dagre-BM42HDAG-CGLXhpG-.js → dagre-BM42HDAG-mtpIrAd-.js} +1 -1
  37. package/dist/web/assets/{diagram-2AECGRRQ-Yz0i_67x.js → diagram-2AECGRRQ-C2daXqRz.js} +1 -1
  38. package/dist/web/assets/{diagram-5GNKFQAL-BnSJ2Z2M.js → diagram-5GNKFQAL-Yn3Kkd1e.js} +1 -1
  39. package/dist/web/assets/{diagram-KO2AKTUF-B15W-hhD.js → diagram-KO2AKTUF-BSZBlF1_.js} +1 -1
  40. package/dist/web/assets/{diagram-LMA3HP47-BLsYWrS9.js → diagram-LMA3HP47-C-o1wwy4.js} +1 -1
  41. package/dist/web/assets/{diagram-OG6HWLK6-73omsPnV.js → diagram-OG6HWLK6-BadUuiav.js} +1 -1
  42. package/dist/web/assets/{edge-Dhh-Fs5C.js → edge-IP3F2dQ8.js} +1 -1
  43. package/dist/web/assets/{elixir-COHWreUF.js → elixir-DNv4dzV2.js} +1 -1
  44. package/dist/web/assets/{elm-CYxhy8U2.js → elm-DGlxwCzv.js} +1 -1
  45. package/dist/web/assets/{erDiagram-TEJ5UH35-a5r4A3JM.js → erDiagram-TEJ5UH35-uLYwqFWM.js} +1 -1
  46. package/dist/web/assets/{erb-Dl2ohr-E.js → erb-Cx4MSh15.js} +1 -1
  47. package/dist/web/assets/eventmodeling-FCH6USID-CHKTnhfi.js +1 -0
  48. package/dist/web/assets/{flowDiagram-I6XJVG4X-Dmc3vUIl.js → flowDiagram-I6XJVG4X-C-JzpWfx.js} +1 -1
  49. package/dist/web/assets/{ganttDiagram-6RSMTGT7-CRZVm0L0.js → ganttDiagram-6RSMTGT7-Be3WGPbk.js} +1 -1
  50. package/dist/web/assets/{git-rebase-DpESal6m.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-DhRyyOcn.js → gitGraphDiagram-PVQCEYII-DqJ0yj3L.js} +1 -1
  53. package/dist/web/assets/{glimmer-js-X_N0IQZ1.js → glimmer-js-B7LRT5BS.js} +1 -1
  54. package/dist/web/assets/{glimmer-ts-CoiHLEBh.js → glimmer-ts-CojGZlfZ.js} +1 -1
  55. package/dist/web/assets/{glsl-jTdcbPse.js → glsl-CbmtoTb8.js} +1 -1
  56. package/dist/web/assets/{graphql-CSN8TYN_.js → graphql-qYEIhTt7.js} +1 -1
  57. package/dist/web/assets/{hack-Dv67vUdK.js → hack-DxHXCCdq.js} +1 -1
  58. package/dist/web/assets/{haml-BFAyoHic.js → haml-CiwrkJ8T.js} +1 -1
  59. package/dist/web/assets/{handlebars-DsO_0Mb7.js → handlebars-Cn5UPYUQ.js} +1 -1
  60. package/dist/web/assets/{highlighted-body-OFNGDK62-Bsqvzkqq.js → highlighted-body-OFNGDK62-ehiuxKzz.js} +1 -1
  61. package/dist/web/assets/{html-hcrGAGxi.js → html-Cst8cPsk.js} +1 -1
  62. package/dist/web/assets/{html-derivative-nsyf9xDk.js → html-derivative-lnPAgvry.js} +1 -1
  63. package/dist/web/assets/{http-CQ7kMaQ9.js → http-C5VDSUzW.js} +1 -1
  64. package/dist/web/assets/{hurl-w8EbNg5E.js → hurl-BFlMJrL0.js} +1 -1
  65. package/dist/web/assets/{index-DAN9Q3vY.js → index-Ai406YO8.js} +186 -186
  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-C2F76qt2.js → infoDiagram-5YYISTIA-CIanyf0S.js} +1 -1
  69. package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-CZ272mnP.js → ishikawaDiagram-YF4QCWOH--1RL7uDF.js} +1 -1
  70. package/dist/web/assets/{java-BL-j72lp.js → java-BGahCCe1.js} +1 -1
  71. package/dist/web/assets/{javascript-D-RCZsZB.js → javascript-D5uMI8kB.js} +1 -1
  72. package/dist/web/assets/{jinja-3re3UTai.js → jinja-B4krr1-l.js} +1 -1
  73. package/dist/web/assets/{jison-aPrUrs4_.js → jison-BMMSlNZQ.js} +1 -1
  74. package/dist/web/assets/{journeyDiagram-JHISSGLW-Kbp9PKPx.js → journeyDiagram-JHISSGLW-lkcDMGR5.js} +1 -1
  75. package/dist/web/assets/{json-DVgA95cK.js → json-DXJMlo78.js} +1 -1
  76. package/dist/web/assets/{jsx-CTgF7iub.js → jsx-C_bl3hBI.js} +1 -1
  77. package/dist/web/assets/{julia-Deji7okx.js → julia-Sc_vYT5t.js} +1 -1
  78. package/dist/web/assets/{just-CaYOtqeh.js → just-CILkVz1n.js} +1 -1
  79. package/dist/web/assets/{kanban-definition-UN3LZRKU-zVFckm48.js → kanban-definition-UN3LZRKU-Bi_hSUym.js} +1 -1
  80. package/dist/web/assets/{latex-DjTDu_8e.js → latex-DUmFelG8.js} +1 -1
  81. package/dist/web/assets/{linear-HBblcI1g.js → linear-Dpnh4Vbv.js} +1 -1
  82. package/dist/web/assets/{liquid-BcpQGDFc.js → liquid-BnB6JKD1.js} +1 -1
  83. package/dist/web/assets/{lua-CuYpYTBI.js → lua-DhxAQE0G.js} +1 -1
  84. package/dist/web/assets/{marko-DgfLsR3g.js → marko-CyzzY1hm.js} +1 -1
  85. package/dist/web/assets/{mdc-CJyxXGTG.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-I66T9jsL.js → mermaid-parser.core-DShH95hL.js} +2 -2
  88. package/dist/web/assets/{mindmap-definition-RKZ34NQL-DaGOAyRB.js → mindmap-definition-RKZ34NQL-LqYVQ-zW.js} +1 -1
  89. package/dist/web/assets/{nginx-BoUl7sBZ.js → nginx-CR9TIk16.js} +1 -1
  90. package/dist/web/assets/{nim-CrEFP-aj.js → nim-DzWiwbcf.js} +1 -1
  91. package/dist/web/assets/packet-YPE3B663-DuGIOCMW.js +1 -0
  92. package/dist/web/assets/{perl-CZELP4Ds.js → perl-CgrDNBt5.js} +1 -1
  93. package/dist/web/assets/{php-CWKE0mzg.js → php-DtuJQDgr.js} +1 -1
  94. package/dist/web/assets/pie-LRSECV5Y-BYeTdyfo.js +1 -0
  95. package/dist/web/assets/{pieDiagram-4H26LBE5-CZEnKsEr.js → pieDiagram-4H26LBE5-B7_i9XPe.js} +1 -1
  96. package/dist/web/assets/{pug-ZalflcaW.js → pug--9BJCC2D.js} +1 -1
  97. package/dist/web/assets/{qml-D8g9QUr3.js → qml-C3iH27Ik.js} +1 -1
  98. package/dist/web/assets/{quadrantDiagram-W4KKPZXB-DsVGBWIs.js → quadrantDiagram-W4KKPZXB-C2tSamWU.js} +1 -1
  99. package/dist/web/assets/{r-t-w7nxIu.js → r-CqKTpWRo.js} +1 -1
  100. package/dist/web/assets/radar-GUYGQ44K-Cqw7n4df.js +1 -0
  101. package/dist/web/assets/{razor-KKTzZ9mx.js → razor-BNavdAx6.js} +1 -1
  102. package/dist/web/assets/{regexp-a1jUuWbn.js → regexp-zwVenqvj.js} +1 -1
  103. package/dist/web/assets/{requirementDiagram-4Y6WPE33-BTfqkfe2.js → requirementDiagram-4Y6WPE33-iIbtgBoU.js} +1 -1
  104. package/dist/web/assets/{rst-aqfBGGXb.js → rst-BHNeA4na.js} +1 -1
  105. package/dist/web/assets/{ruby-BEZTW4wN.js → ruby-CkHs-w2j.js} +1 -1
  106. package/dist/web/assets/{sankeyDiagram-5OEKKPKP-DypLejq4.js → sankeyDiagram-5OEKKPKP-B05ECsV0.js} +1 -1
  107. package/dist/web/assets/{sas-C6p8OBCV.js → sas-COQs-ItK.js} +1 -1
  108. package/dist/web/assets/{scss-C8GTc8vc.js → scss-DVsO7q0E.js} +1 -1
  109. package/dist/web/assets/{sequenceDiagram-3UESZ5HK-DNDK0sjs.js → sequenceDiagram-3UESZ5HK-CHtmtTcK.js} +1 -1
  110. package/dist/web/assets/{shellscript-CDcqP7X6.js → shellscript-DoNwQ9Oz.js} +1 -1
  111. package/dist/web/assets/{shellsession-CWzpCh1C.js → shellsession-DvIRWuae.js} +1 -1
  112. package/dist/web/assets/{soy-CepCYBkA.js → soy--AEUdRNF.js} +1 -1
  113. package/dist/web/assets/{sql-CAysnE0k.js → sql-BVv9DbTm.js} +1 -1
  114. package/dist/web/assets/{stata-BGr_WZqN.js → stata-Bzv07ePm.js} +1 -1
  115. package/dist/web/assets/{stateDiagram-AJRCARHV-CnSW064B.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-C7gNmBEt.js → surrealql-ChG0u1_y.js} +1 -1
  118. package/dist/web/assets/{svelte-D-4kj-uB.js → svelte-CTk-krYk.js} +1 -1
  119. package/dist/web/assets/{templ-BP_fNCUb.js → templ-CIOvt0Gn.js} +1 -1
  120. package/dist/web/assets/{tex-vDHHhfH8.js → tex-BLbgub0O.js} +1 -1
  121. package/dist/web/assets/{timeline-definition-PNZ67QCA-RE08Mx9v.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-BhUtB8KF.js → ts-tags-Cjp84wCR.js} +1 -1
  125. package/dist/web/assets/{tsx-CYOMJ3wf.js → tsx-BYXwwi4R.js} +1 -1
  126. package/dist/web/assets/{twig-DMJPvFzt.js → twig-CJXgadPB.js} +1 -1
  127. package/dist/web/assets/{typescript-JJOZ5Q74.js → typescript-SkJO5gXX.js} +1 -1
  128. package/dist/web/assets/{vennDiagram-CIIHVFJN-CTiIiyn-.js → vennDiagram-CIIHVFJN-DotTRq6i.js} +1 -1
  129. package/dist/web/assets/{vue-D6hN_u9i.js → vue-DnNvWsLF.js} +1 -1
  130. package/dist/web/assets/{vue-html-B8MKJvvw.js → vue-html-BtDfjhCy.js} +1 -1
  131. package/dist/web/assets/{vue-vine-BZtSJFfi.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-CpT1RVZp.js → wardleyDiagram-YWT4CUSO-C4mIx6nt.js} +1 -1
  134. package/dist/web/assets/{xml-Detdg-FS.js → xml-BwxGZI3_.js} +1 -1
  135. package/dist/web/assets/{xsl-CWUN_pbH.js → xsl-V11HisfD.js} +1 -1
  136. package/dist/web/assets/{xychartDiagram-2RQKCTM6-C8BtIz3X.js → xychartDiagram-2RQKCTM6-CR2seeGg.js} +1 -1
  137. package/dist/web/assets/{yaml-8W-QgFv_.js → yaml-DoTdQeS-.js} +1 -1
  138. package/dist/web/index.html +2 -2
  139. package/package.json +5 -6
  140. package/dist/server/prompts/agents/turn-prompt-rewriter.md +0 -15
  141. package/dist/web/assets/architecture-7EHR7CIX-BuetZCIk.js +0 -1
  142. package/dist/web/assets/channel-CD2ivr_e.js +0 -1
  143. package/dist/web/assets/chunk-QZHKN3VN-Bf-N5Jmd.js +0 -1
  144. package/dist/web/assets/classDiagram-4FO5ZUOK-F8ONWgXY.js +0 -1
  145. package/dist/web/assets/classDiagram-v2-Q7XG4LA2-F8ONWgXY.js +0 -1
  146. package/dist/web/assets/eventmodeling-FCH6USID-C5QIAlur.js +0 -1
  147. package/dist/web/assets/gitGraph-WXDBUCRP-Broa5-99.js +0 -1
  148. package/dist/web/assets/index-BFZtO7ji.css +0 -2
  149. package/dist/web/assets/info-J43DQDTF-5yru-lYW.js +0 -1
  150. package/dist/web/assets/mermaid-GHXKKRXX-tc_NirJc.js +0 -1
  151. package/dist/web/assets/packet-YPE3B663-eYBiCMjG.js +0 -1
  152. package/dist/web/assets/pie-LRSECV5Y-CEs_o2N-.js +0 -1
  153. package/dist/web/assets/radar-GUYGQ44K-D6SysS4p.js +0 -1
  154. package/dist/web/assets/stateDiagram-v2-BHNVJYJU-BtuE7Ay0.js +0 -1
  155. package/dist/web/assets/treeView-BLDUP644-DEYw0jRo.js +0 -1
  156. package/dist/web/assets/treemap-LRROVOQU-BaQ1oGDR.js +0 -1
  157. package/dist/web/assets/wardley-L42UT6IY-F8LWMBYs.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,46 @@ function loadDesignPageAgentCorePrompt() {
57983
57356
  }
57984
57357
  function buildPageTargetProtocolPrompt() {
57985
57358
  return [
57986
- "## Page Target Protocol",
57359
+ "## Single HTML Target Protocol",
57987
57360
  "",
57988
- "Resolve the target HTML page before creating or updating previewable output.",
57361
+ "The project has one previewable file: `index.html`.",
57989
57362
  "",
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.",
58000
- "",
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
- "- 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.",
57386
+ "- Use `patch` for coordinated changes or repeated replacements.",
57387
+ "- Use `write` only for deliberate full-file replacement of `index.html`.",
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
+ "Single HTML create vs update flow:",
57393
+ '- When `index.html` is missing, call `createHtml({ path: "index.html" })`, then read it and replace the default placeholder markup, CSS, and script with a complete designed prototype.',
57394
+ "- When `index.html` exists, read it before editing and do not call `createHtml`.",
58030
57395
  "",
58031
57396
  "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.",
58034
- "",
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."
57397
+ "- If an edit fails, read the file again and retry with a smaller edit or patch.",
57398
+ "- If a generated prototype becomes too large or brittle, simplify the file while preserving visible quality."
58038
57399
  ].join("\n");
58039
57400
  }
58040
57401
  function buildResourcePolicyPrompt(resources) {
@@ -58047,17 +57408,22 @@ function buildResourcePolicyPrompt(resources) {
58047
57408
  "Use these global resource settings when designing HTML preview pages.",
58048
57409
  defaultFontLibrary ? `Default font library: ${defaultFontLibrary.name}.` : "Default font library: none configured.",
58049
57410
  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.",
57411
+ "The default HTML template already configures Inter and Noto Sans SC on the `html` element.",
57412
+ "Unless the user explicitly asks for a different typeface, do not change `font-family`; adjust typography with size, weight, line-height, spacing, and hierarchy.",
57413
+ "Lucide icons are already configured by the default HTML template.",
57414
+ 'Use Lucide icons with `<i data-lucide="menu"></i>` syntax, replacing `menu` with the appropriate Lucide icon name.',
57415
+ "Do not use other icon systems, inline SVG icons, emoji icons, or decorative emoji as UI icons.",
57416
+ "When styling Lucide icons, do not target `i`, `i[data-lucide]`, or tag selectors because Lucide replaces the placeholder with inline `svg` elements.",
57417
+ "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; }`.",
57418
+ "If JavaScript dynamically inserts markup that contains Lucide placeholders, call `lucide.createIcons()` after updating the DOM.",
57419
+ "Prefer configured resources and local CSS before adding any external resource.",
57420
+ "Add an extra external resource only when the user explicitly requests it or when it is necessary for the prototype quality.",
57421
+ "When a configured library has no CDN, follow the library choice in CSS naming only.",
58055
57422
  "Configured font libraries:",
58056
57423
  fontLines.length ? fontLines.join("\n") : "- none",
58057
57424
  "Configured icon libraries:",
58058
57425
  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."
57426
+ "Use regular inline CSS as the primary styling method."
58061
57427
  ].join("\n");
58062
57428
  }
58063
57429
  function buildApprovedCdnUrls(resources) {
@@ -58075,75 +57441,6 @@ function formatResourceLibraryList(libraries) {
58075
57441
  );
58076
57442
  }
58077
57443
 
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
57444
  // ../core/src/preview/preview-server-manager.ts
58148
57445
  import { access, readFile as readFile2 } from "node:fs/promises";
58149
57446
  import path5 from "node:path";
@@ -58167,9 +57464,13 @@ var PreviewServerManager = class {
58167
57464
  }
58168
57465
  async ensure(projectId, clientId, previewPath) {
58169
57466
  const entry = await this.getOrStartEntry(projectId, clientId);
58170
- const files = await this.workspaceStore.listProjectHtmlFiles(projectId);
57467
+ const [files, pageManifest] = await Promise.all([
57468
+ this.workspaceStore.listProjectHtmlFiles(projectId),
57469
+ this.workspaceStore.readProjectHtmlPageManifest(projectId)
57470
+ ]);
58171
57471
  const activePath = resolveActivePreviewPath(
58172
57472
  files,
57473
+ pageManifest,
58173
57474
  previewPath ?? entry.activePath
58174
57475
  );
58175
57476
  entry.activePath = activePath;
@@ -58178,6 +57479,7 @@ var PreviewServerManager = class {
58178
57479
  return {
58179
57480
  ...activePath ? { activePath } : {},
58180
57481
  files,
57482
+ pageManifest,
58181
57483
  url: buildPreviewUrl(entry.baseUrl, activePath)
58182
57484
  };
58183
57485
  }
@@ -58211,9 +57513,7 @@ var PreviewServerManager = class {
58211
57513
  return this.entries.size;
58212
57514
  }
58213
57515
  getLeaseCount(projectId) {
58214
- return Array.from(this.entries.values()).filter(
58215
- (entry) => entry.projectId === projectId
58216
- ).length;
57516
+ return Array.from(this.entries.values()).filter((entry) => entry.projectId === projectId).length;
58217
57517
  }
58218
57518
  async cleanupExpiredLeases() {
58219
57519
  const now2 = this.now();
@@ -58253,24 +57553,21 @@ var PreviewServerManager = class {
58253
57553
  key,
58254
57554
  projectId
58255
57555
  };
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
- );
57556
+ app2.get("/", async () => readIndexHtmlOrNotFound(workspaceDirectory, entry));
57557
+ app2.get("/index.html", async () => readIndexHtmlOrNotFound(workspaceDirectory, entry));
58264
57558
  app2.use("*", async (context2, next) => {
58265
57559
  context2.header("Cache-Control", PREVIEW_CACHE_CONTROL);
58266
57560
  await next();
58267
57561
  });
58268
- app2.use("*", serveStatic({
58269
- onFound: (filePath) => {
58270
- recordServedStaticPath(entry, workspaceDirectory, filePath);
58271
- },
58272
- root: workspaceDirectory
58273
- }));
57562
+ app2.use(
57563
+ "*",
57564
+ serveStatic({
57565
+ onFound: (filePath) => {
57566
+ recordServedStaticPath(entry, workspaceDirectory, filePath);
57567
+ },
57568
+ root: workspaceDirectory
57569
+ })
57570
+ );
58274
57571
  const server2 = await listenOnRandomPort(app2);
58275
57572
  entry.server = server2.server;
58276
57573
  entry.baseUrl = server2.baseUrl;
@@ -58349,123 +57646,32 @@ function getPreviewServerManager(workspaceStore) {
58349
57646
  });
58350
57647
  return globalThis.__owndesignPreviewServerManager;
58351
57648
  }
58352
- async function readIndexHtmlOrEmptyPreview(workspaceDirectory, entry) {
57649
+ async function readIndexHtmlOrNotFound(workspaceDirectory, entry) {
58353
57650
  const indexPath = path5.join(workspaceDirectory, "index.html");
58354
57651
  try {
58355
57652
  await access(indexPath);
58356
57653
  entry.activePath = "index.html";
58357
- return readFile2(indexPath, "utf8");
57654
+ return htmlResponse(await readFile2(indexPath, "utf8"));
58358
57655
  } 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) {
57656
+ return new Response(null, {
57657
+ headers: {
57658
+ "Cache-Control": PREVIEW_CACHE_CONTROL
57659
+ },
57660
+ status: 404
57661
+ });
57662
+ }
57663
+ }
57664
+ function resolveActivePreviewPath(files, pageManifest, previewPath) {
58463
57665
  if (previewPath && files.includes(previewPath)) {
58464
57666
  return previewPath;
58465
57667
  }
58466
57668
  if (files.includes("index.html")) {
58467
57669
  return "index.html";
58468
57670
  }
57671
+ const manifestPath = pageManifest.pages.find((page) => files.includes(page.htmlPath))?.htmlPath;
57672
+ if (manifestPath) {
57673
+ return manifestPath;
57674
+ }
58469
57675
  return files[0];
58470
57676
  }
58471
57677
  function buildPreviewUrl(baseUrl, previewPath) {
@@ -58493,11 +57699,7 @@ function recordServedStaticPath(entry, workspaceDirectory, servedPath) {
58493
57699
  }
58494
57700
 
58495
57701
  // ../core/src/navigation.ts
58496
- function buildWorkspaceHref({
58497
- conversationId,
58498
- previewPath,
58499
- projectId
58500
- }) {
57702
+ function buildWorkspaceHref({ conversationId, previewPath, projectId }) {
58501
57703
  if (!projectId) {
58502
57704
  return "/";
58503
57705
  }
@@ -58548,11 +57750,7 @@ var ConversationService = class {
58548
57750
  titleManuallySet: true,
58549
57751
  updatedAt: this.now()
58550
57752
  };
58551
- return this.workspaceStore.updateConversation(
58552
- projectId,
58553
- conversationId,
58554
- updatedConversation
58555
- );
57753
+ return this.workspaceStore.updateConversation(projectId, conversationId, updatedConversation);
58556
57754
  }
58557
57755
  async switchConversation(projectId, conversationId) {
58558
57756
  return this.workspaceStore.getConversation(projectId, conversationId);
@@ -58571,7 +57769,7 @@ var ConversationService = class {
58571
57769
  };
58572
57770
  const agentResult = await this.generateProjectOutputSafely({
58573
57771
  content,
58574
- outputType: project.outputType,
57772
+ projectType: project.projectType ?? "single_html",
58575
57773
  projectId
58576
57774
  });
58577
57775
  const mockReply = {
@@ -58579,11 +57777,7 @@ var ConversationService = class {
58579
57777
  content: agentResult.content,
58580
57778
  createdAt: timestamp
58581
57779
  };
58582
- const messages = [
58583
- ...existingConversation.messages,
58584
- userMessage,
58585
- mockReply
58586
- ];
57780
+ const messages = [...existingConversation.messages, userMessage, mockReply];
58587
57781
  const updatedConversation = {
58588
57782
  ...existingConversation,
58589
57783
  title: !existingConversation.titleManuallySet && existingConversation.title === DEFAULT_CONVERSATION_TITLE && existingConversation.messages.length === 0 ? summarizeConversationTitle(content) : existingConversation.title,
@@ -58596,11 +57790,7 @@ var ConversationService = class {
58596
57790
  updatedAt: timestamp
58597
57791
  };
58598
57792
  await this.workspaceStore.updateProject(projectId, updatedProject);
58599
- return this.workspaceStore.updateConversation(
58600
- projectId,
58601
- conversationId,
58602
- updatedConversation
58603
- );
57793
+ return this.workspaceStore.updateConversation(projectId, conversationId, updatedConversation);
58604
57794
  }
58605
57795
  async saveUIMessageStream(projectId, conversationId, messages) {
58606
57796
  const existingConversation = await this.workspaceStore.getConversation(
@@ -58622,22 +57812,13 @@ var ConversationService = class {
58622
57812
  updatedAt: timestamp
58623
57813
  };
58624
57814
  await this.workspaceStore.updateProject(projectId, updatedProject);
58625
- return this.workspaceStore.updateConversation(
58626
- projectId,
58627
- conversationId,
58628
- updatedConversation
58629
- );
57815
+ return this.workspaceStore.updateConversation(projectId, conversationId, updatedConversation);
58630
57816
  }
58631
57817
  async deleteConversation(projectId, conversationId, defaultTitle) {
58632
57818
  await this.workspaceStore.deleteConversation(projectId, conversationId);
58633
- let remainingConversations = await this.workspaceStore.listConversations(
58634
- projectId
58635
- );
57819
+ let remainingConversations = await this.workspaceStore.listConversations(projectId);
58636
57820
  if (remainingConversations.length === 0) {
58637
- const replacementConversation = await this.createConversation(
58638
- projectId,
58639
- defaultTitle
58640
- );
57821
+ const replacementConversation = await this.createConversation(projectId, defaultTitle);
58641
57822
  remainingConversations = [replacementConversation];
58642
57823
  }
58643
57824
  return remainingConversations;
@@ -58676,11 +57857,16 @@ var ProjectService = class {
58676
57857
  this.previewServerManager = options.previewServerManager;
58677
57858
  }
58678
57859
  async createProject(input) {
57860
+ const projectType = input.projectType ?? "single_html";
57861
+ if (projectType === "react") {
57862
+ throw new Error("React project type is reserved but not supported yet.");
57863
+ }
58679
57864
  const timestamp = this.now();
58680
57865
  const project = {
58681
57866
  id: this.createId(),
58682
57867
  name: input.name,
58683
57868
  description: input.description,
57869
+ projectType,
58684
57870
  outputType: "html",
58685
57871
  createdAt: timestamp,
58686
57872
  updatedAt: timestamp
@@ -58694,6 +57880,11 @@ var ProjectService = class {
58694
57880
  messages: []
58695
57881
  };
58696
57882
  await this.workspaceStore.createProject(project);
57883
+ await this.workspaceStore.writeProjectWorkspaceFile(
57884
+ project.id,
57885
+ "index.html",
57886
+ buildSingleHtmlTemplate({ title: input.name })
57887
+ );
58697
57888
  await this.workspaceStore.createConversation(conversation);
58698
57889
  return { conversation, project };
58699
57890
  }
@@ -58720,15 +57911,7 @@ var ProjectService = class {
58720
57911
  };
58721
57912
 
58722
57913
  // ../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";
57914
+ import { copyFile, lstat, mkdir as mkdir2, readdir, readFile as readFile4, rm, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
58732
57915
  import os5 from "node:os";
58733
57916
  import path13 from "node:path";
58734
57917
  import process12 from "node:process";
@@ -58744,7 +57927,7 @@ function normalizePositiveInteger(value, fallback, name21) {
58744
57927
  }
58745
57928
  return value;
58746
57929
  }
58747
- function countOccurrences2(content, needle) {
57930
+ function countOccurrences(content, needle) {
58748
57931
  if (!needle) {
58749
57932
  return 0;
58750
57933
  }
@@ -58757,27 +57940,25 @@ function applyTextEdit(content, oldText, newText, replaceAll = false, relativePa
58757
57940
  if (oldText === newText) {
58758
57941
  throw new Error("No changes to apply: oldText and newText are identical.");
58759
57942
  }
58760
- const normalizedOldText = convertToLineEnding2(oldText, detectLineEnding2(content));
58761
- const normalizedNewText = convertToLineEnding2(newText, detectLineEnding2(content));
57943
+ const normalizedOldText = convertToLineEnding(oldText, detectLineEnding(content));
57944
+ const normalizedNewText = convertToLineEnding(newText, detectLineEnding(content));
58762
57945
  const firstIndex = content.indexOf(normalizedOldText);
58763
57946
  if (firstIndex === -1) {
58764
57947
  throw new Error(`oldText was not found in Project Workspace file: ${relativePath}`);
58765
57948
  }
58766
- const replacements = countOccurrences2(content, normalizedOldText);
57949
+ const replacements = countOccurrences(content, normalizedOldText);
58767
57950
  if (!replaceAll && replacements > 1) {
58768
- throw new Error(
58769
- `oldText appears more than once in Project Workspace file: ${relativePath}`
58770
- );
57951
+ throw new Error(`oldText appears more than once in Project Workspace file: ${relativePath}`);
58771
57952
  }
58772
57953
  return {
58773
57954
  content: replaceAll ? content.split(normalizedOldText).join(normalizedNewText) : content.slice(0, firstIndex) + normalizedNewText + content.slice(firstIndex + normalizedOldText.length),
58774
57955
  replacements: replaceAll ? replacements : 1
58775
57956
  };
58776
57957
  }
58777
- function detectLineEnding2(text2) {
57958
+ function detectLineEnding(text2) {
58778
57959
  return text2.includes("\r\n") ? "\r\n" : "\n";
58779
57960
  }
58780
- function convertToLineEnding2(text2, ending) {
57961
+ function convertToLineEnding(text2, ending) {
58781
57962
  const normalized = text2.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
58782
57963
  return ending === "\n" ? normalized : normalized.replaceAll("\n", "\r\n");
58783
57964
  }
@@ -58832,6 +58013,39 @@ function buildUnifiedDiff(oldContent, newContent, relativePath) {
58832
58013
  return lines.join("\n");
58833
58014
  }
58834
58015
 
58016
+ // ../core/src/html-page-manifest.ts
58017
+ var HTML_PAGE_MANIFEST_PATH = ".owndesign-pages.json";
58018
+ var EMPTY_HTML_PAGE_MANIFEST = {
58019
+ pages: []
58020
+ };
58021
+ function parseHtmlPageManifest(content) {
58022
+ if (!content) {
58023
+ return EMPTY_HTML_PAGE_MANIFEST;
58024
+ }
58025
+ let value;
58026
+ try {
58027
+ value = JSON.parse(content);
58028
+ } catch {
58029
+ return EMPTY_HTML_PAGE_MANIFEST;
58030
+ }
58031
+ if (!isRecord2(value) || !Array.isArray(value.pages)) {
58032
+ return EMPTY_HTML_PAGE_MANIFEST;
58033
+ }
58034
+ const pages = value.pages.filter(isRecord2).map((page) => ({
58035
+ componentSource: typeof page.componentSource === "string" ? page.componentSource.trim() : "",
58036
+ componentTag: typeof page.componentTag === "string" ? page.componentTag.trim() : "",
58037
+ displayName: typeof page.displayName === "string" ? page.displayName.trim() : "",
58038
+ htmlPath: typeof page.htmlPath === "string" ? page.htmlPath.trim() : "",
58039
+ slug: typeof page.slug === "string" ? page.slug.trim() : ""
58040
+ })).filter(
58041
+ (page) => page.slug && page.displayName && page.htmlPath && page.componentTag && page.componentSource
58042
+ );
58043
+ return { pages };
58044
+ }
58045
+ function isRecord2(value) {
58046
+ return typeof value === "object" && value !== null;
58047
+ }
58048
+
58835
58049
  // ../core/src/workspace-store/files.ts
58836
58050
  import { open, readFile as readFile3, stat as stat2 } from "node:fs/promises";
58837
58051
 
@@ -59153,10 +58367,10 @@ var isSymlinkSync = isTypeSync.bind(void 0, "lstatSync", "isSymbolicLink");
59153
58367
  // ../../node_modules/.pnpm/unicorn-magic@0.3.0/node_modules/unicorn-magic/node.js
59154
58368
  import { promisify } from "node:util";
59155
58369
  import { execFile as execFileCallback, execFileSync as execFileSyncOriginal } from "node:child_process";
59156
- import { fileURLToPath as fileURLToPath2 } from "node:url";
58370
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
59157
58371
  var execFileOriginal = promisify(execFileCallback);
59158
58372
  function toPath(urlOrPath) {
59159
- return urlOrPath instanceof URL ? fileURLToPath2(urlOrPath) : urlOrPath;
58373
+ return urlOrPath instanceof URL ? fileURLToPath3(urlOrPath) : urlOrPath;
59160
58374
  }
59161
58375
  var TEN_MEGABYTES_IN_BYTES = 10 * 1024 * 1024;
59162
58376
 
@@ -59543,14 +58757,7 @@ if ($item.PSIsContainer) {
59543
58757
  `;
59544
58758
  await execFileAsync(
59545
58759
  "powershell.exe",
59546
- [
59547
- "-NoProfile",
59548
- "-NonInteractive",
59549
- "-ExecutionPolicy",
59550
- "Bypass",
59551
- "-Command",
59552
- recycleScript
59553
- ],
58760
+ ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", recycleScript],
59554
58761
  {
59555
58762
  env: {
59556
58763
  ...process11.env,
@@ -59604,7 +58811,7 @@ var WorkspaceStore = class {
59604
58811
  path13.join(projectsRoot, entry.name, "project.json"),
59605
58812
  "utf8"
59606
58813
  );
59607
- return JSON.parse(projectJson);
58814
+ return normalizeProjectRecord(JSON.parse(projectJson));
59608
58815
  })
59609
58816
  );
59610
58817
  return projects.sort(
@@ -59622,7 +58829,7 @@ var WorkspaceStore = class {
59622
58829
  path13.join(this.getProjectDirectory(projectId), "project.json"),
59623
58830
  "utf8"
59624
58831
  );
59625
- return JSON.parse(projectJson);
58832
+ return normalizeProjectRecord(JSON.parse(projectJson));
59626
58833
  }
59627
58834
  async updateProject(projectId, project) {
59628
58835
  await writeFile2(
@@ -59685,6 +58892,70 @@ var WorkspaceStore = class {
59685
58892
  );
59686
58893
  return conversation;
59687
58894
  }
58895
+ async createCheckpoint(input) {
58896
+ assertSafeCheckpointId(input.id);
58897
+ const record2 = {
58898
+ ...input,
58899
+ files: ["index.html"]
58900
+ };
58901
+ const checkpointDirectory = this.getCheckpointDirectory(input.projectId, input.id);
58902
+ const filesDirectory = path13.join(checkpointDirectory, "files");
58903
+ const sourceFilePath = await this.resolveProjectWorkspacePath(input.projectId, "index.html", {
58904
+ checkTargetSymlink: true
58905
+ });
58906
+ await mkdir2(filesDirectory, { recursive: true });
58907
+ await copyFile(sourceFilePath, path13.join(filesDirectory, "index.html"));
58908
+ await writeFile2(
58909
+ path13.join(checkpointDirectory, "meta.json"),
58910
+ `${JSON.stringify(record2, null, 2)}
58911
+ `,
58912
+ "utf8"
58913
+ );
58914
+ return record2;
58915
+ }
58916
+ async listCheckpoints(projectId) {
58917
+ const checkpointsDirectory = this.getCheckpointsDirectory(projectId);
58918
+ try {
58919
+ const entries = await readdir(checkpointsDirectory, { withFileTypes: true });
58920
+ const checkpoints = await Promise.all(
58921
+ entries.filter((entry) => entry.isDirectory()).map(async (entry) => this.readCheckpoint(projectId, entry.name))
58922
+ );
58923
+ return checkpoints.sort(
58924
+ (left, right) => new Date(right.createdAt).getTime() - new Date(left.createdAt).getTime()
58925
+ );
58926
+ } catch (error51) {
58927
+ if (isMissingPathError3(error51)) {
58928
+ return [];
58929
+ }
58930
+ throw error51;
58931
+ }
58932
+ }
58933
+ async readCheckpoint(projectId, checkpointId) {
58934
+ assertSafeCheckpointId(checkpointId);
58935
+ const checkpointJson = await readFile4(
58936
+ path13.join(this.getCheckpointDirectory(projectId, checkpointId), "meta.json"),
58937
+ "utf8"
58938
+ );
58939
+ const checkpoint = JSON.parse(checkpointJson);
58940
+ if (checkpoint.projectId !== projectId || checkpoint.id !== checkpointId) {
58941
+ throw new Error("Checkpoint metadata does not match the requested project.");
58942
+ }
58943
+ return checkpoint;
58944
+ }
58945
+ async restoreCheckpointFiles(projectId, checkpointId) {
58946
+ const checkpoint = await this.readCheckpoint(projectId, checkpointId);
58947
+ const checkpointDirectory = this.getCheckpointDirectory(projectId, checkpointId);
58948
+ for (const file2 of checkpoint.files) {
58949
+ const sourcePath = path13.join(checkpointDirectory, "files", file2);
58950
+ const targetPath = await this.resolveProjectWorkspacePath(projectId, file2, {
58951
+ checkTargetSymlink: true,
58952
+ targetMayBeMissing: true
58953
+ });
58954
+ await mkdir2(path13.dirname(targetPath), { recursive: true });
58955
+ await copyFile(sourcePath, targetPath);
58956
+ }
58957
+ return checkpoint;
58958
+ }
59688
58959
  async writeProjectOutput(projectId, outputType, content) {
59689
58960
  const outputPath = this.getProjectOutputFilePath(projectId, outputType);
59690
58961
  await mkdir2(path13.dirname(outputPath), { recursive: true });
@@ -59721,18 +58992,24 @@ var WorkspaceStore = class {
59721
58992
  return left.localeCompare(right);
59722
58993
  });
59723
58994
  }
58995
+ async readProjectHtmlPageManifest(projectId) {
58996
+ try {
58997
+ return parseHtmlPageManifest(
58998
+ await this.readProjectWorkspaceFile(projectId, HTML_PAGE_MANIFEST_PATH)
58999
+ );
59000
+ } catch (error51) {
59001
+ if (isMissingPathError3(error51)) {
59002
+ return parseHtmlPageManifest(void 0);
59003
+ }
59004
+ throw error51;
59005
+ }
59006
+ }
59724
59007
  async readProjectWorkspaceEntry(projectId, relativePath, options = {}) {
59725
59008
  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
- );
59009
+ const limit = normalizePositiveInteger(options.limit, DEFAULT_READ_LIMIT, "limit");
59010
+ const targetPath = relativePath === "." ? this.getProjectWorkspaceDirectory(projectId) : await this.resolveProjectWorkspacePath(projectId, relativePath, {
59011
+ checkTargetSymlink: true
59012
+ });
59736
59013
  const targetStats = await lstat(targetPath);
59737
59014
  const normalizedPath = normalizeWorkspaceRelativePath(relativePath);
59738
59015
  if (targetStats.isSymbolicLink()) {
@@ -59752,15 +59029,11 @@ var WorkspaceStore = class {
59752
59029
  return void 0;
59753
59030
  }
59754
59031
  return {
59755
- path: normalizeWorkspaceRelativePath(
59756
- path13.relative(rootPath, absolutePath)
59757
- ),
59032
+ path: normalizeWorkspaceRelativePath(path13.relative(rootPath, absolutePath)),
59758
59033
  type: entryStats.isDirectory() ? "directory" : "file"
59759
59034
  };
59760
59035
  })
59761
- )).filter(
59762
- (entry) => Boolean(entry)
59763
- ).sort((left, right) => left.path.localeCompare(right.path));
59036
+ )).filter((entry) => Boolean(entry)).sort((left, right) => left.path.localeCompare(right.path));
59764
59037
  const start2 = offset - 1;
59765
59038
  const sliced = visibleEntries.slice(start2, start2 + limit);
59766
59039
  return {
@@ -59833,20 +59106,16 @@ var WorkspaceStore = class {
59833
59106
  const startPath = relativePath && relativePath !== "." ? relativePath : "";
59834
59107
  const matcher = globToRegExp(pattern);
59835
59108
  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
- }
59109
+ await this.walkProjectWorkspace(projectId, startPath, async (entry) => {
59110
+ const pathFromStart = startPath ? normalizeWorkspaceRelativePath(path13.relative(startPath, entry.path)) : entry.path;
59111
+ if (matcher.test(pathFromStart) || matcher.test(path13.basename(entry.path))) {
59112
+ matches.push({
59113
+ path: entry.path,
59114
+ type: entry.type,
59115
+ updatedAt: entry.updatedAt
59116
+ });
59848
59117
  }
59849
- );
59118
+ });
59850
59119
  const sortedMatches = matches.sort(
59851
59120
  (left, right) => new Date(right.updatedAt).getTime() - new Date(left.updatedAt).getTime()
59852
59121
  );
@@ -59944,11 +59213,7 @@ var WorkspaceStore = class {
59944
59213
  if (!changes.length) {
59945
59214
  throw new Error("Project Workspace patch must include at least one change.");
59946
59215
  }
59947
- const prepared = await this.prepareProjectWorkspacePatch(
59948
- projectId,
59949
- changes,
59950
- options
59951
- );
59216
+ const prepared = await this.prepareProjectWorkspacePatch(projectId, changes, options);
59952
59217
  for (const change of prepared) {
59953
59218
  if (change.operation === "delete") {
59954
59219
  await rm(change.absolutePath, { force: false, recursive: true });
@@ -59978,26 +59243,14 @@ var WorkspaceStore = class {
59978
59243
  }) : this.getProjectWorkspaceDirectory(projectId);
59979
59244
  const startStats = await stat3(startPath);
59980
59245
  if (startStats.isFile()) {
59981
- await this.searchWorkspaceFile(
59982
- projectId,
59983
- startPath,
59984
- query,
59985
- matches,
59986
- skippedFiles
59987
- );
59246
+ await this.searchWorkspaceFile(projectId, startPath, query, matches, skippedFiles);
59988
59247
  } else if (startStats.isDirectory()) {
59989
59248
  await this.walkProjectWorkspace(
59990
59249
  projectId,
59991
59250
  relativePath === "." ? "" : relativePath,
59992
59251
  async (entry, absolutePath) => {
59993
59252
  if (entry.type === "file") {
59994
- await this.searchWorkspaceFile(
59995
- projectId,
59996
- absolutePath,
59997
- query,
59998
- matches,
59999
- skippedFiles
60000
- );
59253
+ await this.searchWorkspaceFile(projectId, absolutePath, query, matches, skippedFiles);
60001
59254
  }
60002
59255
  }
60003
59256
  );
@@ -60011,11 +59264,9 @@ var WorkspaceStore = class {
60011
59264
  };
60012
59265
  }
60013
59266
  async readProjectWorkspaceFile(projectId, relativePath) {
60014
- const filePath = await this.resolveProjectWorkspacePath(
60015
- projectId,
60016
- relativePath,
60017
- { checkTargetSymlink: true }
60018
- );
59267
+ const filePath = await this.resolveProjectWorkspacePath(projectId, relativePath, {
59268
+ checkTargetSymlink: true
59269
+ });
60019
59270
  const fileStats = await stat3(filePath);
60020
59271
  if (!fileStats.isFile()) {
60021
59272
  throw new Error(`Project Workspace path is not a file: ${relativePath}`);
@@ -60023,11 +59274,9 @@ var WorkspaceStore = class {
60023
59274
  return readFile4(filePath, "utf8");
60024
59275
  }
60025
59276
  async readProjectWorkspaceFileBuffer(projectId, relativePath) {
60026
- const filePath = await this.resolveProjectWorkspacePath(
60027
- projectId,
60028
- relativePath,
60029
- { checkTargetSymlink: true }
60030
- );
59277
+ const filePath = await this.resolveProjectWorkspacePath(projectId, relativePath, {
59278
+ checkTargetSymlink: true
59279
+ });
60031
59280
  const fileStats = await stat3(filePath);
60032
59281
  if (!fileStats.isFile()) {
60033
59282
  throw new Error(`Project Workspace path is not a file: ${relativePath}`);
@@ -60035,11 +59284,10 @@ var WorkspaceStore = class {
60035
59284
  return readFile4(filePath);
60036
59285
  }
60037
59286
  async writeProjectWorkspaceFile(projectId, relativePath, content) {
60038
- const filePath = await this.resolveProjectWorkspacePath(
60039
- projectId,
60040
- relativePath,
60041
- { checkTargetSymlink: true, targetMayBeMissing: true }
60042
- );
59287
+ const filePath = await this.resolveProjectWorkspacePath(projectId, relativePath, {
59288
+ checkTargetSymlink: true,
59289
+ targetMayBeMissing: true
59290
+ });
60043
59291
  const previousContent = await readTextFileIfExists(filePath);
60044
59292
  await mkdir2(path13.dirname(filePath), { recursive: true });
60045
59293
  await writeFile2(filePath, content, "utf8");
@@ -60074,21 +59322,15 @@ var WorkspaceStore = class {
60074
59322
  );
60075
59323
  await this.writeProjectWorkspaceFile(projectId, relativePath, updatedContent);
60076
59324
  return {
60077
- diff: buildUnifiedDiff(
60078
- content,
60079
- updatedContent,
60080
- normalizeWorkspaceRelativePath(relativePath)
60081
- ),
59325
+ diff: buildUnifiedDiff(content, updatedContent, normalizeWorkspaceRelativePath(relativePath)),
60082
59326
  path: normalizeWorkspaceRelativePath(relativePath),
60083
59327
  replacements: replaceAll ? replacements : 1
60084
59328
  };
60085
59329
  }
60086
59330
  async deleteProjectWorkspacePath(projectId, relativePath) {
60087
- const targetPath = await this.resolveProjectWorkspacePath(
60088
- projectId,
60089
- relativePath,
60090
- { checkTargetSymlink: true }
60091
- );
59331
+ const targetPath = await this.resolveProjectWorkspacePath(projectId, relativePath, {
59332
+ checkTargetSymlink: true
59333
+ });
60092
59334
  await rm(targetPath, { force: false, recursive: true });
60093
59335
  return {
60094
59336
  deleted: true,
@@ -60096,9 +59338,7 @@ var WorkspaceStore = class {
60096
59338
  };
60097
59339
  }
60098
59340
  async deleteConversation(projectId, conversationId) {
60099
- await this.moveToTrash(
60100
- this.getConversationFilePath(projectId, conversationId)
60101
- );
59341
+ await this.moveToTrash(this.getConversationFilePath(projectId, conversationId));
60102
59342
  }
60103
59343
  async deleteProject(projectId) {
60104
59344
  await this.moveToTrash(this.getProjectDirectory(projectId));
@@ -60112,17 +59352,17 @@ var WorkspaceStore = class {
60112
59352
  getConversationsDirectory(projectId) {
60113
59353
  return path13.join(this.getProjectDirectory(projectId), "conversations");
60114
59354
  }
59355
+ getCheckpointsDirectory(projectId) {
59356
+ return path13.join(this.getProjectDirectory(projectId), "checkpoints");
59357
+ }
59358
+ getCheckpointDirectory(projectId, checkpointId) {
59359
+ return path13.join(this.getCheckpointsDirectory(projectId), checkpointId);
59360
+ }
60115
59361
  getProjectOutputFilePath(projectId, outputType) {
60116
- return path13.join(
60117
- this.getProjectWorkspaceDirectory(projectId),
60118
- `index.${outputType}`
60119
- );
59362
+ return path13.join(this.getProjectWorkspaceDirectory(projectId), `index.${outputType}`);
60120
59363
  }
60121
59364
  getConversationFilePath(projectId, conversationId) {
60122
- return path13.join(
60123
- this.getConversationsDirectory(projectId),
60124
- `${conversationId}.json`
60125
- );
59365
+ return path13.join(this.getConversationsDirectory(projectId), `${conversationId}.json`);
60126
59366
  }
60127
59367
  async walkProjectWorkspace(projectId, relativePath, visit2) {
60128
59368
  const rootPath = this.getProjectWorkspaceDirectory(projectId);
@@ -60224,25 +59464,20 @@ var WorkspaceStore = class {
60224
59464
  if (state.has(relativePath)) {
60225
59465
  return state.get(relativePath);
60226
59466
  }
60227
- const absolutePath = await this.resolveProjectWorkspacePath(
60228
- projectId,
60229
- relativePath,
60230
- { checkTargetSymlink: true, targetMayBeMissing: true }
60231
- );
59467
+ const absolutePath = await this.resolveProjectWorkspacePath(projectId, relativePath, {
59468
+ checkTargetSymlink: true,
59469
+ targetMayBeMissing: true
59470
+ });
60232
59471
  const content = await readTextFileIfExists(absolutePath);
60233
59472
  state.set(relativePath, content);
60234
59473
  return content;
60235
59474
  };
60236
59475
  for (const change of changes) {
60237
59476
  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
- );
59477
+ const absolutePath = await this.resolveProjectWorkspacePath(projectId, relativePath, {
59478
+ checkTargetSymlink: true,
59479
+ targetMayBeMissing: change.operation !== "delete"
59480
+ });
60246
59481
  if (change.operation === "delete") {
60247
59482
  const targetStats = await lstat(absolutePath);
60248
59483
  const previousContent2 = targetStats.isFile() ? await readCurrentContent(relativePath) : void 0;
@@ -60313,11 +59548,7 @@ var WorkspaceStore = class {
60313
59548
  if (!relativeFromWorkspace || relativeFromWorkspace.startsWith("..") || path13.isAbsolute(relativeFromWorkspace)) {
60314
59549
  throw new Error(`Project Workspace path escapes workspace: ${relativePath}`);
60315
59550
  }
60316
- await this.assertNoWorkspaceSymlinkPath(
60317
- workspaceDirectory,
60318
- relativeFromWorkspace,
60319
- options
60320
- );
59551
+ await this.assertNoWorkspaceSymlinkPath(workspaceDirectory, relativeFromWorkspace, options);
60321
59552
  return targetPath;
60322
59553
  }
60323
59554
  async assertNoWorkspaceSymlinkPath(workspaceDirectory, relativeFromWorkspace, options) {
@@ -60341,6 +59572,17 @@ var WorkspaceStore = class {
60341
59572
  }
60342
59573
  }
60343
59574
  };
59575
+ function normalizeProjectRecord(project) {
59576
+ return {
59577
+ ...project,
59578
+ projectType: project.projectType ?? "single_html"
59579
+ };
59580
+ }
59581
+ function assertSafeCheckpointId(checkpointId) {
59582
+ if (!/^[A-Za-z0-9_-]+$/.test(checkpointId)) {
59583
+ throw new Error(`Invalid checkpoint id: ${checkpointId}`);
59584
+ }
59585
+ }
60344
59586
 
60345
59587
  // src/services.ts
60346
59588
  function createWorkspaceStore(options = {}) {
@@ -60371,9 +59613,7 @@ function createOwnDesignServices(options = {}) {
60371
59613
  function sanitizePublicConversation(conversation) {
60372
59614
  return {
60373
59615
  ...conversation,
60374
- messages: Array.isArray(conversation.messages) ? conversation.messages.map(
60375
- (message) => sanitizePublicUIMessage(message)
60376
- ) : conversation.messages
59616
+ messages: Array.isArray(conversation.messages) ? conversation.messages.map((message) => sanitizePublicUIMessage(message)) : conversation.messages
60377
59617
  };
60378
59618
  }
60379
59619
  function sanitizePublicUIMessage(message) {
@@ -60592,10 +59832,14 @@ var ChatRunManager = class {
60592
59832
  } else {
60593
59833
  run.status = "failed";
60594
59834
  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 });
59835
+ this.publish(
59836
+ run,
59837
+ {
59838
+ errorText: error51 instanceof Error ? error51.message : "\u751F\u6210\u5931\u8D25\uFF1AUnknown error",
59839
+ type: "error"
59840
+ },
59841
+ { immediate: true }
59842
+ );
60599
59843
  }
60600
59844
  } finally {
60601
59845
  run.completedAt = run.completedAt ?? (/* @__PURE__ */ new Date()).toISOString();
@@ -60615,10 +59859,7 @@ var ChatRunManager = class {
60615
59859
  message: getLastAssistantMessage(run.initialMessages),
60616
59860
  stream: stream2
60617
59861
  })) {
60618
- run.latestMessages = mergeResponseMessage(
60619
- run.initialMessages,
60620
- responseMessage
60621
- );
59862
+ run.latestMessages = mergeResponseMessage(run.initialMessages, responseMessage);
60622
59863
  if (isRunAtResumableBoundary(run)) {
60623
59864
  run.safeMessages = run.latestMessages;
60624
59865
  run.safeSnapshotChunkIndex = run.safeResumeChunkIndex;
@@ -60812,25 +60053,19 @@ function createOwnDesignApp(options = {}) {
60812
60053
  activeConversationId: activeConversation?.id,
60813
60054
  activeProject,
60814
60055
  activeRun: activeProject ? chatRunManager.getActiveRun(activeProject.id) : void 0,
60815
- conversations: conversationState.conversations.map(
60816
- sanitizePublicConversation
60817
- ),
60056
+ conversations: conversationState.conversations.map(sanitizePublicConversation),
60818
60057
  projects: projectState.projects,
60819
60058
  settings
60820
60059
  });
60821
60060
  });
60822
60061
  app2.get("/api/settings", async (context2) => {
60823
- return context2.json(
60824
- await createOwnDesignServices(options).settingsService.getPublicSettings()
60825
- );
60062
+ return context2.json(await createOwnDesignServices(options).settingsService.getPublicSettings());
60826
60063
  });
60827
60064
  app2.put("/api/settings", async (context2) => {
60828
60065
  try {
60829
60066
  const body = await context2.req.json();
60830
60067
  return context2.json(
60831
- await createOwnDesignServices(options).settingsService.updatePublicSettings(
60832
- body
60833
- )
60068
+ await createOwnDesignServices(options).settingsService.updatePublicSettings(body)
60834
60069
  );
60835
60070
  } catch (error51) {
60836
60071
  return context2.text(
@@ -60872,17 +60107,20 @@ function createOwnDesignApp(options = {}) {
60872
60107
  app2.post("/api/projects", async (context2) => {
60873
60108
  const body = await context2.req.json();
60874
60109
  const trimmedName = asNonEmptyString(body.name);
60875
- if (!trimmedName) {
60110
+ const projectType = parseProjectType(body.projectType);
60111
+ if (!trimmedName || !projectType) {
60876
60112
  return context2.json({}, 400);
60877
60113
  }
60114
+ if (projectType === "react") {
60115
+ return context2.text("React project type is reserved but not supported yet.", 400);
60116
+ }
60878
60117
  const services = createOwnDesignServices(options);
60879
60118
  const settings = await services.settingsService.getSettings();
60880
60119
  const result = await services.projectService.createProject({
60881
- defaultConversationTitle: getDefaultConversationTitle(
60882
- settings.interfaceLanguage
60883
- ),
60120
+ defaultConversationTitle: getDefaultConversationTitle(settings.interfaceLanguage),
60884
60121
  name: trimmedName,
60885
- description: asNonEmptyString(body.description)
60122
+ description: asNonEmptyString(body.description),
60123
+ projectType
60886
60124
  });
60887
60125
  return context2.json({
60888
60126
  href: buildWorkspaceHref({
@@ -60909,9 +60147,7 @@ function createOwnDesignApp(options = {}) {
60909
60147
  await createProjectService(options).deleteProject(projectId);
60910
60148
  const projectState = await createProjectService(options).getProjectState();
60911
60149
  const fallbackProject = projectState.projects[0];
60912
- const fallbackConversation = fallbackProject ? (await createConversationService(options).getConversationState(
60913
- fallbackProject.id
60914
- )).conversations[0] : void 0;
60150
+ const fallbackConversation = fallbackProject ? (await createConversationService(options).getConversationState(fallbackProject.id)).conversations[0] : void 0;
60915
60151
  return context2.json({
60916
60152
  href: buildWorkspaceHref({
60917
60153
  conversationId: fallbackConversation?.id,
@@ -60934,76 +60170,58 @@ function createOwnDesignApp(options = {}) {
60934
60170
  })
60935
60171
  });
60936
60172
  });
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,
60173
+ app2.post("/api/projects/:projectId/conversations/:conversationId/select", async (context2) => {
60174
+ const projectId = context2.req.param("projectId");
60175
+ const conversationId = context2.req.param("conversationId");
60176
+ await createConversationService(options).switchConversation(projectId, conversationId);
60177
+ return context2.json({
60178
+ href: buildWorkspaceHref({
60982
60179
  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
- });
60180
+ projectId
60181
+ })
60182
+ });
60183
+ });
60184
+ app2.patch("/api/projects/:projectId/conversations/:conversationId", async (context2) => {
60185
+ const projectId = context2.req.param("projectId");
60186
+ const conversationId = context2.req.param("conversationId");
60187
+ const body = await context2.req.json();
60188
+ const trimmedTitle = asNonEmptyString(body.title);
60189
+ if (!trimmedTitle) {
60190
+ return context2.json({}, 400);
60992
60191
  }
60993
- );
60192
+ await createConversationService(options).renameConversation(projectId, conversationId, {
60193
+ title: trimmedTitle
60194
+ });
60195
+ return context2.json({});
60196
+ });
60197
+ app2.delete("/api/projects/:projectId/conversations/:conversationId", async (context2) => {
60198
+ const projectId = context2.req.param("projectId");
60199
+ const conversationId = context2.req.param("conversationId");
60200
+ const currentConversationId = context2.req.query("currentConversationId");
60201
+ const services = createOwnDesignServices(options);
60202
+ const settings = await services.settingsService.getSettings();
60203
+ const remainingConversations = await services.conversationService.deleteConversation(
60204
+ projectId,
60205
+ conversationId,
60206
+ getDefaultConversationTitle(settings.interfaceLanguage)
60207
+ );
60208
+ const nextConversationId = currentConversationId === conversationId ? remainingConversations[0]?.id : currentConversationId;
60209
+ return context2.json({
60210
+ href: buildWorkspaceHref({
60211
+ conversationId: nextConversationId,
60212
+ projectId
60213
+ })
60214
+ });
60215
+ });
60994
60216
  app2.post("/api/chat", async (context2) => {
60995
60217
  const body = await context2.req.json();
60996
60218
  const projectId = asNonEmptyString(body.projectId);
60997
60219
  const conversationId = asNonEmptyString(body.conversationId);
60998
60220
  const requestedPreviewPath = asNonEmptyString(body.previewPath);
60999
- const pageEditMode = parsePageEditMode(body.pageEditMode);
61000
60221
  const currentUserMessage = createCurrentUserMessage(body.message);
61001
60222
  if (!projectId || !conversationId || !currentUserMessage) {
61002
60223
  return context2.text("Invalid chat request.", 400);
61003
60224
  }
61004
- if (!pageEditMode) {
61005
- return context2.text("Invalid page edit mode.", 400);
61006
- }
61007
60225
  const workspaceStore = createWorkspaceStore(options);
61008
60226
  const project = await workspaceStore.getProject(projectId);
61009
60227
  const previewPath = await resolveExistingPreviewPath(
@@ -61014,29 +60232,29 @@ function createOwnDesignApp(options = {}) {
61014
60232
  if (chatRunManager.getActiveRun(projectId)) {
61015
60233
  return context2.text("\u5F53\u524D\u9879\u76EE\u5DF2\u6709\u4EFB\u52A1\u6B63\u5728\u6267\u884C\u3002", 409);
61016
60234
  }
61017
- let conversation = await workspaceStore.getConversation(
60235
+ await workspaceStore.createCheckpoint({
60236
+ id: createCheckpointId(),
60237
+ conversationId,
60238
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
61018
60239
  projectId,
61019
- conversationId
61020
- );
60240
+ userMessageId: currentUserMessage.id,
60241
+ userPrompt: getUIMessageText(currentUserMessage)
60242
+ });
60243
+ let conversation = await workspaceStore.getConversation(projectId, conversationId);
61021
60244
  const storedMessages = normalizeConversationMessages(
61022
60245
  conversation.messages
61023
60246
  );
61024
- let messages = [
61025
- ...storedMessages,
61026
- currentUserMessage
61027
- ];
60247
+ let messages = [...storedMessages, currentUserMessage];
61028
60248
  let agentContext;
61029
60249
  try {
61030
60250
  agentContext = await createDesignPageAgentContext({
61031
60251
  currentPreviewPath: previewPath,
61032
60252
  frontendTabId: asNonEmptyString(body.frontendTabId),
61033
60253
  modelConfigurationId: asNonEmptyString(body.modelConfigurationId),
61034
- outputType: project.outputType,
61035
- pageEditMode,
60254
+ projectType: project.projectType ?? "single_html",
61036
60255
  projectId,
61037
- providerOptionsSelection: parseProviderOptionsSelection(
61038
- body.providerOptionsSelection
61039
- ),
60256
+ providerOptionsSelection: parseProviderOptionsSelection(body.providerOptionsSelection),
60257
+ settingsPath: options.settingsPath,
61040
60258
  workspaceStore
61041
60259
  });
61042
60260
  } catch (error51) {
@@ -61046,33 +60264,13 @@ function createOwnDesignApp(options = {}) {
61046
60264
  );
61047
60265
  }
61048
60266
  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
60267
+ conversation = await workspaceStore.updateConversation(projectId, conversationId, {
60268
+ ...conversation,
60269
+ agentInstructions: buildDesignPageConversationInstructions(agentContext.resources),
60270
+ agentPromptVersion: DESIGN_PAGE_AGENT_PROMPT_VERSION
61069
60271
  });
61070
- } catch (error51) {
61071
- return context2.text(
61072
- error51 instanceof Error ? error51.message : "Failed to rewrite user prompt.",
61073
- 500
61074
- );
61075
60272
  }
60273
+ agentContext.agentInstructions = conversation.agentInstructions;
61076
60274
  const agent = createDesignPageAgent(agentContext);
61077
60275
  let latestStepUsage;
61078
60276
  const run = chatRunManager.startRun({
@@ -61124,6 +60322,49 @@ function createOwnDesignApp(options = {}) {
61124
60322
  }
61125
60323
  return createChatRunStreamResponse(run.stream);
61126
60324
  });
60325
+ app2.get("/api/projects/:projectId/checkpoints", async (context2) => {
60326
+ const workspaceStore = createWorkspaceStore(options);
60327
+ const projectId = context2.req.param("projectId");
60328
+ await workspaceStore.getProject(projectId);
60329
+ return context2.json(await workspaceStore.listCheckpoints(projectId));
60330
+ });
60331
+ app2.post("/api/projects/:projectId/checkpoints/:checkpointId/restore", async (context2) => {
60332
+ const workspaceStore = createWorkspaceStore(options);
60333
+ const projectId = context2.req.param("projectId");
60334
+ const checkpointId = context2.req.param("checkpointId");
60335
+ const body = await readJson(context2.req.raw);
60336
+ const mode = parseCheckpointRestoreMode(body?.mode);
60337
+ if (!mode) {
60338
+ return context2.text("Invalid checkpoint restore request.", 400);
60339
+ }
60340
+ if (chatRunManager.getActiveRun(projectId)) {
60341
+ return context2.text("\u5F53\u524D\u9879\u76EE\u5DF2\u6709\u4EFB\u52A1\u6B63\u5728\u6267\u884C\u3002", 409);
60342
+ }
60343
+ try {
60344
+ const checkpoint = mode === "conversation" ? await workspaceStore.readCheckpoint(projectId, checkpointId) : await workspaceStore.restoreCheckpointFiles(projectId, checkpointId);
60345
+ if (mode === "conversation" || mode === "both") {
60346
+ await restoreCheckpointConversation({
60347
+ options,
60348
+ workspaceStore,
60349
+ checkpoint
60350
+ });
60351
+ }
60352
+ return context2.json({
60353
+ href: buildWorkspaceHref({
60354
+ conversationId: checkpoint.conversationId,
60355
+ projectId
60356
+ })
60357
+ });
60358
+ } catch (error51) {
60359
+ if (isMissingPathError4(error51)) {
60360
+ return context2.text("Checkpoint not found.", 404);
60361
+ }
60362
+ return context2.text(
60363
+ error51 instanceof Error ? error51.message : "Checkpoint restore failed.",
60364
+ 400
60365
+ );
60366
+ }
60367
+ });
61127
60368
  app2.get("/api/projects/:projectId/runs/active", async (context2) => {
61128
60369
  const activeRun = chatRunManager.getActiveRun(context2.req.param("projectId"));
61129
60370
  if (!activeRun) {
@@ -61132,9 +60373,7 @@ function createOwnDesignApp(options = {}) {
61132
60373
  return context2.json(activeRun);
61133
60374
  });
61134
60375
  app2.delete("/api/projects/:projectId/runs/active", async (context2) => {
61135
- const activeRun = chatRunManager.cancelActiveRun(
61136
- context2.req.param("projectId")
61137
- );
60376
+ const activeRun = chatRunManager.cancelActiveRun(context2.req.param("projectId"));
61138
60377
  if (!activeRun) {
61139
60378
  return new Response(null, { status: 204 });
61140
60379
  }
@@ -61143,10 +60382,7 @@ function createOwnDesignApp(options = {}) {
61143
60382
  app2.get(
61144
60383
  "/api/projects/:projectId/conversations/:conversationId/runs/active/stream",
61145
60384
  async (context2) => {
61146
- const afterChunkIndex = Number.parseInt(
61147
- context2.req.query("after") ?? "0",
61148
- 10
61149
- );
60385
+ const afterChunkIndex = Number.parseInt(context2.req.query("after") ?? "0", 10);
61150
60386
  const stream2 = chatRunManager.subscribeActiveRun(
61151
60387
  context2.req.param("projectId"),
61152
60388
  context2.req.param("conversationId"),
@@ -61197,57 +60433,51 @@ function createOwnDesignApp(options = {}) {
61197
60433
  await manager.release(context2.req.param("projectId"), clientId);
61198
60434
  return new Response(null, { status: 204 });
61199
60435
  });
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);
60436
+ app2.post("/api/projects/:projectId/preview-session/heartbeat", async (context2) => {
60437
+ const body = await readJson(context2.req.raw);
60438
+ const clientId = asNonEmptyString(body?.clientId);
60439
+ if (!clientId) {
60440
+ return context2.text("Invalid preview heartbeat request.", 400);
61216
60441
  }
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);
60442
+ const workspaceStore = createWorkspaceStore(options);
60443
+ const manager = getPreviewServerManager(workspaceStore);
60444
+ const session = await manager.heartbeat(
60445
+ context2.req.param("projectId"),
60446
+ clientId,
60447
+ asNonEmptyString(body?.previewPath)
60448
+ );
60449
+ return context2.json(session);
60450
+ });
60451
+ app2.get("/api/projects/:projectId/frontend-capabilities/stream", async (context2) => {
60452
+ const projectId = context2.req.param("projectId");
60453
+ const tabId = context2.req.query("tabId")?.trim();
60454
+ if (!tabId) {
60455
+ return context2.text("Invalid frontend capability stream request.", 400);
60456
+ }
60457
+ await createWorkspaceStore(options).getProject(projectId);
60458
+ context2.header("Cache-Control", "no-cache, no-transform");
60459
+ context2.header("Connection", "keep-alive");
60460
+ context2.header("Content-Type", "text/event-stream; charset=utf-8");
60461
+ return stream(context2, async (streamApi) => {
60462
+ const commandStream = registerFrontendConnection({
60463
+ frontendTabId: tabId,
60464
+ projectId,
60465
+ signal: context2.req.raw.signal
60466
+ });
60467
+ const reader = commandStream.getReader();
60468
+ try {
60469
+ while (true) {
60470
+ const { done, value } = await reader.read();
60471
+ if (done) {
60472
+ break;
61244
60473
  }
61245
- } finally {
61246
- reader.releaseLock();
60474
+ await streamApi.write(value);
61247
60475
  }
61248
- });
61249
- }
61250
- );
60476
+ } finally {
60477
+ reader.releaseLock();
60478
+ }
60479
+ });
60480
+ });
61251
60481
  app2.get("/api/projects/:projectId/download", async (context2) => {
61252
60482
  const workspaceStore = createWorkspaceStore(options);
61253
60483
  const projectId = context2.req.param("projectId");
@@ -61327,15 +60557,10 @@ async function downloadCurrentHtml(workspaceStore, projectId, previewPath) {
61327
60557
  return new Response("Invalid download request.", { status: 400 });
61328
60558
  }
61329
60559
  try {
61330
- const content = await workspaceStore.readProjectWorkspaceFileBuffer(
61331
- projectId,
61332
- previewPath
61333
- );
60560
+ const content = await workspaceStore.readProjectWorkspaceFileBuffer(projectId, previewPath);
61334
60561
  return new Response(content, {
61335
60562
  headers: {
61336
- "Content-Disposition": createAttachmentDisposition(
61337
- path14.basename(previewPath)
61338
- ),
60563
+ "Content-Disposition": createAttachmentDisposition(path14.basename(previewPath)),
61339
60564
  "Content-Type": "text/html; charset=utf-8"
61340
60565
  },
61341
60566
  status: 200
@@ -61348,15 +60573,10 @@ async function downloadWorkspaceZip(workspaceStore, projectId) {
61348
60573
  let tempDirectory;
61349
60574
  try {
61350
60575
  const project = await workspaceStore.getProject(projectId);
61351
- tempDirectory = await mkdtemp(
61352
- path14.join(os6.tmpdir(), "owndesign-project-download-")
61353
- );
60576
+ tempDirectory = await mkdtemp(path14.join(os6.tmpdir(), "owndesign-project-download-"));
61354
60577
  const zipPath = path14.join(tempDirectory, "workspace.zip");
61355
60578
  await writeWorkspaceZip(workspaceStore, projectId, zipPath);
61356
- const [zipStats, zipBuffer] = await Promise.all([
61357
- stat4(zipPath),
61358
- readFile5(zipPath)
61359
- ]);
60579
+ const [zipStats, zipBuffer] = await Promise.all([stat4(zipPath), readFile5(zipPath)]);
61360
60580
  await rm2(tempDirectory, { force: true, recursive: true });
61361
60581
  tempDirectory = void 0;
61362
60582
  return new Response(zipBuffer, {
@@ -61391,10 +60611,7 @@ async function writeWorkspaceZip(workspaceStore, projectId, zipPath) {
61391
60611
  if (entry.type !== "file") {
61392
60612
  continue;
61393
60613
  }
61394
- const content = await workspaceStore.readProjectWorkspaceFileBuffer(
61395
- projectId,
61396
- entry.path
61397
- );
60614
+ const content = await workspaceStore.readProjectWorkspaceFileBuffer(projectId, entry.path);
61398
60615
  zipFile.addBuffer(content, entry.path);
61399
60616
  }
61400
60617
  zipFile.end();
@@ -61448,16 +60665,13 @@ async function readJson(request) {
61448
60665
  }
61449
60666
  }
61450
60667
  function createCurrentUserMessage(value) {
61451
- if (!isRecord2(value)) {
60668
+ if (!isRecord3(value)) {
61452
60669
  return void 0;
61453
60670
  }
61454
60671
  const id = asNonEmptyString(value.id);
61455
60672
  const text2 = typeof value.text === "string" ? value.text : "";
61456
60673
  const files = Array.isArray(value.files) ? value.files.filter(isFileUIPart2) : [];
61457
- const parts = [
61458
- ...text2 ? [{ text: text2, type: "text" }] : [],
61459
- ...files
61460
- ];
60674
+ const parts = [...text2 ? [{ text: text2, type: "text" }] : [], ...files];
61461
60675
  if (!id || parts.length === 0) {
61462
60676
  return void 0;
61463
60677
  }
@@ -61468,93 +60682,9 @@ function createCurrentUserMessage(value) {
61468
60682
  };
61469
60683
  }
61470
60684
  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";
60685
+ return isRecord3(value) && value.type === "file";
61556
60686
  }
61557
- function isRecord2(value) {
60687
+ function isRecord3(value) {
61558
60688
  return typeof value === "object" && value !== null;
61559
60689
  }
61560
60690
  async function resolveExistingPreviewPath(workspaceStore, projectId, previewPath) {
@@ -61567,8 +60697,20 @@ async function resolveExistingPreviewPath(workspaceStore, projectId, previewPath
61567
60697
  function asNonEmptyString(value) {
61568
60698
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
61569
60699
  }
60700
+ function parseProjectType(value) {
60701
+ if (value === void 0 || value === null || value === "") {
60702
+ return "single_html";
60703
+ }
60704
+ if (value === "single_html" || value === "react") {
60705
+ return value;
60706
+ }
60707
+ return void 0;
60708
+ }
60709
+ function parseCheckpointRestoreMode(value) {
60710
+ return value === "files" || value === "conversation" || value === "both" ? value : void 0;
60711
+ }
61570
60712
  function parseProviderOptionsSelection(value) {
61571
- if (!isRecord2(value)) {
60713
+ if (!isRecord3(value)) {
61572
60714
  return void 0;
61573
60715
  }
61574
60716
  const deepseek2 = parseDeepSeekThinkingMode(value.deepseek);
@@ -61581,6 +60723,36 @@ function parseProviderOptionsSelection(value) {
61581
60723
  ...deepseek2 ? { deepseek: deepseek2 } : {}
61582
60724
  };
61583
60725
  }
60726
+ function createCheckpointId() {
60727
+ const id = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2)}`;
60728
+ return `cp_${id.replace(/[^A-Za-z0-9_-]/g, "_")}`;
60729
+ }
60730
+ async function restoreCheckpointConversation({
60731
+ checkpoint,
60732
+ options,
60733
+ workspaceStore
60734
+ }) {
60735
+ const conversation = await workspaceStore.getConversation(
60736
+ checkpoint.projectId,
60737
+ checkpoint.conversationId
60738
+ );
60739
+ const messages = normalizeConversationMessages(conversation.messages);
60740
+ const targetIndex = messages.findIndex((message) => message.id === checkpoint.userMessageId);
60741
+ if (targetIndex < 0) {
60742
+ throw new Error("Checkpoint user message was not found in the conversation.");
60743
+ }
60744
+ const nextMessages = messages.slice(0, targetIndex);
60745
+ const settings = await createOwnDesignServices(options).settingsService.getSettings();
60746
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
60747
+ const lastMessage = nextMessages.at(-1);
60748
+ await workspaceStore.updateConversation(checkpoint.projectId, checkpoint.conversationId, {
60749
+ ...conversation,
60750
+ lastMessageAt: lastMessage ? timestamp : void 0,
60751
+ messages: nextMessages,
60752
+ title: !conversation.titleManuallySet && nextMessages.length === 0 ? getDefaultConversationTitle(settings.interfaceLanguage) : conversation.title,
60753
+ updatedAt: timestamp
60754
+ });
60755
+ }
61584
60756
 
61585
60757
  // src/index.ts
61586
60758
  var DEFAULT_HOST = "127.0.0.1";