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,24 @@
1
+ import type { Annotations } from "../types.js";
2
+ import type { ExportPage } from "./collect.js";
3
+ import { type Size } from "./imageSize.js";
4
+ export interface ImageInfo {
5
+ /** Caminho no manual: `assets/tela.png`. */
6
+ asset: string;
7
+ size: Size | null;
8
+ annotations: Annotations;
9
+ }
10
+ /** `../assets/tela.png` (como está na página) → `assets/tela.png`. Imagem de fora do manual: null. */
11
+ export declare function assetFromSrc(src: string): string | null;
12
+ /** Marcações e tamanho de cada imagem do manual usada nas páginas. Imagem apagada fica de fora. */
13
+ export declare function loadImages(dir: string, pages: ExportPage[]): Promise<Map<string, ImageInfo>>;
14
+ /** Id da seção de uma página no manual exportado. */
15
+ export declare const pageAnchor: (pageId: string) => string;
16
+ /** Id de um título dentro de uma página: `#secao` de um link vira este id. */
17
+ export declare const headingAnchor: (pageId: string, text: string) => string;
18
+ /**
19
+ * Link entre páginas (`./arquivo.md` ou `./arquivo.md#secao`) → âncora dentro do manual exportado.
20
+ * Outro tipo de link: null (fica como está).
21
+ */
22
+ export declare function pageLink(href: string, pages: ExportPage[]): string | null;
23
+ /** Só endereços seguros saem como link no manual exportado. */
24
+ export declare function safeHref(href: string): string | null;
@@ -0,0 +1,53 @@
1
+ import path from "node:path";
2
+ import { readAnnotations } from "../assets.js";
3
+ import { exists, resolveInside } from "../fs.js";
4
+ import { slugify } from "../text.js";
5
+ import { imageSize } from "./imageSize.js";
6
+ const IMAGE = /!\[[^\]]*\]\(([^)\s]+)/g;
7
+ /** `../assets/tela.png` (como está na página) → `assets/tela.png`. Imagem de fora do manual: null. */
8
+ export function assetFromSrc(src) {
9
+ if (/^[a-z][a-z0-9+.-]*:/i.test(src))
10
+ return null;
11
+ const p = path.posix.normalize(path.posix.join("pages", decodeURI(src)));
12
+ return p.startsWith("assets/") ? p : null;
13
+ }
14
+ /** Marcações e tamanho de cada imagem do manual usada nas páginas. Imagem apagada fica de fora. */
15
+ export async function loadImages(dir, pages) {
16
+ const images = new Map();
17
+ for (const page of pages) {
18
+ for (const [, src] of page.body.matchAll(IMAGE)) {
19
+ const asset = src && assetFromSrc(src);
20
+ if (!asset || images.has(asset))
21
+ continue;
22
+ const abs = resolveInside(dir, asset);
23
+ if (!(await exists(abs)))
24
+ continue;
25
+ images.set(asset, { asset, size: await imageSize(abs), annotations: await readAnnotations(dir, asset) });
26
+ }
27
+ }
28
+ return images;
29
+ }
30
+ /** Id da seção de uma página no manual exportado. */
31
+ export const pageAnchor = (pageId) => `p-${pageId}`;
32
+ /** Id de um título dentro de uma página: `#secao` de um link vira este id. */
33
+ export const headingAnchor = (pageId, text) => `p-${pageId}--${slugify(text)}`;
34
+ /**
35
+ * Link entre páginas (`./arquivo.md` ou `./arquivo.md#secao`) → âncora dentro do manual exportado.
36
+ * Outro tipo de link: null (fica como está).
37
+ */
38
+ export function pageLink(href, pages) {
39
+ const m = /^(?:\.\/)?([^/#?]+\.md)(?:#(.+))?$/.exec(href);
40
+ if (!m)
41
+ return null;
42
+ const page = pages.find((p) => p.fileName === decodeURI(m[1] ?? ""));
43
+ if (!page)
44
+ return null;
45
+ return `#${m[2] ? headingAnchor(page.id, decodeURI(m[2])) : pageAnchor(page.id)}`;
46
+ }
47
+ /** Só endereços seguros saem como link no manual exportado. */
48
+ export function safeHref(href) {
49
+ if (/^(#|mailto:|https?:\/\/)/i.test(href))
50
+ return href;
51
+ return null;
52
+ }
53
+ //# sourceMappingURL=refs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refs.js","sourceRoot":"","sources":["../../../src/core/export/refs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare function splitFrontmatter(text: string): {
2
+ data: Record<string, unknown>;
3
+ body: string;
4
+ };
5
+ export declare function joinFrontmatter(data: Record<string, unknown>, body: string): string;
@@ -0,0 +1,17 @@
1
+ import { parse, stringify } from "yaml";
2
+ const FENCE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
3
+ export function splitFrontmatter(text) {
4
+ const match = FENCE.exec(text);
5
+ if (!match)
6
+ return { data: {}, body: text };
7
+ const data = parse(match[1] ?? "");
8
+ if (data !== null && (typeof data !== "object" || Array.isArray(data))) {
9
+ throw new Error("Frontmatter inválido: esperado um mapa YAML.");
10
+ }
11
+ // joinFrontmatter põe uma linha em branco depois do bloco; ela não faz parte do corpo.
12
+ return { data: (data ?? {}), body: text.slice(match[0].length).replace(/^\r?\n/, "") };
13
+ }
14
+ export function joinFrontmatter(data, body) {
15
+ return `---\n${stringify(data).trimEnd()}\n---\n\n${body.replace(/^\n+/, "")}`;
16
+ }
17
+ //# sourceMappingURL=frontmatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/core/frontmatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAExC,MAAM,KAAK,GAAG,mCAAmC,CAAC;AAElD,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC5C,MAAM,IAAI,GAAY,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5C,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACvE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IACD,uFAAuF;IACvF,OAAO,EAAE,IAAI,EAAE,CAAC,IAAI,IAAI,EAAE,CAA4B,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC;AACpH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAA6B,EAAE,IAAY;IACzE,OAAO,QAAQ,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,YAAY,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC;AACjF,CAAC"}
@@ -0,0 +1,9 @@
1
+ /** Grava num arquivo temporário e renomeia, para um travamento no meio não deixar o arquivo pela metade. */
2
+ export declare function atomicWrite(file: string, content: string): Promise<void>;
3
+ /** Barreira de confiança: caminhos vêm do Agent e do navegador, então nada pode sair da pasta base. */
4
+ export declare function resolveInside(dir: string, relative: string): string;
5
+ /** `base.ext`, ou `base-2.ext`, `base-3.ext`… se já existir. */
6
+ export declare function uniquePath(parent: string, base: string, ext: string): Promise<string>;
7
+ export declare function exists(p: string): Promise<boolean>;
8
+ /** Caminho relativo com `/`, igual em qualquer sistema, para gravar em manual.json e no Markdown. */
9
+ export declare function relativePosix(from: string, to: string): string;
@@ -0,0 +1,33 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { rename, stat, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ /** Grava num arquivo temporário e renomeia, para um travamento no meio não deixar o arquivo pela metade. */
5
+ export async function atomicWrite(file, content) {
6
+ const tmp = `${file}.${randomUUID()}.tmp`;
7
+ await writeFile(tmp, content, "utf8");
8
+ await rename(tmp, file);
9
+ }
10
+ /** Barreira de confiança: caminhos vêm do Agent e do navegador, então nada pode sair da pasta base. */
11
+ export function resolveInside(dir, relative) {
12
+ const root = path.resolve(dir);
13
+ const abs = path.resolve(root, relative);
14
+ if (abs !== root && !abs.startsWith(root + path.sep))
15
+ throw new Error(`Caminho fora do manual: ${relative}`);
16
+ return abs;
17
+ }
18
+ /** `base.ext`, ou `base-2.ext`, `base-3.ext`… se já existir. */
19
+ export async function uniquePath(parent, base, ext) {
20
+ for (let i = 1;; i++) {
21
+ const candidate = path.join(parent, `${i === 1 ? base : `${base}-${i}`}${ext}`);
22
+ if (!(await exists(candidate)))
23
+ return candidate;
24
+ }
25
+ }
26
+ export async function exists(p) {
27
+ return stat(p).then(() => true, () => false);
28
+ }
29
+ /** Caminho relativo com `/`, igual em qualquer sistema, para gravar em manual.json e no Markdown. */
30
+ export function relativePosix(from, to) {
31
+ return path.relative(from, to).split(path.sep).join("/");
32
+ }
33
+ //# sourceMappingURL=fs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/core/fs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,4GAA4G;AAC5G,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,OAAe;IAC7D,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,UAAU,EAAE,MAAM,CAAC;IAC1C,MAAM,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACtC,MAAM,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,uGAAuG;AACvG,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,QAAgB;IACzD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACzC,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,QAAQ,EAAE,CAAC,CAAC;IAC7G,OAAO,GAAG,CAAC;AACb,CAAC;AAED,gEAAgE;AAChE,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAc,EAAE,IAAY,EAAE,GAAW;IACxE,KAAK,IAAI,CAAC,GAAG,CAAC,GAAI,CAAC,EAAE,EAAE,CAAC;QACtB,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC;QAChF,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;YAAE,OAAO,SAAS,CAAC;IACnD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,CAAS;IACpC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CACjB,GAAG,EAAE,CAAC,IAAI,EACV,GAAG,EAAE,CAAC,KAAK,CACZ,CAAC;AACJ,CAAC;AAED,qGAAqG;AACrG,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,EAAU;IACpD,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,11 @@
1
+ export { emptyAnnotations, parseAnnotations } from "./annotations.js";
2
+ export { cleanUnusedAssets, findUnusedAssets, readAnnotations, saveAsset, writeAnnotations } from "./assets.js";
3
+ export { EXPORT_DIR, EXPORT_FORMATS, type ExportFormat, type ExportResult, exportManual, findBrowser, setManualTheme, } from "./export/index.js";
4
+ export { resolveInside } from "./fs.js";
5
+ export { createManual, listManuals, readManual } from "./manuals.js";
6
+ export { addPage, movePage, type PageTreeNode, readPage, readPageTree, updatePage } from "./pages.js";
7
+ export { slugify } from "./text.js";
8
+ export { listTrash, removePage, restorePage, TRASH_DIR, type TrashItem } from "./trash.js";
9
+ export { flatten } from "./tree.js";
10
+ export * from "./types.js";
11
+ export { listVersions, publishVersion, readVersion, restoreVersion, saveVersion, VERSIONS_DIR, type Version, type VersionInfo, } from "./versions.js";
@@ -0,0 +1,12 @@
1
+ export { emptyAnnotations, parseAnnotations } from "./annotations.js";
2
+ export { cleanUnusedAssets, findUnusedAssets, readAnnotations, saveAsset, writeAnnotations } from "./assets.js";
3
+ export { EXPORT_DIR, EXPORT_FORMATS, exportManual, findBrowser, setManualTheme, } from "./export/index.js";
4
+ export { resolveInside } from "./fs.js";
5
+ export { createManual, listManuals, readManual } from "./manuals.js";
6
+ export { addPage, movePage, readPage, readPageTree, updatePage } from "./pages.js";
7
+ export { slugify } from "./text.js";
8
+ export { listTrash, removePage, restorePage, TRASH_DIR } from "./trash.js";
9
+ export { flatten } from "./tree.js";
10
+ export * from "./types.js";
11
+ export { listVersions, publishVersion, readVersion, restoreVersion, saveVersion, VERSIONS_DIR, } from "./versions.js";
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,eAAe,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAChH,OAAO,EACL,UAAU,EACV,cAAc,EAGd,YAAY,EACZ,WAAW,EACX,cAAc,GACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAqB,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtG,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAkB,MAAM,YAAY,CAAC;AAC3F,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,OAAO,EACL,YAAY,EACZ,cAAc,EACd,WAAW,EACX,cAAc,EACd,WAAW,EACX,YAAY,GAGb,MAAM,eAAe,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { type Manual, type ManualMeta } from "./types.js";
2
+ export declare const PAGES_DIR = "pages";
3
+ export declare const ASSETS_DIR = "assets";
4
+ export declare function createManual(workspace: string, opts: {
5
+ title: string;
6
+ language: string;
7
+ }): Promise<Manual>;
8
+ /**
9
+ * Manuais são as subpastas diretas do workspace que têm manual.json.
10
+ * Manual com manual.json ilegível fica de fora da lista, para um manual estragado não impedir
11
+ * de abrir os outros. Abrir ele pelo endereço mostra o erro de readManual.
12
+ */
13
+ export declare function listManuals(workspace: string): Promise<Manual[]>;
14
+ export declare function readManual(dir: string): Promise<ManualMeta>;
15
+ export declare function writeManual(dir: string, meta: ManualMeta): Promise<void>;
16
+ /** Lê o manual, aplica a mudança e grava de volta. Concentra o ciclo ler-alterar-gravar da árvore. */
17
+ export declare function updateManual<T>(dir: string, change: (meta: ManualMeta) => T): Promise<T>;
@@ -0,0 +1,66 @@
1
+ import { mkdir, readdir, readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { atomicWrite, exists, uniquePath } from "./fs.js";
4
+ import { required, slugify } from "./text.js";
5
+ import { FORMAT_VERSION } from "./types.js";
6
+ const META_FILE = "manual.json";
7
+ export const PAGES_DIR = "pages";
8
+ export const ASSETS_DIR = "assets";
9
+ export async function createManual(workspace, opts) {
10
+ const title = required(opts.title, "título do manual");
11
+ const dir = await uniquePath(workspace, slugify(title), "");
12
+ const meta = { version: FORMAT_VERSION, title, language: opts.language, pages: [] };
13
+ await mkdir(path.join(dir, PAGES_DIR), { recursive: true });
14
+ await mkdir(path.join(dir, ASSETS_DIR), { recursive: true });
15
+ await writeManual(dir, meta);
16
+ return { dir, meta };
17
+ }
18
+ /**
19
+ * Manuais são as subpastas diretas do workspace que têm manual.json.
20
+ * Manual com manual.json ilegível fica de fora da lista, para um manual estragado não impedir
21
+ * de abrir os outros. Abrir ele pelo endereço mostra o erro de readManual.
22
+ */
23
+ export async function listManuals(workspace) {
24
+ const entries = await readdir(workspace, { withFileTypes: true });
25
+ const manuals = [];
26
+ for (const e of entries) {
27
+ if (!e.isDirectory())
28
+ continue;
29
+ const dir = path.join(workspace, e.name);
30
+ if (!(await exists(path.join(dir, META_FILE))))
31
+ continue;
32
+ try {
33
+ manuals.push({ dir, meta: await readManual(dir) });
34
+ }
35
+ catch {
36
+ // manual.json corrompido: os outros manuais continuam abrindo.
37
+ }
38
+ }
39
+ return manuals.sort((a, b) => a.meta.title.localeCompare(b.meta.title));
40
+ }
41
+ export async function readManual(dir) {
42
+ let meta;
43
+ try {
44
+ meta = JSON.parse(await readFile(path.join(dir, META_FILE), "utf8"));
45
+ }
46
+ catch (err) {
47
+ if (err instanceof SyntaxError)
48
+ throw new Error(`O ${META_FILE} do manual "${path.basename(dir)}" não é um JSON válido: ${err.message}`);
49
+ throw err;
50
+ }
51
+ if (meta?.version !== FORMAT_VERSION) {
52
+ throw new Error(`${META_FILE}: versão de formato não suportada (${String(meta?.version)})`);
53
+ }
54
+ return meta;
55
+ }
56
+ export async function writeManual(dir, meta) {
57
+ await atomicWrite(path.join(dir, META_FILE), `${JSON.stringify(meta, null, 2)}\n`);
58
+ }
59
+ /** Lê o manual, aplica a mudança e grava de volta. Concentra o ciclo ler-alterar-gravar da árvore. */
60
+ export async function updateManual(dir, change) {
61
+ const meta = await readManual(dir);
62
+ const result = change(meta);
63
+ await writeManual(dir, meta);
64
+ return result;
65
+ }
66
+ //# sourceMappingURL=manuals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manuals.js","sourceRoot":"","sources":["../../src/core/manuals.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import { type Page, type PageStatus } from "./types.js";
2
+ /** Página pode ficar sem título enquanto é escrita: a árvore mostra "Sem título" e o arquivo vira sem-titulo.md. */
3
+ export declare function addPage(dir: string, opts: {
4
+ title: string;
5
+ body?: string;
6
+ parentId?: string | null;
7
+ index?: number;
8
+ }): Promise<Page>;
9
+ export declare function readPage(dir: string, id: string): Promise<Page>;
10
+ export interface PageTreeNode {
11
+ id: string;
12
+ title: string;
13
+ status: PageStatus;
14
+ file: string;
15
+ /** Preenchido quando o arquivo da página sumiu ou está com erro: a página aparece, com o aviso. */
16
+ problem?: string;
17
+ /** Versão escolhida para o manual final, quando há uma (ver versions.ts). */
18
+ published?: string;
19
+ children: PageTreeNode[];
20
+ }
21
+ /**
22
+ * Árvore do manual com o título de cada página (que fica no frontmatter). Lê o manual.json uma vez.
23
+ * Uma página com problema não derruba a árvore: ela vem com `problem` e o resto continua.
24
+ */
25
+ export declare function readPageTree(dir: string): Promise<PageTreeNode[]>;
26
+ /**
27
+ * Quando o título muda, o arquivo é renomeado para acompanhar, para a pasta continuar legível
28
+ * para pessoas e IAs. O id no frontmatter não muda, e é ele que as áreas clicáveis usam.
29
+ */
30
+ export declare function updatePage(dir: string, id: string, patch: {
31
+ title?: string;
32
+ body?: string;
33
+ status?: PageStatus;
34
+ }): Promise<Page>;
35
+ export declare function movePage(dir: string, id: string, to: {
36
+ parentId: string | null;
37
+ index: number;
38
+ }): Promise<void>;
@@ -0,0 +1,138 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { readFile, rm } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { joinFrontmatter, splitFrontmatter } from "./frontmatter.js";
5
+ import { atomicWrite, relativePosix, resolveInside, uniquePath } from "./fs.js";
6
+ import { PAGES_DIR, readManual, updateManual } from "./manuals.js";
7
+ import { slugify } from "./text.js";
8
+ import { childrenOf, clampIndex, findIn, findNode, flatten } from "./tree.js";
9
+ import { PAGE_STATUSES } from "./types.js";
10
+ /** Página pode ficar sem título enquanto é escrita: a árvore mostra "Sem título" e o arquivo vira sem-titulo.md. */
11
+ export async function addPage(dir, opts) {
12
+ const title = opts.title.trim();
13
+ const page = {
14
+ id: randomUUID(),
15
+ title,
16
+ status: "rascunho",
17
+ body: opts.body ?? "",
18
+ file: await newPageFile(dir, title),
19
+ };
20
+ await updateManual(dir, (meta) => {
21
+ const siblings = childrenOf(meta.pages, opts.parentId);
22
+ siblings.splice(clampIndex(opts.index, siblings.length), 0, { id: page.id, file: page.file, children: [] });
23
+ });
24
+ await writePageFile(dir, page);
25
+ return page;
26
+ }
27
+ export async function readPage(dir, id) {
28
+ const { node } = findNode((await readManual(dir)).pages, id);
29
+ return { id, ...(await readPageFile(dir, node.file)), file: node.file };
30
+ }
31
+ /**
32
+ * Árvore do manual com o título de cada página (que fica no frontmatter). Lê o manual.json uma vez.
33
+ * Uma página com problema não derruba a árvore: ela vem com `problem` e o resto continua.
34
+ */
35
+ export async function readPageTree(dir) {
36
+ const walk = (nodes) => Promise.all(nodes.map(async (n) => {
37
+ const base = {
38
+ id: n.id,
39
+ file: n.file,
40
+ ...(n.published ? { published: n.published } : {}),
41
+ children: await walk(n.children),
42
+ };
43
+ try {
44
+ const { title, status } = await readPageFile(dir, n.file);
45
+ return { ...base, title, status };
46
+ }
47
+ catch (err) {
48
+ return { ...base, title: "", status: "rascunho", problem: err instanceof Error ? err.message : String(err) };
49
+ }
50
+ }));
51
+ return walk((await readManual(dir)).pages);
52
+ }
53
+ /** Lê título e corpo de um arquivo de página, com mensagem clara quando ele foi apagado ou estragado. */
54
+ async function readPageFile(dir, file) {
55
+ let text;
56
+ try {
57
+ text = await readFile(resolveInside(dir, file), "utf8");
58
+ }
59
+ catch (err) {
60
+ if (err.code === "ENOENT") {
61
+ throw new Error(`O arquivo ${file} não foi encontrado. Ele foi apagado ou renomeado fora do Studio.`);
62
+ }
63
+ throw err;
64
+ }
65
+ try {
66
+ const { data, body } = splitFrontmatter(text);
67
+ const status = PAGE_STATUSES.includes(data.status) ? data.status : "rascunho";
68
+ return { title: typeof data.title === "string" ? data.title : "", status, body };
69
+ }
70
+ catch (err) {
71
+ const detail = err instanceof Error ? err.message.split("\n")[0] : String(err);
72
+ throw new Error(`O cabeçalho (frontmatter) de ${file} tem um erro: ${detail}`);
73
+ }
74
+ }
75
+ /**
76
+ * Quando o título muda, o arquivo é renomeado para acompanhar, para a pasta continuar legível
77
+ * para pessoas e IAs. O id no frontmatter não muda, e é ele que as áreas clicáveis usam.
78
+ */
79
+ export async function updatePage(dir, id, patch) {
80
+ const page = await readPage(dir, id);
81
+ if (patch.status !== undefined && !PAGE_STATUSES.includes(patch.status)) {
82
+ throw new Error(`Status inválido: ${patch.status}. Use ${PAGE_STATUSES.join(", ")}.`);
83
+ }
84
+ const next = {
85
+ ...page,
86
+ ...(patch.status === undefined ? {} : { status: patch.status }),
87
+ ...(patch.title === undefined ? {} : { title: patch.title.trim() }),
88
+ ...(patch.body === undefined ? {} : { body: patch.body }),
89
+ };
90
+ if (slugify(next.title) === slugify(page.title)) {
91
+ await writePageFile(dir, next);
92
+ return next;
93
+ }
94
+ next.file = await newPageFile(dir, next.title);
95
+ await writePageFile(dir, next);
96
+ const others = await updateManual(dir, (meta) => {
97
+ findNode(meta.pages, id).node.file = next.file;
98
+ return flatten(meta.pages).filter((n) => n.id !== id);
99
+ });
100
+ await rm(resolveInside(dir, page.file), { force: true });
101
+ await relink(dir, others.map((n) => n.file), page.file, next.file);
102
+ return next;
103
+ }
104
+ /** Links entre páginas são `./arquivo.md`. Quando um arquivo muda de nome, as outras páginas acompanham. */
105
+ async function relink(dir, files, from, to) {
106
+ const name = (f) => path.posix.basename(f);
107
+ const escaped = name(from).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
108
+ // ](arquivo.md ou ](./arquivo.md seguido de ) ou de #âncora
109
+ // Precisa ser new RegExp: o nome do arquivo entra no padrão (teste: "atualiza os links das outras páginas")
110
+ const pattern = new RegExp(`(\\]\\()(?:\\./)?${escaped}(?=[)#])`, "g");
111
+ for (const file of files) {
112
+ const abs = resolveInside(dir, file);
113
+ const text = await readFile(abs, "utf8");
114
+ const updated = text.replace(pattern, `$1./${name(to)}`);
115
+ if (updated !== text)
116
+ await atomicWrite(abs, updated);
117
+ }
118
+ }
119
+ export async function movePage(dir, id, to) {
120
+ await updateManual(dir, (meta) => {
121
+ const { node, siblings } = findNode(meta.pages, id);
122
+ if (to.parentId && (to.parentId === id || findIn(node.children, to.parentId))) {
123
+ throw new Error("Não dá para mover uma página para dentro dela mesma.");
124
+ }
125
+ siblings.splice(siblings.indexOf(node), 1);
126
+ const target = childrenOf(meta.pages, to.parentId);
127
+ target.splice(clampIndex(to.index, target.length), 0, node);
128
+ });
129
+ }
130
+ async function newPageFile(dir, title) {
131
+ return relativePosix(dir, await uniquePath(path.join(dir, PAGES_DIR), slugify(title), ".md"));
132
+ }
133
+ async function writePageFile(dir, page) {
134
+ // Rascunho é o padrão e não vai para o arquivo: página escrita à mão continua igual.
135
+ const status = page.status === "rascunho" ? {} : { status: page.status };
136
+ await atomicWrite(resolveInside(dir, page.file), joinFrontmatter({ id: page.id, title: page.title, ...status }, page.body));
137
+ }
138
+ //# sourceMappingURL=pages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pages.js","sourceRoot":"","sources":["../../src/core/pages.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare function slugify(title: string): string;
2
+ /** Texto obrigatório, sem espaços nas pontas. `what` entra na mensagem: "O título da página não pode ficar vazio." */
3
+ export declare function required(value: string, what: string): string;
@@ -0,0 +1,19 @@
1
+ export function slugify(title) {
2
+ const slug = title
3
+ .normalize("NFD")
4
+ .replace(/[̀-ͯ]/g, "")
5
+ .toLowerCase()
6
+ .replace(/[^a-z0-9]+/g, "-")
7
+ .replace(/^-+|-+$/g, "")
8
+ .slice(0, 60)
9
+ .replace(/-+$/, "");
10
+ return slug || "sem-titulo";
11
+ }
12
+ /** Texto obrigatório, sem espaços nas pontas. `what` entra na mensagem: "O título da página não pode ficar vazio." */
13
+ export function required(value, what) {
14
+ const v = value.trim();
15
+ if (!v)
16
+ throw new Error(`O ${what} não pode ficar vazio.`);
17
+ return v;
18
+ }
19
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/core/text.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,MAAM,IAAI,GAAG,KAAK;SACf,SAAS,CAAC,KAAK,CAAC;SAChB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACtB,OAAO,IAAI,IAAI,YAAY,CAAC;AAC9B,CAAC;AAED,sHAAsH;AACtH,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAE,IAAY;IAClD,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACvB,IAAI,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,KAAK,IAAI,wBAAwB,CAAC,CAAC;IAC3D,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,25 @@
1
+ /** Páginas excluídas ficam aqui até serem restauradas ou apagadas à mão. */
2
+ export declare const TRASH_DIR = ".lixeira";
3
+ export interface TrashItem {
4
+ trashId: string;
5
+ /** Id da página principal, para abrir de novo depois de restaurar. */
6
+ pageId: string;
7
+ title: string;
8
+ /** Quantas páginas voltam ao restaurar (a página mais as subpáginas). */
9
+ pages: number;
10
+ deletedAt: string;
11
+ }
12
+ /** O que está na lixeira, da exclusão mais recente para a mais antiga. */
13
+ export declare function listTrash(dir: string): Promise<TrashItem[]>;
14
+ /**
15
+ * Tira a página (e as subpáginas) da árvore e move os arquivos para a lixeira do manual.
16
+ * Devolve o id da lixeira, usado para desfazer. Imagens ficam em assets/, porque outras páginas podem usar.
17
+ */
18
+ export declare function removePage(dir: string, id: string): Promise<{
19
+ trashId: string;
20
+ }>;
21
+ /**
22
+ * Devolve a página ao lugar de onde saiu. Se o pai também foi excluído, ela volta para a raiz.
23
+ * Se outro arquivo ocupou o nome nesse meio tempo, ela ganha um nome novo.
24
+ */
25
+ export declare function restorePage(dir: string, trashId: string): Promise<void>;
@@ -0,0 +1,79 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdir, readdir, readFile, rename, rm } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { splitFrontmatter } from "./frontmatter.js";
5
+ import { atomicWrite, exists, relativePosix, resolveInside, uniquePath } from "./fs.js";
6
+ import { PAGES_DIR, updateManual } from "./manuals.js";
7
+ import { childrenOf, clampIndex, findIn, findNode, flatten } from "./tree.js";
8
+ /** Páginas excluídas ficam aqui até serem restauradas ou apagadas à mão. */
9
+ export const TRASH_DIR = ".lixeira";
10
+ /** O que está na lixeira, da exclusão mais recente para a mais antiga. */
11
+ export async function listTrash(dir) {
12
+ const root = path.join(dir, TRASH_DIR);
13
+ if (!(await exists(root)))
14
+ return [];
15
+ const items = [];
16
+ for (const trashId of await readdir(root)) {
17
+ const entryFile = path.join(root, trashId, "entry.json");
18
+ if (!(await exists(entryFile)))
19
+ continue;
20
+ const entry = JSON.parse(await readFile(entryFile, "utf8"));
21
+ const pageFile = path.join(root, trashId, entry.node.file);
22
+ const data = (await exists(pageFile)) ? splitFrontmatter(await readFile(pageFile, "utf8")).data : {};
23
+ const title = typeof data.title === "string" ? data.title : "";
24
+ items.push({ trashId, pageId: entry.node.id, title, pages: flatten([entry.node]).length, deletedAt: entry.deletedAt });
25
+ }
26
+ return items.sort((a, b) => b.deletedAt.localeCompare(a.deletedAt));
27
+ }
28
+ /**
29
+ * Tira a página (e as subpáginas) da árvore e move os arquivos para a lixeira do manual.
30
+ * Devolve o id da lixeira, usado para desfazer. Imagens ficam em assets/, porque outras páginas podem usar.
31
+ */
32
+ export async function removePage(dir, id) {
33
+ const entry = await updateManual(dir, (meta) => {
34
+ const { node, siblings } = findNode(meta.pages, id);
35
+ const index = siblings.indexOf(node);
36
+ siblings.splice(index, 1);
37
+ return { node, parentId: parentOf(meta.pages, siblings), index, deletedAt: new Date().toISOString() };
38
+ });
39
+ const trashId = randomUUID();
40
+ const trash = path.join(dir, TRASH_DIR, trashId);
41
+ await mkdir(path.join(trash, PAGES_DIR), { recursive: true });
42
+ for (const n of flatten([entry.node])) {
43
+ const from = resolveInside(dir, n.file);
44
+ if (await exists(from))
45
+ await rename(from, path.join(trash, n.file));
46
+ }
47
+ await atomicWrite(path.join(trash, "entry.json"), `${JSON.stringify(entry, null, 2)}\n`);
48
+ return { trashId };
49
+ }
50
+ /**
51
+ * Devolve a página ao lugar de onde saiu. Se o pai também foi excluído, ela volta para a raiz.
52
+ * Se outro arquivo ocupou o nome nesse meio tempo, ela ganha um nome novo.
53
+ */
54
+ export async function restorePage(dir, trashId) {
55
+ const trash = resolveInside(path.join(dir, TRASH_DIR), trashId);
56
+ const entry = JSON.parse(await readFile(path.join(trash, "entry.json"), "utf8"));
57
+ for (const n of flatten([entry.node])) {
58
+ const from = path.join(trash, n.file);
59
+ let to = resolveInside(dir, n.file);
60
+ if (await exists(to)) {
61
+ to = await uniquePath(path.join(dir, PAGES_DIR), path.basename(n.file, ".md"), ".md");
62
+ n.file = relativePosix(dir, to);
63
+ }
64
+ if (await exists(from))
65
+ await rename(from, to);
66
+ }
67
+ await updateManual(dir, (meta) => {
68
+ const parentId = entry.parentId && findIn(meta.pages, entry.parentId) ? entry.parentId : null;
69
+ const siblings = childrenOf(meta.pages, parentId);
70
+ siblings.splice(clampIndex(entry.index, siblings.length), 0, entry.node);
71
+ });
72
+ await rm(trash, { recursive: true, force: true });
73
+ }
74
+ function parentOf(nodes, siblings) {
75
+ if (siblings === nodes)
76
+ return null;
77
+ return flatten(nodes).find((n) => n.children === siblings)?.id ?? null;
78
+ }
79
+ //# sourceMappingURL=trash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trash.js","sourceRoot":"","sources":["../../src/core/trash.ts"],"names":[],"mappings":"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"}