minimojs 1.0.0-alpha.43 → 1.0.0-alpha.44

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.
@@ -5,9 +5,10 @@
5
5
  // property is misnamed; a property that must be named differently is
6
6
  // declared here as an explicit exception.
7
7
  //
8
- // The one exception is a decoration's properties, `DesignOutline`'s
8
+ // The exceptions are a decoration's properties, `DesignOutline`'s
9
9
  // `outlineWidth` and `outlineColor` (X-023). They belong to the optional
10
10
  // `outline` module, not to the sprite, which the core never gives an outline
11
11
  // of its own; in code they are `outlines.set(sprite, { width, color })`, and
12
- // the prefix says which module reads them in a file.
12
+ // the prefix says which module reads them in a file. And `comment` (X-026),
13
+ // the creator's request to their agent, which no runtime object takes.
13
14
  export {};
@@ -62,6 +62,18 @@ export interface SoundAsset {
62
62
  * The grid starts at the image's top-left corner, with no margin or spacing;
63
63
  * pixels left over at the right or bottom edge belong to no frame.
64
64
  *
65
+ * Every frame must be clean: its whole figure inside its own cell, with a
66
+ * transparent margin on every side, and nothing of a neighboring frame in it.
67
+ * The grid is cut exactly as declared, so a stray piece of a neighbor near a
68
+ * cell's edge is drawn as part of that frame. This is where agents most often
69
+ * go wrong, and most often without checking: an image generator does not keep
70
+ * its figures inside an even grid, and arranging or resizing frames afterwards
71
+ * pushes pieces across the lines. Lay the sheet out from separate figures,
72
+ * each centered in its cell, rather than cutting a generated picture along a
73
+ * grid; make the image exactly as wide and as tall as its columns and rows of
74
+ * frames; and before declaring it, cut out every frame and check each one on
75
+ * its own.
76
+ *
65
77
  * @example
66
78
  * ```json
67
79
  * { "key": "frog", "type": "spritesheet", "src": "assets/frog-frames.png", "frameWidth": 320, "frameHeight": 320 }
@@ -290,6 +302,17 @@ export interface DesignObjectBase {
290
302
  * `Sprite.data`. Default: `{}`.
291
303
  */
292
304
  data?: Record<string, unknown>;
305
+ /**
306
+ * The creator's request about this object, written in Studio for the
307
+ * creator's AI agent, such as `"Remove this cloud"` (X-026). Not empty, and
308
+ * at most {@link DESIGN_COMMENT_MAX_LENGTH} characters. The engine checks it
309
+ * and never uses it: the runtime object does not take it.
310
+ *
311
+ * An agent that does what a comment asks removes the comment in the same
312
+ * change, and answers the creator in its conversation, never in the design.
313
+ * A comment is never a thread and never the agent's own note.
314
+ */
315
+ comment?: string;
293
316
  }
294
317
  /** The properties of every sprite type: `image`, `emoji`, and `text`. */
295
318
  export interface DesignSpriteBase extends DesignObjectBase {
@@ -627,6 +650,8 @@ export interface DesignProperty {
627
650
  */
628
651
  readonly default: unknown;
629
652
  }
653
+ /** The most characters a design object's `comment` may hold (X-026). */
654
+ export declare const DESIGN_COMMENT_MAX_LENGTH = 1000;
630
655
  /**
631
656
  * Every property each type of design object accepts besides `id` and `type`,
632
657
  * with its JSON type, whether it is required, and its default.
@@ -294,6 +294,9 @@ function oneOf(values, defaultValue) {
294
294
  const NAME = { name: spec("string", undefined) };
295
295
  const TAGS = { tags: spec("string-list", []) };
296
296
  const DATA = { data: spec("object", {}) };
297
+ const COMMENT = { comment: spec("string", undefined) };
298
+ /** The most characters a design object's `comment` may hold (X-026). */
299
+ export const DESIGN_COMMENT_MAX_LENGTH = 1000;
297
300
  const SPRITE_SPECS = {
298
301
  ...NAME,
299
302
  ...TAGS,
@@ -317,6 +320,7 @@ const SPRITE_SPECS = {
317
320
  bodyOffsetX: spec("number", 0),
318
321
  bodyOffsetY: spec("number", 0),
319
322
  ...DATA,
323
+ ...COMMENT,
320
324
  };
321
325
  const OUTLINE_SPECS = {
322
326
  outlineWidth: spec("number", 0),
@@ -370,6 +374,7 @@ const BACKGROUND_SPECS = {
370
374
  layer: spec("number", 0),
371
375
  fit: oneOf(["none", "stretch", "contain", "cover"], "cover"),
372
376
  ...DATA,
377
+ ...COMMENT,
373
378
  };
374
379
  const MODULE_SPECS = {
375
380
  module: requiredString(),
@@ -523,7 +528,7 @@ export function enabledDecorations(object) {
523
528
  });
524
529
  }
525
530
  /** The properties every module object takes, whatever its module. */
526
- const MODULE_OBJECT_FIELDS = new Set(["id", "type", "module", "kind", "options", "name", "tags", "data"]);
531
+ const MODULE_OBJECT_FIELDS = new Set(["id", "type", "module", "kind", "options", "name", "tags", "data", "comment"]);
527
532
  /** Checks a module object, already checked as a `module`, against its module's schema. */
528
533
  function checkModuleObject(object, label, fail) {
529
534
  const quoted = (names) => names.map((name) => `"${name}"`).join(", ");
@@ -608,6 +613,8 @@ export function parseDesignObject(json, where, kind = "design object") {
608
613
  if (!isValidValue(value, property)) {
609
614
  throw fail(`${label} has "${field}" set to ${describe(value)}, but it must be ${describeExpected(property)}.`);
610
615
  }
616
+ if (field === "comment")
617
+ checkComment(value, label, fail);
611
618
  copy[field] = copyJson(value);
612
619
  }
613
620
  for (const property of properties.values()) {
@@ -619,6 +626,15 @@ export function parseDesignObject(json, where, kind = "design object") {
619
626
  checkModuleObject(copy, label, fail);
620
627
  return copy;
621
628
  }
629
+ /** A comment is the creator's words for their agent: something to read, and not a document. */
630
+ function checkComment(comment, label, fail) {
631
+ if (comment.trim().length === 0) {
632
+ throw fail(`${label} has an empty "comment". Remove the property instead: an object without a request has no comment.`);
633
+ }
634
+ if (comment.length > DESIGN_COMMENT_MAX_LENGTH) {
635
+ throw fail(`${label} has a "comment" of ${comment.length} characters, more than the ${DESIGN_COMMENT_MAX_LENGTH} a comment may hold.`);
636
+ }
637
+ }
622
638
  function isValidValue(value, property) {
623
639
  if (value === null)
624
640
  return property.nullable;
package/dist/minimo.js CHANGED
@@ -2062,6 +2062,9 @@ export class Game {
2062
2062
  for (const [field, value] of Object.entries(obj)) {
2063
2063
  if (field === "id" || field === "type" || field === "module" || field === "kind" || field === "options")
2064
2064
  continue;
2065
+ // A comment is the creator's request to their agent, never the sprite's (X-026).
2066
+ if (field === "comment")
2067
+ continue;
2065
2068
  // The module's own tags, such as "fire", stay beside the design's.
2066
2069
  if (field === "tags") {
2067
2070
  created.tags = [...new Set([...created.tags, ...value])];
@@ -2106,6 +2109,9 @@ export class Game {
2106
2109
  for (const [field, value] of Object.entries(obj)) {
2107
2110
  if (field === "id" || field === "type")
2108
2111
  continue;
2112
+ // A comment is the creator's request to their agent, never the object's (X-026).
2113
+ if (field === "comment")
2114
+ continue;
2109
2115
  if (field === "size" && created instanceof EmojiSprite)
2110
2116
  continue;
2111
2117
  if (field === "imageKey" && created instanceof ImageSprite) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minimojs",
3
- "version": "1.0.0-alpha.43",
3
+ "version": "1.0.0-alpha.44",
4
4
  "description": "MinimoJS v1 — ultra-minimal, flat, deterministic 2D web game engine. Image-first sprites with emoji as a zero-asset alternative, rAF loop, TypeScript-first, LLM-friendly.",
5
5
  "type": "module",
6
6
  "main": "dist/minimo.js",