@creative-ia/cortex 1.0.5
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/README.md +41 -0
- package/dist/config/cloud-proxy.d.ts +15 -0
- package/dist/config/cloud-proxy.js +63 -0
- package/dist/config/cloudwatch-store.d.ts +13 -0
- package/dist/config/cloudwatch-store.js +66 -0
- package/dist/config/license.d.ts +29 -0
- package/dist/config/license.js +165 -0
- package/dist/config/ssm-store.d.ts +2 -0
- package/dist/config/ssm-store.js +38 -0
- package/dist/config/telemetry.d.ts +17 -0
- package/dist/config/telemetry.js +93 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +460 -0
- package/dist/knowledge/dynamo-store.d.ts +17 -0
- package/dist/knowledge/dynamo-store.js +85 -0
- package/dist/knowledge/embeddings.d.ts +2 -0
- package/dist/knowledge/embeddings.js +36 -0
- package/dist/knowledge/loader.d.ts +8 -0
- package/dist/knowledge/loader.js +57 -0
- package/dist/lambda-package.zip +0 -0
- package/dist/lambda.d.ts +22 -0
- package/dist/lambda.js +496 -0
- package/dist/package.json +1 -0
- package/dist/tools/advance-process.d.ts +7 -0
- package/dist/tools/advance-process.js +128 -0
- package/dist/tools/analyze-code.d.ts +7 -0
- package/dist/tools/analyze-code.js +131 -0
- package/dist/tools/analyze-docs.d.ts +8 -0
- package/dist/tools/analyze-docs.js +147 -0
- package/dist/tools/config-registry.d.ts +3 -0
- package/dist/tools/config-registry.js +20 -0
- package/dist/tools/create-process.d.ts +6 -0
- package/dist/tools/create-process.js +257 -0
- package/dist/tools/decompose-epic.d.ts +7 -0
- package/dist/tools/decompose-epic.js +603 -0
- package/dist/tools/diagrams.d.ts +51 -0
- package/dist/tools/diagrams.js +304 -0
- package/dist/tools/generate-report.d.ts +9 -0
- package/dist/tools/generate-report.js +891 -0
- package/dist/tools/generate-wiki.d.ts +10 -0
- package/dist/tools/generate-wiki.js +700 -0
- package/dist/tools/get-architecture.d.ts +6 -0
- package/dist/tools/get-architecture.js +78 -0
- package/dist/tools/get-code-standards.d.ts +7 -0
- package/dist/tools/get-code-standards.js +52 -0
- package/dist/tools/init-process.d.ts +7 -0
- package/dist/tools/init-process.js +82 -0
- package/dist/tools/knowledge-crud.d.ts +26 -0
- package/dist/tools/knowledge-crud.js +142 -0
- package/dist/tools/logo-base64.d.ts +1 -0
- package/dist/tools/logo-base64.js +1 -0
- package/dist/tools/logs-query.d.ts +15 -0
- package/dist/tools/logs-query.js +46 -0
- package/dist/tools/reverse-engineer.d.ts +13 -0
- package/dist/tools/reverse-engineer.js +956 -0
- package/dist/tools/semantic-search.d.ts +7 -0
- package/dist/tools/semantic-search.js +68 -0
- package/dist/tools/update-process.d.ts +17 -0
- package/dist/tools/update-process.js +195 -0
- package/dist/tools/validate-idea.d.ts +7 -0
- package/dist/tools/validate-idea.js +339 -0
- package/dist/tools/validate-process.d.ts +6 -0
- package/dist/tools/validate-process.js +102 -0
- package/package.json +31 -0
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: generate_wiki
|
|
3
|
+
* Gera/atualiza wiki navegável dentro do processo com:
|
|
4
|
+
* - index.html (home com nav-grid de cards)
|
|
5
|
+
* - styles.css (CSS compartilhado dark theme)
|
|
6
|
+
* - loader.js (navegação lateral, prev/next, lightbox)
|
|
7
|
+
* - pages/ (cada .md convertido em .html com layout sidebar + breadcrumb)
|
|
8
|
+
*
|
|
9
|
+
* Formato de referência: processo 6ed1771f
|
|
10
|
+
*/
|
|
11
|
+
import { writeFile, mkdir, readFile, readdir } from "node:fs/promises";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { existsSync } from "node:fs";
|
|
14
|
+
// ============================================================
|
|
15
|
+
// MARKDOWN → HTML CONVERTER (lightweight)
|
|
16
|
+
// ============================================================
|
|
17
|
+
function mdToHtml(md) {
|
|
18
|
+
let html = md;
|
|
19
|
+
html = html.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
20
|
+
// Code blocks (``` ... ```)
|
|
21
|
+
html = html.replace(/```[\w]*\n([\s\S]*?)```/g, (_m, code) => `<pre style="background:rgba(0,224,255,.04);border:1px solid var(--line);border-radius:12px;padding:16px;overflow-x:auto;font-size:.85rem;color:var(--accent)"><code>${code.trim()}</code></pre>`);
|
|
22
|
+
html = html.replace(/^#### (.+)$/gm, '<h4>$1</h4>');
|
|
23
|
+
html = html.replace(/^### (.+)$/gm, (_m, title) => {
|
|
24
|
+
const id = title.replace(/[^\w\s-]/g, '').replace(/\s+/g, '-').toLowerCase();
|
|
25
|
+
return `<h3 id="${id}">${title}</h3>`;
|
|
26
|
+
});
|
|
27
|
+
html = html.replace(/^## (.+)$/gm, (_m, title) => {
|
|
28
|
+
const id = title.replace(/[^\w\s-]/g, '').replace(/\s+/g, '-').toLowerCase();
|
|
29
|
+
return `<h2 id="${id}">${title}</h2>`;
|
|
30
|
+
});
|
|
31
|
+
html = html.replace(/^# (.+)$/gm, '<h1>$1</h1>');
|
|
32
|
+
html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
|
33
|
+
html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
|
|
34
|
+
html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
35
|
+
// Images: 
|
|
36
|
+
html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<div class="diagram-container"><img src="$2" alt="$1" style="max-width:100%;border-radius:12px" /></div>');
|
|
37
|
+
// Links: [text](url)
|
|
38
|
+
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
|
|
39
|
+
html = html.replace(/^> (.+)$/gm, '<div class="callout">$1</div>');
|
|
40
|
+
html = html.replace(/^---$/gm, '<hr>');
|
|
41
|
+
html = convertTables(html);
|
|
42
|
+
html = html.replace(/^- \[x\] (.+)$/gm, '<li class="check done">✅ $1</li>');
|
|
43
|
+
html = html.replace(/^- \[ \] (.+)$/gm, '<li class="check">☐ $1</li>');
|
|
44
|
+
html = html.replace(/^- (.+)$/gm, '<li>$1</li>');
|
|
45
|
+
html = html.replace(/^(\d+)\. (.+)$/gm, '<li><strong>$1.</strong> $2</li>');
|
|
46
|
+
html = html.replace(/((?:<li[^>]*>.*<\/li>\n?)+)/g, '<ul>$1</ul>');
|
|
47
|
+
html = html.replace(/^(?!<[a-z/]|$)(.+)$/gm, '<p>$1</p>');
|
|
48
|
+
html = html.replace(/<p>\s*<\/p>/g, '');
|
|
49
|
+
return html;
|
|
50
|
+
}
|
|
51
|
+
function convertTables(html) {
|
|
52
|
+
const lines = html.split('\n');
|
|
53
|
+
const result = [];
|
|
54
|
+
let inTable = false;
|
|
55
|
+
let tableRows = [];
|
|
56
|
+
for (const line of lines) {
|
|
57
|
+
const trimmed = line.trim();
|
|
58
|
+
if (trimmed.startsWith('|') && trimmed.endsWith('|')) {
|
|
59
|
+
if (trimmed.match(/^\|[\s-:|]+\|$/))
|
|
60
|
+
continue;
|
|
61
|
+
tableRows.push(trimmed);
|
|
62
|
+
inTable = true;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
if (inTable) {
|
|
66
|
+
result.push(renderTable(tableRows));
|
|
67
|
+
tableRows = [];
|
|
68
|
+
inTable = false;
|
|
69
|
+
}
|
|
70
|
+
result.push(line);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (inTable)
|
|
74
|
+
result.push(renderTable(tableRows));
|
|
75
|
+
return result.join('\n');
|
|
76
|
+
}
|
|
77
|
+
function renderTable(rows) {
|
|
78
|
+
if (rows.length === 0)
|
|
79
|
+
return '';
|
|
80
|
+
const parseRow = (r) => r.split('|').slice(1, -1).map(c => c.trim());
|
|
81
|
+
const header = parseRow(rows[0]);
|
|
82
|
+
const body = rows.slice(1).map(parseRow);
|
|
83
|
+
let t = '<table class="data-table"><thead><tr>';
|
|
84
|
+
header.forEach(h => { t += `<th>${h}</th>`; });
|
|
85
|
+
t += '</tr></thead><tbody>';
|
|
86
|
+
body.forEach(row => {
|
|
87
|
+
t += '<tr>';
|
|
88
|
+
row.forEach(c => { t += `<td>${c}</td>`; });
|
|
89
|
+
t += '</tr>';
|
|
90
|
+
});
|
|
91
|
+
t += '</tbody></table>';
|
|
92
|
+
return t;
|
|
93
|
+
}
|
|
94
|
+
// ============================================================
|
|
95
|
+
// Extract TOC headings from markdown
|
|
96
|
+
// ============================================================
|
|
97
|
+
function extractTocFromMd(md) {
|
|
98
|
+
const toc = [];
|
|
99
|
+
const lines = md.split('\n');
|
|
100
|
+
for (const line of lines) {
|
|
101
|
+
const m = line.match(/^(#{2,3}) (.+)$/);
|
|
102
|
+
if (m) {
|
|
103
|
+
const level = m[1].length;
|
|
104
|
+
const title = m[2].trim();
|
|
105
|
+
const id = title.replace(/[^\w\s-]/g, '').replace(/\s+/g, '-').toLowerCase();
|
|
106
|
+
toc.push({ id, title, level });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return toc;
|
|
110
|
+
}
|
|
111
|
+
// ============================================================
|
|
112
|
+
// Extract first paragraph as description
|
|
113
|
+
// ============================================================
|
|
114
|
+
function extractDescription(md) {
|
|
115
|
+
const lines = md.split('\n');
|
|
116
|
+
for (const line of lines) {
|
|
117
|
+
const trimmed = line.trim();
|
|
118
|
+
if (!trimmed)
|
|
119
|
+
continue;
|
|
120
|
+
if (trimmed.startsWith('#'))
|
|
121
|
+
continue;
|
|
122
|
+
if (trimmed.startsWith('>'))
|
|
123
|
+
continue;
|
|
124
|
+
if (trimmed.startsWith('|'))
|
|
125
|
+
continue;
|
|
126
|
+
if (trimmed.startsWith('-'))
|
|
127
|
+
continue;
|
|
128
|
+
if (trimmed.startsWith('```'))
|
|
129
|
+
continue;
|
|
130
|
+
// Found a text paragraph
|
|
131
|
+
return trimmed.replace(/\*\*/g, '').replace(/\*/g, '').substring(0, 200);
|
|
132
|
+
}
|
|
133
|
+
return '';
|
|
134
|
+
}
|
|
135
|
+
// ============================================================
|
|
136
|
+
// ICON MAPPING — guess icon from artifact name
|
|
137
|
+
// ============================================================
|
|
138
|
+
function guessIcon(id, category) {
|
|
139
|
+
const lower = id.toLowerCase();
|
|
140
|
+
if (lower.includes('readme'))
|
|
141
|
+
return 'info';
|
|
142
|
+
if (lower.includes('engenharia-reversa') || lower.includes('analise'))
|
|
143
|
+
return 'search';
|
|
144
|
+
if (lower.includes('repo') || lower.includes('pacote') || lower.includes('package'))
|
|
145
|
+
return 'folder_copy';
|
|
146
|
+
if (lower.includes('bff') || lower.includes('graphql') || lower.includes('contrato'))
|
|
147
|
+
return 'hub';
|
|
148
|
+
if (lower.includes('backend') || lower.includes('micro'))
|
|
149
|
+
return 'dns';
|
|
150
|
+
if (lower.includes('infra') || lower.includes('cloud') || lower.includes('aws'))
|
|
151
|
+
return 'cloud';
|
|
152
|
+
if (lower.includes('diagrama') || lower.includes('arquitetura'))
|
|
153
|
+
return 'schema';
|
|
154
|
+
if (lower.includes('epico') || lower.includes('feature') || lower.includes('epic'))
|
|
155
|
+
return 'view_kanban';
|
|
156
|
+
if (lower.includes('onda') || lower.includes('execu'))
|
|
157
|
+
return 'rocket_launch';
|
|
158
|
+
if (lower.includes('branch') || lower.includes('git'))
|
|
159
|
+
return 'account_tree';
|
|
160
|
+
if (lower.includes('baseline') || lower.includes('as-is'))
|
|
161
|
+
return 'assessment';
|
|
162
|
+
if (lower.includes('estrategia') || lower.includes('strategy'))
|
|
163
|
+
return 'lightbulb';
|
|
164
|
+
if (lower.includes('relatorio') || lower.includes('report') || lower.includes('gap'))
|
|
165
|
+
return 'summarize';
|
|
166
|
+
if (lower.includes('wireframe') || lower.includes('design') || lower.includes('ui'))
|
|
167
|
+
return 'palette';
|
|
168
|
+
if (lower.includes('dashboard') || lower.includes('realtime'))
|
|
169
|
+
return 'monitoring';
|
|
170
|
+
if (lower.includes('databricks') || lower.includes('dados') || lower.includes('data'))
|
|
171
|
+
return 'database';
|
|
172
|
+
if (lower.includes('stakeholder'))
|
|
173
|
+
return 'groups';
|
|
174
|
+
if (lower.includes('knowledge') || lower.includes('conhecimento'))
|
|
175
|
+
return 'school';
|
|
176
|
+
if (lower.includes('task'))
|
|
177
|
+
return 'task_alt';
|
|
178
|
+
if (lower.includes('user-stor'))
|
|
179
|
+
return 'person';
|
|
180
|
+
if (category === 'Épicos')
|
|
181
|
+
return 'inventory_2';
|
|
182
|
+
if (category === 'Features')
|
|
183
|
+
return 'category';
|
|
184
|
+
if (category === 'User Stories')
|
|
185
|
+
return 'person';
|
|
186
|
+
if (category === 'Tasks')
|
|
187
|
+
return 'task_alt';
|
|
188
|
+
return 'description';
|
|
189
|
+
}
|
|
190
|
+
// ============================================================
|
|
191
|
+
// SCAN ARTIFACTS
|
|
192
|
+
// ============================================================
|
|
193
|
+
const CATEGORY_MAP = {
|
|
194
|
+
"root": { label: "Documentos do Processo", priority: 0 },
|
|
195
|
+
"epics": { label: "Épicos", priority: 1 },
|
|
196
|
+
"features": { label: "Features", priority: 2 },
|
|
197
|
+
"user-stories": { label: "User Stories", priority: 3 },
|
|
198
|
+
"tasks": { label: "Tasks", priority: 4 },
|
|
199
|
+
};
|
|
200
|
+
async function scanArtifacts(processDir) {
|
|
201
|
+
const artifacts = [];
|
|
202
|
+
// Scan root .md files
|
|
203
|
+
try {
|
|
204
|
+
const rootFiles = await readdir(processDir);
|
|
205
|
+
for (const f of rootFiles) {
|
|
206
|
+
if (!f.endsWith(".md"))
|
|
207
|
+
continue;
|
|
208
|
+
const content = await readFile(join(processDir, f), "utf-8");
|
|
209
|
+
const titleMatch = content.match(/^# (.+)$/m);
|
|
210
|
+
const title = titleMatch ? titleMatch[1].trim() : f.replace(".md", "");
|
|
211
|
+
const id = f.replace(".md", "");
|
|
212
|
+
const slug = id;
|
|
213
|
+
const cat = CATEGORY_MAP.root;
|
|
214
|
+
const description = extractDescription(content);
|
|
215
|
+
artifacts.push({
|
|
216
|
+
file: f, dir: "root", id, slug, title,
|
|
217
|
+
icon: guessIcon(id, cat.label),
|
|
218
|
+
description,
|
|
219
|
+
category: cat.label, priority: cat.priority,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
catch { /* ignore */ }
|
|
224
|
+
// Scan subdirectories
|
|
225
|
+
for (const subdir of ["epics", "features", "user-stories", "tasks"]) {
|
|
226
|
+
const dirPath = join(processDir, subdir);
|
|
227
|
+
if (!existsSync(dirPath))
|
|
228
|
+
continue;
|
|
229
|
+
try {
|
|
230
|
+
const files = await readdir(dirPath);
|
|
231
|
+
for (const f of files) {
|
|
232
|
+
if (!f.endsWith(".md"))
|
|
233
|
+
continue;
|
|
234
|
+
const content = await readFile(join(dirPath, f), "utf-8");
|
|
235
|
+
const titleMatch = content.match(/^# (.+)$/m);
|
|
236
|
+
const title = titleMatch ? titleMatch[1].trim() : f.replace(".md", "");
|
|
237
|
+
const id = f.replace(".md", "");
|
|
238
|
+
const slug = `${subdir}--${id}`;
|
|
239
|
+
const cat = CATEGORY_MAP[subdir] || CATEGORY_MAP.root;
|
|
240
|
+
const description = extractDescription(content);
|
|
241
|
+
artifacts.push({
|
|
242
|
+
file: f, dir: subdir, id, slug, title,
|
|
243
|
+
icon: guessIcon(id, cat.label),
|
|
244
|
+
description,
|
|
245
|
+
category: cat.label, priority: cat.priority,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
catch { /* ignore */ }
|
|
250
|
+
}
|
|
251
|
+
artifacts.sort((a, b) => a.priority - b.priority || a.id.localeCompare(b.id));
|
|
252
|
+
return artifacts;
|
|
253
|
+
}
|
|
254
|
+
// ============================================================
|
|
255
|
+
// GENERATE styles.css
|
|
256
|
+
// ============================================================
|
|
257
|
+
function generateStylesCSS() {
|
|
258
|
+
return `:root {
|
|
259
|
+
--bg: #0A0F1A; --paper: #111827; --ink: #E5E7EB; --muted: #9AA3AD;
|
|
260
|
+
--brand: #2F6DB3; --brand-2: #4FB3FF; --accent: #00E0FF; --line: #1E293B;
|
|
261
|
+
--soft: #0F1C2E; --shadow: 0 14px 40px rgba(0,224,255,0.06);
|
|
262
|
+
--silver: #C9CCD1; --warn: #F59E0B; --warn-bg: #1C1708;
|
|
263
|
+
--danger: #EF4444; --danger-bg: #1C0808; --ok: #22C55E; --ok-bg: #081C10;
|
|
264
|
+
}
|
|
265
|
+
* { box-sizing: border-box; }
|
|
266
|
+
html { scroll-behavior: smooth; }
|
|
267
|
+
body { margin:0; font-family:'Inter',-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial,sans-serif; letter-spacing:.01em; background:radial-gradient(circle at top right,rgba(0,224,255,.06),transparent 40%),radial-gradient(circle at bottom left,rgba(47,109,179,.08),transparent 50%),var(--bg); color:var(--ink); line-height:1.6; }
|
|
268
|
+
.page { width:92%; max-width:1600px; margin:0 auto; padding:clamp(16px,3vw,40px) clamp(16px,4vw,48px) 80px; }
|
|
269
|
+
.cover { background:linear-gradient(135deg,#0F1C2E 0%,#1B3B6F 50%,#2F6DB3 100%); color:#fff; border-radius:clamp(16px,2vw,28px); padding:clamp(24px,3vw,40px); box-shadow:0 14px 40px rgba(0,224,255,.08); position:relative; overflow:hidden; border:1px solid rgba(0,224,255,.1); }
|
|
270
|
+
.cover::after { content:""; position:absolute; width:clamp(160px,22vw,300px); height:clamp(160px,22vw,300px); right:-70px; top:-80px; border-radius:50%; background:radial-gradient(circle,rgba(0,224,255,.12),transparent 70%); }
|
|
271
|
+
.brand { display:flex; align-items:center; gap:clamp(12px,2vw,22px); position:relative; z-index:1; }
|
|
272
|
+
.eyebrow { letter-spacing:.14em; text-transform:uppercase; font-size:clamp(.68rem,1vw,.82rem); opacity:.88; margin-bottom:8px; }
|
|
273
|
+
h1 { margin:0 0 10px; font-size:clamp(1.4rem,3.5vw,2.4rem); line-height:1.08; }
|
|
274
|
+
.subtitle { max-width:860px; font-size:clamp(.92rem,1.4vw,1.08rem); color:#fff; margin:0; }
|
|
275
|
+
.cover-meta { margin-top:22px; display:flex; flex-wrap:wrap; gap:8px; position:relative; z-index:1; }
|
|
276
|
+
.pill { border:1px solid rgba(0,224,255,.2); background:rgba(0,224,255,.08); color:#C9CCD1; padding:5px 12px; border-radius:999px; font-size:clamp(.78rem,1vw,.9rem); letter-spacing:.04em; text-transform:uppercase; }
|
|
277
|
+
.pill-active { background:linear-gradient(135deg,#22C55E,#16A34A); color:#fff; font-weight:600; }
|
|
278
|
+
.layout { display:grid; grid-template-columns:clamp(220px,22%,300px) minmax(0,1fr); gap:clamp(14px,2vw,26px); margin-top:clamp(16px,2vw,28px); }
|
|
279
|
+
.toc { position:sticky; top:18px; align-self:start; background:rgba(17,24,39,.92); backdrop-filter:blur(14px); border:1px solid var(--line); border-radius:22px; box-shadow:var(--shadow); padding:clamp(14px,2vw,24px); }
|
|
280
|
+
.toc h2 { margin:0 0 14px; font-size:clamp(.88rem,1.1vw,1rem); color:var(--brand); }
|
|
281
|
+
.toc a { display:block; color:var(--silver); text-decoration:none; padding:7px 10px; border-radius:10px; font-size:clamp(.82rem,1vw,.95rem); margin-bottom:3px; }
|
|
282
|
+
.toc a:hover { background:rgba(0,224,255,.06); color:var(--accent); }
|
|
283
|
+
.toc a.sub { padding-left:24px; font-size:clamp(.76rem,.9vw,.88rem); opacity:.8; }
|
|
284
|
+
.toc a.sub::before { content:"└ "; opacity:.4; }
|
|
285
|
+
.content { display:grid; gap:clamp(12px,1.5vw,20px); }
|
|
286
|
+
section.card { background:var(--paper); border:1px solid var(--line); border-radius:clamp(16px,2vw,24px); box-shadow:var(--shadow); padding:clamp(18px,2.5vw,32px); }
|
|
287
|
+
section.card h2 { margin:0 0 14px; font-size:clamp(1.15rem,2vw,1.5rem); color:var(--brand); }
|
|
288
|
+
h3 { margin:20px 0 10px; color:var(--brand-2); font-size:clamp(.95rem,1.3vw,1.08rem); }
|
|
289
|
+
p { margin:10px 0 14px; color:var(--muted); }
|
|
290
|
+
strong { color:var(--ink); }
|
|
291
|
+
.content-section { background:var(--paper); border:1px solid var(--line); border-radius:clamp(16px,2vw,24px); box-shadow:var(--shadow); padding:clamp(18px,2.5vw,32px); }
|
|
292
|
+
.content-section h2 { margin:0 0 14px; font-size:clamp(1.15rem,2vw,1.5rem); color:var(--brand); }
|
|
293
|
+
table { width:100%; border-collapse:collapse; margin-top:12px; border:1px solid var(--line); border-radius:16px; overflow:hidden; background:var(--paper); }
|
|
294
|
+
th,td { padding:11px 14px; text-align:left; vertical-align:top; border-bottom:1px solid var(--line); }
|
|
295
|
+
th { background:#0F1C2E; color:var(--brand-2); font-weight:700; font-size:.93rem; }
|
|
296
|
+
tr:last-child td { border-bottom:none; }
|
|
297
|
+
.data-table { width:100%; border-collapse:collapse; margin-top:12px; border:1px solid var(--line); border-radius:16px; overflow:hidden; background:var(--paper); }
|
|
298
|
+
.data-table th,.data-table td { padding:11px 14px; text-align:left; vertical-align:top; border-bottom:1px solid var(--line); }
|
|
299
|
+
.data-table th { background:#0F1C2E; color:var(--brand-2); font-weight:700; font-size:.93rem; }
|
|
300
|
+
.data-table tr:last-child td { border-bottom:none; }
|
|
301
|
+
.badge { display:inline-block; padding:3px 10px; border-radius:999px; font-size:.8rem; font-weight:600; }
|
|
302
|
+
.badge.ok { background:var(--ok-bg); color:var(--ok); }
|
|
303
|
+
.badge.warn { background:var(--warn-bg); color:var(--warn); }
|
|
304
|
+
.badge.danger { background:var(--danger-bg); color:var(--danger); }
|
|
305
|
+
.badge.info { background:rgba(47,109,179,.2); color:#4FB3FF; }
|
|
306
|
+
.breadcrumb { display:flex; align-items:center; gap:8px; font-size:.88rem; color:var(--muted); margin-bottom:16px; }
|
|
307
|
+
.breadcrumb a { color:var(--brand-2); text-decoration:none; }
|
|
308
|
+
.breadcrumb a:hover { color:var(--accent); text-decoration:underline; }
|
|
309
|
+
.breadcrumb .sep { opacity:.4; }
|
|
310
|
+
.page-header { margin-bottom:24px; }
|
|
311
|
+
.page-header h1 { margin:0 0 8px; font-size:clamp(1.4rem,3vw,2rem); color:var(--ink); }
|
|
312
|
+
.page-header .subtitle { margin:0; font-size:clamp(.9rem,1.3vw,1.05rem); color:var(--muted); }
|
|
313
|
+
ul { margin:10px 0 14px 20px; color:var(--muted); }
|
|
314
|
+
li { margin:6px 0; }
|
|
315
|
+
code { background:rgba(0,224,255,.06); padding:2px 6px; border-radius:6px; font-size:.88rem; color:var(--accent); }
|
|
316
|
+
a { color:var(--brand-2); text-decoration:none; }
|
|
317
|
+
a:hover { text-decoration:underline; color:var(--accent); }
|
|
318
|
+
.back-btn { position:absolute; top:20px; right:24px; z-index:2; display:inline-flex; align-items:center; gap:7px; padding:8px 16px; background:rgba(0,224,255,.08); border:1px solid rgba(0,224,255,.2); color:var(--accent); border-radius:999px; font-size:.85rem; text-decoration:none; font-weight:500; }
|
|
319
|
+
.back-btn:hover { background:rgba(0,224,255,.18); text-decoration:none; }
|
|
320
|
+
.diagram-container { overflow-x:auto; cursor:zoom-in; position:relative; }
|
|
321
|
+
.diagram-lightbox { position:fixed; inset:0; z-index:9999; background:rgba(0,0,0,.88); backdrop-filter:blur(8px); display:flex; align-items:center; justify-content:center; opacity:0; pointer-events:none; transition:opacity .25s; cursor:zoom-out; }
|
|
322
|
+
.diagram-lightbox.active { opacity:1; pointer-events:auto; }
|
|
323
|
+
.diagram-lightbox-inner { width:92vw; max-height:92vh; overflow:auto; background:linear-gradient(180deg,#0F1C2E,#111827); border:1px solid var(--line); border-radius:18px; padding:32px; box-shadow:0 20px 60px rgba(0,0,0,.5); display:flex; align-items:flex-start; justify-content:center; }
|
|
324
|
+
.diagram-lightbox-inner svg { width:100%; height:auto; }
|
|
325
|
+
.diagram-lightbox-close { position:fixed; top:16px; right:20px; z-index:10000; width:40px; height:40px; border:1px solid rgba(255,255,255,.2); border-radius:10px; background:rgba(0,0,0,.6); color:#fff; font-size:20px; cursor:pointer; display:flex; align-items:center; justify-content:center; opacity:0; pointer-events:none; transition:opacity .25s; }
|
|
326
|
+
.diagram-lightbox.active + .diagram-lightbox-close, .diagram-lightbox.active ~ .diagram-lightbox-close { opacity:1; pointer-events:auto; }
|
|
327
|
+
footer { text-align:center; padding:24px; opacity:.5; font-size:.85rem; }
|
|
328
|
+
/* Light theme */
|
|
329
|
+
html.light { --bg:#F8FAFC; --paper:#FFFFFF; --ink:#1E293B; --muted:#64748B; --brand:#1D4ED8; --brand-2:#2563EB; --accent:#0284C7; --line:#E2E8F0; --soft:#F1F5F9; --shadow:0 14px 40px rgba(0,0,0,.06); --silver:#475569; --warn:#D97706; --warn-bg:#FEF3C7; --danger:#DC2626; --danger-bg:#FEE2E2; --ok:#16A34A; --ok-bg:#DCFCE7; }
|
|
330
|
+
html.light body { background:var(--bg); }
|
|
331
|
+
html.light .cover { background:linear-gradient(135deg,#DBEAFE 0%,#93C5FD 50%,#3B82F6 100%); }
|
|
332
|
+
html.light .cover .eyebrow, html.light .cover h1, html.light .cover .subtitle { color:#1E293B; }
|
|
333
|
+
html.light .nav-card { background:linear-gradient(180deg,#FFFFFF,#F8FAFC); }
|
|
334
|
+
html.light .toc { background:rgba(255,255,255,.95); }
|
|
335
|
+
html.light .pill { background:rgba(2,132,199,.08); border-color:rgba(2,132,199,.2); color:#334155; }
|
|
336
|
+
html.light .back-btn { background:rgba(2,132,199,.1); border-color:rgba(2,132,199,.3); color:#0284C7; }
|
|
337
|
+
html.light .metrics-bar .m { background:rgba(2,132,199,.04); }
|
|
338
|
+
html.light pre { background:rgba(2,132,199,.04)!important; }
|
|
339
|
+
html.light code { background:rgba(2,132,199,.08); color:#0284C7; }
|
|
340
|
+
.theme-toggle { position:fixed; bottom:20px; right:20px; z-index:999; width:44px; height:44px; border-radius:50%; border:1px solid var(--line); background:var(--paper); color:var(--accent); cursor:pointer; display:flex; align-items:center; justify-content:center; font-size:20px; box-shadow:var(--shadow); transition:background .2s,color .2s; }
|
|
341
|
+
.theme-toggle:hover { background:var(--accent); color:var(--bg); }
|
|
342
|
+
@media (max-width:600px) { .page { width:100%; padding:12px 14px 48px; } .layout { grid-template-columns:1fr; } .toc { position:static; } }
|
|
343
|
+
@media (min-width:601px) and (max-width:960px) { .layout { grid-template-columns:1fr; } .toc { position:static; } }
|
|
344
|
+
@media print { .back-btn,.toc { display:none!important; } html,body { background:#fff!important; } .page { max-width:100%!important; padding:0!important; } .layout { display:block!important; } }
|
|
345
|
+
`;
|
|
346
|
+
}
|
|
347
|
+
// ============================================================
|
|
348
|
+
// GENERATE loader.js (dynamic based on artifacts)
|
|
349
|
+
// ============================================================
|
|
350
|
+
function generateLoaderJS(artifacts) {
|
|
351
|
+
const pagesJson = JSON.stringify(artifacts.map(a => ({ slug: a.slug, icon: a.icon, title: a.title })), null, 2);
|
|
352
|
+
return `(function () {
|
|
353
|
+
var pages = ${pagesJson};
|
|
354
|
+
var path = location.pathname;
|
|
355
|
+
var match = path.match(/pages\\/([^/.]+)\\.html/);
|
|
356
|
+
var currentSlug = match ? match[1] : null;
|
|
357
|
+
var layout = document.querySelector('.layout');
|
|
358
|
+
if (layout && currentSlug) {
|
|
359
|
+
var aside = layout.querySelector('.toc');
|
|
360
|
+
if (aside) {
|
|
361
|
+
var navHtml = '<h2 style="margin-top:24px;padding-top:16px;border-top:1px solid var(--line)">Páginas</h2>';
|
|
362
|
+
pages.forEach(function (p) {
|
|
363
|
+
var active = p.slug === currentSlug ? ' style="color:var(--accent);font-weight:700"' : '';
|
|
364
|
+
navHtml += '<a href="' + p.slug + '.html"' + active + '><span class="material-symbols-outlined" style="font-size:16px;vertical-align:middle;margin-right:4px">' + p.icon + '</span>' + p.title + '</a>';
|
|
365
|
+
});
|
|
366
|
+
aside.insertAdjacentHTML('beforeend', navHtml);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (currentSlug) {
|
|
370
|
+
var idx = pages.findIndex(function (p) { return p.slug === currentSlug; });
|
|
371
|
+
if (idx !== -1) {
|
|
372
|
+
var prev = idx > 0 ? pages[idx - 1] : null;
|
|
373
|
+
var next = idx < pages.length - 1 ? pages[idx + 1] : null;
|
|
374
|
+
var navBar = document.createElement('nav');
|
|
375
|
+
navBar.style.cssText = 'display:flex;justify-content:space-between;padding:24px 0;margin-top:32px;border-top:1px solid var(--line);font-size:.88rem';
|
|
376
|
+
navBar.innerHTML = (prev ? '<a href="' + prev.slug + '.html" style="color:var(--accent);text-decoration:none">\\u2190 ' + prev.title + '</a>' : '<span></span>') + (next ? '<a href="' + next.slug + '.html" style="color:var(--accent);text-decoration:none">' + next.title + ' \\u2192</a>' : '<span></span>');
|
|
377
|
+
var content = document.querySelector('.content') || document.querySelector('.page');
|
|
378
|
+
if (content) content.appendChild(navBar);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
(function initLightbox() {
|
|
382
|
+
if (!document.getElementById('diagramLightbox')) {
|
|
383
|
+
var lbDiv = document.createElement('div');
|
|
384
|
+
lbDiv.className = 'diagram-lightbox';
|
|
385
|
+
lbDiv.id = 'diagramLightbox';
|
|
386
|
+
lbDiv.innerHTML = '<div class="diagram-lightbox-inner" id="diagramLightboxInner"></div>';
|
|
387
|
+
document.body.appendChild(lbDiv);
|
|
388
|
+
var closeBtn = document.createElement('button');
|
|
389
|
+
closeBtn.className = 'diagram-lightbox-close';
|
|
390
|
+
closeBtn.id = 'diagramLightboxClose';
|
|
391
|
+
closeBtn.setAttribute('aria-label', 'Fechar');
|
|
392
|
+
closeBtn.textContent = '\\u2715';
|
|
393
|
+
document.body.appendChild(closeBtn);
|
|
394
|
+
}
|
|
395
|
+
var lb = document.getElementById('diagramLightbox');
|
|
396
|
+
var inner = document.getElementById('diagramLightboxInner');
|
|
397
|
+
var closeBtnEl = document.getElementById('diagramLightboxClose');
|
|
398
|
+
function closeLb() { lb.classList.remove('active'); inner.innerHTML = ''; document.body.style.overflow = ''; }
|
|
399
|
+
document.querySelectorAll('.diagram-container').forEach(function (el) {
|
|
400
|
+
el.style.cursor = 'zoom-in';
|
|
401
|
+
el.addEventListener('click', function (e) {
|
|
402
|
+
if (e.target.closest('a')) return;
|
|
403
|
+
inner.innerHTML = '';
|
|
404
|
+
var clone = el.cloneNode(true);
|
|
405
|
+
clone.style.cursor = 'default';
|
|
406
|
+
inner.appendChild(clone);
|
|
407
|
+
document.body.style.overflow = 'hidden';
|
|
408
|
+
lb.classList.add('active');
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
closeBtnEl.addEventListener('click', closeLb);
|
|
412
|
+
lb.addEventListener('click', function (e) { if (e.target === lb) closeLb(); });
|
|
413
|
+
document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && lb.classList.contains('active')) closeLb(); });
|
|
414
|
+
})();
|
|
415
|
+
window.__processPages = pages;
|
|
416
|
+
|
|
417
|
+
// Theme toggle (dark/light)
|
|
418
|
+
(function initThemeToggle() {
|
|
419
|
+
var saved = localStorage.getItem('wiki-theme');
|
|
420
|
+
if (saved === 'light') document.documentElement.classList.add('light');
|
|
421
|
+
var btn = document.createElement('button');
|
|
422
|
+
btn.className = 'theme-toggle';
|
|
423
|
+
btn.setAttribute('aria-label', 'Alternar tema');
|
|
424
|
+
btn.innerHTML = document.documentElement.classList.contains('light') ? '<span class="material-symbols-outlined">dark_mode</span>' : '<span class="material-symbols-outlined">light_mode</span>';
|
|
425
|
+
btn.addEventListener('click', function () {
|
|
426
|
+
document.documentElement.classList.toggle('light');
|
|
427
|
+
var isLight = document.documentElement.classList.contains('light');
|
|
428
|
+
localStorage.setItem('wiki-theme', isLight ? 'light' : 'dark');
|
|
429
|
+
btn.innerHTML = isLight ? '<span class="material-symbols-outlined">dark_mode</span>' : '<span class="material-symbols-outlined">light_mode</span>';
|
|
430
|
+
});
|
|
431
|
+
document.body.appendChild(btn);
|
|
432
|
+
})();
|
|
433
|
+
})();
|
|
434
|
+
`;
|
|
435
|
+
}
|
|
436
|
+
// ============================================================
|
|
437
|
+
// GENERATE page HTML (pages/{slug}.html)
|
|
438
|
+
// ============================================================
|
|
439
|
+
function generatePageHtml(artifact, mdContent, processId, processTitle) {
|
|
440
|
+
const toc = extractTocFromMd(mdContent);
|
|
441
|
+
const bodyHtml = mdToHtml(mdContent);
|
|
442
|
+
const tocLinks = toc.map(t => {
|
|
443
|
+
const cls = t.level === 3 ? ' class="sub"' : '';
|
|
444
|
+
return ` <a href="#${t.id}"${cls}>${t.title}</a>`;
|
|
445
|
+
}).join('\n');
|
|
446
|
+
return `<!DOCTYPE html>
|
|
447
|
+
<html lang="pt-BR">
|
|
448
|
+
<head>
|
|
449
|
+
<meta charset="UTF-8" />
|
|
450
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
451
|
+
<title>${artifact.title} — ${processTitle}</title>
|
|
452
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;900&display=swap" />
|
|
453
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
|
454
|
+
<link rel="stylesheet" href="../styles.css" />
|
|
455
|
+
</head>
|
|
456
|
+
<body>
|
|
457
|
+
<div class="page">
|
|
458
|
+
<nav class="breadcrumb">
|
|
459
|
+
<a href="../index.html">Home</a> <span class="sep">/</span> <span>${artifact.title}</span>
|
|
460
|
+
</nav>
|
|
461
|
+
<header class="page-header">
|
|
462
|
+
<h1>${artifact.title}</h1>
|
|
463
|
+
<p class="subtitle">${artifact.description}</p>
|
|
464
|
+
</header>
|
|
465
|
+
<div class="layout">
|
|
466
|
+
<aside class="toc" role="navigation" aria-label="Sumário">
|
|
467
|
+
<h2>Nesta página</h2>
|
|
468
|
+
${tocLinks}
|
|
469
|
+
</aside>
|
|
470
|
+
<main class="content" id="main-content">
|
|
471
|
+
<section class="content-section">
|
|
472
|
+
${bodyHtml}
|
|
473
|
+
</section>
|
|
474
|
+
</main>
|
|
475
|
+
</div>
|
|
476
|
+
<footer>processo ${processId} · creative-wiki</footer>
|
|
477
|
+
</div>
|
|
478
|
+
<script src="../loader.js"></script>
|
|
479
|
+
</body>
|
|
480
|
+
</html>`;
|
|
481
|
+
}
|
|
482
|
+
// ============================================================
|
|
483
|
+
// GENERATE index.html (home page with nav-grid cards)
|
|
484
|
+
// ============================================================
|
|
485
|
+
function generateIndexHtml(processId, processTitle, processDescription, artifacts, date, pageTitle, diagramFiles) {
|
|
486
|
+
// Group artifacts by category
|
|
487
|
+
const groups = new Map();
|
|
488
|
+
for (const a of artifacts) {
|
|
489
|
+
if (!groups.has(a.category))
|
|
490
|
+
groups.set(a.category, []);
|
|
491
|
+
groups.get(a.category).push(a);
|
|
492
|
+
}
|
|
493
|
+
// Build nav-grid sections
|
|
494
|
+
let sectionsHtml = '';
|
|
495
|
+
for (const [category, items] of groups) {
|
|
496
|
+
sectionsHtml += `\n <div class="section-title">${category}</div>\n <div class="nav-grid">\n`;
|
|
497
|
+
for (const a of items) {
|
|
498
|
+
sectionsHtml += ` <a href="pages/${a.slug}.html" class="nav-card">
|
|
499
|
+
<span class="material-symbols-outlined card-icon">${a.icon}</span>
|
|
500
|
+
<h3>${a.title}</h3>
|
|
501
|
+
<p>${a.description}</p>
|
|
502
|
+
</a>\n`;
|
|
503
|
+
}
|
|
504
|
+
sectionsHtml += ` </div>\n`;
|
|
505
|
+
}
|
|
506
|
+
return `<!DOCTYPE html>
|
|
507
|
+
<html lang="pt-BR">
|
|
508
|
+
<head>
|
|
509
|
+
<meta charset="UTF-8" />
|
|
510
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
511
|
+
<title>${pageTitle || `Processo ${processId} — ${processTitle}`}</title>
|
|
512
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;900&display=swap" />
|
|
513
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
|
514
|
+
<link rel="stylesheet" href="styles.css" />
|
|
515
|
+
<style>
|
|
516
|
+
.nav-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(300px,1fr)); gap:16px; margin:24px 0; }
|
|
517
|
+
.nav-card { background:linear-gradient(180deg,#111827,#0F1C2E); border:1px solid var(--line); border-radius:18px; padding:24px; text-decoration:none; color:var(--ink); transition:border-color .2s,box-shadow .2s,transform .15s; display:flex; flex-direction:column; gap:8px; }
|
|
518
|
+
.nav-card:hover { border-color:var(--accent); box-shadow:0 8px 32px rgba(0,224,255,.12); transform:translateY(-2px); text-decoration:none; }
|
|
519
|
+
.nav-card .card-icon { font-size:28px; color:var(--accent); margin-bottom:4px; }
|
|
520
|
+
.nav-card h3 { margin:0; font-size:1.1rem; color:var(--brand-2); }
|
|
521
|
+
.nav-card p { margin:0; font-size:.88rem; color:var(--muted); line-height:1.5; }
|
|
522
|
+
.metrics-bar { display:grid; grid-template-columns:repeat(auto-fit,minmax(100px,1fr)); gap:12px; margin:20px 0; }
|
|
523
|
+
.metrics-bar .m { text-align:center; padding:14px 8px; background:rgba(0,224,255,.04); border:1px solid var(--line); border-radius:14px; }
|
|
524
|
+
.metrics-bar .m-val { display:block; font-size:1.4rem; font-weight:900; color:var(--accent); }
|
|
525
|
+
.metrics-bar .m-lbl { font-size:.72rem; color:var(--muted); margin-top:2px; }
|
|
526
|
+
.section-title { font-size:1rem; color:var(--muted); text-transform:uppercase; letter-spacing:.12em; margin:32px 0 12px; padding-bottom:8px; border-bottom:1px solid var(--line); }
|
|
527
|
+
.diagrams-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(280px,1fr)); gap:16px; margin:24px 0; }
|
|
528
|
+
.diagram-thumb { background:var(--paper); border:1px solid var(--line); border-radius:16px; overflow:hidden; cursor:zoom-in; transition:border-color .2s,transform .15s; }
|
|
529
|
+
.diagram-thumb:hover { border-color:var(--accent); transform:translateY(-2px); }
|
|
530
|
+
.diagram-thumb img { width:100%; display:block; }
|
|
531
|
+
.diagram-thumb .caption { padding:12px 16px; font-size:.85rem; color:var(--muted); }
|
|
532
|
+
</style>
|
|
533
|
+
</head>
|
|
534
|
+
<body>
|
|
535
|
+
<div class="page">
|
|
536
|
+
<header class="cover">
|
|
537
|
+
<div class="brand">
|
|
538
|
+
<div>
|
|
539
|
+
<div class="eyebrow">processo ${processId} · ativo · ${date}</div>
|
|
540
|
+
<h1>${processTitle}</h1>
|
|
541
|
+
<p class="subtitle">${processDescription}</p>
|
|
542
|
+
</div>
|
|
543
|
+
</div>
|
|
544
|
+
<div class="cover-meta">
|
|
545
|
+
<span class="pill pill-active">ativo</span>
|
|
546
|
+
<span class="pill">${artifacts.length} artefatos</span>
|
|
547
|
+
</div>
|
|
548
|
+
</header>
|
|
549
|
+
|
|
550
|
+
<div class="metrics-bar">
|
|
551
|
+
<div class="m"><span class="m-val">${artifacts.length}</span><span class="m-lbl">Artefatos</span></div>
|
|
552
|
+
<div class="m"><span class="m-val">${groups.size}</span><span class="m-lbl">Categorias</span></div>
|
|
553
|
+
</div>
|
|
554
|
+
${sectionsHtml}
|
|
555
|
+
${diagramFiles && diagramFiles.length > 0 ? `
|
|
556
|
+
<div class="section-title">Diagramas</div>
|
|
557
|
+
<div class="diagrams-grid">
|
|
558
|
+
${diagramFiles.map(f => {
|
|
559
|
+
const label = f.replace(/\.(png|jpg|jpeg|gif|svg)$/i, '').replace(/[-_]/g, ' ');
|
|
560
|
+
return ` <div class="diagram-thumb diagram-container">
|
|
561
|
+
<img src="diagrams/${f}" alt="${label}" />
|
|
562
|
+
<div class="caption">${label}</div>
|
|
563
|
+
</div>`;
|
|
564
|
+
}).join('\n')}
|
|
565
|
+
</div>
|
|
566
|
+
` : ''}
|
|
567
|
+
<footer>Creative IA 50 · Processo ${processId} · ${new Date().getFullYear()}</footer>
|
|
568
|
+
</div>
|
|
569
|
+
<script src="loader.js"></script>
|
|
570
|
+
</body>
|
|
571
|
+
</html>`;
|
|
572
|
+
}
|
|
573
|
+
// ============================================================
|
|
574
|
+
// MAIN EXPORT — generateWiki
|
|
575
|
+
// ============================================================
|
|
576
|
+
// ---------------------------------------------------------------------------
|
|
577
|
+
// server.js — Simple Node.js static server for the wiki
|
|
578
|
+
// ---------------------------------------------------------------------------
|
|
579
|
+
function generateServerJS() {
|
|
580
|
+
return `const http = require("http");
|
|
581
|
+
const fs = require("fs");
|
|
582
|
+
const path = require("path");
|
|
583
|
+
|
|
584
|
+
const PORT = process.env.PORT || 3000;
|
|
585
|
+
const DIR = __dirname;
|
|
586
|
+
|
|
587
|
+
const MIME = {
|
|
588
|
+
".html": "text/html; charset=utf-8",
|
|
589
|
+
".css": "text/css; charset=utf-8",
|
|
590
|
+
".js": "application/javascript; charset=utf-8",
|
|
591
|
+
".json": "application/json; charset=utf-8",
|
|
592
|
+
".png": "image/png",
|
|
593
|
+
".jpg": "image/jpeg",
|
|
594
|
+
".jpeg": "image/jpeg",
|
|
595
|
+
".gif": "image/gif",
|
|
596
|
+
".svg": "image/svg+xml",
|
|
597
|
+
".ico": "image/x-icon",
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
const server = http.createServer((req, res) => {
|
|
601
|
+
let url = req.url.split("?")[0];
|
|
602
|
+
if (url === "/") url = "/index.html";
|
|
603
|
+
|
|
604
|
+
const filePath = path.join(DIR, url);
|
|
605
|
+
|
|
606
|
+
// Security: block path traversal
|
|
607
|
+
if (!filePath.startsWith(DIR)) {
|
|
608
|
+
res.writeHead(403);
|
|
609
|
+
return res.end("Forbidden");
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
fs.readFile(filePath, (err, data) => {
|
|
613
|
+
if (err) {
|
|
614
|
+
res.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
|
|
615
|
+
return res.end("<h1>404 — Página não encontrada</h1>");
|
|
616
|
+
}
|
|
617
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
618
|
+
const mime = MIME[ext] || "application/octet-stream";
|
|
619
|
+
res.writeHead(200, { "Content-Type": mime });
|
|
620
|
+
res.end(data);
|
|
621
|
+
});
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
server.listen(PORT, () => {
|
|
625
|
+
console.log("\\n 🌐 Wiki disponível em: http://localhost:" + PORT + "\\n");
|
|
626
|
+
});
|
|
627
|
+
`;
|
|
628
|
+
}
|
|
629
|
+
export async function generateWiki(req) {
|
|
630
|
+
const { processId, processDir } = req;
|
|
631
|
+
const files = [];
|
|
632
|
+
// 1. Scan artifacts
|
|
633
|
+
const artifacts = await scanArtifacts(processDir);
|
|
634
|
+
if (artifacts.length === 0) {
|
|
635
|
+
return { success: false, message: "Nenhum artefato .md encontrado no processo.", files: [] };
|
|
636
|
+
}
|
|
637
|
+
// 2. Detect process title from first root artifact or README
|
|
638
|
+
let processTitle = `Processo ${processId}`;
|
|
639
|
+
let processDescription = '';
|
|
640
|
+
const readmeArtifact = artifacts.find(a => a.id.toLowerCase().includes('readme'));
|
|
641
|
+
const firstArtifact = readmeArtifact || artifacts[0];
|
|
642
|
+
if (firstArtifact) {
|
|
643
|
+
processTitle = firstArtifact.title;
|
|
644
|
+
processDescription = firstArtifact.description;
|
|
645
|
+
}
|
|
646
|
+
// Avoid duplication: if title already contains processId, use it as-is for the <title>
|
|
647
|
+
const pageTitle = processTitle.toLowerCase().includes(processId.toLowerCase())
|
|
648
|
+
? processTitle
|
|
649
|
+
: `Processo ${processId} — ${processTitle}`;
|
|
650
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
651
|
+
// 3. Create wiki/pages/ directory
|
|
652
|
+
const wikiDir = join(processDir, "wiki");
|
|
653
|
+
const pagesDir = join(wikiDir, "pages");
|
|
654
|
+
await mkdir(pagesDir, { recursive: true });
|
|
655
|
+
// 3b. Copy generated-diagrams to wiki/diagrams/
|
|
656
|
+
const srcDiagramsDir = join(processDir, "generated-diagrams");
|
|
657
|
+
const dstDiagramsDir = join(wikiDir, "diagrams");
|
|
658
|
+
let diagramFiles = [];
|
|
659
|
+
if (existsSync(srcDiagramsDir)) {
|
|
660
|
+
await mkdir(dstDiagramsDir, { recursive: true });
|
|
661
|
+
const dFiles = await readdir(srcDiagramsDir);
|
|
662
|
+
for (const df of dFiles) {
|
|
663
|
+
if (/\.(png|jpg|jpeg|gif|svg)$/i.test(df)) {
|
|
664
|
+
const data = await readFile(join(srcDiagramsDir, df));
|
|
665
|
+
await writeFile(join(dstDiagramsDir, df), data);
|
|
666
|
+
diagramFiles.push(df);
|
|
667
|
+
files.push(`wiki/diagrams/${df}`);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
// 4. Write styles.css
|
|
672
|
+
await writeFile(join(wikiDir, "styles.css"), generateStylesCSS(), "utf-8");
|
|
673
|
+
files.push("wiki/styles.css");
|
|
674
|
+
// 5. Write loader.js
|
|
675
|
+
await writeFile(join(wikiDir, "loader.js"), generateLoaderJS(artifacts), "utf-8");
|
|
676
|
+
files.push("wiki/loader.js");
|
|
677
|
+
// 6. Generate each page
|
|
678
|
+
for (const artifact of artifacts) {
|
|
679
|
+
const mdPath = artifact.dir === "root"
|
|
680
|
+
? join(processDir, artifact.file)
|
|
681
|
+
: join(processDir, artifact.dir, artifact.file);
|
|
682
|
+
const mdContent = await readFile(mdPath, "utf-8");
|
|
683
|
+
const pageHtml = generatePageHtml(artifact, mdContent, processId, processTitle);
|
|
684
|
+
const pagePath = join(pagesDir, `${artifact.slug}.html`);
|
|
685
|
+
await writeFile(pagePath, pageHtml, "utf-8");
|
|
686
|
+
files.push(`wiki/pages/${artifact.slug}.html`);
|
|
687
|
+
}
|
|
688
|
+
// 7. Write index.html
|
|
689
|
+
const indexHtml = generateIndexHtml(processId, processTitle, processDescription, artifacts, date, pageTitle, diagramFiles);
|
|
690
|
+
await writeFile(join(wikiDir, "index.html"), indexHtml, "utf-8");
|
|
691
|
+
files.push("wiki/index.html");
|
|
692
|
+
// 8. Write server.cjs
|
|
693
|
+
await writeFile(join(wikiDir, "server.cjs"), generateServerJS(), "utf-8");
|
|
694
|
+
files.push("wiki/server.cjs");
|
|
695
|
+
return {
|
|
696
|
+
success: true,
|
|
697
|
+
message: `Wiki gerada com sucesso: ${files.length} arquivos (${artifacts.length} páginas + index + styles + loader + server).`,
|
|
698
|
+
files,
|
|
699
|
+
};
|
|
700
|
+
}
|