synthesisui 0.16.504 → 0.16.505

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,10 +5,8 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, wri
5
5
  import { createRequire } from "node:module";
6
6
  import { homedir } from "node:os";
7
7
  import { join, relative } from "node:path";
8
- import { classify, pixelRatio } from "../reading/classify.js";
9
- import { PAGE_SCRIPT } from "../reading/page-script.js";
8
+ import { readSite } from "../reading/read-page.js";
10
9
  import { headingsOf } from "../reading/reread.js";
11
- import { decodePng } from "../reading/png.js";
12
10
  import { PENDING, globalClassOf, loadRoleRules, globalStyles, moduleClassName, pxOf, readPending } from "../role-rules.js";
13
11
  import { agentsToMaintain } from "../agents-chosen.js";
14
12
  import { syncClaudeMd } from "../claude-md.js";
@@ -29,10 +27,7 @@ import { projectRootFrom } from "./hook.js";
29
27
  */
30
28
  const READER_VERSION = "1.58.2";
31
29
  const READER_DIR = join(homedir(), ".synthesisui", "reader");
32
- export const VIEWPORTS = [
33
- ["desktop", 1440, 900],
34
- ["celular", 390, 844],
35
- ];
30
+ export { VIEWPORTS, confirmByPixels } from "../reading/read-page.js";
36
31
  /** `download: false` é o fim do turno: ele nunca baixa nada sozinho - sem o leitor na máquina, não relê (E01, fase 3). */
37
32
  export function loadReader(say, download = true) {
38
33
  const override = process.env.SYNTHESISUI_PLAYWRIGHT;
@@ -93,26 +88,6 @@ export async function findServer(url, root, waitMs = 4000) {
93
88
  }
94
89
  return null;
95
90
  }
96
- /**
97
- * CADA CONTRASTE BAIXO CONFERIDO PELOS PIXELS: sem tinta é texto não pintado; acima do alvo, o DOM errou o fundo. A mesma
98
- * conferência na leitura e no fim do turno (E01 da fase 3) - duas cópias divergiriam no primeiro conserto.
99
- */
100
- export async function confirmByPixels(found, pageOf) {
101
- const confirmed = [];
102
- for (const f of found) {
103
- const page = pageOf(f.sample);
104
- let px = null;
105
- if (page && f.sample.w >= 2 && f.sample.h >= 2) {
106
- const png = await page.screenshot({ fullPage: true, clip: { x: Math.max(0, f.sample.x), y: Math.max(0, f.sample.y), width: f.sample.w, height: f.sample.h } }).catch(() => null);
107
- const rgba = png ? decodePng(png) : null;
108
- px = rgba ? pixelRatio(rgba) : null;
109
- }
110
- if (px !== null && (px < 1.2 || px >= f.alvo))
111
- continue;
112
- confirmed.push({ ...f, pixels: px });
113
- }
114
- return confirmed;
115
- }
116
91
  /** O maior tamanho entre as classes de um texto (o do desktop), sem o prefixo de tela - a mesma escolha da checagem. */
117
92
  function sizeClassOf(cls) {
118
93
  const toks = cls.match(/(?:[a-z0-9]+:)?text-(?:\[(?:clamp\([^\]\s]+\)|[\d.]+(?:rem|px|em))\]|xs|sm|base|lg|[2-9]?xl)(?=\s|$|\|)/g) ?? [];
@@ -372,23 +347,12 @@ export async function read(opts) {
372
347
  const routes = opts.routes?.length ? opts.routes : ["/"];
373
348
  const pw = loadReader(say);
374
349
  const browser = (await launch(pw, say));
375
- const seen = [];
376
- const pages = new Map();
350
+ const site = await readSite(browser, base, routes).catch(async (e) => {
351
+ await browser.close().catch(() => { });
352
+ throw e;
353
+ });
354
+ const { reading, seen } = site;
377
355
  try {
378
- for (const rota of routes) {
379
- for (const [disp, w, h] of VIEWPORTS) {
380
- const ctx = await browser.newContext({ viewport: { width: w, height: h }, reducedMotion: "reduce" });
381
- const page = await ctx.newPage();
382
- await page.goto(new URL(rota, base).href, { waitUntil: "networkidle", timeout: 90_000 });
383
- await page.waitForTimeout(800);
384
- const textos = (await page.evaluate(PAGE_SCRIPT));
385
- for (const t of textos)
386
- seen.push({ ...t, rota, disp });
387
- pages.set(`${rota}|${disp}`, { page, ctx });
388
- }
389
- }
390
- const reading = classify(seen);
391
- reading.contraste = await confirmByPixels(reading.contraste, (t) => pages.get(`${t.rota}|${t.disp}`)?.page);
392
356
  const files = sources(root);
393
357
  const home = (t) => homeOf(root, files, t);
394
358
  const out = {
@@ -436,8 +400,7 @@ export async function read(opts) {
436
400
  say(`\n Written to _synthesisui/reading.json. Nothing in your code changed.`);
437
401
  }
438
402
  finally {
439
- for (const { ctx } of pages.values())
440
- await ctx.close().catch(() => { });
403
+ await site.close();
441
404
  await browser.close().catch(() => { });
442
405
  }
443
406
  }
@@ -0,0 +1,93 @@
1
+ import { classify, pixelRatio, } from "./classify.js";
2
+ import { PAGE_SCRIPT } from "./page-script.js";
3
+ import { decodePng } from "./png.js";
4
+ export const VIEWPORTS = [
5
+ ["desktop", 1440, 900],
6
+ ["celular", 390, 844],
7
+ ];
8
+ /**
9
+ * CADA CONTRASTE BAIXO CONFERIDO PELOS PIXELS: sem tinta é texto não pintado; acima do alvo, o DOM errou o fundo. A mesma
10
+ * conferência na leitura e no fim do turno (E01 da fase 3) - duas cópias divergiriam no primeiro conserto.
11
+ */
12
+ export async function confirmByPixels(found, pageOf) {
13
+ const confirmed = [];
14
+ for (const f of found) {
15
+ const page = pageOf(f.sample);
16
+ let px = null;
17
+ if (page && f.sample.w >= 2 && f.sample.h >= 2) {
18
+ const png = await page
19
+ .screenshot({
20
+ fullPage: true,
21
+ clip: {
22
+ x: Math.max(0, f.sample.x),
23
+ y: Math.max(0, f.sample.y),
24
+ width: f.sample.w,
25
+ height: f.sample.h,
26
+ },
27
+ })
28
+ .catch(() => null);
29
+ const rgba = png ? decodePng(png) : null;
30
+ px = rgba ? pixelRatio(rgba) : null;
31
+ }
32
+ if (px !== null && (px < 1.2 || px >= f.alvo))
33
+ continue;
34
+ confirmed.push({ ...f, pixels: px });
35
+ }
36
+ return confirmed;
37
+ }
38
+ /**
39
+ * Mede `routes` de `base` nas duas larguras, classifica e confere o contraste pelos pixels. Quem chama fecha (`close`).
40
+ * As duas larguras abrem AO MESMO TEMPO (medido em 21/10: resend.com levava 46 s em sequência), e os textos entram na
41
+ * ordem de `VIEWPORTS`, a mesma de antes - a classificação não muda.
42
+ */
43
+ export async function readSite(browser, base, routes, opts = {}) {
44
+ const seen = [];
45
+ const pages = new Map();
46
+ const statuses = new Map();
47
+ const ctxs = [];
48
+ const close = async () => {
49
+ for (const ctx of ctxs)
50
+ await ctx.close().catch(() => { });
51
+ };
52
+ const open = async (rota, disp, w, h) => {
53
+ const ctx = await browser.newContext({
54
+ ...opts.context,
55
+ viewport: { width: w, height: h },
56
+ reducedMotion: "reduce",
57
+ });
58
+ ctxs.push(ctx);
59
+ if (opts.prepare)
60
+ await opts.prepare(ctx);
61
+ const page = await ctx.newPage();
62
+ const href = new URL(rota, base).href;
63
+ const res = opts.settleMs === undefined
64
+ ? await page.goto(href, { waitUntil: "networkidle", timeout: 90_000 })
65
+ : await page
66
+ .goto(href, { waitUntil: "networkidle", timeout: opts.settleMs })
67
+ .catch((e) => {
68
+ if (!/timeout/i.test(e.message))
69
+ throw e;
70
+ return null;
71
+ });
72
+ const code = res?.status?.();
73
+ statuses.set(`${rota}|${disp}`, typeof code === "number" ? code : null);
74
+ await page.waitForTimeout(800);
75
+ const textos = (await page.evaluate(PAGE_SCRIPT));
76
+ pages.set(`${rota}|${disp}`, page);
77
+ return textos.map((t) => ({ ...t, rota, disp }));
78
+ };
79
+ try {
80
+ for (const rota of routes) {
81
+ const byWidth = await Promise.all(VIEWPORTS.map(([disp, w, h]) => open(rota, disp, w, h)));
82
+ for (const textos of byWidth)
83
+ seen.push(...textos);
84
+ }
85
+ const reading = classify(seen);
86
+ reading.contraste = await confirmByPixels(reading.contraste, (t) => pages.get(`${t.rota}|${t.disp}`));
87
+ return { reading, seen, pages, statuses, close };
88
+ }
89
+ catch (e) {
90
+ await close();
91
+ throw e;
92
+ }
93
+ }
@@ -1,7 +1,8 @@
1
1
  import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
2
2
  import { mkdir, readFile, writeFile } from "node:fs/promises";
3
3
  import { basename, extname, join, relative, sep } from "node:path";
4
- import { confirmByPixels, declaredPort, findServer, homeOf, launch, loadReader, sources } from "../commands/read.js";
4
+ import { declaredPort, findServer, homeOf, launch, loadReader, sources } from "../commands/read.js";
5
+ import { confirmByPixels } from "./read-page.js";
5
6
  import { loadRoleRules } from "../role-rules.js";
6
7
  import { classify } from "./classify.js";
7
8
  import { PAGE_SCRIPT } from "./page-script.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.504",
3
+ "version": "0.16.505",
4
4
  "description": "Bring SynthesisUI design systems into any project - tokens, typed components, whole pages and an agent-ready CLAUDE.md manifest.",
5
5
  "type": "module",
6
6
  "bin": {