@lovo/matter-cli 1.0.0 → 2.0.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @lovo/matter-cli
2
2
 
3
+ ## 2.0.0
4
+
3
5
  ## 1.0.0
4
6
 
5
7
  ## 0.6.0
package/README.md CHANGED
@@ -100,13 +100,13 @@ pnpm exec playwright install chromium
100
100
 
101
101
  ```bash
102
102
  # Default — writes ./public/hero.jpg (JPEG q80)
103
- npx matter-cli poster --from ./src/components/matter/hero.tsx --out ./public/hero
103
+ npx matter-cli poster --source ./src/components/matter/hero.tsx --output ./public/hero.jpg
104
104
 
105
105
  # Posterized shader — PNG compresses smaller
106
- npx matter-cli poster --from ./gradient.tsx --out ./public/gradient --type png
106
+ npx matter-cli poster --source ./gradient.tsx --output ./public/gradient.png --format png
107
107
 
108
108
  # Higher quality JPEG
109
- npx matter-cli poster --from ./aurora.tsx --out ./public/aurora --quality 92
109
+ npx matter-cli poster --source ./aurora.tsx --output ./public/aurora.jpg --quality 92
110
110
  ```
111
111
 
112
112
  Wire it up:
@@ -1,5 +1,6 @@
1
1
  import type React from 'react';
2
2
 
3
+ import { setReducedMotionPolicy } from '@lovo/matter';
3
4
  import { createRoot } from 'react-dom/client';
4
5
 
5
6
  import { installFrameReadyWatcher } from './frameReady.js';
@@ -36,6 +37,12 @@ if (!rootEl) throw new Error('matter poster: #root missing from harness HTML');
36
37
 
37
38
  const root = createRoot(rootEl);
38
39
 
40
+ // Pin the animation clock so the poster captures a deterministic t=0 frame,
41
+ // matching what users see at mount (ShaderScene also resets to t=0 at first
42
+ // paint). 'paused' sets the reduced-motion time scale to 0, so elapsedTime
43
+ // stays 0 regardless of how many settle frames elapse before the screenshot.
44
+ setReducedMotionPolicy('paused');
45
+
39
46
  root.render(<Component />);
40
47
 
41
48
  installFrameReadyWatcher();
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ function fail(caughtError) {
9
9
  process.exit(1);
10
10
  }
11
11
  var program = new Command();
12
- program.name("matter-cli").description("CLI for Matter \u2014 copy-paste components from the registry into your project").version("1.0.0");
12
+ program.name("matter-cli").description("CLI for Matter \u2014 copy-paste components from the registry into your project").version("2.0.0");
13
13
  program.command("init").description("one-time project setup \u2014 writes matter.config.json").option("--force", "overwrite an existing matter.config.json").action(async (opts) => {
14
14
  try {
15
15
  const { runInit } = await import("./init-NB5EOU5H.js");
@@ -21,7 +21,7 @@ program.command("init").description("one-time project setup \u2014 writes matter
21
21
  program.command("list").description("show available components in the registry").option("--registry <url>", "override the registryUrl from matter.config.json").option("--reference <ref>", "tag, branch, or commit (defaults to the CLI version)").action(async (opts) => {
22
22
  try {
23
23
  const { runList } = await import("./list-L725RQM3.js");
24
- await runList({ registry: opts.registry, ref: opts.reference, cliVersion: "1.0.0" });
24
+ await runList({ registry: opts.registry, ref: opts.reference, cliVersion: "2.0.0" });
25
25
  } catch (caughtError) {
26
26
  fail(caughtError);
27
27
  }
@@ -34,7 +34,7 @@ program.command("add").description("copy one or more components from the registr
34
34
  registry: opts.registry,
35
35
  ref: opts.reference,
36
36
  force: opts.force,
37
- cliVersion: "1.0.0"
37
+ cliVersion: "2.0.0"
38
38
  });
39
39
  } catch (caughtError) {
40
40
  fail(caughtError);
@@ -49,7 +49,7 @@ program.command("update").description("re-fetch a previously-added component (or
49
49
  registry: opts.registry,
50
50
  ref: opts.reference,
51
51
  force: opts.force,
52
- cliVersion: "1.0.0"
52
+ cliVersion: "2.0.0"
53
53
  });
54
54
  } catch (caughtError) {
55
55
  fail(caughtError);
@@ -17,7 +17,7 @@ import {
17
17
  import "../chunk-D4HDZEJT.js";
18
18
 
19
19
  // src/poster/e2e.test.ts
20
- import { mkdtemp, rm, stat } from "fs/promises";
20
+ import { mkdtemp, readFile, rm, stat } from "fs/promises";
21
21
  import { tmpdir } from "os";
22
22
  import { join } from "path";
23
23
  var E2E_ENABLED = process.env.MATTER_E2E === "1";
@@ -100,5 +100,22 @@ describe.skipIf(!E2E_ENABLED)("runPoster \u2014 E2E (MATTER_E2E=1)", () => {
100
100
  }
101
101
  }, 3e4);
102
102
  }
103
+ it("captures a deterministic t=0 frame across runs", async () => {
104
+ const outA = join(outDir, "determinism-a.png");
105
+ const outB = join(outDir, "determinism-b.png");
106
+ const base = {
107
+ from: join(FIXTURES, "gradient-plus-grain.tsx"),
108
+ type: "png",
109
+ exportName: "default",
110
+ timeSeconds: 0,
111
+ width: 320,
112
+ height: 240,
113
+ deviceScaleFactor: 1
114
+ };
115
+ await runPoster({ ...base, out: outA }, { cwd: process.cwd(), log: vi.fn() });
116
+ await runPoster({ ...base, out: outB }, { cwd: process.cwd(), log: vi.fn() });
117
+ const [bytesA, bytesB] = await Promise.all([readFile(outA), readFile(outB)]);
118
+ globalExpect(Buffer.compare(bytesA, bytesB)).toBe(0);
119
+ }, 6e4);
103
120
  });
104
121
  //# sourceMappingURL=e2e.test.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/poster/e2e.test.ts"],"sourcesContent":["import { mkdtemp, rm, stat } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';\n\nimport { runPoster } from '../commands/poster.js';\n\nconst E2E_ENABLED = process.env.MATTER_E2E === '1';\n\nconst FIXTURES = new URL('../test-fixtures/posters/', import.meta.url).pathname;\n\nconst cases = [\n {\n name: 'single-linear-gradient',\n file: 'single-linear-gradient.tsx',\n type: 'png',\n extra: {},\n },\n {\n name: 'gradient-plus-grain',\n file: 'gradient-plus-grain.tsx',\n type: 'png',\n extra: {},\n },\n {\n name: 'aurora-with-time',\n file: 'aurora-with-time.tsx',\n type: 'png',\n extra: { timeSeconds: 2 },\n },\n {\n name: 'named-export',\n file: 'named-export.tsx',\n type: 'png',\n extra: { exportName: 'NamedExport' },\n },\n // Exercises the default JPEG path and verifies the magic-byte header.\n {\n name: 'jpeg-default',\n file: 'single-linear-gradient.tsx',\n type: 'jpg',\n extra: {},\n },\n] as const;\n\ndescribe.skipIf(!E2E_ENABLED)('runPoster — E2E (MATTER_E2E=1)', () => {\n let outDir: string;\n\n beforeEach(async () => {\n outDir = await mkdtemp(join(tmpdir(), 'matter-poster-e2e-'));\n });\n\n afterEach(async () => {\n await rm(outDir, { recursive: true, force: true });\n });\n\n for (const c of cases) {\n it(`produces a ${c.type.toUpperCase()} for ${c.name}`, async () => {\n const out = join(outDir, `${c.name}.${c.type === 'jpg' ? 'jpg' : 'png'}`);\n\n await runPoster(\n {\n from: join(FIXTURES, c.file),\n out,\n type: c.type,\n exportName: 'default',\n timeSeconds: 0,\n width: 800,\n height: 600,\n deviceScaleFactor: 1,\n ...c.extra,\n },\n { cwd: process.cwd(), log: vi.fn() },\n );\n const s = await stat(out);\n\n expect(s.size).toBeGreaterThan(1024); // > 1 KB\n expect(s.size).toBeLessThan(5 * 1024 * 1024); // < 5 MB\n\n const { open } = await import('node:fs/promises');\n const fh = await open(out, 'r');\n\n try {\n const head = Buffer.alloc(4);\n\n await fh.read(head, 0, 4, 0);\n if (c.type === 'png') {\n expect(head[0]).toBe(0x89);\n expect(head[1]).toBe(0x50);\n } else {\n expect(head[0]).toBe(0xff);\n expect(head[1]).toBe(0xd8);\n }\n } finally {\n await fh.close();\n }\n }, 30_000);\n }\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA,SAAS,SAAS,IAAI,YAAY;AAClC,SAAS,cAAc;AACvB,SAAS,YAAY;AAKrB,IAAM,cAAc,QAAQ,IAAI,eAAe;AAE/C,IAAM,WAAW,IAAI,IAAI,6BAA6B,YAAY,GAAG,EAAE;AAEvE,IAAM,QAAQ;AAAA,EACZ;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO,CAAC;AAAA,EACV;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO,CAAC;AAAA,EACV;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO,EAAE,aAAa,EAAE;AAAA,EAC1B;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO,EAAE,YAAY,cAAc;AAAA,EACrC;AAAA;AAAA,EAEA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,OAAO,CAAC,WAAW,EAAE,uCAAkC,MAAM;AACpE,MAAI;AAEJ,aAAW,YAAY;AACrB,aAAS,MAAM,QAAQ,KAAK,OAAO,GAAG,oBAAoB,CAAC;AAAA,EAC7D,CAAC;AAED,YAAU,YAAY;AACpB,UAAM,GAAG,QAAQ,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EACnD,CAAC;AAED,aAAW,KAAK,OAAO;AACrB,OAAG,cAAc,EAAE,KAAK,YAAY,CAAC,QAAQ,EAAE,IAAI,IAAI,YAAY;AACjE,YAAM,MAAM,KAAK,QAAQ,GAAG,EAAE,IAAI,IAAI,EAAE,SAAS,QAAQ,QAAQ,KAAK,EAAE;AAExE,YAAM;AAAA,QACJ;AAAA,UACE,MAAM,KAAK,UAAU,EAAE,IAAI;AAAA,UAC3B;AAAA,UACA,MAAM,EAAE;AAAA,UACR,YAAY;AAAA,UACZ,aAAa;AAAA,UACb,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,mBAAmB;AAAA,UACnB,GAAG,EAAE;AAAA,QACP;AAAA,QACA,EAAE,KAAK,QAAQ,IAAI,GAAG,KAAK,GAAG,GAAG,EAAE;AAAA,MACrC;AACA,YAAM,IAAI,MAAM,KAAK,GAAG;AAExB,mBAAO,EAAE,IAAI,EAAE,gBAAgB,IAAI;AACnC,mBAAO,EAAE,IAAI,EAAE,aAAa,IAAI,OAAO,IAAI;AAE3C,YAAM,EAAE,KAAK,IAAI,MAAM,OAAO,aAAkB;AAChD,YAAM,KAAK,MAAM,KAAK,KAAK,GAAG;AAE9B,UAAI;AACF,cAAM,OAAO,OAAO,MAAM,CAAC;AAE3B,cAAM,GAAG,KAAK,MAAM,GAAG,GAAG,CAAC;AAC3B,YAAI,EAAE,SAAS,OAAO;AACpB,uBAAO,KAAK,CAAC,CAAC,EAAE,KAAK,GAAI;AACzB,uBAAO,KAAK,CAAC,CAAC,EAAE,KAAK,EAAI;AAAA,QAC3B,OAAO;AACL,uBAAO,KAAK,CAAC,CAAC,EAAE,KAAK,GAAI;AACzB,uBAAO,KAAK,CAAC,CAAC,EAAE,KAAK,GAAI;AAAA,QAC3B;AAAA,MACF,UAAE;AACA,cAAM,GAAG,MAAM;AAAA,MACjB;AAAA,IACF,GAAG,GAAM;AAAA,EACX;AACF,CAAC;","names":[]}
1
+ {"version":3,"sources":["../../src/poster/e2e.test.ts"],"sourcesContent":["import { mkdtemp, readFile, rm, stat } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';\n\nimport { runPoster } from '../commands/poster.js';\n\nconst E2E_ENABLED = process.env.MATTER_E2E === '1';\n\nconst FIXTURES = new URL('../test-fixtures/posters/', import.meta.url).pathname;\n\nconst cases = [\n {\n name: 'single-linear-gradient',\n file: 'single-linear-gradient.tsx',\n type: 'png',\n extra: {},\n },\n {\n name: 'gradient-plus-grain',\n file: 'gradient-plus-grain.tsx',\n type: 'png',\n extra: {},\n },\n {\n name: 'aurora-with-time',\n file: 'aurora-with-time.tsx',\n type: 'png',\n extra: { timeSeconds: 2 },\n },\n {\n name: 'named-export',\n file: 'named-export.tsx',\n type: 'png',\n extra: { exportName: 'NamedExport' },\n },\n // Exercises the default JPEG path and verifies the magic-byte header.\n {\n name: 'jpeg-default',\n file: 'single-linear-gradient.tsx',\n type: 'jpg',\n extra: {},\n },\n] as const;\n\ndescribe.skipIf(!E2E_ENABLED)('runPoster — E2E (MATTER_E2E=1)', () => {\n let outDir: string;\n\n beforeEach(async () => {\n outDir = await mkdtemp(join(tmpdir(), 'matter-poster-e2e-'));\n });\n\n afterEach(async () => {\n await rm(outDir, { recursive: true, force: true });\n });\n\n for (const c of cases) {\n it(`produces a ${c.type.toUpperCase()} for ${c.name}`, async () => {\n const out = join(outDir, `${c.name}.${c.type === 'jpg' ? 'jpg' : 'png'}`);\n\n await runPoster(\n {\n from: join(FIXTURES, c.file),\n out,\n type: c.type,\n exportName: 'default',\n timeSeconds: 0,\n width: 800,\n height: 600,\n deviceScaleFactor: 1,\n ...c.extra,\n },\n { cwd: process.cwd(), log: vi.fn() },\n );\n const s = await stat(out);\n\n expect(s.size).toBeGreaterThan(1024); // > 1 KB\n expect(s.size).toBeLessThan(5 * 1024 * 1024); // < 5 MB\n\n const { open } = await import('node:fs/promises');\n const fh = await open(out, 'r');\n\n try {\n const head = Buffer.alloc(4);\n\n await fh.read(head, 0, 4, 0);\n if (c.type === 'png') {\n expect(head[0]).toBe(0x89);\n expect(head[1]).toBe(0x50);\n } else {\n expect(head[0]).toBe(0xff);\n expect(head[1]).toBe(0xd8);\n }\n } finally {\n await fh.close();\n }\n }, 30_000);\n }\n\n it('captures a deterministic t=0 frame across runs', async () => {\n const outA = join(outDir, 'determinism-a.png');\n const outB = join(outDir, 'determinism-b.png');\n const base = {\n from: join(FIXTURES, 'gradient-plus-grain.tsx'),\n type: 'png' as const,\n exportName: 'default',\n timeSeconds: 0,\n width: 320,\n height: 240,\n deviceScaleFactor: 1,\n };\n\n await runPoster({ ...base, out: outA }, { cwd: process.cwd(), log: vi.fn() });\n await runPoster({ ...base, out: outB }, { cwd: process.cwd(), log: vi.fn() });\n\n const [bytesA, bytesB] = await Promise.all([readFile(outA), readFile(outB)]);\n\n // Determinism regression guard: the grain overlay animates with elapsedTime,\n // so if the harness ever stopped pinning the clock (paused -> scale 0), a run\n // that rode a different warmup time could shift the grain and diverge. This\n // asserts two runs stay byte-identical; it does not by itself prove the\n // paused clock is load-bearing (warmup may coincide), only that determinism\n // holds.\n expect(Buffer.compare(bytesA, bytesB)).toBe(0);\n }, 60_000);\n});\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lovo/matter-cli",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "CLI for Matter — copy-paste components from the registry into your project.",
5
5
  "keywords": [
6
6
  "cli",
@@ -48,9 +48,10 @@
48
48
  "typescript": "^5.6.0",
49
49
  "vite": "^8.0.14",
50
50
  "vitest": "^4.1.7",
51
- "@lovo/matter-react": "1.0.0",
51
+ "@lovo/matter": "2.0.0",
52
+ "@matter/registry": "0.0.0",
52
53
  "@matter/tsconfig": "0.0.0",
53
- "@matter/registry": "0.0.0"
54
+ "@lovo/matter-react": "2.0.0"
54
55
  },
55
56
  "peerDependencies": {
56
57
  "playwright": "*"