unoverse 0.1.179 → 0.1.181

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unoverse",
3
- "version": "0.1.179",
3
+ "version": "0.1.181",
4
4
  "description": "The Unoverse front door — create a Studio project, a universe, or a client app, and launch Studio.",
5
5
  "license": "SEE LICENSE IN README.md",
6
6
  "type": "module",
@@ -101,7 +101,12 @@ function lintFile(file) {
101
101
  // manifest-only form must resolve a layout; an envelope-form template supplies its own.
102
102
  const hasEnvelope = !!defPath(root, basename(root));
103
103
  if (!hasEnvelope) {
104
- const layoutName = json.layout ?? "main";
104
+ // The base defaults to the FIRST DECLARED STATE (states own layouts and
105
+ // declaration order ranks them); "main" only when no states block exists.
106
+ const firstState = json.states && typeof json.states === "object" && !Array.isArray(json.states)
107
+ ? Object.keys(json.states)[0]
108
+ : undefined;
109
+ const layoutName = json.layout ?? firstState ?? "main";
105
110
  if (!defPath(join(root, "layouts"), layoutName))
106
111
  report("error", file, `manifest.layout "${layoutName}" → layouts/${layoutName} does not exist (and no <name> envelope) (docs.unoverse.ai/design/apps)`);
107
112
  }
@@ -123,7 +128,7 @@ function lintFile(file) {
123
128
  report("error", file, `manifest "states" must be an object — the template tree: { <base>: { states: {…} }, <reaction>: {}, … } (STATE_MODEL §5)`);
124
129
  } else {
125
130
  const names = Object.keys(json.states);
126
- const base = json.layout ?? "main";
131
+ const base = json.layout ?? Object.keys(json.states)[0] ?? "main";
127
132
  if (!names.includes(base))
128
133
  report("error", file, `the template tree must include the BASE state "${base}" (named for the default layout) — the ladder derives as the top level minus it (STATE_MODEL §5)`);
129
134
  const subs = [];
@@ -279,16 +284,21 @@ function lintFile(file) {
279
284
 
280
285
  if (isEnvelope) {
281
286
  // COMPONENT envelope (templates have no envelope — their manifest is it).
282
- for (const req of ["kind", "name", "root"])
287
+ // The declaration word is `type:`; `kind:` is the legacy spelling, still read.
288
+ const declared = json.type ?? json.kind;
289
+ for (const req of ["name", "root"])
283
290
  if (json[req] === undefined) report("error", file, `envelope missing "${req}" (docs.unoverse.ai/design/sdui-and-mcp-apps)`);
284
- if (json.kind && !["component", "template", "atom"].includes(json.kind))
285
- report("error", file, `unknown kind "${json.kind}"`);
286
- if (json.kind === "component" && !json.category)
291
+ if (declared === undefined) report("error", file, `envelope missing "type" (docs.unoverse.ai/design/sdui-and-mcp-apps)`);
292
+ if (json.kind !== undefined && json.type === undefined)
293
+ report("warn", file, `"kind" is the legacy spelling of the declaration — write "type: ${json.kind}"`);
294
+ if (declared && !["component", "template", "atom"].includes(declared))
295
+ report("error", file, `unknown type "${declared}"`);
296
+ if (declared === "component" && !json.category)
287
297
  report("warn", file, `component has no "category". Used to group it in the palette (docs.unoverse.ai/design/sdui-and-mcp-apps)`);
288
298
  if (json.root) walkNode(json.root, file, root);
289
299
 
290
300
  // ── the contained-microapp discipline (mirrors microapp-structure.test.ts) ──
291
- if (json.kind === "component") {
301
+ if (declared === "component") {
292
302
  const hasLayouts = existsSync(join(root, "layouts"));
293
303
  const statesDir = join(root, "states");
294
304
  const stateFiles = existsSync(statesDir)
@@ -13,7 +13,7 @@
13
13
  // ── ground truth (mirrors design/_schema/unoverse.schema.json + server guards) ──
14
14
  export const PRIMITIVES = new Set([
15
15
  "Box", "Stack", "Row", "Column", "Each", "Switch", "ComponentSlot", "Timeline",
16
- "Text", "Image", "Button", "Input", "Select", "Markdown", "Skeleton", "Icon", "Ref", "Orb",
16
+ "Text", "Image", "Button", "Input", "Select", "Markdown", "Skeleton", "Icon", "Ref", "Orb", "Template",
17
17
  ]);
18
18
  export const CONDITION_KEYS = new Set(["field", "eq", "ne", "in"]);
19
19
 
@@ -56,6 +56,10 @@ export const PRIMITIVE_PROPS = {
56
56
  Switch: ["on", "cases", "fallback"],
57
57
  Ref: ["ref", "props", "with", "children", "name"],
58
58
  ComponentSlot: ["select", "frame", "fallback", "pinned", "appWidth"],
59
+ // THE MOUNT (UNOVERSE_TEMPLATE_MODEL §12): a layout place that runs a mounted
60
+ // template's machinery. `name` addresses the manifest's `templates:` entry;
61
+ // `frame` is the dressing drawn around it when occupied.
62
+ Template: ["name", "frame", "template", "director", "appWidth"],
59
63
  Timeline: ["user", "assistant", "userData", "assistantData", "autoScroll"],
60
64
  };
61
65