explicai 0.0.0-stage → 0.1.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.
Files changed (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +53 -2
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +51 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/core/annotations.d.ts +7 -0
  7. package/dist/core/annotations.js +108 -0
  8. package/dist/core/annotations.js.map +1 -0
  9. package/dist/core/assets.d.ts +15 -0
  10. package/dist/core/assets.js +81 -0
  11. package/dist/core/assets.js.map +1 -0
  12. package/dist/core/export/collect.d.ts +27 -0
  13. package/dist/core/export/collect.js +49 -0
  14. package/dist/core/export/collect.js.map +1 -0
  15. package/dist/core/export/html.d.ts +6 -0
  16. package/dist/core/export/html.js +195 -0
  17. package/dist/core/export/html.js.map +1 -0
  18. package/dist/core/export/imageSize.d.ts +9 -0
  19. package/dist/core/export/imageSize.js +55 -0
  20. package/dist/core/export/imageSize.js.map +1 -0
  21. package/dist/core/export/index.d.ts +23 -0
  22. package/dist/core/export/index.js +71 -0
  23. package/dist/core/export/index.js.map +1 -0
  24. package/dist/core/export/labels.d.ts +16 -0
  25. package/dist/core/export/labels.js +29 -0
  26. package/dist/core/export/labels.js.map +1 -0
  27. package/dist/core/export/markdown.d.ts +8 -0
  28. package/dist/core/export/markdown.js +74 -0
  29. package/dist/core/export/markdown.js.map +1 -0
  30. package/dist/core/export/pdf.d.ts +6 -0
  31. package/dist/core/export/pdf.js +67 -0
  32. package/dist/core/export/pdf.js.map +1 -0
  33. package/dist/core/export/refs.d.ts +24 -0
  34. package/dist/core/export/refs.js +53 -0
  35. package/dist/core/export/refs.js.map +1 -0
  36. package/dist/core/frontmatter.d.ts +5 -0
  37. package/dist/core/frontmatter.js +17 -0
  38. package/dist/core/frontmatter.js.map +1 -0
  39. package/dist/core/fs.d.ts +9 -0
  40. package/dist/core/fs.js +33 -0
  41. package/dist/core/fs.js.map +1 -0
  42. package/dist/core/index.d.ts +11 -0
  43. package/dist/core/index.js +12 -0
  44. package/dist/core/index.js.map +1 -0
  45. package/dist/core/manuals.d.ts +17 -0
  46. package/dist/core/manuals.js +66 -0
  47. package/dist/core/manuals.js.map +1 -0
  48. package/dist/core/pages.d.ts +38 -0
  49. package/dist/core/pages.js +138 -0
  50. package/dist/core/pages.js.map +1 -0
  51. package/dist/core/text.d.ts +3 -0
  52. package/dist/core/text.js +19 -0
  53. package/dist/core/text.js.map +1 -0
  54. package/dist/core/trash.d.ts +25 -0
  55. package/dist/core/trash.js +79 -0
  56. package/dist/core/trash.js.map +1 -0
  57. package/dist/core/tree.d.ts +13 -0
  58. package/dist/core/tree.js +28 -0
  59. package/dist/core/tree.js.map +1 -0
  60. package/dist/core/types.d.ts +86 -0
  61. package/dist/core/types.js +5 -0
  62. package/dist/core/types.js.map +1 -0
  63. package/dist/core/versions.d.ts +27 -0
  64. package/dist/core/versions.js +82 -0
  65. package/dist/core/versions.js.map +1 -0
  66. package/dist/server/http.d.ts +23 -0
  67. package/dist/server/http.js +78 -0
  68. package/dist/server/http.js.map +1 -0
  69. package/dist/server/index.d.ts +4 -0
  70. package/dist/server/index.js +30 -0
  71. package/dist/server/index.js.map +1 -0
  72. package/dist/server/router.d.ts +30 -0
  73. package/dist/server/router.js +28 -0
  74. package/dist/server/router.js.map +1 -0
  75. package/dist/server/routes/assets.d.ts +2 -0
  76. package/dist/server/routes/assets.js +44 -0
  77. package/dist/server/routes/assets.js.map +1 -0
  78. package/dist/server/routes/export.d.ts +2 -0
  79. package/dist/server/routes/export.js +33 -0
  80. package/dist/server/routes/export.js.map +1 -0
  81. package/dist/server/routes/manuals.d.ts +2 -0
  82. package/dist/server/routes/manuals.js +40 -0
  83. package/dist/server/routes/manuals.js.map +1 -0
  84. package/dist/server/routes/pages.d.ts +2 -0
  85. package/dist/server/routes/pages.js +77 -0
  86. package/dist/server/routes/pages.js.map +1 -0
  87. package/dist/server/routes/versions.d.ts +2 -0
  88. package/dist/server/routes/versions.js +43 -0
  89. package/dist/server/routes/versions.js.map +1 -0
  90. package/dist/server/routes/web.d.ts +3 -0
  91. package/dist/server/routes/web.js +23 -0
  92. package/dist/server/routes/web.js.map +1 -0
  93. package/dist/server/security.d.ts +6 -0
  94. package/dist/server/security.js +16 -0
  95. package/dist/server/security.js.map +1 -0
  96. package/dist/server/workspace.d.ts +5 -0
  97. package/dist/server/workspace.js +17 -0
  98. package/dist/server/workspace.js.map +1 -0
  99. package/dist/web/assets/archivo-latin-ext-wdth-normal-7khWdh9v.woff2 +0 -0
  100. package/dist/web/assets/archivo-latin-wdth-normal-DY7AcnAa.woff2 +0 -0
  101. package/dist/web/assets/archivo-vietnamese-wdth-normal-rJmnGBSt.woff2 +0 -0
  102. package/dist/web/assets/atkinson-hyperlegible-next-latin-400-normal-DPp7N0cD.woff +0 -0
  103. package/dist/web/assets/atkinson-hyperlegible-next-latin-400-normal-FfmJh7DR.woff2 +0 -0
  104. package/dist/web/assets/atkinson-hyperlegible-next-latin-700-normal-C2qZD_Q9.woff +0 -0
  105. package/dist/web/assets/atkinson-hyperlegible-next-latin-700-normal-Dpiyiu63.woff2 +0 -0
  106. package/dist/web/assets/atkinson-hyperlegible-next-latin-ext-400-normal-BalHKn7d.woff2 +0 -0
  107. package/dist/web/assets/atkinson-hyperlegible-next-latin-ext-400-normal-qxMiU51n.woff +0 -0
  108. package/dist/web/assets/atkinson-hyperlegible-next-latin-ext-700-normal-292QVhS1.woff +0 -0
  109. package/dist/web/assets/atkinson-hyperlegible-next-latin-ext-700-normal-CBw-mJf_.woff2 +0 -0
  110. package/dist/web/assets/index-BXkBk-2Z.css +1 -0
  111. package/dist/web/assets/index-C02XKPHS.js +260 -0
  112. package/dist/web/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  113. package/dist/web/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  114. package/dist/web/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  115. package/dist/web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  116. package/dist/web/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  117. package/dist/web/favicon.svg +1 -0
  118. package/dist/web/index.html +24 -0
  119. package/package.json +90 -4
@@ -0,0 +1,195 @@
1
+ import { Marked } from "marked";
2
+ import { labelsFor } from "./labels.js";
3
+ import { assetFromSrc, headingAnchor, pageAnchor, pageLink, safeHref } from "./refs.js";
4
+ /** Cor da marca, quando o manual não escolheu outra. */
5
+ export const DEFAULT_ACCENT = "#2b4fe0";
6
+ const esc = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
7
+ const pct = (n) => `${(n * 100).toFixed(3)}%`;
8
+ /** Um arquivo HTML com o manual inteiro: sumário ao lado, uma seção por página, marcações sobre os prints. */
9
+ export function renderHtml(collected, images) {
10
+ const { meta, pages } = collected;
11
+ const L = labelsFor(meta.language);
12
+ const accent = meta.theme?.accent ?? DEFAULT_ACCENT;
13
+ const toc = pages
14
+ .map((p) => `<li style="--d:${p.depth}"><a href="#${pageAnchor(p.id)}"><span class="num">${p.number}</span> ${esc(p.title)}</a></li>`)
15
+ .join("\n");
16
+ const sections = pages.map((p) => renderPage(p, pages, images, L)).join("\n");
17
+ return `<!doctype html>
18
+ <html lang="${esc(meta.language)}">
19
+ <head>
20
+ <meta charset="utf-8">
21
+ <meta name="viewport" content="width=device-width, initial-scale=1">
22
+ <meta name="generator" content="Explicaí Studio">
23
+ <title>${esc(meta.title)}</title>
24
+ <style>${CSS.replace("__ACCENT__", accent)}</style>
25
+ </head>
26
+ <body>
27
+ <div class="layout">
28
+ <nav class="toc" aria-label="${L.toc}">
29
+ <p class="toc-title">${esc(meta.title)}</p>
30
+ <ol>
31
+ ${toc}
32
+ </ol>
33
+ </nav>
34
+ <main>
35
+ <header class="cover">
36
+ <h1>${esc(meta.title)}</h1>
37
+ <p>${L.pages(pages.length)}</p>
38
+ </header>
39
+ ${sections}
40
+ <footer class="colophon">${L.colophon}</footer>
41
+ </main>
42
+ </div>
43
+ </body>
44
+ </html>
45
+ `;
46
+ }
47
+ function renderPage(page, pages, images, L) {
48
+ const marked = new Marked({
49
+ gfm: true,
50
+ renderer: {
51
+ // Os títulos da página descem um nível: o título da página é o h2 da seção.
52
+ heading({ tokens, depth, text }) {
53
+ const level = Math.min(depth + 2, 6);
54
+ return `<h${level} id="${headingAnchor(page.id, text)}">${this.parser.parseInline(tokens)}</h${level}>\n`;
55
+ },
56
+ link({ href, tokens }) {
57
+ const target = pageLink(href, pages) ?? safeHref(href);
58
+ const text = this.parser.parseInline(tokens);
59
+ if (!target)
60
+ return text;
61
+ const external = /^https?:/i.test(target) ? ' rel="noopener noreferrer"' : "";
62
+ return `<a href="${esc(target)}"${external}>${text}</a>`;
63
+ },
64
+ image({ href, text }) {
65
+ const asset = assetFromSrc(href);
66
+ const info = asset ? images.get(asset) : undefined;
67
+ if (!info)
68
+ return `<span class="missing">[${L.missingImage}: ${esc(text || href)}]</span>`;
69
+ return renderShot(info, text, pages, L);
70
+ },
71
+ // HTML colado no texto sai como texto: o manual exportado não roda código de ninguém.
72
+ html({ text }) {
73
+ return esc(text);
74
+ },
75
+ },
76
+ });
77
+ const body = marked.parse(page.body, { async: false });
78
+ const level = Math.min(page.depth + 2, 3);
79
+ return `<section class="page" id="${pageAnchor(page.id)}">
80
+ <h${level} class="page-title"><span class="num">${page.number}</span> ${esc(page.title)}</h${level}>
81
+ <div class="page-body">${body}</div>
82
+ </section>`;
83
+ }
84
+ function hotspotHref(target, pages) {
85
+ if (target.type === "url") {
86
+ const href = safeHref(target.href);
87
+ return href ? { href, label: target.href } : null;
88
+ }
89
+ const page = pages.find((p) => p.id === target.pageId);
90
+ if (!page)
91
+ return null;
92
+ const href = target.type === "anchor" ? `#${headingAnchor(page.id, target.anchor)}` : `#${pageAnchor(page.id)}`;
93
+ return { href, label: page.title };
94
+ }
95
+ /** O print com as marcações por cima (posições em %, acompanham o tamanho) e a legenda embaixo. */
96
+ function renderShot(info, alt, pages, L) {
97
+ const a = info.annotations;
98
+ const w = info.size?.w ?? 1000;
99
+ const h = info.size?.h ?? 1000;
100
+ const box = (r) => `left:${pct(r.x)};top:${pct(r.y)};width:${pct(r.w)};height:${pct(r.h)}`;
101
+ const highlights = a.highlights.map((r) => `<span class="hl" style="${box(r)}"></span>`).join("");
102
+ const hotspots = a.hotspots
103
+ .map((r) => {
104
+ const link = hotspotHref(r.target, pages);
105
+ return link
106
+ ? `<a class="hot" href="${esc(link.href)}" style="${box(r)}"><span class="sr">${L.goTo} ${esc(link.label)}</span></a>`
107
+ : "";
108
+ })
109
+ .join("");
110
+ const unit = Math.max(w, h);
111
+ const px = (p) => ({ x: p.x * w, y: p.y * h });
112
+ const arrows = a.arrows.length
113
+ ? `<svg class="arrows" viewBox="0 0 ${w} ${h}" preserveAspectRatio="none" aria-hidden="true">${a.arrows
114
+ .map(({ from, to }) => {
115
+ const f = px(from);
116
+ const t = px(to);
117
+ const angle = Math.atan2(t.y - f.y, t.x - f.x);
118
+ const size = unit * 0.022;
119
+ const wing = (d) => `${(t.x - size * Math.cos(angle + d)).toFixed(1)},${(t.y - size * Math.sin(angle + d)).toFixed(1)}`;
120
+ return `<line x1="${f.x.toFixed(1)}" y1="${f.y.toFixed(1)}" x2="${t.x.toFixed(1)}" y2="${t.y.toFixed(1)}" stroke-width="${(unit * 0.005).toFixed(1)}"/><polygon points="${t.x.toFixed(1)},${t.y.toFixed(1)} ${wing(0.45)} ${wing(-0.45)}"/>`;
121
+ })
122
+ .join("")}</svg>`
123
+ : "";
124
+ const markers = a.markers
125
+ .map((m) => `<span class="mk" style="left:${pct(m.x)};top:${pct(m.y)}" aria-hidden="true">${m.n}</span>`)
126
+ .join("");
127
+ const legend = [...a.markers]
128
+ .sort((x, y) => x.n - y.n)
129
+ .map((m) => `<span role="listitem"><span class="n">${m.n}</span> ${esc(m.label ?? "")}</span>`)
130
+ .join("");
131
+ return `<span class="shot"><span class="frame" style="aspect-ratio:${w} / ${h}"><img src="${esc(info.asset)}" alt="${esc(alt)}" loading="lazy">${highlights}${arrows}${hotspots}${markers}</span>${legend ? `<span class="legend" role="list" aria-label="${L.legend}">${legend}</span>` : ""}</span>`;
132
+ }
133
+ const CSS = `
134
+ :root{--accent:__ACCENT__;--anotacao:#2b4fe0;--marca:#ffd43b;--tinta:#151a33;--grafite:#575e78;--linha:#dce0eb;--papel:#f5f6fa;
135
+ --texto:"Atkinson Hyperlegible Next","Segoe UI",system-ui,sans-serif;--display:"Archivo","Arial Narrow",system-ui,sans-serif;--mono:"JetBrains Mono",ui-monospace,Consolas,monospace}
136
+ *{box-sizing:border-box}
137
+ body{margin:0;background:#fff;color:var(--tinta);font:16px/1.65 var(--texto);-webkit-font-smoothing:antialiased}
138
+ .layout{display:grid;grid-template-columns:17rem minmax(0,1fr)}
139
+ .toc{position:sticky;top:0;height:100vh;overflow:auto;padding:24px 16px;background:var(--papel);border-right:1px solid var(--linha)}
140
+ .toc-title{margin:0 0 12px;font:800 18px/1.2 var(--display)}
141
+ .toc ol{list-style:none;margin:0;padding:0}
142
+ .toc li{padding-left:calc(var(--d) * 14px)}
143
+ .toc a{display:block;padding:4px 6px;border-radius:6px;color:var(--tinta);text-decoration:none;font-size:14px}
144
+ .toc a:hover{background:#e9ecf5}
145
+ .num{font-family:var(--mono);font-size:.8em;color:var(--accent);margin-right:4px}
146
+ main{max-width:52rem;padding:32px 40px 96px;margin:0 auto;width:100%}
147
+ .cover{padding:48px 0 32px;border-bottom:3px solid var(--marca);margin-bottom:24px}
148
+ .cover h1{margin:0;font:800 clamp(32px,5vw,48px)/1.1 var(--display);letter-spacing:-.02em}
149
+ .cover p{margin:8px 0 0;color:var(--grafite)}
150
+ .page{padding-top:32px;scroll-margin-top:16px}
151
+ .page-title{margin:0 0 12px;font:800 30px/1.2 var(--display);letter-spacing:-.01em}
152
+ .page-body h3,.page-body h4,.page-body h5,.page-body h6{font-family:var(--display);line-height:1.25;margin:1.4em 0 .4em;scroll-margin-top:16px}
153
+ .page-body h3{font-size:22px}.page-body h4{font-size:19px}.page-body h5,.page-body h6{font-size:17px}
154
+ .page-body p{margin:.6em 0}
155
+ .page-body a{color:var(--accent);text-underline-offset:.2em}
156
+ .page-body code{font:.88em var(--mono);background:var(--papel);border:1px solid var(--linha);border-radius:4px;padding:.05em .35em}
157
+ .page-body pre{font:14px/1.5 var(--mono);background:var(--papel);border:1px solid var(--linha);border-radius:8px;padding:12px 14px;overflow-x:auto}
158
+ .page-body pre code{background:none;border:0;padding:0}
159
+ .page-body blockquote{margin:.6em 0;padding:2px 0 2px 16px;border-left:2px solid var(--linha);color:var(--grafite)}
160
+ .page-body table{border-collapse:collapse;width:100%;margin:.8em 0;font-size:15px}
161
+ .page-body th,.page-body td{border:1px solid var(--linha);padding:6px 10px;text-align:left;vertical-align:top}
162
+ .page-body th{background:var(--papel)}
163
+ .page-body ul{padding-left:1.4em}
164
+ .page-body>ol,.page-body li>ol{list-style:none;counter-reset:passo;padding-left:2.25em}
165
+ .page-body>ol>li,.page-body li>ol>li{counter-increment:passo;position:relative}
166
+ .page-body>ol>li+li,.page-body li>ol>li+li{margin-top:.6em}
167
+ .page-body>ol>li::before,.page-body li>ol>li::before{content:counter(passo);position:absolute;left:-2.25em;top:.12em;display:grid;place-items:center;width:1.55em;height:1.55em;border-radius:50%;background:var(--accent);color:#fff;font:700 .82em var(--mono)}
168
+ .page-body li>blockquote{padding:6px 12px;border-left:3px solid var(--marca);border-radius:0 6px 6px 0;background:rgba(255,212,59,.35);color:var(--tinta)}
169
+ .shot{display:block;margin:.6em 0}
170
+ .frame{position:relative;display:block;max-width:100%;width:fit-content}
171
+ .frame img{display:block;max-width:100%;height:auto;border:1px solid var(--linha);border-radius:8px}
172
+ .hl{position:absolute;border:2px solid var(--marca);background:rgba(255,212,59,.3);border-radius:3px}
173
+ .hot{position:absolute;border:2px dashed var(--anotacao);background:rgba(43,79,224,.08);border-radius:3px}
174
+ .arrows{position:absolute;inset:0;width:100%;height:100%;overflow:visible;pointer-events:none}
175
+ .arrows line{stroke:var(--anotacao);stroke-linecap:round}
176
+ .arrows polygon{fill:var(--anotacao)}
177
+ .mk{position:absolute;transform:translate(-50%,-50%);display:grid;place-items:center;width:28px;height:28px;border-radius:50%;background:var(--anotacao);color:#fff;font:600 13px var(--mono);box-shadow:0 0 0 2px #fff,0 2px 6px rgba(21,26,51,.35)}
178
+ .legend{display:grid;gap:4px;margin-top:8px;font-size:15px}
179
+ .legend .n{display:inline-grid;place-items:center;width:20px;height:20px;margin-right:6px;border-radius:50%;background:var(--anotacao);color:#fff;font:600 11px var(--mono);vertical-align:1px}
180
+ .missing{color:#c8411f;font-style:italic}
181
+ .sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
182
+ .colophon{margin-top:64px;padding-top:16px;border-top:1px solid var(--linha);color:var(--grafite);font-size:13px}
183
+ @media (max-width:800px){.layout{display:block}.toc{position:static;height:auto;border-right:0;border-bottom:1px solid var(--linha)}main{padding:24px 20px 64px}}
184
+ @media print{
185
+ @page{margin:18mm 16mm}
186
+ body{font-size:11.5pt}
187
+ .layout{display:block}.toc{display:none}main{max-width:none;padding:0}
188
+ .cover{padding:30vh 0 0;border:0;break-after:page}
189
+ .page{break-before:page;padding-top:0}
190
+ .shot,.page-body table,.page-body pre,.page-body li{break-inside:avoid}
191
+ .page-body a{color:var(--tinta)}
192
+ .hot{border-style:dashed}
193
+ }
194
+ `;
195
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../../../src/core/export/html.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface Size {
2
+ w: number;
3
+ h: number;
4
+ }
5
+ /**
6
+ * Largura e altura de um print lendo só o cabeçalho do arquivo (PNG, JPEG, GIF, WebP e SVG).
7
+ * A exportação precisa da proporção para desenhar setas sem entortar. Formato desconhecido: null.
8
+ */
9
+ export declare function imageSize(file: string): Promise<Size | null>;
@@ -0,0 +1,55 @@
1
+ import { readFile } from "node:fs/promises";
2
+ /**
3
+ * Largura e altura de um print lendo só o cabeçalho do arquivo (PNG, JPEG, GIF, WebP e SVG).
4
+ * A exportação precisa da proporção para desenhar setas sem entortar. Formato desconhecido: null.
5
+ */
6
+ export async function imageSize(file) {
7
+ const b = await readFile(file);
8
+ if (b.length > 24 && b.toString("ascii", 1, 4) === "PNG")
9
+ return { w: b.readUInt32BE(16), h: b.readUInt32BE(20) };
10
+ if (b.length >= 10 && b.toString("ascii", 0, 3) === "GIF")
11
+ return { w: b.readUInt16LE(6), h: b.readUInt16LE(8) };
12
+ if (b.length > 30 && b.toString("ascii", 0, 4) === "RIFF" && b.toString("ascii", 8, 12) === "WEBP")
13
+ return webp(b);
14
+ if (b[0] === 0xff && b[1] === 0xd8)
15
+ return jpeg(b);
16
+ if (b.toString("utf8", 0, 512).includes("<svg"))
17
+ return svg(b.toString("utf8"));
18
+ return null;
19
+ }
20
+ function webp(b) {
21
+ const chunk = b.toString("ascii", 12, 16);
22
+ if (chunk === "VP8 ")
23
+ return { w: b.readUInt16LE(26) & 0x3fff, h: b.readUInt16LE(28) & 0x3fff };
24
+ if (chunk === "VP8L") {
25
+ const bits = b.readUInt32LE(21);
26
+ return { w: (bits & 0x3fff) + 1, h: ((bits >> 14) & 0x3fff) + 1 };
27
+ }
28
+ if (chunk === "VP8X")
29
+ return { w: b.readUIntLE(24, 3) + 1, h: b.readUIntLE(27, 3) + 1 };
30
+ return null;
31
+ }
32
+ function jpeg(b) {
33
+ let i = 2;
34
+ while (i + 9 < b.length) {
35
+ if (b[i] !== 0xff)
36
+ return null;
37
+ const marker = b[i + 1] ?? 0;
38
+ // SOF0 a SOF15, menos DHT (C4), JPG (C8) e DAC (CC): é onde ficam as dimensões.
39
+ if (marker >= 0xc0 && marker <= 0xcf && ![0xc4, 0xc8, 0xcc].includes(marker)) {
40
+ return { h: b.readUInt16BE(i + 5), w: b.readUInt16BE(i + 7) };
41
+ }
42
+ i += 2 + b.readUInt16BE(i + 2);
43
+ }
44
+ return null;
45
+ }
46
+ function svg(text) {
47
+ const tag = /<svg[^>]*>/i.exec(text)?.[0] ?? "";
48
+ const num = (name) => Number.parseFloat(new RegExp(`s${name}="([d.]+)`, "i").exec(tag)?.[1] ?? "");
49
+ const [w, h] = [num("width"), num("height")];
50
+ if (w > 0 && h > 0)
51
+ return { w, h };
52
+ const box = /viewBox="[\d.\s-]*?([\d.]+)\s+([\d.]+)"/i.exec(tag);
53
+ return box ? { w: Number(box[1]), h: Number(box[2]) } : null;
54
+ }
55
+ //# sourceMappingURL=imageSize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imageSize.js","sourceRoot":"","sources":["../../../src/core/export/imageSize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { ManualTheme } from "../types.js";
2
+ import { type CollectOptions } from "./collect.js";
3
+ export { findBrowser } from "./pdf.js";
4
+ /** Pasta da exportação, dentro do manual. Cada exportação substitui a anterior do mesmo formato. */
5
+ export declare const EXPORT_DIR = "exportacao";
6
+ export type ExportFormat = "html" | "pdf" | "markdown";
7
+ export declare const EXPORT_FORMATS: ExportFormat[];
8
+ export interface ExportResult {
9
+ format: ExportFormat;
10
+ /** Arquivo principal, relativo à pasta do manual (`exportacao/html/index.html`). */
11
+ file: string;
12
+ pages: number;
13
+ /** Páginas que ficaram de fora e por quê. */
14
+ warnings: string[];
15
+ }
16
+ /**
17
+ * Exporta o manual: HTML (um arquivo com sumário, para publicar ou abrir no navegador), PDF (impresso
18
+ * a partir desse HTML) ou Markdown (um arquivo só, para quem lê com IA). Usa a versão marcada
19
+ * "No manual" de cada página.
20
+ */
21
+ export declare function exportManual(dir: string, format: ExportFormat, opts?: CollectOptions): Promise<ExportResult>;
22
+ /** Cor do manual exportado. Só aceita hexadecimal de 6 dígitos; sem cor, volta à da marca. */
23
+ export declare function setManualTheme(dir: string, theme: ManualTheme): Promise<void>;
@@ -0,0 +1,71 @@
1
+ import { copyFile, mkdir, rm } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { atomicWrite, relativePosix, resolveInside } from "../fs.js";
4
+ import { updateManual } from "../manuals.js";
5
+ import { slugify } from "../text.js";
6
+ import { collectPages } from "./collect.js";
7
+ import { renderHtml } from "./html.js";
8
+ import { renderMarkdown } from "./markdown.js";
9
+ import { printPdf } from "./pdf.js";
10
+ import { loadImages } from "./refs.js";
11
+ export { findBrowser } from "./pdf.js";
12
+ /** Pasta da exportação, dentro do manual. Cada exportação substitui a anterior do mesmo formato. */
13
+ export const EXPORT_DIR = "exportacao";
14
+ export const EXPORT_FORMATS = ["html", "pdf", "markdown"];
15
+ /**
16
+ * Exporta o manual: HTML (um arquivo com sumário, para publicar ou abrir no navegador), PDF (impresso
17
+ * a partir desse HTML) ou Markdown (um arquivo só, para quem lê com IA). Usa a versão marcada
18
+ * "No manual" de cada página.
19
+ */
20
+ export async function exportManual(dir, format, opts = {}) {
21
+ const collected = await collectPages(dir, opts);
22
+ const images = await loadImages(dir, collected.pages);
23
+ const root = resolveInside(dir, EXPORT_DIR);
24
+ const result = (file) => ({
25
+ format,
26
+ file: relativePosix(dir, file),
27
+ pages: collected.pages.length,
28
+ warnings: collected.warnings,
29
+ });
30
+ if (format === "markdown") {
31
+ const out = await freshDir(path.join(root, "markdown"));
32
+ await copyImages(dir, images, out);
33
+ const file = path.join(out, "manual.md");
34
+ await atomicWrite(file, renderMarkdown(collected, images));
35
+ return result(file);
36
+ }
37
+ const out = await freshDir(path.join(root, "html"));
38
+ await copyImages(dir, images, out);
39
+ const html = path.join(out, "index.html");
40
+ await atomicWrite(html, renderHtml(collected, images));
41
+ if (format === "html")
42
+ return result(html);
43
+ const pdf = path.join(root, `${slugify(collected.meta.title) || "manual"}.pdf`);
44
+ await rm(pdf, { force: true });
45
+ await printPdf(html, pdf);
46
+ return result(pdf);
47
+ }
48
+ /** Cor do manual exportado. Só aceita hexadecimal de 6 dígitos; sem cor, volta à da marca. */
49
+ export async function setManualTheme(dir, theme) {
50
+ if (theme.accent !== undefined && !/^#[0-9a-f]{6}$/i.test(theme.accent)) {
51
+ throw new Error("Cor inválida: use o formato #2b4fe0.");
52
+ }
53
+ await updateManual(dir, (meta) => {
54
+ if (theme.accent)
55
+ meta.theme = { ...meta.theme, accent: theme.accent.toLowerCase() };
56
+ else
57
+ delete meta.theme;
58
+ });
59
+ }
60
+ async function freshDir(dir) {
61
+ await rm(dir, { recursive: true, force: true });
62
+ await mkdir(dir, { recursive: true });
63
+ return dir;
64
+ }
65
+ /** Copia só as imagens usadas, mantendo `assets/` para os caminhos do HTML e do Markdown valerem. */
66
+ async function copyImages(dir, images, out) {
67
+ await mkdir(path.join(out, "assets"), { recursive: true });
68
+ for (const asset of images.keys())
69
+ await copyFile(resolveInside(dir, asset), path.join(out, asset));
70
+ }
71
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/export/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /** Textos que o Explicaí escreve no manual exportado. Seguem o idioma do manual, não o da interface. */
2
+ export interface Labels {
3
+ toc: string;
4
+ pages: (n: number) => string;
5
+ colophon: string;
6
+ missingImage: string;
7
+ goTo: string;
8
+ legend: string;
9
+ noDescription: string;
10
+ hotspotUrl: (href: string) => string;
11
+ hotspotPage: (title: string) => string;
12
+ removedPage: string;
13
+ untitled: string;
14
+ }
15
+ /** Manual em português (pt, pt-BR, pt-PT) recebe os textos em português; os outros, em inglês. */
16
+ export declare const labelsFor: (language: string) => Labels;
@@ -0,0 +1,29 @@
1
+ const PT = {
2
+ toc: "Sumário",
3
+ pages: (n) => `${n} ${n === 1 ? "página" : "páginas"}`,
4
+ colophon: "Manual feito com o Explicaí Studio.",
5
+ missingImage: "imagem não encontrada",
6
+ goTo: "Ir para",
7
+ legend: "Legenda da imagem",
8
+ noDescription: "(sem descrição)",
9
+ hotspotUrl: (href) => `Área clicável: leva a ${href}`,
10
+ hotspotPage: (title) => `Área clicável: leva à página "${title}"`,
11
+ removedPage: "removida",
12
+ untitled: "Sem título",
13
+ };
14
+ const EN = {
15
+ toc: "Contents",
16
+ pages: (n) => `${n} ${n === 1 ? "page" : "pages"}`,
17
+ colophon: "Manual made with Explicaí Studio.",
18
+ missingImage: "image not found",
19
+ goTo: "Go to",
20
+ legend: "Image legend",
21
+ noDescription: "(no description)",
22
+ hotspotUrl: (href) => `Clickable area: goes to ${href}`,
23
+ hotspotPage: (title) => `Clickable area: goes to the page "${title}"`,
24
+ removedPage: "removed",
25
+ untitled: "Untitled",
26
+ };
27
+ /** Manual em português (pt, pt-BR, pt-PT) recebe os textos em português; os outros, em inglês. */
28
+ export const labelsFor = (language) => (language.toLowerCase().startsWith("pt") ? PT : EN);
29
+ //# sourceMappingURL=labels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"labels.js","sourceRoot":"","sources":["../../../src/core/export/labels.ts"],"names":[],"mappings":"AAeA,MAAM,EAAE,GAAW;IACjB,GAAG,EAAE,SAAS;IACd,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EAAE;IACtD,QAAQ,EAAE,qCAAqC;IAC/C,YAAY,EAAE,uBAAuB;IACrC,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,mBAAmB;IAC3B,aAAa,EAAE,iBAAiB;IAChC,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,yBAAyB,IAAI,EAAE;IACrD,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,iCAAiC,KAAK,GAAG;IACjE,WAAW,EAAE,UAAU;IACvB,QAAQ,EAAE,YAAY;CACvB,CAAC;AAEF,MAAM,EAAE,GAAW;IACjB,GAAG,EAAE,UAAU;IACf,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE;IAClD,QAAQ,EAAE,mCAAmC;IAC7C,YAAY,EAAE,iBAAiB;IAC/B,IAAI,EAAE,OAAO;IACb,MAAM,EAAE,cAAc;IACtB,aAAa,EAAE,kBAAkB;IACjC,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,2BAA2B,IAAI,EAAE;IACvD,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,qCAAqC,KAAK,GAAG;IACrE,WAAW,EAAE,SAAS;IACtB,QAAQ,EAAE,UAAU;CACrB,CAAC;AAEF,kGAAkG;AAClG,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,QAAgB,EAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { Collected } from "./collect.js";
2
+ import { type ImageInfo } from "./refs.js";
3
+ /**
4
+ * O manual num Markdown só, pensado para quem lê com IA: sumário, uma seção por página na ordem da
5
+ * árvore, títulos um nível abaixo do da página, e depois de cada print a legenda e as áreas clicáveis
6
+ * em texto (a IA não vê a imagem, mas lê o que ela aponta).
7
+ */
8
+ export declare function renderMarkdown(collected: Collected, images: Map<string, ImageInfo>): string;
@@ -0,0 +1,74 @@
1
+ import { slugify } from "../text.js";
2
+ import { labelsFor } from "./labels.js";
3
+ import { assetFromSrc } from "./refs.js";
4
+ const IMAGE_LINE = /!\[([^\]]*)\]\(([^)\s]+)\)/g;
5
+ const PAGE_LINK = /\]\((?:\.\/)?([^/)#\s]+\.md)(#[^)\s]*)?\)/g;
6
+ /**
7
+ * O manual num Markdown só, pensado para quem lê com IA: sumário, uma seção por página na ordem da
8
+ * árvore, títulos um nível abaixo do da página, e depois de cada print a legenda e as áreas clicáveis
9
+ * em texto (a IA não vê a imagem, mas lê o que ela aponta).
10
+ */
11
+ export function renderMarkdown(collected, images) {
12
+ const { meta, pages } = collected;
13
+ const L = labelsFor(meta.language);
14
+ const anchor = (p) => slugify(`${p.number} ${p.title}`);
15
+ const toc = pages.map((p) => `${" ".repeat(p.depth)}- [${p.number} ${p.title}](#${anchor(p)})`).join("\n");
16
+ const sections = pages.map((page) => {
17
+ const level = Math.min(page.depth + 2, 6);
18
+ const body = shiftHeadings(page.body, level)
19
+ .replace(PAGE_LINK, (all, file, hash) => {
20
+ const target = pages.find((p) => p.fileName === decodeURI(file));
21
+ return target ? `](#${hash ? hash.slice(1) : anchor(target)})` : all;
22
+ })
23
+ .split("\n")
24
+ .map((line) => {
25
+ // A legenda vai depois da linha do print, com a mesma indentação (o print pode estar num passo).
26
+ const indent = /^\s*/.exec(line)?.[0] ?? "";
27
+ const notes = [];
28
+ const out = line.replace(IMAGE_LINE, (all, alt, src) => {
29
+ const asset = assetFromSrc(src);
30
+ const info = asset ? images.get(asset) : undefined;
31
+ if (!info)
32
+ return all;
33
+ notes.push(...imageNotes(info, pages, L));
34
+ return `![${alt}](${info.asset})`;
35
+ });
36
+ if (!notes.length)
37
+ return out;
38
+ return `${out}\n\n${indent}> ${L.legend}:\n${notes.map((n) => `${indent}> ${n}`).join("\n")}`;
39
+ })
40
+ .join("\n");
41
+ return `${"#".repeat(level)} ${page.number} ${page.title}\n\n${body.trim()}`;
42
+ });
43
+ return `# ${meta.title}\n\n## ${L.toc}\n\n${toc}\n\n${sections.join("\n\n")}\n`;
44
+ }
45
+ /** Legenda e áreas clicáveis do print, em texto, uma por linha. */
46
+ function imageNotes(info, pages, L) {
47
+ const { markers, hotspots } = info.annotations;
48
+ const lines = [...markers]
49
+ .sort((a, b) => a.n - b.n)
50
+ .map((m) => `${m.n}. ${m.label ?? L.noDescription}`)
51
+ .concat(hotspots.map((h) => {
52
+ if (h.target.type === "url")
53
+ return L.hotspotUrl(h.target.href);
54
+ const id = h.target.pageId;
55
+ const page = pages.find((p) => p.id === id);
56
+ return L.hotspotPage(page?.title ?? L.removedPage);
57
+ }));
58
+ return lines;
59
+ }
60
+ /** Desce os títulos da página para ficarem abaixo do título dela (`level`). Ignora blocos de código. */
61
+ function shiftHeadings(body, level) {
62
+ let fence = false;
63
+ return body
64
+ .split("\n")
65
+ .map((line) => {
66
+ if (/^\s*(```|~~~)/.test(line))
67
+ fence = !fence;
68
+ if (fence)
69
+ return line;
70
+ return line.replace(/^(#{1,6}) /, (_, hashes) => `${"#".repeat(Math.min(hashes.length + level, 6))} `);
71
+ })
72
+ .join("\n");
73
+ }
74
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../../src/core/export/markdown.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export declare function findBrowser(): Promise<string | null>;
2
+ /**
3
+ * Imprime o HTML exportado em PDF com o Chrome (ou Edge) da máquina, sem janela.
4
+ * Usa um perfil temporário: com o perfil normal, um Chrome já aberto "engolia" o pedido.
5
+ */
6
+ export declare function printPdf(htmlFile: string, pdfFile: string): Promise<void>;
@@ -0,0 +1,67 @@
1
+ import { execFile } from "node:child_process";
2
+ import { mkdtemp, rm } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import path from "node:path";
5
+ import { pathToFileURL } from "node:url";
6
+ import { promisify } from "node:util";
7
+ import { exists } from "../fs.js";
8
+ const run = promisify(execFile);
9
+ /** Onde costumam ficar Chrome, Edge e Chromium. EXPLICAI_BROWSER aponta outro. */
10
+ function candidates() {
11
+ const env = process.env;
12
+ const custom = env.EXPLICAI_BROWSER ? [env.EXPLICAI_BROWSER] : [];
13
+ if (process.platform === "win32") {
14
+ const roots = [env.PROGRAMFILES, env["PROGRAMFILES(X86)"], env.LOCALAPPDATA].filter(Boolean);
15
+ return custom.concat(roots.flatMap((r) => [
16
+ path.join(r, "Google", "Chrome", "Application", "chrome.exe"),
17
+ path.join(r, "Microsoft", "Edge", "Application", "msedge.exe"),
18
+ path.join(r, "Chromium", "Application", "chrome.exe"),
19
+ ]));
20
+ }
21
+ if (process.platform === "darwin") {
22
+ return custom.concat([
23
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
24
+ "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
25
+ "/Applications/Chromium.app/Contents/MacOS/Chromium",
26
+ ]);
27
+ }
28
+ const dirs = (env.PATH ?? "").split(path.delimiter).filter(Boolean);
29
+ const names = ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser", "microsoft-edge"];
30
+ return custom.concat(names.flatMap((n) => dirs.map((d) => path.join(d, n))));
31
+ }
32
+ export async function findBrowser() {
33
+ for (const file of candidates())
34
+ if (await exists(file))
35
+ return file;
36
+ return null;
37
+ }
38
+ /**
39
+ * Imprime o HTML exportado em PDF com o Chrome (ou Edge) da máquina, sem janela.
40
+ * Usa um perfil temporário: com o perfil normal, um Chrome já aberto "engolia" o pedido.
41
+ */
42
+ export async function printPdf(htmlFile, pdfFile) {
43
+ const browser = await findBrowser();
44
+ if (!browser) {
45
+ throw new Error("Para gerar o PDF, o Explicaí usa o Chrome, o Edge ou o Chromium da sua máquina, e nenhum foi encontrado. " +
46
+ "Instale um deles ou informe o caminho na variável EXPLICAI_BROWSER.");
47
+ }
48
+ const profile = await mkdtemp(path.join(tmpdir(), "explicai-pdf-"));
49
+ try {
50
+ await run(browser, [
51
+ "--headless=new",
52
+ "--disable-gpu",
53
+ "--no-first-run",
54
+ "--no-default-browser-check",
55
+ `--user-data-dir=${profile}`,
56
+ "--no-pdf-header-footer",
57
+ `--print-to-pdf=${pdfFile}`,
58
+ pathToFileURL(htmlFile).href,
59
+ ], { timeout: 120_000 });
60
+ }
61
+ finally {
62
+ await rm(profile, { recursive: true, force: true }).catch(() => { });
63
+ }
64
+ if (!(await exists(pdfFile)))
65
+ throw new Error("O navegador terminou sem gerar o PDF.");
66
+ }
67
+ //# sourceMappingURL=pdf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf.js","sourceRoot":"","sources":["../../../src/core/export/pdf.ts"],"names":[],"mappings":"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"}