@mmapp/react-compiler 0.1.0-alpha.17 → 0.1.0-alpha.19

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 (190) 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-4HX4PI4R.mjs +734 -0
  17. package/dist/chunk-4VU56NTZ.mjs +544 -0
  18. package/dist/chunk-4XHK6FWL.mjs +2058 -0
  19. package/dist/chunk-52C2JKH2.mjs +186 -0
  20. package/dist/chunk-52XHYD2V.mjs +214 -0
  21. package/dist/chunk-5GUFFFGL.mjs +148 -0
  22. package/dist/chunk-5N2FDDS6.mjs +214 -0
  23. package/dist/chunk-5ZSJXWZT.mjs +1646 -0
  24. package/dist/chunk-6CQOAAMV.mjs +1803 -0
  25. package/dist/chunk-6SEVAAVT.mjs +3516 -0
  26. package/dist/chunk-6YLR5ZDA.mjs +2829 -0
  27. package/dist/chunk-77UJB356.mjs +244 -0
  28. package/dist/chunk-7QOAJPQF.mjs +774 -0
  29. package/dist/chunk-A5RCMIBP.mjs +1795 -0
  30. package/dist/chunk-A63R3GKH.mjs +1803 -0
  31. package/dist/chunk-ABNTZXF5.mjs +951 -0
  32. package/dist/chunk-ADHWW56I.mjs +214 -0
  33. package/dist/chunk-AOGY2GK6.mjs +3292 -0
  34. package/dist/chunk-AXXUXRNA.mjs +1434 -0
  35. package/dist/chunk-BZLU5YK5.mjs +1025 -0
  36. package/dist/chunk-C7XCDDBH.mjs +1802 -0
  37. package/dist/chunk-CHLVKMQW.mjs +175 -0
  38. package/dist/chunk-CKGOZAB7.mjs +939 -0
  39. package/dist/chunk-CSXYDIVC.mjs +214 -0
  40. package/dist/chunk-CXTV4VGG.mjs +148 -0
  41. package/dist/chunk-D34RAZUX.mjs +2223 -0
  42. package/dist/chunk-DDIC7WM6.mjs +3127 -0
  43. package/dist/chunk-DPUQOBU6.mjs +4810 -0
  44. package/dist/chunk-E6MDVTGT.mjs +148 -0
  45. package/dist/chunk-EGKMUEM6.mjs +544 -0
  46. package/dist/chunk-EO6SYNCG.mjs +175 -0
  47. package/dist/chunk-EQGA6A6D.mjs +121 -0
  48. package/dist/chunk-EY2CSXYA.mjs +822 -0
  49. package/dist/chunk-FF5BQVII.mjs +148 -0
  50. package/dist/chunk-FIQ65CDR.mjs +925 -0
  51. package/dist/chunk-FOZXJFAR.mjs +186 -0
  52. package/dist/chunk-G2IAZ5Q6.mjs +148 -0
  53. package/dist/chunk-G7SMOWOL.mjs +828 -0
  54. package/dist/chunk-GK7NU6DO.mjs +214 -0
  55. package/dist/chunk-GMW45YVD.mjs +868 -0
  56. package/dist/chunk-HDSCPEHY.mjs +4061 -0
  57. package/dist/chunk-HJELFNEA.mjs +186 -0
  58. package/dist/chunk-HOIUP6IF.mjs +690 -0
  59. package/dist/chunk-HRJGDPNE.mjs +148 -0
  60. package/dist/chunk-I3AU7GRD.mjs +120 -0
  61. package/dist/chunk-I3VQQJZ6.mjs +2843 -0
  62. package/dist/chunk-IPTX5MJU.mjs +3223 -0
  63. package/dist/chunk-ITGUSH2Z.mjs +2783 -0
  64. package/dist/chunk-IXHBCAMF.mjs +3306 -0
  65. package/dist/chunk-J7JUAHS4.mjs +186 -0
  66. package/dist/chunk-J7TWJ3TM.mjs +2784 -0
  67. package/dist/chunk-JDPLDGVF.mjs +4810 -0
  68. package/dist/chunk-JK72MQ4N.mjs +877 -0
  69. package/dist/chunk-K2HHCAS2.mjs +148 -0
  70. package/dist/chunk-K5HX2SVL.mjs +1902 -0
  71. package/dist/chunk-KAUEQ2F3.mjs +148 -0
  72. package/dist/chunk-KFVVOS5N.mjs +925 -0
  73. package/dist/chunk-KIH4AN3Y.mjs +154 -0
  74. package/dist/chunk-KPDMN5IX.mjs +175 -0
  75. package/dist/chunk-LZL2IRCH.mjs +186 -0
  76. package/dist/chunk-MIZV3TAN.mjs +3293 -0
  77. package/dist/chunk-MRH4J7IX.mjs +2846 -0
  78. package/dist/chunk-NKBL5GUC.mjs +186 -0
  79. package/dist/chunk-NQCNSCF6.mjs +148 -0
  80. package/dist/chunk-NRP5J3BR.mjs +4811 -0
  81. package/dist/chunk-NTB7OEX2.mjs +2918 -0
  82. package/dist/chunk-NUPJYPFU.mjs +801 -0
  83. package/dist/chunk-NVQUTSQX.mjs +3128 -0
  84. package/dist/chunk-OGMG64EY.mjs +148 -0
  85. package/dist/chunk-OL5B2HTH.mjs +175 -0
  86. package/dist/chunk-OPJKP747.mjs +7506 -0
  87. package/dist/chunk-OQLGGBNE.mjs +2918 -0
  88. package/dist/chunk-OW4AQUDL.mjs +544 -0
  89. package/dist/chunk-OWI6XWCD.mjs +3375 -0
  90. package/dist/chunk-OZT2EAF2.mjs +2776 -0
  91. package/dist/chunk-PBRBRKIQ.mjs +175 -0
  92. package/dist/chunk-PRUMNNDI.mjs +3192 -0
  93. package/dist/chunk-QPNHDTSM.mjs +4839 -0
  94. package/dist/chunk-RNEIAJDR.mjs +897 -0
  95. package/dist/chunk-RY7POBNT.mjs +3127 -0
  96. package/dist/chunk-S6FJ4DXL.mjs +1813 -0
  97. package/dist/chunk-SU4E6E7B.mjs +3153 -0
  98. package/dist/chunk-SYUUKW5A.mjs +3379 -0
  99. package/dist/chunk-THB5SX2S.mjs +113 -0
  100. package/dist/chunk-THFYE5ZX.mjs +244 -0
  101. package/dist/chunk-TK3QMXIP.mjs +2921 -0
  102. package/dist/chunk-TRR2NPAV.mjs +248 -0
  103. package/dist/chunk-TTTTOT7P.mjs +1803 -0
  104. package/dist/chunk-TXONBY6A.mjs +7509 -0
  105. package/dist/chunk-U2PX3JSY.mjs +1933 -0
  106. package/dist/chunk-UASOWKDI.mjs +186 -0
  107. package/dist/chunk-UDDTWG5J.mjs +734 -0
  108. package/dist/chunk-UIWLAQCL.mjs +175 -0
  109. package/dist/chunk-UL2XZEMA.mjs +3128 -0
  110. package/dist/chunk-US3AVDAI.mjs +3456 -0
  111. package/dist/chunk-V5DIDOTT.mjs +148 -0
  112. package/dist/chunk-VLTKQDJ3.mjs +244 -0
  113. package/dist/chunk-VVC42PTS.mjs +175 -0
  114. package/dist/chunk-VX3T3NIR.mjs +897 -0
  115. package/dist/chunk-WBYMW4NQ.mjs +3450 -0
  116. package/dist/chunk-XMWUHQVV.mjs +939 -0
  117. package/dist/chunk-Y6FXYEAI.mjs +10 -0
  118. package/dist/chunk-YCWC2SCO.mjs +148 -0
  119. package/dist/chunk-YFS6JMYO.mjs +3342 -0
  120. package/dist/chunk-ZE67HOSN.mjs +148 -0
  121. package/dist/chunk-ZSK5TPIV.mjs +148 -0
  122. package/dist/cli/index.js +317 -656
  123. package/dist/cli/index.mjs +8 -8
  124. package/dist/config-PL24KEWL.mjs +219 -0
  125. package/dist/dev-server-Bs_sz2DG.d.mts +111 -0
  126. package/dist/dev-server-Bs_sz2DG.d.ts +111 -0
  127. package/dist/dev-server-RmGHIntF.d.mts +113 -0
  128. package/dist/dev-server-RmGHIntF.d.ts +113 -0
  129. package/dist/dev-server.js +194 -526
  130. package/dist/dev-server.mjs +4 -4
  131. package/dist/engine-binary-KQB23JDR.mjs +97 -0
  132. package/dist/envelope-DD7v0v6E.d.mts +265 -0
  133. package/dist/envelope-DD7v0v6E.d.ts +265 -0
  134. package/dist/envelope.js +9 -6
  135. package/dist/envelope.mjs +2 -2
  136. package/dist/index-B5gSgvnd.d.mts +44 -0
  137. package/dist/index-B5gSgvnd.d.ts +44 -0
  138. package/dist/index-Bs0MnR54.d.mts +103 -0
  139. package/dist/index-Bs0MnR54.d.ts +103 -0
  140. package/dist/index-DR0nNc_f.d.mts +101 -0
  141. package/dist/index-DR0nNc_f.d.ts +101 -0
  142. package/dist/index-revho_gS.d.mts +104 -0
  143. package/dist/index-revho_gS.d.ts +104 -0
  144. package/dist/index-vQjwYekL.d.mts +104 -0
  145. package/dist/index-vQjwYekL.d.ts +104 -0
  146. package/dist/index.d.mts +2 -2
  147. package/dist/index.d.ts +2 -2
  148. package/dist/index.js +198 -528
  149. package/dist/index.mjs +9 -8
  150. package/dist/init-2XLTUF7O.mjs +407 -0
  151. package/dist/init-7FJENUDK.mjs +407 -0
  152. package/dist/init-DQDX3QK6.mjs +369 -0
  153. package/dist/init-K3GVM4JS.mjs +538 -0
  154. package/dist/init-NXS5BJN3.mjs +454 -0
  155. package/dist/init-UC3FWPIW.mjs +367 -0
  156. package/dist/init-UNV5XIDE.mjs +367 -0
  157. package/dist/project-compiler-2HOPO7GC.mjs +10 -0
  158. package/dist/project-compiler-D245L5QR.mjs +10 -0
  159. package/dist/project-compiler-FUQRMB4X.mjs +10 -0
  160. package/dist/project-compiler-LA5OBI5P.mjs +10 -0
  161. package/dist/project-compiler-OP2VVGJQ.mjs +10 -0
  162. package/dist/project-compiler-PZNFE6AH.mjs +10 -0
  163. package/dist/project-compiler-QBVF6MFI.mjs +10 -0
  164. package/dist/project-compiler-VFR6CSDX.mjs +10 -0
  165. package/dist/project-compiler-WMJZA4UH.mjs +10 -0
  166. package/dist/project-compiler-XXS27TZT.mjs +10 -0
  167. package/dist/project-compiler-YYGDSHY5.mjs +10 -0
  168. package/dist/project-decompiler-5GY2KSG4.mjs +7 -0
  169. package/dist/project-decompiler-7I2OMUVY.mjs +7 -0
  170. package/dist/project-decompiler-US7GAVIC.mjs +7 -0
  171. package/dist/pull-2Q53HF3H.mjs +107 -0
  172. package/dist/pull-5AFHD3QG.mjs +109 -0
  173. package/dist/pull-65GUSX6F.mjs +109 -0
  174. package/dist/pull-6LVI4LMM.mjs +109 -0
  175. package/dist/pull-A2QUHW4K.mjs +109 -0
  176. package/dist/pull-B6T5BUKV.mjs +109 -0
  177. package/dist/pull-CKHWZT33.mjs +109 -0
  178. package/dist/pull-GM74ERRT.mjs +109 -0
  179. package/dist/pull-JBEQWVPE.mjs +109 -0
  180. package/dist/pull-P44LDRWB.mjs +109 -0
  181. package/dist/pull-W2US3T3E.mjs +109 -0
  182. package/dist/testing/index.js +9 -6
  183. package/dist/testing/index.mjs +1 -1
  184. package/dist/verify-3PPS4XAM.mjs +1833 -0
  185. package/dist/verify-GKEH5FZQ.mjs +1833 -0
  186. package/dist/verify-HDKUNHZ4.mjs +1833 -0
  187. package/dist/verify-SEIXUGN4.mjs +1833 -0
  188. package/dist/vite/index.js +10 -7
  189. package/dist/vite/index.mjs +2 -2
  190. 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: ["page"],
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`;
@@ -14883,7 +14894,7 @@ function mindmatrixReact(options) {
14883
14894
  slug: ir.slug,
14884
14895
  name: ir.name,
14885
14896
  version: ir.version,
14886
- category: ir.category || "workflow",
14897
+ category: ir.category || ["workflow"],
14887
14898
  fields: ir.fields || [],
14888
14899
  states: ir.states || [],
14889
14900
  transitions: ir.transitions || [],
@@ -15033,431 +15044,6 @@ var init_vite = __esm({
15033
15044
  }
15034
15045
  });
15035
15046
 
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
15047
  // src/cli/seed.ts
15462
15048
  var seed_exports = {};
15463
15049
  __export(seed_exports, {
@@ -15797,34 +15383,33 @@ async function createDevServer(options = {}) {
15797
15383
  open = false
15798
15384
  } = options;
15799
15385
  const clients = /* @__PURE__ */ new Set();
15800
- let localServer = null;
15801
15386
  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";
15387
+ if (rawApiUrl === "local" || rawApiUrl === "auto") {
15388
+ const localHealth = await checkBackendHealth("http://localhost:4200/api/v1");
15389
+ if (localHealth.ok) {
15390
+ apiUrl = "http://localhost:4200/api/v1";
15391
+ } else if (rawApiUrl === "auto") {
15392
+ const defaultRemote = "https://dev.mindmatrix.club/api/v1";
15393
+ const remoteHealth = await checkBackendHealth(defaultRemote);
15394
+ if (remoteHealth.ok) {
15395
+ apiUrl = defaultRemote;
15816
15396
  } else {
15817
- console.log("[mm-dev] No backend detected \u2014 starting local in-memory API server...");
15818
- localServer = await startLocalServer({ port: 4200 });
15397
+ console.error("[mm-dev] No backend detected. Start the engine first:");
15398
+ console.error(" mmrc dev (auto-starts engine)");
15399
+ console.error(" mmrc engine start (manual start)");
15400
+ console.error("[mm-dev] Or specify a remote API: mmrc dev --api-url https://...");
15819
15401
  apiUrl = "http://localhost:4200/api/v1";
15820
- isLocalMode = true;
15821
15402
  }
15403
+ } else {
15404
+ console.error("[mm-dev] Local engine not running on port 4200.");
15405
+ console.error(" Start it with: mmrc dev (or mmrc engine start)");
15406
+ apiUrl = "http://localhost:4200/api/v1";
15822
15407
  }
15823
15408
  } else {
15824
15409
  apiUrl = rawApiUrl;
15825
15410
  }
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);
15411
+ const token = await resolveDevToken(apiUrl, explicitToken);
15412
+ const health = await checkBackendHealth(apiUrl);
15828
15413
  let initialSlug, initialCompiled = 0, initialDeployed = false, initialErrors = 0;
15829
15414
  if (token && health.ok) {
15830
15415
  const r = await initialBuildDeploy(src, outDir, mode, apiUrl, token);
@@ -15884,74 +15469,163 @@ createRoot(document.getElementById('root')).render(React.createElement(App));
15884
15469
  `;
15885
15470
  }
15886
15471
  };
15887
- const devHtml = `<!DOCTYPE html>
15472
+ const { existsSync: fsExists } = await import("fs");
15473
+ const projectIndexPath = require("path").join(process.cwd(), "index.html");
15474
+ const hasProjectIndex = fsExists(projectIndexPath);
15475
+ if (!hasProjectIndex) {
15476
+ const { writeFileSync: fsWrite } = await import("fs");
15477
+ fsWrite(projectIndexPath, `<!DOCTYPE html>
15888
15478
  <html lang="en">
15889
15479
  <head>
15890
15480
  <meta charset="UTF-8">
15891
15481
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
15892
15482
  <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
15483
  </head>
15902
15484
  <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 type="module">
15910
- import React from 'react';
15911
- import { createRoot } from 'react-dom/client';
15485
+ <div id="root"></div>
15486
+ <script type="module" src="/__mm_dev_entry.tsx"></script>
15487
+ </body>
15488
+ </html>`, "utf-8");
15489
+ }
15490
+ const devEntryPlugin = {
15491
+ name: "mindmatrix-dev-entry",
15492
+ resolveId(id) {
15493
+ if (id === "/__mm_dev_entry.tsx") return id;
15494
+ return null;
15495
+ },
15496
+ load(id) {
15497
+ if (id !== "/__mm_dev_entry.tsx") return null;
15498
+ return `
15499
+ import React, { useState, useEffect } from 'react';
15500
+ import { createRoot } from 'react-dom/client';
15501
+ import { DevPlayer } from '@mmapp/react/player';
15502
+ import { PlayerProvider, ExperienceRenderer, createApiResolver } from '@mmapp/react/player';
15912
15503
 
15913
- function App() {
15914
- const [data, setData] = React.useState({ definitions: [], instances: [] });
15915
- const [error, setError] = React.useState(null);
15504
+ // Detect API base URL \u2014 proxy through dev server or use direct URL
15505
+ const API_BASE = window.location.origin + '/api/v1';
15506
+ const resolver = createApiResolver({ baseUrl: API_BASE, token: () => localStorage.getItem('auth_token') });
15916
15507
 
15917
- React.useEffect(() => {
15918
- fetch('/api/v1/workflow/definitions')
15919
- .then(r => r.ok ? r.json() : Promise.reject('API ' + r.status))
15920
- .then(defs => setData(d => ({ ...d, definitions: Array.isArray(defs) ? defs : defs.data || [] })))
15921
- .catch(e => setError(String(e)));
15922
- }, []);
15508
+ /**
15509
+ * Prepare experience tree for rendering.
15510
+ * Attaches metadata.dataSources to the root node and remaps
15511
+ * dataSource slugs to use definition-relative naming.
15512
+ *
15513
+ * @param definition - The main blueprint definition
15514
+ * @param allDefinitions - ALL definitions from the API (used for slug resolution)
15515
+ */
15516
+ function prepareTree(definition, allDefinitions) {
15517
+ const exp = definition.experience;
15518
+ if (!exp || typeof exp !== 'object') return null;
15923
15519
 
15924
- if (error) return React.createElement('div', { style: { padding: 40 } },
15925
- React.createElement('h1', null, 'MindMatrix Dev'),
15926
- React.createElement('p', { style: { color: '#c00', marginTop: 8 } }, 'API Error: ' + error),
15927
- React.createElement('p', { style: { color: '#888', marginTop: 8 } }, 'The engine is running but the API returned an error. This may be an auth issue in SQLite mode.')
15928
- );
15520
+ const meta = definition.metadata || {};
15521
+ const dataSources = meta.dataSources || [];
15522
+ const mutationTargets = meta.mutationTargets || [];
15929
15523
 
15930
- return React.createElement('div', { style: { padding: 40, maxWidth: 800 } },
15931
- React.createElement('h1', { style: { marginBottom: 16 } }, 'MindMatrix Dev'),
15932
- React.createElement('p', { style: { color: '#666', marginBottom: 24 } },
15933
- data.definitions.length + ' definition(s) deployed'),
15934
- data.definitions.map((def, i) =>
15935
- React.createElement('div', { key: i, style: { border: '1px solid #ddd', borderRadius: 8, padding: 16, marginBottom: 12, background: '#fff' } },
15936
- React.createElement('h3', { style: { marginBottom: 4 } }, def.name || def.slug || 'Unnamed'),
15937
- React.createElement('p', { style: { color: '#888', fontSize: 13 } },
15938
- 'slug: ' + (def.slug || '?') + ' | states: ' + (def.states?.length || 0) + ' | fields: ' + (def.fields?.length || 0))
15939
- )
15940
- ),
15941
- data.definitions.length === 0 && React.createElement('p', { style: { color: '#888' } },
15942
- 'No definitions deployed yet. Edit your model files and save \u2014 mmrc dev will auto-compile and deploy.')
15943
- );
15524
+ // Build a slug map from ALL deployed definitions
15525
+ // This resolves bare model names (e.g. "item") to full slugs (e.g. "tutorial-1-item")
15526
+ const blueprintSlug = definition.slug || '';
15527
+ const allSlugs = (allDefinitions || []).map(d => d.slug).filter(Boolean);
15528
+
15529
+ function resolveSlug(bareSlug) {
15530
+ if (!bareSlug) return bareSlug;
15531
+ // Already a full slug (contains hyphen matching a known definition)
15532
+ if (allSlugs.includes(bareSlug)) return bareSlug;
15533
+ // Try blueprint-prefixed slug: "item" \u2192 "tutorial-1-item"
15534
+ const prefixed = blueprintSlug + '-' + bareSlug;
15535
+ if (allSlugs.includes(prefixed)) return prefixed;
15536
+ // Try matching by suffix across all definitions
15537
+ const match = allSlugs.find(s => s.endsWith('-' + bareSlug));
15538
+ if (match) return match;
15539
+ // Return original (will attempt as-is)
15540
+ return bareSlug;
15541
+ }
15542
+
15543
+ // Clone root node and attach dataSources
15544
+ const root = { ...exp };
15545
+ if (dataSources.length > 0 && !root.dataSources) {
15546
+ root.dataSources = dataSources.map(ds => ({
15547
+ ...ds,
15548
+ slug: resolveSlug(ds.slug),
15549
+ }));
15550
+ }
15551
+
15552
+ // Remap mutation targets the same way
15553
+ if (mutationTargets.length > 0) {
15554
+ const resolvedTargets = mutationTargets.map(t => resolveSlug(t));
15555
+ root.config = { ...(root.config || {}), _mutationTargets: resolvedTargets };
15556
+ }
15557
+
15558
+ return root;
15559
+ }
15560
+
15561
+ function App() {
15562
+ const [tree, setTree] = useState(null);
15563
+ const [error, setError] = useState(null);
15564
+
15565
+ useEffect(() => {
15566
+ async function loadTree() {
15567
+ try {
15568
+ const res = await fetch(API_BASE + '/workflow/definitions');
15569
+ const json = await res.json();
15570
+ const items = json.items || json.data || [];
15571
+ // Find the main blueprint definition (has experience tree)
15572
+ 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];
15573
+ if (main?.experience) {
15574
+ const prepared = prepareTree(main, items);
15575
+ if (prepared) {
15576
+ setTree(prepared);
15577
+ return;
15578
+ }
15579
+ }
15580
+ if (main) {
15581
+ // No experience tree \u2014 show definition info
15582
+ setTree({
15583
+ type: 'Stack',
15584
+ props: { style: { padding: 40, gap: 16 } },
15585
+ children: [
15586
+ { type: 'Text', props: { children: main.name || main.slug, style: { fontSize: 24, fontWeight: 'bold' } } },
15587
+ { type: 'Text', props: { children: (main.states?.length || 0) + ' states, ' + (main.fields?.length || 0) + ' fields', style: { color: '#666' } } },
15588
+ ...items.map((d, i) => ({
15589
+ type: 'Card',
15590
+ props: { key: i, style: { padding: 16, border: '1px solid #ddd', borderRadius: 8 } },
15591
+ children: [
15592
+ { type: 'Text', props: { children: d.name || d.slug, style: { fontWeight: 600 } } },
15593
+ { type: 'Text', props: { children: 'slug: ' + d.slug + ' | ' + (d.states?.length || 0) + ' states', style: { fontSize: 13, color: '#888' } } },
15594
+ ],
15595
+ })),
15596
+ ],
15597
+ });
15598
+ }
15599
+ } catch (e) {
15600
+ setError(e.message);
15601
+ }
15944
15602
  }
15603
+ loadTree();
15604
+ }, []);
15945
15605
 
15946
- createRoot(document.getElementById('root')).render(React.createElement(App));
15947
- </script>
15948
- </body>
15949
- </html>`;
15950
- const { mkdtempSync, writeFileSync: writeFileSync10 } = await import("fs");
15951
- const { join: join8 } = await import("path");
15952
- const { tmpdir } = await import("os");
15953
- const devRoot = mkdtempSync(join8(tmpdir(), "mm-dev-"));
15954
- writeFileSync10(join8(devRoot, "index.html"), devHtml, "utf-8");
15606
+ if (error) return React.createElement('div', { style: { padding: 40 } },
15607
+ React.createElement('h1', null, 'MindMatrix Dev'),
15608
+ React.createElement('p', { style: { color: '#c00' } }, 'Error: ' + error)
15609
+ );
15610
+
15611
+ if (!tree) return React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh' } },
15612
+ React.createElement('p', null, 'Loading...')
15613
+ );
15614
+
15615
+ // If tree has 'component' field, use ExperienceRenderer; otherwise legacy DevPlayer
15616
+ if (tree.component) {
15617
+ return React.createElement(PlayerProvider, { resolver },
15618
+ React.createElement(ExperienceRenderer, { tree, className: 'mm-experience-root' })
15619
+ );
15620
+ }
15621
+
15622
+ return React.createElement(DevPlayer, { tree, title: document.title || 'MindMatrix Dev' });
15623
+ }
15624
+
15625
+ createRoot(document.getElementById('root')).render(React.createElement(App));
15626
+ `;
15627
+ }
15628
+ };
15955
15629
  let deployInFlight = false;
15956
15630
  const compileDeployPlugin = {
15957
15631
  name: "mindmatrix-dev-compile-deploy",
@@ -15982,16 +15656,16 @@ createRoot(document.getElementById('root')).render(React.createElement(App));
15982
15656
  }
15983
15657
  };
15984
15658
  const viteConfig = {
15985
- // Physical index.html lives in a temp dir so Vite serves it natively.
15986
- root: devRoot,
15987
- // Pre-bundle React so Vite doesn't warn about missing entry point.
15659
+ // Use the blueprint directory as Vite root (has node_modules with React).
15660
+ root: process.cwd(),
15661
+ // Pre-bundle React and the player for fast dev startup.
15988
15662
  optimizeDeps: { include: ["react", "react-dom/client"] },
15989
15663
  server: {
15990
15664
  port,
15991
15665
  open,
15992
15666
  host: true,
15993
15667
  // Allow serving files from the real project directory (fs.allow).
15994
- fs: { allow: [devRoot, process.cwd(), ".."] },
15668
+ fs: { allow: [process.cwd(), ".."] },
15995
15669
  proxy: {
15996
15670
  "/api": { target: proxyTarget, changeOrigin: true, secure: true, ws: true },
15997
15671
  "/health": { target: proxyTarget, changeOrigin: true, secure: true },
@@ -16001,6 +15675,7 @@ createRoot(document.getElementById('root')).render(React.createElement(App));
16001
15675
  plugins: [
16002
15676
  mindmatrixReact(pluginOpts),
16003
15677
  compileDeployPlugin,
15678
+ devEntryPlugin,
16004
15679
  devPlayerPlugin,
16005
15680
  { name: "mindmatrix-error-overlay", configureServer(server) {
16006
15681
  server.middlewares.use(errorOverlayMiddleware());
@@ -16008,8 +15683,8 @@ createRoot(document.getElementById('root')).render(React.createElement(App));
16008
15683
  ],
16009
15684
  logLevel: "warn"
16010
15685
  };
16011
- const { createServer: createServer2 } = await import("vite");
16012
- const vite = await createServer2(viteConfig);
15686
+ const { createServer } = await import("vite");
15687
+ const vite = await createServer(viteConfig);
16013
15688
  await vite.listen();
16014
15689
  const resolvedPort = vite.config.server.port ?? port;
16015
15690
  if (enableWs && vite.httpServer) {
@@ -16027,7 +15702,7 @@ createRoot(document.getElementById('root')).render(React.createElement(App));
16027
15702
  } catch {
16028
15703
  }
16029
15704
  }
16030
- printBanner({ port: resolvedPort, apiUrl: isLocalMode ? `${apiUrl} (local in-memory)` : apiUrl, src, include, health, token: !!token, compiled: initialCompiled, deployed: initialDeployed, slug: initialSlug, errors: initialErrors });
15705
+ printBanner({ port: resolvedPort, apiUrl, src, include, health, token: !!token, compiled: initialCompiled, deployed: initialDeployed, slug: initialSlug, errors: initialErrors });
16031
15706
  return {
16032
15707
  vite,
16033
15708
  port: resolvedPort,
@@ -16059,14 +15734,6 @@ createRoot(document.getElementById('root')).render(React.createElement(App));
16059
15734
  clients.clear();
16060
15735
  currentErrors = null;
16061
15736
  await vite.close();
16062
- if (localServer) {
16063
- await localServer.close();
16064
- }
16065
- try {
16066
- const { rmSync } = await import("fs");
16067
- rmSync(devRoot, { recursive: true, force: true });
16068
- } catch {
16069
- }
16070
15737
  }
16071
15738
  };
16072
15739
  }
@@ -16076,7 +15743,6 @@ var init_dev_server = __esm({
16076
15743
  "use strict";
16077
15744
  init_vite();
16078
15745
  init_build();
16079
- init_local_server();
16080
15746
  currentErrors = null;
16081
15747
  }
16082
15748
  });
@@ -16385,99 +16051,10 @@ function generateIndexHtml(name) {
16385
16051
  <meta charset="UTF-8">
16386
16052
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
16387
16053
  <title>${title} \u2014 MindMatrix Dev</title>
16388
- <style>
16389
- * { margin: 0; padding: 0; box-sizing: border-box; }
16390
- body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #fafafa; color: #111; }
16391
- #root { min-height: 100vh; padding: 40px; max-width: 960px; margin: 0 auto; }
16392
- h1 { font-size: 24px; margin-bottom: 8px; }
16393
- .subtitle { color: #666; margin-bottom: 24px; }
16394
- .card { border: 1px solid #e0e0e0; border-radius: 8px; padding: 16px; margin-bottom: 12px; background: #fff; }
16395
- .card h3 { font-size: 16px; margin-bottom: 4px; }
16396
- .card .meta { color: #888; font-size: 13px; }
16397
- .badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 600; }
16398
- .badge-state { background: #e3f2fd; color: #1565c0; }
16399
- .empty { text-align: center; padding: 60px 20px; color: #888; }
16400
- .error { color: #c62828; background: #ffebee; padding: 16px; border-radius: 8px; margin-bottom: 16px; }
16401
- .actions { display: flex; gap: 8px; margin-top: 8px; }
16402
- .btn { padding: 6px 12px; border-radius: 4px; border: 1px solid #ddd; background: #fff; cursor: pointer; font-size: 13px; }
16403
- .btn:hover { background: #f5f5f5; }
16404
- .btn-primary { background: #1976d2; color: #fff; border-color: #1976d2; }
16405
- .btn-primary:hover { background: #1565c0; }
16406
- </style>
16407
16054
  </head>
16408
16055
  <body>
16409
- <div id="root">
16410
- <h1>${title}</h1>
16411
- <p class="subtitle">Loading...</p>
16412
- </div>
16413
- <script type="module">
16414
- const API = '/api/v1';
16415
-
16416
- async function api(path, opts) {
16417
- const res = await fetch(API + path, {
16418
- headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer dev-local' },
16419
- ...opts,
16420
- });
16421
- return res.json();
16422
- }
16423
-
16424
- async function render() {
16425
- const root = document.getElementById('root');
16426
- try {
16427
- const defs = await api('/workflow/definitions');
16428
- const items = defs.items || defs.data || [];
16429
-
16430
- let html = '<h1>${title}</h1>';
16431
- html += '<p class="subtitle">' + items.length + ' definition(s) deployed</p>';
16432
-
16433
- if (items.length === 0) {
16434
- 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>';
16435
- }
16436
-
16437
- for (const def of items) {
16438
- const states = def.states || [];
16439
- const fields = def.fields || [];
16440
- const transitions = def.transitions || [];
16441
- html += '<div class="card">';
16442
- html += '<h3>' + (def.name || def.slug || 'Unnamed') + '</h3>';
16443
- html += '<p class="meta">' + states.length + ' states \xB7 ' + fields.length + ' fields \xB7 ' + transitions.length + ' transitions</p>';
16444
- html += '<div class="actions">';
16445
- html += '<button class="btn btn-primary" onclick="createInstance(\\'' + def.slug + '\\')">+ Create Instance</button>';
16446
- html += '</div>';
16447
- html += '</div>';
16448
- }
16449
-
16450
- // Show instances
16451
- const instances = await api('/workflow/instances');
16452
- const instItems = instances.items || instances.data || [];
16453
- if (instItems.length > 0) {
16454
- html += '<h2 style="margin-top:32px;margin-bottom:12px">Instances (' + instItems.length + ')</h2>';
16455
- for (const inst of instItems) {
16456
- html += '<div class="card">';
16457
- html += '<h3>' + (inst.definition_slug || '?') + ' <span class="badge badge-state">' + (inst.current_state || inst.state || '?') + '</span></h3>';
16458
- html += '<p class="meta">ID: ' + inst.id + '</p>';
16459
- html += '</div>';
16460
- }
16461
- }
16462
-
16463
- root.innerHTML = html;
16464
- } catch (e) {
16465
- root.innerHTML = '<h1>${title}</h1><div class="error">API Error: ' + e.message + '</div>';
16466
- }
16467
- }
16468
-
16469
- window.createInstance = async function(slug) {
16470
- await api('/workflow/instances', {
16471
- method: 'POST',
16472
- body: JSON.stringify({ definition_slug: slug, state_data: { title: 'New Item', priority: 'medium' } }),
16473
- });
16474
- render();
16475
- };
16476
-
16477
- render();
16478
- // Auto-refresh every 3 seconds
16479
- setInterval(render, 3000);
16480
- </script>
16056
+ <div id="root"></div>
16057
+ <script type="module" src="/__mm_dev_entry.tsx"></script>
16481
16058
  </body>
16482
16059
  </html>`;
16483
16060
  }
@@ -16638,35 +16215,46 @@ function generatePage(name) {
16638
16215
  return `/**
16639
16216
  * @workflow slug="${name}-home" version="1.0.0" category="page"
16640
16217
  *
16641
- * Index page \u2014 lists items with create and search.
16218
+ * Index page \u2014 full CRUD with create form, search, transitions, and delete.
16642
16219
  */
16643
16220
 
16644
16221
  import { useState } from 'react';
16645
16222
  import itemModel from '../models/item';
16646
16223
  import {
16647
16224
  useQuery, useMutation, useRouter,
16648
- Stack, Row, Text, Button, Icon, Card, Show, TextInput, Badge,
16225
+ Stack, Row, Text, Button, Icon, Card, Show, TextInput, Badge, Select,
16649
16226
  } from '@mmapp/react';
16650
16227
 
16651
- const PRIORITY_COLORS: Record<string, string> = {
16652
- high: 'token:error',
16653
- medium: 'token:warning',
16654
- low: 'token:success',
16655
- };
16656
-
16657
16228
  export default function ${pascal}Home() {
16658
16229
  const { data: items, loading } = useQuery(itemModel);
16659
16230
  const mutation = useMutation(itemModel);
16660
16231
  const router = useRouter();
16661
16232
  const [search, setSearch] = useState('');
16233
+ const [showCreate, setShowCreate] = useState(false);
16234
+ const [newTitle, setNewTitle] = useState('');
16235
+ const [newDescription, setNewDescription] = useState('');
16236
+ const [newPriority, setNewPriority] = useState('medium');
16237
+ const [editingId, setEditingId] = useState<string | null>(null);
16238
+ const [editTitle, setEditTitle] = useState('');
16662
16239
 
16663
- const activeItems = items.filter(i => i.state !== 'archived');
16664
- const filtered = activeItems.filter(i =>
16665
- i.fields.title.toLowerCase().includes(search.toLowerCase())
16666
- );
16240
+ const visibleItems = items.filter(i => i.state !== 'archived');
16241
+ const filtered = search
16242
+ ? visibleItems.filter(i => i.fields.title.toLowerCase().includes(search.toLowerCase()))
16243
+ : visibleItems;
16667
16244
 
16668
16245
  const handleCreate = async () => {
16669
- await mutation.create({ title: 'New Item', priority: 'medium' });
16246
+ if (!newTitle.trim()) return;
16247
+ await mutation.create({ title: newTitle.trim(), description: newDescription, priority: newPriority });
16248
+ setNewTitle('');
16249
+ setNewDescription('');
16250
+ setNewPriority('medium');
16251
+ setShowCreate(false);
16252
+ };
16253
+
16254
+ const handleUpdate = async (id: string) => {
16255
+ if (!editTitle.trim()) return;
16256
+ await mutation.update(id, { title: editTitle.trim() });
16257
+ setEditingId(null);
16670
16258
  };
16671
16259
 
16672
16260
  return (
@@ -16675,9 +16263,9 @@ export default function ${pascal}Home() {
16675
16263
  <Row justify="space-between" align="center">
16676
16264
  <Stack gap={4}>
16677
16265
  <Text variant="h3" weight="bold" value="${title}" />
16678
- <Text variant="muted" size="sm" value={\`\${activeItems.length} items\`} />
16266
+ <Text variant="muted" size="sm" value={\`\${visibleItems.length} item\${visibleItems.length !== 1 ? 's' : ''}\`} />
16679
16267
  </Stack>
16680
- <Button variant="primary" onPress={handleCreate}>
16268
+ <Button variant="primary" onPress={() => setShowCreate(true)}>
16681
16269
  <Row gap={6} align="center">
16682
16270
  <Icon name="plus" size={16} />
16683
16271
  <Text value="Add Item" />
@@ -16685,6 +16273,42 @@ export default function ${pascal}Home() {
16685
16273
  </Button>
16686
16274
  </Row>
16687
16275
 
16276
+ {/* Create form */}
16277
+ <Show when={showCreate}>
16278
+ <Card padding={16}>
16279
+ <Stack gap={12}>
16280
+ <Text weight="bold" value="New Item" />
16281
+ <TextInput
16282
+ value={newTitle}
16283
+ onChange={setNewTitle}
16284
+ placeholder="Item title (required)"
16285
+ />
16286
+ <TextInput
16287
+ value={newDescription}
16288
+ onChange={setNewDescription}
16289
+ placeholder="Description (optional)"
16290
+ />
16291
+ <Select
16292
+ value={newPriority}
16293
+ onChange={setNewPriority}
16294
+ options={[
16295
+ { label: 'Low', value: 'low' },
16296
+ { label: 'Medium', value: 'medium' },
16297
+ { label: 'High', value: 'high' },
16298
+ ]}
16299
+ />
16300
+ <Row gap={8} justify="flex-end">
16301
+ <Button variant="ghost" onPress={() => setShowCreate(false)}>
16302
+ <Text value="Cancel" />
16303
+ </Button>
16304
+ <Button variant="primary" onPress={handleCreate}>
16305
+ <Text value="Create" />
16306
+ </Button>
16307
+ </Row>
16308
+ </Stack>
16309
+ </Card>
16310
+ </Show>
16311
+
16688
16312
  {/* Search */}
16689
16313
  <TextInput
16690
16314
  value={search}
@@ -16706,10 +16330,12 @@ export default function ${pascal}Home() {
16706
16330
  <Card padding={32}>
16707
16331
  <Stack align="center" gap={12}>
16708
16332
  <Icon name="inbox" size={40} color="token:muted" />
16709
- <Text variant="muted" value="No items yet" />
16710
- <Button variant="outline" onPress={handleCreate}>
16711
- <Text value="Create your first item" />
16712
- </Button>
16333
+ <Text variant="muted" value={search ? 'No items match your search' : 'No items yet'} />
16334
+ <Show when={!search}>
16335
+ <Button variant="outline" onPress={() => setShowCreate(true)}>
16336
+ <Text value="Create your first item" />
16337
+ </Button>
16338
+ </Show>
16713
16339
  </Stack>
16714
16340
  </Card>
16715
16341
  </Show>
@@ -16721,13 +16347,48 @@ export default function ${pascal}Home() {
16721
16347
  <Card key={item.id} padding={12}>
16722
16348
  <Row align="center" gap={12}>
16723
16349
  <Stack flex={1} gap={2}>
16724
- <Text weight="medium" value={item.fields.title} />
16725
- <Show when={!!item.fields.description}>
16726
- <Text size="sm" variant="muted" value={item.fields.description} />
16350
+ <Show when={editingId === item.id}>
16351
+ <Row gap={8}>
16352
+ <TextInput
16353
+ value={editTitle}
16354
+ onChange={setEditTitle}
16355
+ placeholder="Title"
16356
+ />
16357
+ <Button variant="primary" size="sm" onPress={() => handleUpdate(item.id)}>
16358
+ <Text value="Save" />
16359
+ </Button>
16360
+ <Button variant="ghost" size="sm" onPress={() => setEditingId(null)}>
16361
+ <Text value="Cancel" />
16362
+ </Button>
16363
+ </Row>
16364
+ </Show>
16365
+ <Show when={editingId !== item.id}>
16366
+ <Text
16367
+ weight="medium"
16368
+ value={item.fields.title}
16369
+ onPress={() => { setEditingId(item.id); setEditTitle(item.fields.title); }}
16370
+ />
16371
+ <Show when={!!item.fields.description}>
16372
+ <Text size="sm" variant="muted" value={item.fields.description} />
16373
+ </Show>
16727
16374
  </Show>
16728
16375
  </Stack>
16729
16376
  <Badge value={item.fields.priority} />
16730
16377
  <Badge value={item.state} variant={item.state === 'active' ? 'success' : 'default'} />
16378
+ {/* Transition buttons */}
16379
+ <Show when={item.state === 'draft'}>
16380
+ <Button variant="outline" size="sm" onPress={() => mutation.transition(item.id, 'activate')}>
16381
+ <Text value="Activate" />
16382
+ </Button>
16383
+ </Show>
16384
+ <Show when={item.state === 'active'}>
16385
+ <Button variant="ghost" size="sm" onPress={() => mutation.transition(item.id, 'archive')}>
16386
+ <Text value="Archive" />
16387
+ </Button>
16388
+ </Show>
16389
+ <Button variant="ghost" size="sm" onPress={() => mutation.remove(item.id)}>
16390
+ <Icon name="trash" size={14} color="token:error" />
16391
+ </Button>
16731
16392
  </Row>
16732
16393
  </Card>
16733
16394
  ))}