@mmapp/react-compiler 0.1.0-alpha.18 → 0.1.0-alpha.20

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 (199) hide show
  1. package/dist/babel/index.d.mts +1 -1
  2. package/dist/babel/index.d.ts +1 -1
  3. package/dist/babel/index.js +9 -6
  4. package/dist/babel/index.mjs +1 -1
  5. package/dist/chunk-26U577GB.mjs +3465 -0
  6. package/dist/chunk-2FBDFAX6.mjs +2362 -0
  7. package/dist/chunk-2REDFOER.mjs +931 -0
  8. package/dist/{chunk-FPAMQXKB.mjs → chunk-2UTXM2QX.mjs} +10 -20
  9. package/dist/chunk-2YDQTFAL.mjs +879 -0
  10. package/dist/chunk-2ZRKQE74.mjs +175 -0
  11. package/dist/chunk-3QHG2JWV.mjs +154 -0
  12. package/dist/chunk-466OWSTT.mjs +186 -0
  13. package/dist/chunk-467SFYOD.mjs +3127 -0
  14. package/dist/chunk-4AIJO7DZ.mjs +214 -0
  15. package/dist/chunk-4D43TZYL.mjs +7495 -0
  16. package/dist/chunk-4FP5DXY4.mjs +3456 -0
  17. package/dist/chunk-4HX4PI4R.mjs +734 -0
  18. package/dist/chunk-4VU56NTZ.mjs +544 -0
  19. package/dist/chunk-4XHK6FWL.mjs +2058 -0
  20. package/dist/chunk-52C2JKH2.mjs +186 -0
  21. package/dist/chunk-52XHYD2V.mjs +214 -0
  22. package/dist/chunk-5GUFFFGL.mjs +148 -0
  23. package/dist/chunk-5N2FDDS6.mjs +214 -0
  24. package/dist/chunk-5ZSJXWZT.mjs +1646 -0
  25. package/dist/chunk-6CQOAAMV.mjs +1803 -0
  26. package/dist/chunk-6SEVAAVT.mjs +3516 -0
  27. package/dist/chunk-6YLR5ZDA.mjs +2829 -0
  28. package/dist/chunk-77UJB356.mjs +244 -0
  29. package/dist/chunk-7QOAJPQF.mjs +774 -0
  30. package/dist/chunk-A5RCMIBP.mjs +1795 -0
  31. package/dist/chunk-A63R3GKH.mjs +1803 -0
  32. package/dist/chunk-ABNTZXF5.mjs +951 -0
  33. package/dist/chunk-ADHWW56I.mjs +214 -0
  34. package/dist/chunk-AOGY2GK6.mjs +3292 -0
  35. package/dist/chunk-AXXUXRNA.mjs +1434 -0
  36. package/dist/chunk-BZLU5YK5.mjs +1025 -0
  37. package/dist/chunk-C7XCDDBH.mjs +1802 -0
  38. package/dist/chunk-CHLVKMQW.mjs +175 -0
  39. package/dist/chunk-CKGOZAB7.mjs +939 -0
  40. package/dist/chunk-CSXYDIVC.mjs +214 -0
  41. package/dist/chunk-CXTV4VGG.mjs +148 -0
  42. package/dist/chunk-D34RAZUX.mjs +2223 -0
  43. package/dist/chunk-DDIC7WM6.mjs +3127 -0
  44. package/dist/chunk-DPUQOBU6.mjs +4810 -0
  45. package/dist/chunk-E6MDVTGT.mjs +148 -0
  46. package/dist/chunk-EGKMUEM6.mjs +544 -0
  47. package/dist/chunk-EO6SYNCG.mjs +175 -0
  48. package/dist/chunk-EQGA6A6D.mjs +121 -0
  49. package/dist/chunk-EY2CSXYA.mjs +822 -0
  50. package/dist/chunk-EYLOSECJ.mjs +544 -0
  51. package/dist/chunk-FF5BQVII.mjs +148 -0
  52. package/dist/chunk-FIQ65CDR.mjs +925 -0
  53. package/dist/chunk-FOZXJFAR.mjs +186 -0
  54. package/dist/chunk-G2IAZ5Q6.mjs +148 -0
  55. package/dist/chunk-G7SMOWOL.mjs +828 -0
  56. package/dist/chunk-GK7NU6DO.mjs +214 -0
  57. package/dist/chunk-HDSCPEHY.mjs +4061 -0
  58. package/dist/chunk-HJELFNEA.mjs +186 -0
  59. package/dist/chunk-HOIUP6IF.mjs +690 -0
  60. package/dist/chunk-HRJGDPNE.mjs +148 -0
  61. package/dist/chunk-I3AU7GRD.mjs +120 -0
  62. package/dist/chunk-I3VQQJZ6.mjs +2843 -0
  63. package/dist/chunk-I6SSPILI.mjs +550 -0
  64. package/dist/chunk-IPTX5MJU.mjs +3223 -0
  65. package/dist/chunk-ITGUSH2Z.mjs +2783 -0
  66. package/dist/chunk-IXHBCAMF.mjs +3306 -0
  67. package/dist/chunk-J7JUAHS4.mjs +186 -0
  68. package/dist/chunk-J7TWJ3TM.mjs +2784 -0
  69. package/dist/chunk-JDPLDGVF.mjs +4810 -0
  70. package/dist/chunk-JK72MQ4N.mjs +877 -0
  71. package/dist/chunk-K2HHCAS2.mjs +148 -0
  72. package/dist/chunk-K5HX2SVL.mjs +1902 -0
  73. package/dist/chunk-KAUEQ2F3.mjs +148 -0
  74. package/dist/chunk-KFVVOS5N.mjs +925 -0
  75. package/dist/chunk-KIH4AN3Y.mjs +154 -0
  76. package/dist/chunk-KPDMN5IX.mjs +175 -0
  77. package/dist/chunk-LZL2IRCH.mjs +186 -0
  78. package/dist/chunk-MIZV3TAN.mjs +3293 -0
  79. package/dist/chunk-MRH4J7IX.mjs +2846 -0
  80. package/dist/chunk-NKBL5GUC.mjs +186 -0
  81. package/dist/chunk-NQCNSCF6.mjs +148 -0
  82. package/dist/chunk-NRP5J3BR.mjs +4811 -0
  83. package/dist/chunk-NTB7OEX2.mjs +2918 -0
  84. package/dist/chunk-NUPJYPFU.mjs +801 -0
  85. package/dist/chunk-NVQUTSQX.mjs +3128 -0
  86. package/dist/chunk-OGMG64EY.mjs +148 -0
  87. package/dist/chunk-OL5B2HTH.mjs +175 -0
  88. package/dist/chunk-OPJKP747.mjs +7506 -0
  89. package/dist/chunk-OQLGGBNE.mjs +2918 -0
  90. package/dist/chunk-OW4AQUDL.mjs +544 -0
  91. package/dist/chunk-OWI6XWCD.mjs +3375 -0
  92. package/dist/chunk-OZT2EAF2.mjs +2776 -0
  93. package/dist/chunk-PBRBRKIQ.mjs +175 -0
  94. package/dist/chunk-PRUMNNDI.mjs +3192 -0
  95. package/dist/chunk-QPNHDTSM.mjs +4839 -0
  96. package/dist/chunk-RNEIAJDR.mjs +897 -0
  97. package/dist/chunk-RY7POBNT.mjs +3127 -0
  98. package/dist/chunk-S6FJ4DXL.mjs +1813 -0
  99. package/dist/chunk-SU4E6E7B.mjs +3153 -0
  100. package/dist/chunk-SYUUKW5A.mjs +3379 -0
  101. package/dist/chunk-THB5SX2S.mjs +113 -0
  102. package/dist/chunk-THFYE5ZX.mjs +244 -0
  103. package/dist/chunk-TK3QMXIP.mjs +2921 -0
  104. package/dist/chunk-TRR2NPAV.mjs +248 -0
  105. package/dist/chunk-TTTTOT7P.mjs +1803 -0
  106. package/dist/chunk-TXONBY6A.mjs +7509 -0
  107. package/dist/chunk-U2PX3JSY.mjs +1933 -0
  108. package/dist/chunk-U6F7CTHK.mjs +550 -0
  109. package/dist/chunk-UASOWKDI.mjs +186 -0
  110. package/dist/chunk-UDDTWG5J.mjs +734 -0
  111. package/dist/chunk-UIWLAQCL.mjs +175 -0
  112. package/dist/chunk-UL2XZEMA.mjs +3128 -0
  113. package/dist/chunk-US3AVDAI.mjs +3456 -0
  114. package/dist/chunk-V5DIDOTT.mjs +148 -0
  115. package/dist/chunk-VLTKQDJ3.mjs +244 -0
  116. package/dist/chunk-VVC42PTS.mjs +175 -0
  117. package/dist/chunk-VX3T3NIR.mjs +897 -0
  118. package/dist/chunk-WBYMW4NQ.mjs +3450 -0
  119. package/dist/chunk-XMWUHQVV.mjs +939 -0
  120. package/dist/chunk-XUQ5R6F3.mjs +213 -0
  121. package/dist/chunk-Y6FXYEAI.mjs +10 -0
  122. package/dist/chunk-YCWC2SCO.mjs +148 -0
  123. package/dist/chunk-YFS6JMYO.mjs +3342 -0
  124. package/dist/chunk-ZE67HOSN.mjs +148 -0
  125. package/dist/chunk-ZSK5TPIV.mjs +148 -0
  126. package/dist/cli/index.js +329 -653
  127. package/dist/cli/index.mjs +9 -9
  128. package/dist/config-PL24KEWL.mjs +219 -0
  129. package/dist/deploy-VAHWALWB.mjs +9 -0
  130. package/dist/dev-server-Bs_sz2DG.d.mts +111 -0
  131. package/dist/dev-server-Bs_sz2DG.d.ts +111 -0
  132. package/dist/dev-server-RmGHIntF.d.mts +113 -0
  133. package/dist/dev-server-RmGHIntF.d.ts +113 -0
  134. package/dist/dev-server.js +205 -522
  135. package/dist/dev-server.mjs +4 -4
  136. package/dist/engine-binary-KQB23JDR.mjs +97 -0
  137. package/dist/envelope-DD7v0v6E.d.mts +265 -0
  138. package/dist/envelope-DD7v0v6E.d.ts +265 -0
  139. package/dist/envelope.js +9 -6
  140. package/dist/envelope.mjs +2 -2
  141. package/dist/index-B5gSgvnd.d.mts +44 -0
  142. package/dist/index-B5gSgvnd.d.ts +44 -0
  143. package/dist/index-Bs0MnR54.d.mts +103 -0
  144. package/dist/index-Bs0MnR54.d.ts +103 -0
  145. package/dist/index-DR0nNc_f.d.mts +101 -0
  146. package/dist/index-DR0nNc_f.d.ts +101 -0
  147. package/dist/index-revho_gS.d.mts +104 -0
  148. package/dist/index-revho_gS.d.ts +104 -0
  149. package/dist/index-vQjwYekL.d.mts +104 -0
  150. package/dist/index-vQjwYekL.d.ts +104 -0
  151. package/dist/index.d.mts +2 -2
  152. package/dist/index.d.ts +2 -2
  153. package/dist/index.js +210 -525
  154. package/dist/index.mjs +10 -9
  155. package/dist/init-2XLTUF7O.mjs +407 -0
  156. package/dist/init-7FJENUDK.mjs +407 -0
  157. package/dist/init-AVZJHZYY.mjs +538 -0
  158. package/dist/init-DQDX3QK6.mjs +369 -0
  159. package/dist/init-K3GVM4JS.mjs +538 -0
  160. package/dist/init-NXS5BJN3.mjs +454 -0
  161. package/dist/init-UC3FWPIW.mjs +367 -0
  162. package/dist/init-UNV5XIDE.mjs +367 -0
  163. package/dist/project-compiler-2HOPO7GC.mjs +10 -0
  164. package/dist/project-compiler-D245L5QR.mjs +10 -0
  165. package/dist/project-compiler-FUQRMB4X.mjs +10 -0
  166. package/dist/project-compiler-LA5OBI5P.mjs +10 -0
  167. package/dist/project-compiler-OP2VVGJQ.mjs +10 -0
  168. package/dist/project-compiler-PZNFE6AH.mjs +10 -0
  169. package/dist/project-compiler-QBVF6MFI.mjs +10 -0
  170. package/dist/project-compiler-VFR6CSDX.mjs +10 -0
  171. package/dist/project-compiler-WMJZA4UH.mjs +10 -0
  172. package/dist/project-compiler-XXS27TZT.mjs +10 -0
  173. package/dist/project-compiler-YYGDSHY5.mjs +10 -0
  174. package/dist/project-decompiler-5GY2KSG4.mjs +7 -0
  175. package/dist/project-decompiler-7I2OMUVY.mjs +7 -0
  176. package/dist/project-decompiler-QCZYY4TW.mjs +7 -0
  177. package/dist/project-decompiler-US7GAVIC.mjs +7 -0
  178. package/dist/pull-2Q53HF3H.mjs +107 -0
  179. package/dist/pull-5AFHD3QG.mjs +109 -0
  180. package/dist/pull-5WJ4LW4U.mjs +109 -0
  181. package/dist/pull-65GUSX6F.mjs +109 -0
  182. package/dist/pull-6LVI4LMM.mjs +109 -0
  183. package/dist/pull-A2QUHW4K.mjs +109 -0
  184. package/dist/pull-B6T5BUKV.mjs +109 -0
  185. package/dist/pull-CKHWZT33.mjs +109 -0
  186. package/dist/pull-GM74ERRT.mjs +109 -0
  187. package/dist/pull-JBEQWVPE.mjs +109 -0
  188. package/dist/pull-P44LDRWB.mjs +109 -0
  189. package/dist/pull-W2US3T3E.mjs +109 -0
  190. package/dist/testing/index.js +9 -6
  191. package/dist/testing/index.mjs +1 -1
  192. package/dist/verify-3PPS4XAM.mjs +1833 -0
  193. package/dist/verify-GKEH5FZQ.mjs +1833 -0
  194. package/dist/verify-HDKUNHZ4.mjs +1833 -0
  195. package/dist/verify-SEIXUGN4.mjs +1833 -0
  196. package/dist/vite/index.js +10 -7
  197. package/dist/vite/index.mjs +2 -2
  198. package/mm-dev.db +0 -0
  199. package/package.json +2 -2
package/dist/cli/index.js CHANGED
@@ -6469,7 +6469,8 @@ function convertOnEvent(sub) {
6469
6469
  };
6470
6470
  }
6471
6471
  function emitIR(extracted) {
6472
- const { slug, name, version, description, category, fields, states, transitions } = extracted;
6472
+ const { slug, name, version, description, category: rawCategory, fields, states, transitions } = extracted;
6473
+ const category = Array.isArray(rawCategory) ? rawCategory : [rawCategory];
6473
6474
  const stateArray = Array.from(states.values());
6474
6475
  if (stateArray.length === 0) {
6475
6476
  stateArray.push({
@@ -6857,13 +6858,15 @@ function emitCanonical(extracted, sourceFilename) {
6857
6858
  }
6858
6859
  ]
6859
6860
  });
6860
- const category = extracted.category;
6861
+ const rawCat = extracted.category;
6861
6862
  let categoryArray;
6862
- if (category.includes("/")) {
6863
- const [primary, ...tags] = category.split("/");
6863
+ if (Array.isArray(rawCat)) {
6864
+ categoryArray = rawCat;
6865
+ } else if (rawCat.includes("/")) {
6866
+ const [primary, ...tags] = rawCat.split("/");
6864
6867
  categoryArray = (0, import_player_core.normalizeCategory)(primary, ...tags);
6865
6868
  } else {
6866
- categoryArray = [category];
6869
+ categoryArray = [rawCat];
6867
6870
  }
6868
6871
  return {
6869
6872
  slug: ir.slug,
@@ -7010,7 +7013,7 @@ function compilerStateToWorkflow(state, metadata) {
7010
7013
  name: metadata.name || "Workflow",
7011
7014
  version: metadata.version || "0.1.0",
7012
7015
  description: metadata.description,
7013
- category: metadata.category || "workflow",
7016
+ category: metadata.categoryArray ? metadata.categoryArray : [metadata.category || "workflow"],
7014
7017
  fields: state.fields || [],
7015
7018
  states: state.states || /* @__PURE__ */ new Map(),
7016
7019
  transitions: state.transitions || [],
@@ -7954,7 +7957,7 @@ function extractRouterWorkflow(pages, options = {}) {
7954
7957
  name: "Router",
7955
7958
  version: "1.0.0",
7956
7959
  description: "Auto-derived router workflow from file-based routing",
7957
- category: "router",
7960
+ category: ["router"],
7958
7961
  fields,
7959
7962
  states,
7960
7963
  transitions,
@@ -8053,7 +8056,8 @@ function compileModel(filename, source, options = {}) {
8053
8056
  parserOpts: { plugins: parserPlugins, attachComment: true }
8054
8057
  });
8055
8058
  const ir = babelResult?.metadata?.mindmatrixIR ?? createEmptyModelIR(interfaceName);
8056
- ir.category = options.categoryOverride || annotation.category || "data";
8059
+ const catOverride = options.categoryOverride || annotation.category || "data";
8060
+ ir.category = Array.isArray(catOverride) ? catOverride : [catOverride];
8057
8061
  if (options.slugOverride) {
8058
8062
  ir.slug = options.slugOverride;
8059
8063
  }
@@ -8117,7 +8121,7 @@ function createEmptyModelIR(interfaceName) {
8117
8121
  name: interfaceName,
8118
8122
  version: "1.0.0",
8119
8123
  description: "Data model: " + interfaceName,
8120
- category: "data",
8124
+ category: ["data"],
8121
8125
  fields: [],
8122
8126
  states: [{
8123
8127
  name: "draft",
@@ -8285,7 +8289,7 @@ function buildRouterIR(routes, layouts, allParams, slugPrefix) {
8285
8289
  name: "Router",
8286
8290
  version: "1.0.0",
8287
8291
  description: "Auto-generated router from app/ directory structure",
8288
- category: "router",
8292
+ category: ["router"],
8289
8293
  fields,
8290
8294
  states,
8291
8295
  transitions,
@@ -8534,7 +8538,7 @@ function extractAction(source, filename) {
8534
8538
  slug,
8535
8539
  name: humanName,
8536
8540
  version: "0.1.0",
8537
- category: "action",
8541
+ category: ["action"],
8538
8542
  fields,
8539
8543
  states: states2,
8540
8544
  transitions: transitions2,
@@ -8586,7 +8590,7 @@ function extractAction(source, filename) {
8586
8590
  slug,
8587
8591
  name: humanName,
8588
8592
  version: "0.1.0",
8589
- category: "action",
8593
+ category: ["action"],
8590
8594
  fields,
8591
8595
  states,
8592
8596
  transitions,
@@ -10023,7 +10027,7 @@ function applyConfig(ir, config) {
10023
10027
  if (config.name) ir.name = config.name;
10024
10028
  if (config.version) ir.version = config.version;
10025
10029
  if (config.description !== void 0) ir.description = config.description;
10026
- if (config.category) ir.category = config.category;
10030
+ if (config.category) ir.category = Array.isArray(config.category) ? config.category : [config.category];
10027
10031
  if (!ir.metadata) ir.metadata = {};
10028
10032
  ir.metadata.stable_id = "def-" + ir.slug;
10029
10033
  ir.metadata.provenance = {
@@ -10039,7 +10043,7 @@ function createEmptyIR(config) {
10039
10043
  name: config.name || "Project",
10040
10044
  version: config.version || "0.1.0",
10041
10045
  description: config.description,
10042
- category: config.category || "workflow",
10046
+ category: Array.isArray(config.category) ? config.category : [config.category || "workflow"],
10043
10047
  fields: [],
10044
10048
  states: [{
10045
10049
  name: "draft",
@@ -10197,8 +10201,10 @@ function buildComposedResult(files, fileIRs, config, errors, warnings, options =
10197
10201
  if (isWorkflowFile(filename)) {
10198
10202
  workflowIRs.push(ir);
10199
10203
  } else if (isModelFile2(filename)) {
10200
- if (!ir.category || ir.category === "workflow") {
10201
- ir.category = "data";
10204
+ const cat = ir.category;
10205
+ const isDefault = !cat || cat === "workflow" || Array.isArray(cat) && cat.length === 1 && cat[0] === "workflow";
10206
+ if (isDefault) {
10207
+ ir.category = ["data"];
10202
10208
  }
10203
10209
  modelIRs.push(ir);
10204
10210
  } else if (isServerActionFile2(filename)) {
@@ -12187,7 +12193,8 @@ function determineSplitStrategy(def) {
12187
12193
  const stateCount = def.states.length;
12188
12194
  const hasExperience = !!def.experience || !!def.views?.default;
12189
12195
  const hasChildren = Array.isArray(def.metadata?.childSlugs) && def.metadata.childSlugs.length > 0;
12190
- if (def.category === "blueprint" || hasChildren) {
12196
+ const catArr = Array.isArray(def.category) ? def.category : [def.category];
12197
+ if (catArr.includes("blueprint") || hasChildren) {
12191
12198
  return {
12192
12199
  tier: "large",
12193
12200
  emitModels: fieldCount > 0,
@@ -12474,6 +12481,10 @@ var project_decompiler_exports = {};
12474
12481
  __export(project_decompiler_exports, {
12475
12482
  decompileProjectEnhanced: () => decompileProjectEnhanced
12476
12483
  });
12484
+ function hasCategory(cat, target) {
12485
+ if (!cat) return false;
12486
+ return Array.isArray(cat) ? cat.includes(target) : cat === target;
12487
+ }
12477
12488
  function pascalCase3(slug) {
12478
12489
  return slug.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join("");
12479
12490
  }
@@ -12945,7 +12956,7 @@ function generateLayoutFile(slug, roles) {
12945
12956
  function extractRouteTable(childDefinitions) {
12946
12957
  if (!childDefinitions) return [];
12947
12958
  const router = childDefinitions.find(
12948
- (c) => c.slug.endsWith("-router") || c.category === "router"
12959
+ (c) => c.slug.endsWith("-router") || hasCategory(c.category, "router")
12949
12960
  );
12950
12961
  if (!router?.states) return [];
12951
12962
  return router.states.filter((s) => s.description?.startsWith("Route:")).map((s) => {
@@ -13073,7 +13084,7 @@ function extractPageSections(experience, childDefinitions) {
13073
13084
  if (routerExtracted) return routerExtracted;
13074
13085
  const routeTable = extractRouteTable(childDefinitions);
13075
13086
  const childSlugs = new Set(
13076
- (childDefinitions || []).filter((c) => !c.slug.endsWith("-router") && c.category !== "router").map((c) => c.slug)
13087
+ (childDefinitions || []).filter((c) => !c.slug.endsWith("-router") && !hasCategory(c.category, "router")).map((c) => c.slug)
13077
13088
  );
13078
13089
  const pages = [];
13079
13090
  const childViews = [];
@@ -13242,7 +13253,7 @@ function generatePageFileFromSection(page, modelSlugToPath) {
13242
13253
  slug: page.slug,
13243
13254
  name: page.componentName,
13244
13255
  version: "1.0.0",
13245
- category: "page",
13256
+ category: ["view"],
13246
13257
  states: [],
13247
13258
  transitions: [],
13248
13259
  fields,
@@ -13497,7 +13508,7 @@ function generateComponentFromDefinition(name, experience, props) {
13497
13508
  slug: name.toLowerCase(),
13498
13509
  name,
13499
13510
  version: "1.0.0",
13500
- category: "component",
13511
+ category: ["component"],
13501
13512
  states: [],
13502
13513
  transitions: [],
13503
13514
  fields: [],
@@ -13734,7 +13745,7 @@ function decompileProjectEnhanced(definition) {
13734
13745
  if (definition.childDefinitions) {
13735
13746
  for (const child of definition.childDefinitions) {
13736
13747
  const cs = child.slug;
13737
- if (!cs.endsWith("-router") && child.category !== "router") {
13748
+ if (!cs.endsWith("-router") && !hasCategory(child.category, "router")) {
13738
13749
  modelSlugToPath.set(cs, `models/${cs}.ts`);
13739
13750
  }
13740
13751
  }
@@ -13755,7 +13766,7 @@ function decompileProjectEnhanced(definition) {
13755
13766
  for (const child of definition.childDefinitions) {
13756
13767
  const childSlug = child.slug;
13757
13768
  if (childSlug === slug) continue;
13758
- const isRouter = childSlug.endsWith("-router") || child.category === "router";
13769
+ const isRouter = childSlug.endsWith("-router") || hasCategory(child.category, "router");
13759
13770
  if (isRouter && routerIsCompilerGenerated) continue;
13760
13771
  if (!mergedChildren.has(childSlug)) {
13761
13772
  mergedChildren.set(childSlug, { ...child });
@@ -13810,7 +13821,7 @@ function decompileProjectEnhanced(definition) {
13810
13821
  if (child.version && child.version !== "0.1.0" && (!existing.version || existing.version === "0.1.0")) {
13811
13822
  existing.version = child.version;
13812
13823
  }
13813
- if (child.category && child.category !== "data" && (!existing.category || existing.category === "data")) {
13824
+ if (child.category && !hasCategory(child.category, "data") && (!existing.category || hasCategory(existing.category, "data"))) {
13814
13825
  existing.category = child.category;
13815
13826
  }
13816
13827
  if (child.description && !existing.description) {
@@ -13820,8 +13831,8 @@ function decompileProjectEnhanced(definition) {
13820
13831
  }
13821
13832
  }
13822
13833
  for (const [childSlug, child] of mergedChildren) {
13823
- const isRouter = childSlug.endsWith("-router") || child.category === "router";
13824
- const isAction = child.category === "action";
13834
+ const isRouter = childSlug.endsWith("-router") || hasCategory(child.category, "router");
13835
+ const isAction = hasCategory(child.category, "action");
13825
13836
  const childTransitions = isRouter ? reduceRouterTransitions(child.transitions, child.states) : child.transitions;
13826
13837
  if (isAction) {
13827
13838
  const actionFilePath = `actions/${childSlug}.ts`;
@@ -14078,14 +14089,12 @@ async function deploy(options) {
14078
14089
  }
14079
14090
  async function fetchExistingDefinition(apiUrl, token, slug) {
14080
14091
  try {
14081
- const res = await fetch(`${apiUrl}/workflow/definitions?slug=${encodeURIComponent(slug)}`, {
14092
+ const res = await fetch(`${apiUrl}/workflow/definitions/${encodeURIComponent(slug)}`, {
14082
14093
  headers: { Authorization: `Bearer ${token}` }
14083
14094
  });
14084
14095
  if (!res.ok) return null;
14085
- const data = await res.json();
14086
- const definitions = Array.isArray(data) ? data : data.items ?? data.data;
14087
- if (!definitions || definitions.length === 0) return null;
14088
- const def = definitions[0];
14096
+ const def = await res.json();
14097
+ if (!def || !def.id) return null;
14089
14098
  return {
14090
14099
  id: def.id,
14091
14100
  slug: def.slug,
@@ -14103,7 +14112,7 @@ async function createDefinition(apiUrl, token, ir) {
14103
14112
  "Content-Type": "application/json",
14104
14113
  Authorization: `Bearer ${token}`
14105
14114
  },
14106
- body: JSON.stringify(ir)
14115
+ body: JSON.stringify({ ...ir, visibility: "PUBLIC" })
14107
14116
  });
14108
14117
  if (!res.ok) {
14109
14118
  const errorText = await res.text();
@@ -14883,7 +14892,7 @@ function mindmatrixReact(options) {
14883
14892
  slug: ir.slug,
14884
14893
  name: ir.name,
14885
14894
  version: ir.version,
14886
- category: ir.category || "workflow",
14895
+ category: ir.category || ["workflow"],
14887
14896
  fields: ir.fields || [],
14888
14897
  states: ir.states || [],
14889
14898
  transitions: ir.transitions || [],
@@ -15033,431 +15042,6 @@ var init_vite = __esm({
15033
15042
  }
15034
15043
  });
15035
15044
 
15036
- // src/cli/local-server.ts
15037
- async function startLocalServer(options = {}) {
15038
- const { port = 4200, noAuth = true } = options;
15039
- const store = new MemoryStore();
15040
- const startedAt = (/* @__PURE__ */ new Date()).toISOString();
15041
- function json(res, status, body) {
15042
- const data = JSON.stringify(body);
15043
- res.writeHead(status, {
15044
- "Content-Type": "application/json",
15045
- "Access-Control-Allow-Origin": "*",
15046
- "Access-Control-Allow-Methods": "GET, POST, PATCH, PUT, DELETE, OPTIONS",
15047
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
15048
- "Content-Length": Buffer.byteLength(data)
15049
- });
15050
- res.end(data);
15051
- }
15052
- function readBody(req) {
15053
- return new Promise((resolve7, reject) => {
15054
- const chunks = [];
15055
- req.on("data", (chunk) => chunks.push(chunk));
15056
- req.on("end", () => resolve7(Buffer.concat(chunks).toString()));
15057
- req.on("error", reject);
15058
- });
15059
- }
15060
- function parseQuery(url) {
15061
- const idx = url.indexOf("?");
15062
- if (idx === -1) return {};
15063
- const params = {};
15064
- const qs = url.slice(idx + 1);
15065
- for (const pair of qs.split("&")) {
15066
- const [k, v] = pair.split("=");
15067
- if (k) params[decodeURIComponent(k)] = decodeURIComponent(v ?? "");
15068
- }
15069
- return params;
15070
- }
15071
- const server = http.createServer(async (req, res) => {
15072
- const method = req.method?.toUpperCase() ?? "GET";
15073
- const rawUrl = req.url ?? "/";
15074
- const queryStart = rawUrl.indexOf("?");
15075
- const path = queryStart >= 0 ? rawUrl.slice(0, queryStart) : rawUrl;
15076
- const query = parseQuery(rawUrl);
15077
- if (method === "OPTIONS") {
15078
- res.writeHead(204, {
15079
- "Access-Control-Allow-Origin": "*",
15080
- "Access-Control-Allow-Methods": "GET, POST, PATCH, PUT, DELETE, OPTIONS",
15081
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
15082
- "Access-Control-Max-Age": "86400"
15083
- });
15084
- res.end();
15085
- return;
15086
- }
15087
- try {
15088
- if (path === "/health" && method === "GET") {
15089
- return json(res, 200, {
15090
- status: "ok",
15091
- service: "mm-local-dev",
15092
- mode: "in-memory",
15093
- started_at: startedAt,
15094
- definitions: store.definitions.size,
15095
- instances: store.instances.size
15096
- });
15097
- }
15098
- if (path === "/api/v1/auth/login" && (method === "POST" || method === "GET")) {
15099
- return json(res, 200, {
15100
- token: "dev-token-local",
15101
- user: {
15102
- id: "dev-user-001",
15103
- email: "dev@localhost",
15104
- role: "admin",
15105
- name: "Local Developer"
15106
- }
15107
- });
15108
- }
15109
- if (path === "/api/v1/workflow/definitions" && method === "GET") {
15110
- const result = store.listDefinitions({
15111
- category: query.category,
15112
- limit: query.limit ? parseInt(query.limit, 10) : void 0,
15113
- offset: query.offset ? parseInt(query.offset, 10) : void 0
15114
- });
15115
- if (query.slug) {
15116
- const def = store.getDefinition(query.slug);
15117
- return json(res, 200, { items: def ? [def] : [], total: def ? 1 : 0 });
15118
- }
15119
- return json(res, 200, result);
15120
- }
15121
- const defMatch = path.match(/^\/api\/v1\/workflow\/definitions\/([^/]+)$/);
15122
- if (defMatch && method === "GET") {
15123
- const def = store.getDefinition(defMatch[1]);
15124
- if (!def) return json(res, 404, { error: "Not found" });
15125
- return json(res, 200, def);
15126
- }
15127
- if (path === "/api/v1/workflow/definitions" && method === "POST") {
15128
- const body = JSON.parse(await readBody(req));
15129
- const def = store.createDefinition(body);
15130
- return json(res, 201, def);
15131
- }
15132
- if (defMatch && method === "PATCH") {
15133
- const body = JSON.parse(await readBody(req));
15134
- const updated = store.patchDefinition(defMatch[1], body);
15135
- if (!updated) return json(res, 404, { error: "Not found" });
15136
- return json(res, 200, updated);
15137
- }
15138
- if (defMatch && method === "DELETE") {
15139
- const deleted = store.deleteDefinition(defMatch[1]);
15140
- if (!deleted) return json(res, 404, { error: "Not found" });
15141
- return json(res, 204, null);
15142
- }
15143
- if (path === "/api/v1/workflow/instances" && method === "GET") {
15144
- const result = store.listInstances({
15145
- definition_id: query.definition_id,
15146
- limit: query.limit ? parseInt(query.limit, 10) : void 0,
15147
- offset: query.offset ? parseInt(query.offset, 10) : void 0
15148
- });
15149
- return json(res, 200, result);
15150
- }
15151
- if (path === "/api/v1/workflow/instances" && method === "POST") {
15152
- const body = JSON.parse(await readBody(req));
15153
- const inst = store.createInstance(body);
15154
- if (!inst) return json(res, 404, { error: "Definition not found" });
15155
- return json(res, 201, inst);
15156
- }
15157
- const instMatch = path.match(/^\/api\/v1\/workflow\/instances\/([^/]+)$/);
15158
- if (instMatch && method === "GET") {
15159
- const inst = store.getInstance(instMatch[1]);
15160
- if (!inst) return json(res, 404, { error: "Not found" });
15161
- return json(res, 200, inst);
15162
- }
15163
- if (path === "/api/v1/workflow/execute-action" && method === "POST") {
15164
- const body = JSON.parse(await readBody(req));
15165
- const result = store.executeAction(body);
15166
- return json(res, 200, result);
15167
- }
15168
- const dataMatch = path.match(/^\/api\/v1\/data\/([^/]+)$/);
15169
- if (dataMatch && method === "GET") {
15170
- const def = store.getDefinition(dataMatch[1]);
15171
- if (!def) return json(res, 404, { error: "Not found" });
15172
- const instances = store.listInstances({ definition_id: def.id });
15173
- return json(res, 200, instances);
15174
- }
15175
- if (path.startsWith("/api/v1/")) {
15176
- return json(res, 501, { error: "Not implemented in local dev mode", path, method });
15177
- }
15178
- return json(res, 404, { error: "Not found", path });
15179
- } catch (err) {
15180
- const message = err instanceof Error ? err.message : String(err);
15181
- console.error(`[mm-local] ${method} ${path} \u2014 Error: ${message}`);
15182
- return json(res, 500, { error: message });
15183
- }
15184
- });
15185
- return new Promise((resolve7, reject) => {
15186
- server.on("error", (err) => {
15187
- if (err.code === "EADDRINUSE") {
15188
- reject(new Error(`Port ${port} is already in use. Is another server running?`));
15189
- } else {
15190
- reject(err);
15191
- }
15192
- });
15193
- server.listen(port, () => {
15194
- console.log(`[mm-local] Local API server running at http://localhost:${port}`);
15195
- console.log(`[mm-local] Mode: in-memory (data lost on restart)`);
15196
- console.log(`[mm-local] Auth: disabled (all requests accepted)`);
15197
- resolve7({
15198
- server,
15199
- port,
15200
- store,
15201
- async close() {
15202
- return new Promise((res) => {
15203
- server.close(() => {
15204
- console.log("[mm-local] Local API server stopped");
15205
- res();
15206
- });
15207
- });
15208
- }
15209
- });
15210
- });
15211
- });
15212
- }
15213
- var http, import_node_crypto, MemoryStore;
15214
- var init_local_server = __esm({
15215
- "src/cli/local-server.ts"() {
15216
- "use strict";
15217
- http = __toESM(require("http"));
15218
- import_node_crypto = require("crypto");
15219
- MemoryStore = class {
15220
- constructor() {
15221
- this.definitions = /* @__PURE__ */ new Map();
15222
- this.instances = /* @__PURE__ */ new Map();
15223
- this.slugIndex = /* @__PURE__ */ new Map();
15224
- }
15225
- // slug → id
15226
- // ── Definitions ──────────────────────────────────────────────────────
15227
- createDefinition(input) {
15228
- if (this.slugIndex.has(input.slug)) {
15229
- const existing = this.definitions.get(this.slugIndex.get(input.slug));
15230
- if (existing) return existing;
15231
- }
15232
- const now = (/* @__PURE__ */ new Date()).toISOString();
15233
- const def = {
15234
- id: input.id ?? (0, import_node_crypto.randomUUID)(),
15235
- slug: input.slug,
15236
- name: input.name,
15237
- version: input.version ?? "1.0.0",
15238
- description: input.description ?? null,
15239
- category: input.category ?? "workflow",
15240
- fields: input.fields ?? [],
15241
- states: input.states ?? [],
15242
- transitions: input.transitions ?? [],
15243
- roles: input.roles ?? [],
15244
- experience: input.experience ?? null,
15245
- metadata: input.metadata ?? {},
15246
- child_definitions: input.child_definitions ?? [],
15247
- is_immutable: input.is_immutable ?? false,
15248
- tags: input.tags ?? [],
15249
- inline_tags: input.inline_tags ?? [],
15250
- created_at: now,
15251
- updated_at: now
15252
- };
15253
- this.definitions.set(def.id, def);
15254
- this.slugIndex.set(def.slug, def.id);
15255
- return def;
15256
- }
15257
- getDefinition(idOrSlug) {
15258
- const byId = this.definitions.get(idOrSlug);
15259
- if (byId) return byId;
15260
- const id = this.slugIndex.get(idOrSlug);
15261
- if (id) return this.definitions.get(id);
15262
- return void 0;
15263
- }
15264
- listDefinitions(opts) {
15265
- let items = Array.from(this.definitions.values());
15266
- if (opts?.category) {
15267
- items = items.filter((d) => d.category === opts.category);
15268
- }
15269
- const total = items.length;
15270
- const offset = opts?.offset ?? 0;
15271
- const limit = opts?.limit ?? 50;
15272
- items = items.slice(offset, offset + limit);
15273
- return { items, total };
15274
- }
15275
- patchDefinition(id, patch) {
15276
- const def = this.definitions.get(id);
15277
- if (!def) return void 0;
15278
- Object.assign(def, patch, { updated_at: (/* @__PURE__ */ new Date()).toISOString() });
15279
- return def;
15280
- }
15281
- deleteDefinition(id) {
15282
- const def = this.definitions.get(id);
15283
- if (!def) return false;
15284
- this.slugIndex.delete(def.slug);
15285
- this.definitions.delete(id);
15286
- return true;
15287
- }
15288
- // ── Instances ────────────────────────────────────────────────────────
15289
- createInstance(input) {
15290
- const def = this.getDefinition(input.definition_id) ?? this.getDefinition(input.definition_slug);
15291
- if (!def) return null;
15292
- const initialState = def.states.find((s) => s.type === "START" || s.type === "initial");
15293
- const stateName = initialState?.name ?? "initial";
15294
- const stateData = {};
15295
- for (const field of def.fields) {
15296
- if (field.default_value !== void 0) {
15297
- stateData[field.name] = field.default_value;
15298
- }
15299
- }
15300
- Object.assign(stateData, input.state_data ?? {});
15301
- const now = (/* @__PURE__ */ new Date()).toISOString();
15302
- const inst = {
15303
- id: (0, import_node_crypto.randomUUID)(),
15304
- definition_id: def.id,
15305
- definition_slug: def.slug,
15306
- current_state: stateName,
15307
- state_data: stateData,
15308
- execution_lock_version: 0,
15309
- event_log: [{
15310
- event_type: "transition",
15311
- message: `Instance created in state '${stateName}'`,
15312
- timestamp: now
15313
- }],
15314
- created_at: now,
15315
- updated_at: now
15316
- };
15317
- this.instances.set(inst.id, inst);
15318
- return inst;
15319
- }
15320
- getInstance(id) {
15321
- return this.instances.get(id);
15322
- }
15323
- listInstances(opts) {
15324
- let items = Array.from(this.instances.values());
15325
- if (opts?.definition_id) {
15326
- items = items.filter((i) => i.definition_id === opts.definition_id);
15327
- }
15328
- const total = items.length;
15329
- const offset = opts?.offset ?? 0;
15330
- const limit = opts?.limit ?? 50;
15331
- items = items.slice(offset, offset + limit);
15332
- return { items, total };
15333
- }
15334
- // ── Execute Action (Transition) ──────────────────────────────────────
15335
- executeAction(input) {
15336
- const def = this.getDefinition(input.definition_id);
15337
- if (!def) return { success: false, error: "Definition not found" };
15338
- let inst;
15339
- if (input.instance_id) {
15340
- const existing = this.instances.get(input.instance_id);
15341
- if (!existing) return { success: false, error: "Instance not found" };
15342
- inst = existing;
15343
- } else {
15344
- const created = this.createInstance({
15345
- definition_id: def.id,
15346
- definition_slug: def.slug,
15347
- state_data: input.payload
15348
- });
15349
- if (!created) return { success: false, error: "Failed to create instance" };
15350
- inst = created;
15351
- }
15352
- if (input.payload && input.instance_id) {
15353
- Object.assign(inst.state_data, input.payload);
15354
- }
15355
- const transition = def.transitions.find((t27) => t27.name === input.action_name && t27.from.includes(inst.current_state));
15356
- if (!transition) {
15357
- return {
15358
- success: false,
15359
- instance_id: inst.id,
15360
- from_state: inst.current_state,
15361
- to_state: null,
15362
- state_data: inst.state_data,
15363
- error: `No transition '${input.action_name}' from state '${inst.current_state}'`
15364
- };
15365
- }
15366
- const fromState = inst.current_state;
15367
- const now = (/* @__PURE__ */ new Date()).toISOString();
15368
- const events = [];
15369
- let lastEvalResult = null;
15370
- events.push({ event_type: "transition", message: `Transition '${transition.name}' started: ${fromState} \u2192 ${transition.to}`, timestamp: now });
15371
- for (const action of transition.actions ?? []) {
15372
- try {
15373
- if (action.type === "set_field") {
15374
- const field = action.config?.field;
15375
- if (action.config?.expression) {
15376
- const expr = action.config.expression;
15377
- const result = this.evaluateSimpleExpression(expr, inst.state_data);
15378
- inst.state_data[field] = result;
15379
- } else if (action.config?.value !== void 0) {
15380
- inst.state_data[field] = action.config.value;
15381
- }
15382
- events.push({ event_type: "action_executed", message: `transition action 'set_field' succeeded`, timestamp: now });
15383
- } else if (action.type === "eval") {
15384
- const expr = action.config?.expression;
15385
- lastEvalResult = this.evaluateSimpleExpression(expr, inst.state_data);
15386
- events.push({ event_type: "action_executed", message: `transition action 'eval' succeeded`, timestamp: now });
15387
- } else {
15388
- events.push({ event_type: "action_executed", message: `transition action '${action.type}' succeeded (no-op in local mode)`, timestamp: now });
15389
- }
15390
- } catch (err) {
15391
- const msg = err instanceof Error ? err.message : String(err);
15392
- events.push({ event_type: "action_failed", message: `transition action '${action.type}' failed: ${msg}`, timestamp: now });
15393
- return {
15394
- success: false,
15395
- instance_id: inst.id,
15396
- from_state: fromState,
15397
- to_state: null,
15398
- state_data: inst.state_data,
15399
- event_log: events,
15400
- error: `transition action failed: ${msg}`
15401
- };
15402
- }
15403
- }
15404
- inst.current_state = transition.to;
15405
- inst.execution_lock_version++;
15406
- inst.updated_at = now;
15407
- events.push({ event_type: "transition", message: `State changed: ${fromState} \u2192 ${transition.to}`, timestamp: now });
15408
- inst.event_log.push(...events);
15409
- return {
15410
- success: true,
15411
- instance_id: inst.id,
15412
- from_state: fromState,
15413
- to_state: transition.to,
15414
- state_data: inst.state_data,
15415
- result: lastEvalResult,
15416
- event_log: events
15417
- };
15418
- }
15419
- /**
15420
- * Minimal expression evaluator for local dev mode.
15421
- * Handles: field references, arithmetic, string literals, simple comparisons.
15422
- * Does NOT handle: while loops, if/else, function calls, multi-statement blocks.
15423
- * For full evaluation, use mm-napi when available.
15424
- */
15425
- evaluateSimpleExpression(expr, context) {
15426
- if (context[expr] !== void 0) return context[expr];
15427
- const arithMatch = expr.match(/^(\w+)\s*([+\-*/])\s*(\d+(?:\.\d+)?)$/);
15428
- if (arithMatch) {
15429
- const [, field, op, numStr] = arithMatch;
15430
- const left = Number(context[field] ?? 0);
15431
- const right = Number(numStr);
15432
- switch (op) {
15433
- case "+":
15434
- return left + right;
15435
- case "-":
15436
- return left - right;
15437
- case "*":
15438
- return left * right;
15439
- case "/":
15440
- return right !== 0 ? left / right : 0;
15441
- }
15442
- }
15443
- if (/^\d+(\.\d+)?$/.test(expr.trim())) {
15444
- return Number(expr.trim());
15445
- }
15446
- const strMatch = expr.match(/^["'](.*)["']$/);
15447
- if (strMatch) return strMatch[1];
15448
- try {
15449
- const keys = Object.keys(context);
15450
- const values = Object.values(context);
15451
- const fn = new Function(...keys, `"use strict"; return (${expr});`);
15452
- return fn(...values);
15453
- } catch {
15454
- return null;
15455
- }
15456
- }
15457
- };
15458
- }
15459
- });
15460
-
15461
15045
  // src/cli/seed.ts
15462
15046
  var seed_exports = {};
15463
15047
  __export(seed_exports, {
@@ -15797,34 +15381,33 @@ async function createDevServer(options = {}) {
15797
15381
  open = false
15798
15382
  } = options;
15799
15383
  const clients = /* @__PURE__ */ new Set();
15800
- let localServer = null;
15801
15384
  let apiUrl;
15802
- let isLocalMode = false;
15803
- if (rawApiUrl === "local") {
15804
- localServer = await startLocalServer({ port: 4200 });
15805
- apiUrl = "http://localhost:4200/api/v1";
15806
- isLocalMode = true;
15807
- } else if (rawApiUrl === "auto") {
15808
- const defaultRemote = "https://dev.mindmatrix.club/api/v1";
15809
- const remoteHealth = await checkBackendHealth(defaultRemote);
15810
- if (remoteHealth.ok) {
15811
- apiUrl = defaultRemote;
15812
- } else {
15813
- const localHealth = await checkBackendHealth("http://localhost:4200/api/v1");
15814
- if (localHealth.ok) {
15815
- apiUrl = "http://localhost:4200/api/v1";
15385
+ if (rawApiUrl === "local" || rawApiUrl === "auto") {
15386
+ const localHealth = await checkBackendHealth("http://localhost:4200/api/v1");
15387
+ if (localHealth.ok) {
15388
+ apiUrl = "http://localhost:4200/api/v1";
15389
+ } else if (rawApiUrl === "auto") {
15390
+ const defaultRemote = "https://dev.mindmatrix.club/api/v1";
15391
+ const remoteHealth = await checkBackendHealth(defaultRemote);
15392
+ if (remoteHealth.ok) {
15393
+ apiUrl = defaultRemote;
15816
15394
  } else {
15817
- console.log("[mm-dev] No backend detected \u2014 starting local in-memory API server...");
15818
- localServer = await startLocalServer({ port: 4200 });
15395
+ console.error("[mm-dev] No backend detected. Start the engine first:");
15396
+ console.error(" mmrc dev (auto-starts engine)");
15397
+ console.error(" mmrc engine start (manual start)");
15398
+ console.error("[mm-dev] Or specify a remote API: mmrc dev --api-url https://...");
15819
15399
  apiUrl = "http://localhost:4200/api/v1";
15820
- isLocalMode = true;
15821
15400
  }
15401
+ } else {
15402
+ console.error("[mm-dev] Local engine not running on port 4200.");
15403
+ console.error(" Start it with: mmrc dev (or mmrc engine start)");
15404
+ apiUrl = "http://localhost:4200/api/v1";
15822
15405
  }
15823
15406
  } else {
15824
15407
  apiUrl = rawApiUrl;
15825
15408
  }
15826
- const token = isLocalMode ? "dev-token-local" : await resolveDevToken(apiUrl, explicitToken);
15827
- const health = isLocalMode ? { ok: true, version: "local", db: "in-memory" } : await checkBackendHealth(apiUrl);
15409
+ const token = await resolveDevToken(apiUrl, explicitToken);
15410
+ const health = await checkBackendHealth(apiUrl);
15828
15411
  let initialSlug, initialCompiled = 0, initialDeployed = false, initialErrors = 0;
15829
15412
  if (token && health.ok) {
15830
15413
  const r = await initialBuildDeploy(src, outDir, mode, apiUrl, token);
@@ -15884,63 +15467,169 @@ createRoot(document.getElementById('root')).render(React.createElement(App));
15884
15467
  `;
15885
15468
  }
15886
15469
  };
15887
- const devHtml = `<!DOCTYPE html>
15470
+ const { existsSync: fsExists } = await import("fs");
15471
+ const projectIndexPath = require("path").join(process.cwd(), "index.html");
15472
+ const hasProjectIndex = fsExists(projectIndexPath);
15473
+ if (!hasProjectIndex) {
15474
+ const { writeFileSync: fsWrite } = await import("fs");
15475
+ fsWrite(projectIndexPath, `<!DOCTYPE html>
15888
15476
  <html lang="en">
15889
15477
  <head>
15890
15478
  <meta charset="UTF-8">
15891
15479
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
15892
15480
  <title>MindMatrix Dev</title>
15893
- <style>
15894
- * { margin: 0; padding: 0; box-sizing: border-box; }
15895
- body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #fafafa; color: #111; }
15896
- #root { min-height: 100vh; }
15897
- .mm-loading { display: flex; align-items: center; justify-content: center; min-height: 100vh; flex-direction: column; gap: 12px; }
15898
- .mm-loading h1 { font-size: 20px; font-weight: 600; color: #333; }
15899
- .mm-loading p { color: #888; font-size: 14px; }
15900
- </style>
15901
15481
  </head>
15902
15482
  <body>
15903
- <div id="root">
15904
- <div class="mm-loading">
15905
- <h1>MindMatrix Dev</h1>
15906
- <p>Loading...</p>
15907
- </div>
15908
- </div>
15909
- <script>
15910
- async function render() {
15911
- const root = document.getElementById('root');
15483
+ <div id="root"></div>
15484
+ <script type="module" src="/__mm_dev_entry.tsx"></script>
15485
+ </body>
15486
+ </html>`, "utf-8");
15487
+ }
15488
+ const devEntryPlugin = {
15489
+ name: "mindmatrix-dev-entry",
15490
+ resolveId(id) {
15491
+ if (id === "/__mm_dev_entry.tsx") return id;
15492
+ return null;
15493
+ },
15494
+ load(id) {
15495
+ if (id !== "/__mm_dev_entry.tsx") return null;
15496
+ return `
15497
+ import React, { useState, useEffect } from 'react';
15498
+ import { createRoot } from 'react-dom/client';
15499
+ import { DevPlayer } from '@mmapp/react/player';
15500
+ import { PlayerProvider, ExperienceRenderer, createApiResolver } from '@mmapp/react/player';
15501
+
15502
+ // Detect API base URL \u2014 proxy through dev server or use direct URL
15503
+ const API_BASE = window.location.origin + '/api/v1';
15504
+ const resolver = createApiResolver({ baseUrl: API_BASE, token: () => localStorage.getItem('auth_token') });
15505
+
15506
+ /**
15507
+ * Prepare experience tree for rendering.
15508
+ * Attaches metadata.dataSources to the root node and remaps
15509
+ * dataSource slugs to use definition-relative naming.
15510
+ *
15511
+ * @param definition - The main blueprint definition
15512
+ * @param allDefinitions - ALL definitions from the API (used for slug resolution)
15513
+ */
15514
+ function prepareTree(definition, allDefinitions) {
15515
+ const exp = definition.experience;
15516
+ if (!exp || typeof exp !== 'object') return null;
15517
+
15518
+ const meta = definition.metadata || {};
15519
+ const dataSources = meta.dataSources || [];
15520
+ const mutationTargets = meta.mutationTargets || [];
15521
+
15522
+ // Build a slug map from ALL deployed definitions
15523
+ // This resolves bare model names (e.g. "item") to full slugs (e.g. "tutorial-1-item")
15524
+ const blueprintSlug = definition.slug || '';
15525
+ const allSlugs = (allDefinitions || []).map(d => d.slug).filter(Boolean);
15526
+
15527
+ function resolveSlug(bareSlug) {
15528
+ if (!bareSlug) return bareSlug;
15529
+ // Already a full slug (contains hyphen matching a known definition)
15530
+ if (allSlugs.includes(bareSlug)) return bareSlug;
15531
+ // Try blueprint-prefixed slug: "item" \u2192 "tutorial-1-item"
15532
+ const prefixed = blueprintSlug + '-' + bareSlug;
15533
+ if (allSlugs.includes(prefixed)) return prefixed;
15534
+ // Try matching by suffix across all definitions
15535
+ const match = allSlugs.find(s => s.endsWith('-' + bareSlug));
15536
+ if (match) return match;
15537
+ // Return original (will attempt as-is)
15538
+ return bareSlug;
15539
+ }
15540
+
15541
+ // Clone root node and attach dataSources
15542
+ const root = { ...exp };
15543
+ if (dataSources.length > 0 && !root.dataSources) {
15544
+ root.dataSources = dataSources.map(ds => ({
15545
+ ...ds,
15546
+ slug: resolveSlug(ds.slug),
15547
+ }));
15548
+ }
15549
+
15550
+ // Remap mutation targets the same way
15551
+ if (mutationTargets.length > 0) {
15552
+ const resolvedTargets = mutationTargets.map(t => resolveSlug(t));
15553
+ root.config = { ...(root.config || {}), _mutationTargets: resolvedTargets };
15554
+ }
15555
+
15556
+ return root;
15557
+ }
15558
+
15559
+ function App() {
15560
+ const [tree, setTree] = useState(null);
15561
+ const [error, setError] = useState(null);
15562
+
15563
+ useEffect(() => {
15564
+ async function loadTree() {
15912
15565
  try {
15913
- const res = await fetch('/api/v1/workflow/definitions');
15566
+ const res = await fetch(API_BASE + '/workflow/definitions');
15914
15567
  const json = await res.json();
15915
- const items = json.items || json.data || [];
15916
- let html = '<div style="padding:40px;max-width:960px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,sans-serif">';
15917
- html += '<h1 style="margin-bottom:8px">MindMatrix Dev</h1>';
15918
- html += '<p style="color:#666;margin-bottom:24px">' + items.length + ' definition(s) deployed</p>';
15919
- if (items.length === 0) {
15920
- html += '<div style="text-align:center;padding:60px 20px;color:#888"><p>No definitions yet.</p><p style="margin-top:8px;font-size:13px">Edit your model files and save.</p></div>';
15921
- }
15922
- for (const def of items) {
15923
- html += '<div style="border:1px solid #e0e0e0;border-radius:8px;padding:16px;margin-bottom:12px;background:#fff">';
15924
- html += '<h3 style="margin-bottom:4px">' + (def.name || def.slug || 'Unnamed') + '</h3>';
15925
- html += '<p style="color:#888;font-size:13px">slug: ' + (def.slug || '?') + ' | states: ' + (def.states?.length || 0) + ' | fields: ' + (def.fields?.length || 0) + '</p>';
15926
- html += '</div>';
15927
- }
15928
- html += '</div>';
15929
- root.innerHTML = html;
15568
+ const rawItems = json.items || json.data || [];
15569
+ // Flatten: API returns { id, slug, definition: { experience, ... } }
15570
+ // Merge definition fields up to the top level for easier access
15571
+ const items = rawItems.map(d => {
15572
+ const def = d.definition || {};
15573
+ return { ...d, ...def, definition: undefined };
15574
+ });
15575
+ // Find the main blueprint definition (has experience tree)
15576
+ const main = items.find(d => d.experience && !(Array.isArray(d.category) ? d.category.includes('data') : d.category === 'data')) || items.find(d => d.experience) || items[0];
15577
+ if (main?.experience) {
15578
+ const prepared = prepareTree(main, items);
15579
+ if (prepared) {
15580
+ setTree(prepared);
15581
+ return;
15582
+ }
15583
+ }
15584
+ if (main) {
15585
+ // No experience tree \u2014 show definition info
15586
+ setTree({
15587
+ type: 'Stack',
15588
+ props: { style: { padding: 40, gap: 16 } },
15589
+ children: [
15590
+ { type: 'Text', props: { children: main.name || main.slug, style: { fontSize: 24, fontWeight: 'bold' } } },
15591
+ { type: 'Text', props: { children: (main.states?.length || 0) + ' states, ' + (main.fields?.length || 0) + ' fields', style: { color: '#666' } } },
15592
+ ...items.map((d, i) => ({
15593
+ type: 'Card',
15594
+ props: { key: i, style: { padding: 16, border: '1px solid #ddd', borderRadius: 8 } },
15595
+ children: [
15596
+ { type: 'Text', props: { children: d.name || d.slug, style: { fontWeight: 600 } } },
15597
+ { type: 'Text', props: { children: 'slug: ' + d.slug + ' | ' + (d.states?.length || 0) + ' states', style: { fontSize: 13, color: '#888' } } },
15598
+ ],
15599
+ })),
15600
+ ],
15601
+ });
15602
+ }
15930
15603
  } catch (e) {
15931
- root.innerHTML = '<div style="padding:40px"><h1>MindMatrix Dev</h1><p style="color:#c00;margin-top:8px">API Error: ' + e.message + '</p></div>';
15604
+ setError(e.message);
15932
15605
  }
15933
15606
  }
15934
- render();
15935
- setInterval(render, 3000);
15936
- </script>
15937
- </body>
15938
- </html>`;
15939
- const { mkdtempSync, writeFileSync: writeFileSync10 } = await import("fs");
15940
- const { join: join8 } = await import("path");
15941
- const { tmpdir } = await import("os");
15942
- const devRoot = mkdtempSync(join8(tmpdir(), "mm-dev-"));
15943
- writeFileSync10(join8(devRoot, "index.html"), devHtml, "utf-8");
15607
+ loadTree();
15608
+ }, []);
15609
+
15610
+ if (error) return React.createElement('div', { style: { padding: 40 } },
15611
+ React.createElement('h1', null, 'MindMatrix Dev'),
15612
+ React.createElement('p', { style: { color: '#c00' } }, 'Error: ' + error)
15613
+ );
15614
+
15615
+ if (!tree) return React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh' } },
15616
+ React.createElement('p', null, 'Loading...')
15617
+ );
15618
+
15619
+ // If tree has 'component' field, use ExperienceRenderer; otherwise legacy DevPlayer
15620
+ if (tree.component) {
15621
+ return React.createElement(PlayerProvider, { resolver },
15622
+ React.createElement(ExperienceRenderer, { tree, className: 'mm-experience-root' })
15623
+ );
15624
+ }
15625
+
15626
+ return React.createElement(DevPlayer, { tree, title: document.title || 'MindMatrix Dev' });
15627
+ }
15628
+
15629
+ createRoot(document.getElementById('root')).render(React.createElement(App));
15630
+ `;
15631
+ }
15632
+ };
15944
15633
  let deployInFlight = false;
15945
15634
  const compileDeployPlugin = {
15946
15635
  name: "mindmatrix-dev-compile-deploy",
@@ -15971,16 +15660,16 @@ createRoot(document.getElementById('root')).render(React.createElement(App));
15971
15660
  }
15972
15661
  };
15973
15662
  const viteConfig = {
15974
- // Physical index.html lives in a temp dir so Vite serves it natively.
15975
- root: devRoot,
15976
- // Pre-bundle React so Vite doesn't warn about missing entry point.
15663
+ // Use the blueprint directory as Vite root (has node_modules with React).
15664
+ root: process.cwd(),
15665
+ // Pre-bundle React and the player for fast dev startup.
15977
15666
  optimizeDeps: { include: ["react", "react-dom/client"] },
15978
15667
  server: {
15979
15668
  port,
15980
15669
  open,
15981
15670
  host: true,
15982
15671
  // Allow serving files from the real project directory (fs.allow).
15983
- fs: { allow: [devRoot, process.cwd(), ".."] },
15672
+ fs: { allow: [process.cwd(), ".."] },
15984
15673
  proxy: {
15985
15674
  "/api": { target: proxyTarget, changeOrigin: true, secure: true, ws: true },
15986
15675
  "/health": { target: proxyTarget, changeOrigin: true, secure: true },
@@ -15990,6 +15679,7 @@ createRoot(document.getElementById('root')).render(React.createElement(App));
15990
15679
  plugins: [
15991
15680
  mindmatrixReact(pluginOpts),
15992
15681
  compileDeployPlugin,
15682
+ devEntryPlugin,
15993
15683
  devPlayerPlugin,
15994
15684
  { name: "mindmatrix-error-overlay", configureServer(server) {
15995
15685
  server.middlewares.use(errorOverlayMiddleware());
@@ -15997,8 +15687,8 @@ createRoot(document.getElementById('root')).render(React.createElement(App));
15997
15687
  ],
15998
15688
  logLevel: "warn"
15999
15689
  };
16000
- const { createServer: createServer2 } = await import("vite");
16001
- const vite = await createServer2(viteConfig);
15690
+ const { createServer } = await import("vite");
15691
+ const vite = await createServer(viteConfig);
16002
15692
  await vite.listen();
16003
15693
  const resolvedPort = vite.config.server.port ?? port;
16004
15694
  if (enableWs && vite.httpServer) {
@@ -16016,7 +15706,7 @@ createRoot(document.getElementById('root')).render(React.createElement(App));
16016
15706
  } catch {
16017
15707
  }
16018
15708
  }
16019
- printBanner({ port: resolvedPort, apiUrl: isLocalMode ? `${apiUrl} (local in-memory)` : apiUrl, src, include, health, token: !!token, compiled: initialCompiled, deployed: initialDeployed, slug: initialSlug, errors: initialErrors });
15709
+ printBanner({ port: resolvedPort, apiUrl, src, include, health, token: !!token, compiled: initialCompiled, deployed: initialDeployed, slug: initialSlug, errors: initialErrors });
16020
15710
  return {
16021
15711
  vite,
16022
15712
  port: resolvedPort,
@@ -16048,14 +15738,6 @@ createRoot(document.getElementById('root')).render(React.createElement(App));
16048
15738
  clients.clear();
16049
15739
  currentErrors = null;
16050
15740
  await vite.close();
16051
- if (localServer) {
16052
- await localServer.close();
16053
- }
16054
- try {
16055
- const { rmSync } = await import("fs");
16056
- rmSync(devRoot, { recursive: true, force: true });
16057
- } catch {
16058
- }
16059
15741
  }
16060
15742
  };
16061
15743
  }
@@ -16065,7 +15747,6 @@ var init_dev_server = __esm({
16065
15747
  "use strict";
16066
15748
  init_vite();
16067
15749
  init_build();
16068
- init_local_server();
16069
15750
  currentErrors = null;
16070
15751
  }
16071
15752
  });
@@ -16374,99 +16055,10 @@ function generateIndexHtml(name) {
16374
16055
  <meta charset="UTF-8">
16375
16056
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
16376
16057
  <title>${title} \u2014 MindMatrix Dev</title>
16377
- <style>
16378
- * { margin: 0; padding: 0; box-sizing: border-box; }
16379
- body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #fafafa; color: #111; }
16380
- #root { min-height: 100vh; padding: 40px; max-width: 960px; margin: 0 auto; }
16381
- h1 { font-size: 24px; margin-bottom: 8px; }
16382
- .subtitle { color: #666; margin-bottom: 24px; }
16383
- .card { border: 1px solid #e0e0e0; border-radius: 8px; padding: 16px; margin-bottom: 12px; background: #fff; }
16384
- .card h3 { font-size: 16px; margin-bottom: 4px; }
16385
- .card .meta { color: #888; font-size: 13px; }
16386
- .badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 600; }
16387
- .badge-state { background: #e3f2fd; color: #1565c0; }
16388
- .empty { text-align: center; padding: 60px 20px; color: #888; }
16389
- .error { color: #c62828; background: #ffebee; padding: 16px; border-radius: 8px; margin-bottom: 16px; }
16390
- .actions { display: flex; gap: 8px; margin-top: 8px; }
16391
- .btn { padding: 6px 12px; border-radius: 4px; border: 1px solid #ddd; background: #fff; cursor: pointer; font-size: 13px; }
16392
- .btn:hover { background: #f5f5f5; }
16393
- .btn-primary { background: #1976d2; color: #fff; border-color: #1976d2; }
16394
- .btn-primary:hover { background: #1565c0; }
16395
- </style>
16396
16058
  </head>
16397
16059
  <body>
16398
- <div id="root">
16399
- <h1>${title}</h1>
16400
- <p class="subtitle">Loading...</p>
16401
- </div>
16402
- <script type="module">
16403
- const API = '/api/v1';
16404
-
16405
- async function api(path, opts) {
16406
- const res = await fetch(API + path, {
16407
- headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer dev-local' },
16408
- ...opts,
16409
- });
16410
- return res.json();
16411
- }
16412
-
16413
- async function render() {
16414
- const root = document.getElementById('root');
16415
- try {
16416
- const defs = await api('/workflow/definitions');
16417
- const items = defs.items || defs.data || [];
16418
-
16419
- let html = '<h1>${title}</h1>';
16420
- html += '<p class="subtitle">' + items.length + ' definition(s) deployed</p>';
16421
-
16422
- if (items.length === 0) {
16423
- html += '<div class="empty"><p>No definitions deployed yet.</p><p style="margin-top:8px;font-size:13px">Edit your model files and save \u2014 mmrc dev will auto-compile and deploy.</p></div>';
16424
- }
16425
-
16426
- for (const def of items) {
16427
- const states = def.states || [];
16428
- const fields = def.fields || [];
16429
- const transitions = def.transitions || [];
16430
- html += '<div class="card">';
16431
- html += '<h3>' + (def.name || def.slug || 'Unnamed') + '</h3>';
16432
- html += '<p class="meta">' + states.length + ' states \xB7 ' + fields.length + ' fields \xB7 ' + transitions.length + ' transitions</p>';
16433
- html += '<div class="actions">';
16434
- html += '<button class="btn btn-primary" onclick="createInstance(\\'' + def.slug + '\\')">+ Create Instance</button>';
16435
- html += '</div>';
16436
- html += '</div>';
16437
- }
16438
-
16439
- // Show instances
16440
- const instances = await api('/workflow/instances');
16441
- const instItems = instances.items || instances.data || [];
16442
- if (instItems.length > 0) {
16443
- html += '<h2 style="margin-top:32px;margin-bottom:12px">Instances (' + instItems.length + ')</h2>';
16444
- for (const inst of instItems) {
16445
- html += '<div class="card">';
16446
- html += '<h3>' + (inst.definition_slug || '?') + ' <span class="badge badge-state">' + (inst.current_state || inst.state || '?') + '</span></h3>';
16447
- html += '<p class="meta">ID: ' + inst.id + '</p>';
16448
- html += '</div>';
16449
- }
16450
- }
16451
-
16452
- root.innerHTML = html;
16453
- } catch (e) {
16454
- root.innerHTML = '<h1>${title}</h1><div class="error">API Error: ' + e.message + '</div>';
16455
- }
16456
- }
16457
-
16458
- window.createInstance = async function(slug) {
16459
- await api('/workflow/instances', {
16460
- method: 'POST',
16461
- body: JSON.stringify({ definition_slug: slug, state_data: { title: 'New Item', priority: 'medium' } }),
16462
- });
16463
- render();
16464
- };
16465
-
16466
- render();
16467
- // Auto-refresh every 3 seconds
16468
- setInterval(render, 3000);
16469
- </script>
16060
+ <div id="root"></div>
16061
+ <script type="module" src="/__mm_dev_entry.tsx"></script>
16470
16062
  </body>
16471
16063
  </html>`;
16472
16064
  }
@@ -16625,37 +16217,48 @@ function generatePage(name) {
16625
16217
  const title = toTitleCase(name);
16626
16218
  const pascal = toPascalCase2(name);
16627
16219
  return `/**
16628
- * @workflow slug="${name}-home" version="1.0.0" category="page"
16220
+ * @workflow slug="${name}-home" version="1.0.0" category="view"
16629
16221
  *
16630
- * Index page \u2014 lists items with create and search.
16222
+ * Index page \u2014 full CRUD with create form, search, transitions, and delete.
16631
16223
  */
16632
16224
 
16633
16225
  import { useState } from 'react';
16634
16226
  import itemModel from '../models/item';
16635
16227
  import {
16636
16228
  useQuery, useMutation, useRouter,
16637
- Stack, Row, Text, Button, Icon, Card, Show, TextInput, Badge,
16229
+ Stack, Row, Text, Button, Icon, Card, Show, TextInput, Badge, Select,
16638
16230
  } from '@mmapp/react';
16639
16231
 
16640
- const PRIORITY_COLORS: Record<string, string> = {
16641
- high: 'token:error',
16642
- medium: 'token:warning',
16643
- low: 'token:success',
16644
- };
16645
-
16646
16232
  export default function ${pascal}Home() {
16647
16233
  const { data: items, loading } = useQuery(itemModel);
16648
16234
  const mutation = useMutation(itemModel);
16649
16235
  const router = useRouter();
16650
16236
  const [search, setSearch] = useState('');
16237
+ const [showCreate, setShowCreate] = useState(false);
16238
+ const [newTitle, setNewTitle] = useState('');
16239
+ const [newDescription, setNewDescription] = useState('');
16240
+ const [newPriority, setNewPriority] = useState('medium');
16241
+ const [editingId, setEditingId] = useState<string | null>(null);
16242
+ const [editTitle, setEditTitle] = useState('');
16651
16243
 
16652
- const activeItems = items.filter(i => i.state !== 'archived');
16653
- const filtered = activeItems.filter(i =>
16654
- i.fields.title.toLowerCase().includes(search.toLowerCase())
16655
- );
16244
+ const visibleItems = items.filter(i => i.state !== 'archived');
16245
+ const filtered = search
16246
+ ? visibleItems.filter(i => i.fields.title.toLowerCase().includes(search.toLowerCase()))
16247
+ : visibleItems;
16656
16248
 
16657
16249
  const handleCreate = async () => {
16658
- await mutation.create({ title: 'New Item', priority: 'medium' });
16250
+ if (!newTitle.trim()) return;
16251
+ await mutation.create({ title: newTitle.trim(), description: newDescription, priority: newPriority });
16252
+ setNewTitle('');
16253
+ setNewDescription('');
16254
+ setNewPriority('medium');
16255
+ setShowCreate(false);
16256
+ };
16257
+
16258
+ const handleUpdate = async (id: string) => {
16259
+ if (!editTitle.trim()) return;
16260
+ await mutation.update(id, { title: editTitle.trim() });
16261
+ setEditingId(null);
16659
16262
  };
16660
16263
 
16661
16264
  return (
@@ -16664,9 +16267,9 @@ export default function ${pascal}Home() {
16664
16267
  <Row justify="space-between" align="center">
16665
16268
  <Stack gap={4}>
16666
16269
  <Text variant="h3" weight="bold" value="${title}" />
16667
- <Text variant="muted" size="sm" value={\`\${activeItems.length} items\`} />
16270
+ <Text variant="muted" size="sm" value={\`\${visibleItems.length} item\${visibleItems.length !== 1 ? 's' : ''}\`} />
16668
16271
  </Stack>
16669
- <Button variant="primary" onPress={handleCreate}>
16272
+ <Button variant="primary" onPress={() => setShowCreate(true)}>
16670
16273
  <Row gap={6} align="center">
16671
16274
  <Icon name="plus" size={16} />
16672
16275
  <Text value="Add Item" />
@@ -16674,6 +16277,42 @@ export default function ${pascal}Home() {
16674
16277
  </Button>
16675
16278
  </Row>
16676
16279
 
16280
+ {/* Create form */}
16281
+ <Show when={showCreate}>
16282
+ <Card padding={16}>
16283
+ <Stack gap={12}>
16284
+ <Text weight="bold" value="New Item" />
16285
+ <TextInput
16286
+ value={newTitle}
16287
+ onChange={setNewTitle}
16288
+ placeholder="Item title (required)"
16289
+ />
16290
+ <TextInput
16291
+ value={newDescription}
16292
+ onChange={setNewDescription}
16293
+ placeholder="Description (optional)"
16294
+ />
16295
+ <Select
16296
+ value={newPriority}
16297
+ onChange={setNewPriority}
16298
+ options={[
16299
+ { label: 'Low', value: 'low' },
16300
+ { label: 'Medium', value: 'medium' },
16301
+ { label: 'High', value: 'high' },
16302
+ ]}
16303
+ />
16304
+ <Row gap={8} justify="flex-end">
16305
+ <Button variant="ghost" onPress={() => setShowCreate(false)}>
16306
+ <Text value="Cancel" />
16307
+ </Button>
16308
+ <Button variant="primary" onPress={handleCreate}>
16309
+ <Text value="Create" />
16310
+ </Button>
16311
+ </Row>
16312
+ </Stack>
16313
+ </Card>
16314
+ </Show>
16315
+
16677
16316
  {/* Search */}
16678
16317
  <TextInput
16679
16318
  value={search}
@@ -16695,10 +16334,12 @@ export default function ${pascal}Home() {
16695
16334
  <Card padding={32}>
16696
16335
  <Stack align="center" gap={12}>
16697
16336
  <Icon name="inbox" size={40} color="token:muted" />
16698
- <Text variant="muted" value="No items yet" />
16699
- <Button variant="outline" onPress={handleCreate}>
16700
- <Text value="Create your first item" />
16701
- </Button>
16337
+ <Text variant="muted" value={search ? 'No items match your search' : 'No items yet'} />
16338
+ <Show when={!search}>
16339
+ <Button variant="outline" onPress={() => setShowCreate(true)}>
16340
+ <Text value="Create your first item" />
16341
+ </Button>
16342
+ </Show>
16702
16343
  </Stack>
16703
16344
  </Card>
16704
16345
  </Show>
@@ -16710,13 +16351,48 @@ export default function ${pascal}Home() {
16710
16351
  <Card key={item.id} padding={12}>
16711
16352
  <Row align="center" gap={12}>
16712
16353
  <Stack flex={1} gap={2}>
16713
- <Text weight="medium" value={item.fields.title} />
16714
- <Show when={!!item.fields.description}>
16715
- <Text size="sm" variant="muted" value={item.fields.description} />
16354
+ <Show when={editingId === item.id}>
16355
+ <Row gap={8}>
16356
+ <TextInput
16357
+ value={editTitle}
16358
+ onChange={setEditTitle}
16359
+ placeholder="Title"
16360
+ />
16361
+ <Button variant="primary" size="sm" onPress={() => handleUpdate(item.id)}>
16362
+ <Text value="Save" />
16363
+ </Button>
16364
+ <Button variant="ghost" size="sm" onPress={() => setEditingId(null)}>
16365
+ <Text value="Cancel" />
16366
+ </Button>
16367
+ </Row>
16368
+ </Show>
16369
+ <Show when={editingId !== item.id}>
16370
+ <Text
16371
+ weight="medium"
16372
+ value={item.fields.title}
16373
+ onPress={() => { setEditingId(item.id); setEditTitle(item.fields.title); }}
16374
+ />
16375
+ <Show when={!!item.fields.description}>
16376
+ <Text size="sm" variant="muted" value={item.fields.description} />
16377
+ </Show>
16716
16378
  </Show>
16717
16379
  </Stack>
16718
16380
  <Badge value={item.fields.priority} />
16719
16381
  <Badge value={item.state} variant={item.state === 'active' ? 'success' : 'default'} />
16382
+ {/* Transition buttons */}
16383
+ <Show when={item.state === 'draft'}>
16384
+ <Button variant="outline" size="sm" onPress={() => mutation.transition(item.id, 'activate')}>
16385
+ <Text value="Activate" />
16386
+ </Button>
16387
+ </Show>
16388
+ <Show when={item.state === 'active'}>
16389
+ <Button variant="ghost" size="sm" onPress={() => mutation.transition(item.id, 'archive')}>
16390
+ <Text value="Archive" />
16391
+ </Button>
16392
+ </Show>
16393
+ <Button variant="ghost" size="sm" onPress={() => mutation.remove(item.id)}>
16394
+ <Icon name="trash" size={14} color="token:error" />
16395
+ </Button>
16720
16396
  </Row>
16721
16397
  </Card>
16722
16398
  ))}