onboarding-kit 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.
- package/README.md +89 -2
- package/dist/adapters/nest.d.ts +15 -0
- package/dist/adapters/nest.js +53 -0
- package/dist/adapters/next.d.ts +23 -0
- package/dist/adapters/next.js +41 -0
- package/dist/adapters/node.d.ts +10 -0
- package/dist/adapters/node.js +33 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.js +272 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +57 -0
- package/dist/load.d.ts +19 -0
- package/dist/load.js +59 -0
- package/dist/render/assets.d.ts +4 -0
- package/dist/render/assets.js +381 -0
- package/dist/render/copy.d.ts +132 -0
- package/dist/render/copy.js +290 -0
- package/dist/render/html.d.ts +12 -0
- package/dist/render/html.js +74 -0
- package/dist/render/index.d.ts +5 -0
- package/dist/render/index.js +167 -0
- package/dist/render/slides.d.ts +19 -0
- package/dist/render/slides.js +232 -0
- package/dist/template.d.ts +9 -0
- package/dist/template.js +88 -0
- package/dist/types.d.ts +150 -0
- package/dist/types.js +6 -0
- package/dist/validate.d.ts +17 -0
- package/dist/validate.js +224 -0
- package/package.json +69 -4
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
export type ChapterId = "welcome" | "why" | "business" | "product" | "language" | "rules" | "stack" | "architecture" | "setup" | "conventions" | "tours" | "gotchas" | "decisions" | "mission" | "finale";
|
|
2
|
+
/** Matiz (HSL) de cada capítulo: cada etapa do tour tem a sua cor. */
|
|
3
|
+
export declare const CHAPTER_HUES: Record<ChapterId, number>;
|
|
4
|
+
declare const pt: {
|
|
5
|
+
chapters: Record<ChapterId, string>;
|
|
6
|
+
welcome: {
|
|
7
|
+
hi: string;
|
|
8
|
+
title: (name: string) => string;
|
|
9
|
+
intro: (min: number) => string;
|
|
10
|
+
start: string;
|
|
11
|
+
journey: string;
|
|
12
|
+
};
|
|
13
|
+
why: {
|
|
14
|
+
title: string;
|
|
15
|
+
lede: string;
|
|
16
|
+
audience: string;
|
|
17
|
+
status: string;
|
|
18
|
+
};
|
|
19
|
+
business: {
|
|
20
|
+
title: string;
|
|
21
|
+
lede: string;
|
|
22
|
+
model: string;
|
|
23
|
+
value: string;
|
|
24
|
+
actorsTitle: string;
|
|
25
|
+
actorsLede: string;
|
|
26
|
+
flowsTitle: string;
|
|
27
|
+
flowsLede: string;
|
|
28
|
+
};
|
|
29
|
+
product: {
|
|
30
|
+
title: string;
|
|
31
|
+
lede: string;
|
|
32
|
+
};
|
|
33
|
+
language: {
|
|
34
|
+
title: string;
|
|
35
|
+
lede: string;
|
|
36
|
+
tap: string;
|
|
37
|
+
quizTitle: string;
|
|
38
|
+
quizLede: string;
|
|
39
|
+
question: (term: string) => string;
|
|
40
|
+
right: string;
|
|
41
|
+
wrong: string;
|
|
42
|
+
score: string;
|
|
43
|
+
};
|
|
44
|
+
rules: {
|
|
45
|
+
title: string;
|
|
46
|
+
lede: string;
|
|
47
|
+
rule: string;
|
|
48
|
+
};
|
|
49
|
+
stack: {
|
|
50
|
+
title: string;
|
|
51
|
+
lede: string;
|
|
52
|
+
};
|
|
53
|
+
architecture: {
|
|
54
|
+
title: string;
|
|
55
|
+
modulesTitle: string;
|
|
56
|
+
modulesLede: string;
|
|
57
|
+
};
|
|
58
|
+
setup: {
|
|
59
|
+
title: string;
|
|
60
|
+
lede: string;
|
|
61
|
+
prereq: string;
|
|
62
|
+
commandsTitle: string;
|
|
63
|
+
commandsLede: string;
|
|
64
|
+
envTitle: string;
|
|
65
|
+
envLede: string;
|
|
66
|
+
required: string;
|
|
67
|
+
optional: string;
|
|
68
|
+
copy: string;
|
|
69
|
+
copied: string;
|
|
70
|
+
};
|
|
71
|
+
conventions: {
|
|
72
|
+
title: string;
|
|
73
|
+
lede: string;
|
|
74
|
+
};
|
|
75
|
+
tours: {
|
|
76
|
+
kicker: string;
|
|
77
|
+
intro: string;
|
|
78
|
+
stops: string;
|
|
79
|
+
stop: (i: number, total: number) => string;
|
|
80
|
+
};
|
|
81
|
+
gotchas: {
|
|
82
|
+
title: string;
|
|
83
|
+
lede: string;
|
|
84
|
+
};
|
|
85
|
+
decisions: {
|
|
86
|
+
title: string;
|
|
87
|
+
lede: string;
|
|
88
|
+
context: string;
|
|
89
|
+
decision: string;
|
|
90
|
+
};
|
|
91
|
+
mission: {
|
|
92
|
+
title: string;
|
|
93
|
+
lede: string;
|
|
94
|
+
};
|
|
95
|
+
finale: {
|
|
96
|
+
title: string;
|
|
97
|
+
lede: string;
|
|
98
|
+
recap: string;
|
|
99
|
+
tasks: string;
|
|
100
|
+
restart: string;
|
|
101
|
+
index: string;
|
|
102
|
+
};
|
|
103
|
+
ui: {
|
|
104
|
+
prev: string;
|
|
105
|
+
next: string;
|
|
106
|
+
overview: string;
|
|
107
|
+
theme: string;
|
|
108
|
+
keys: string;
|
|
109
|
+
resume: string;
|
|
110
|
+
resumeYes: string;
|
|
111
|
+
resumeNo: string;
|
|
112
|
+
close: string;
|
|
113
|
+
chapters: string;
|
|
114
|
+
generated: string;
|
|
115
|
+
by: string;
|
|
116
|
+
minutes: string;
|
|
117
|
+
};
|
|
118
|
+
pending: {
|
|
119
|
+
title: string;
|
|
120
|
+
body: string;
|
|
121
|
+
};
|
|
122
|
+
missing: {
|
|
123
|
+
title: string;
|
|
124
|
+
body: string;
|
|
125
|
+
};
|
|
126
|
+
invalid: {
|
|
127
|
+
title: string;
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
export type Copy = typeof pt;
|
|
131
|
+
export declare function copyFor(language?: string): Copy;
|
|
132
|
+
export {};
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CHAPTER_HUES = void 0;
|
|
4
|
+
exports.copyFor = copyFor;
|
|
5
|
+
/** Matiz (HSL) de cada capítulo: cada etapa do tour tem a sua cor. */
|
|
6
|
+
exports.CHAPTER_HUES = {
|
|
7
|
+
welcome: 255,
|
|
8
|
+
why: 275,
|
|
9
|
+
business: 158,
|
|
10
|
+
product: 330,
|
|
11
|
+
language: 192,
|
|
12
|
+
rules: 28,
|
|
13
|
+
stack: 220,
|
|
14
|
+
architecture: 285,
|
|
15
|
+
setup: 145,
|
|
16
|
+
conventions: 42,
|
|
17
|
+
tours: 205,
|
|
18
|
+
gotchas: 12,
|
|
19
|
+
decisions: 305,
|
|
20
|
+
mission: 165,
|
|
21
|
+
finale: 255,
|
|
22
|
+
};
|
|
23
|
+
const pt = {
|
|
24
|
+
chapters: {
|
|
25
|
+
welcome: "Boas-vindas",
|
|
26
|
+
why: "O porquê",
|
|
27
|
+
business: "O negócio",
|
|
28
|
+
product: "O produto",
|
|
29
|
+
language: "Vocabulário",
|
|
30
|
+
rules: "Regras do jogo",
|
|
31
|
+
stack: "A stack",
|
|
32
|
+
architecture: "Arquitetura",
|
|
33
|
+
setup: "Mãos à obra",
|
|
34
|
+
conventions: "Jeito da casa",
|
|
35
|
+
tours: "Tour pelo código",
|
|
36
|
+
gotchas: "Cuidados",
|
|
37
|
+
decisions: "Decisões",
|
|
38
|
+
mission: "Primeira missão",
|
|
39
|
+
finale: "Pronto!",
|
|
40
|
+
},
|
|
41
|
+
welcome: {
|
|
42
|
+
hi: "Oi! Que bom ter você por aqui.",
|
|
43
|
+
title: (name) => `Bem-vindo ao ${name}`,
|
|
44
|
+
intro: (min) => `Este é um tour de uns ${min} minutos com tudo o que vale saber antes de abrir o seu primeiro PR: por que o projeto existe, como o negócio funciona, como o código se organiza e onde pisar com cuidado.`,
|
|
45
|
+
start: "Começar o tour",
|
|
46
|
+
journey: "O caminho até lá",
|
|
47
|
+
},
|
|
48
|
+
why: {
|
|
49
|
+
title: "Primeiro, o porquê",
|
|
50
|
+
lede: "Antes de qualquer linha de código: o problema que a gente resolve.",
|
|
51
|
+
audience: "Quem usa",
|
|
52
|
+
status: "Em que pé estamos",
|
|
53
|
+
},
|
|
54
|
+
business: {
|
|
55
|
+
title: "Como o negócio funciona",
|
|
56
|
+
lede: "Entender o negócio é o que transforma código que funciona em código que faz sentido.",
|
|
57
|
+
model: "O modelo",
|
|
58
|
+
value: "O valor que entregamos",
|
|
59
|
+
actorsTitle: "Quem faz parte dessa história",
|
|
60
|
+
actorsLede: "Os perfis que aparecem no produto e no código.",
|
|
61
|
+
flowsTitle: "As jornadas principais",
|
|
62
|
+
flowsLede: "O caminho que as pessoas percorrem dentro do produto.",
|
|
63
|
+
},
|
|
64
|
+
product: {
|
|
65
|
+
title: "O que o produto faz",
|
|
66
|
+
lede: "As funcionalidades que você vai encontrar (e mexer) por aqui.",
|
|
67
|
+
},
|
|
68
|
+
language: {
|
|
69
|
+
title: "Fale a língua da casa",
|
|
70
|
+
lede: "Termos que aparecem nas conversas, nas telas e no código. Toque num cartão para ver o significado.",
|
|
71
|
+
tap: "toque para virar",
|
|
72
|
+
quizTitle: "Pausa rápida: valendo nada!",
|
|
73
|
+
quizLede: "Umas perguntinhas para fixar o vocabulário.",
|
|
74
|
+
question: (term) => `O que significa “${term}”?`,
|
|
75
|
+
right: "Isso aí! 🎯",
|
|
76
|
+
wrong: "Quase! A resposta certa ficou destacada.",
|
|
77
|
+
score: "Você acertou {right} de {total}.",
|
|
78
|
+
},
|
|
79
|
+
rules: {
|
|
80
|
+
title: "As regras do jogo",
|
|
81
|
+
lede: "Regras de negócio que o código garante. Quebrar uma delas costuma virar bug em produção.",
|
|
82
|
+
rule: "Regra",
|
|
83
|
+
},
|
|
84
|
+
stack: {
|
|
85
|
+
title: "As ferramentas da casa",
|
|
86
|
+
lede: "A stack, e para que cada peça serve aqui dentro.",
|
|
87
|
+
},
|
|
88
|
+
architecture: {
|
|
89
|
+
title: "Como as peças se encaixam",
|
|
90
|
+
modulesTitle: "Onde fica cada coisa",
|
|
91
|
+
modulesLede: "O mapa das pastas que importam.",
|
|
92
|
+
},
|
|
93
|
+
setup: {
|
|
94
|
+
title: "Bora rodar na sua máquina",
|
|
95
|
+
lede: "Do zero ao projeto rodando, passo a passo.",
|
|
96
|
+
prereq: "Tenha instalado",
|
|
97
|
+
commandsTitle: "Comandos do dia a dia",
|
|
98
|
+
commandsLede: "Os scripts que você vai usar toda hora.",
|
|
99
|
+
envTitle: "Variáveis de ambiente",
|
|
100
|
+
envLede: "Peça os valores ao time e nunca commite o seu .env.",
|
|
101
|
+
required: "obrigatória",
|
|
102
|
+
optional: "opcional",
|
|
103
|
+
copy: "Copiar",
|
|
104
|
+
copied: "Copiado!",
|
|
105
|
+
},
|
|
106
|
+
conventions: {
|
|
107
|
+
title: "Como a gente escreve código por aqui",
|
|
108
|
+
lede: "Seguir o jeito da casa deixa o review mais rápido (e mais feliz).",
|
|
109
|
+
},
|
|
110
|
+
tours: {
|
|
111
|
+
kicker: "Tour guiado",
|
|
112
|
+
intro: "Abra os arquivos no editor e siga comigo, uma parada de cada vez.",
|
|
113
|
+
stops: "Paradas",
|
|
114
|
+
stop: (i, total) => `Parada ${i} de ${total}`,
|
|
115
|
+
},
|
|
116
|
+
gotchas: {
|
|
117
|
+
title: "Cuidado com as cascas de banana",
|
|
118
|
+
lede: "Coisas que já pegaram alguém desprevenido. Agora não pegam mais você.",
|
|
119
|
+
},
|
|
120
|
+
decisions: {
|
|
121
|
+
title: "Por que é assim?",
|
|
122
|
+
lede: "Decisões que explicam o formato do código.",
|
|
123
|
+
context: "O contexto",
|
|
124
|
+
decision: "O que decidimos",
|
|
125
|
+
},
|
|
126
|
+
mission: {
|
|
127
|
+
title: "Sua primeira missão",
|
|
128
|
+
lede: "Tarefas pequenas e seguras para começar a contribuir. Marque conforme for fazendo.",
|
|
129
|
+
},
|
|
130
|
+
finale: {
|
|
131
|
+
title: "Você está pronto!",
|
|
132
|
+
lede: "Agora você conhece o porquê, o quê e o como do projeto. Bem-vindo ao time 💜",
|
|
133
|
+
recap: "O que você viu",
|
|
134
|
+
tasks: "{done} de {total} tarefas da primeira missão concluídas",
|
|
135
|
+
restart: "Ver de novo",
|
|
136
|
+
index: "Rever um capítulo",
|
|
137
|
+
},
|
|
138
|
+
ui: {
|
|
139
|
+
prev: "Anterior",
|
|
140
|
+
next: "Próximo",
|
|
141
|
+
overview: "Índice",
|
|
142
|
+
theme: "Alternar tema claro/escuro",
|
|
143
|
+
keys: "use ← → para navegar",
|
|
144
|
+
resume: "Continuar de onde parou (slide {n})?",
|
|
145
|
+
resumeYes: "Continuar",
|
|
146
|
+
resumeNo: "Do início",
|
|
147
|
+
close: "Fechar",
|
|
148
|
+
chapters: "Capítulos",
|
|
149
|
+
generated: "Gerado em",
|
|
150
|
+
by: "por",
|
|
151
|
+
minutes: "min",
|
|
152
|
+
},
|
|
153
|
+
pending: {
|
|
154
|
+
title: "O onboarding ainda não foi gerado",
|
|
155
|
+
body: "O arquivo existe, mas a IA ainda não o preencheu. Abra a IA que você usa neste projeto (Claude Code, Cursor, Copilot…) e envie:",
|
|
156
|
+
},
|
|
157
|
+
missing: {
|
|
158
|
+
title: "Arquivo de onboarding não encontrado",
|
|
159
|
+
body: "Rode o comando abaixo na raiz do projeto para criar o arquivo:",
|
|
160
|
+
},
|
|
161
|
+
invalid: { title: "O arquivo de onboarding tem um erro" },
|
|
162
|
+
};
|
|
163
|
+
const en = {
|
|
164
|
+
chapters: {
|
|
165
|
+
welcome: "Welcome",
|
|
166
|
+
why: "The why",
|
|
167
|
+
business: "The business",
|
|
168
|
+
product: "The product",
|
|
169
|
+
language: "Vocabulary",
|
|
170
|
+
rules: "Ground rules",
|
|
171
|
+
stack: "The stack",
|
|
172
|
+
architecture: "Architecture",
|
|
173
|
+
setup: "Hands on",
|
|
174
|
+
conventions: "House style",
|
|
175
|
+
tours: "Code tour",
|
|
176
|
+
gotchas: "Watch out",
|
|
177
|
+
decisions: "Decisions",
|
|
178
|
+
mission: "First mission",
|
|
179
|
+
finale: "Ready!",
|
|
180
|
+
},
|
|
181
|
+
welcome: {
|
|
182
|
+
hi: "Hi! Great to have you here.",
|
|
183
|
+
title: (name) => `Welcome to ${name}`,
|
|
184
|
+
intro: (min) => `This is a ~${min} minute tour of everything worth knowing before your first PR: why the project exists, how the business works, how the code is organized and where to tread carefully.`,
|
|
185
|
+
start: "Start the tour",
|
|
186
|
+
journey: "The road ahead",
|
|
187
|
+
},
|
|
188
|
+
why: {
|
|
189
|
+
title: "First, the why",
|
|
190
|
+
lede: "Before any line of code: the problem we solve.",
|
|
191
|
+
audience: "Who uses it",
|
|
192
|
+
status: "Where we are",
|
|
193
|
+
},
|
|
194
|
+
business: {
|
|
195
|
+
title: "How the business works",
|
|
196
|
+
lede: "Understanding the business turns code that works into code that makes sense.",
|
|
197
|
+
model: "The model",
|
|
198
|
+
value: "The value we deliver",
|
|
199
|
+
actorsTitle: "Who is part of this story",
|
|
200
|
+
actorsLede: "The roles that show up in the product and in the code.",
|
|
201
|
+
flowsTitle: "The main journeys",
|
|
202
|
+
flowsLede: "The path people take inside the product.",
|
|
203
|
+
},
|
|
204
|
+
product: { title: "What the product does", lede: "The features you will find (and touch) around here." },
|
|
205
|
+
language: {
|
|
206
|
+
title: "Speak the house language",
|
|
207
|
+
lede: "Terms you will hear in conversations, screens and code. Tap a card to see its meaning.",
|
|
208
|
+
tap: "tap to flip",
|
|
209
|
+
quizTitle: "Quick break: just for fun!",
|
|
210
|
+
quizLede: "A few questions to lock in the vocabulary.",
|
|
211
|
+
question: (term) => `What does “${term}” mean?`,
|
|
212
|
+
right: "Nailed it! 🎯",
|
|
213
|
+
wrong: "Almost! The right answer is highlighted.",
|
|
214
|
+
score: "You got {right} of {total}.",
|
|
215
|
+
},
|
|
216
|
+
rules: {
|
|
217
|
+
title: "The ground rules",
|
|
218
|
+
lede: "Business rules the code enforces. Breaking one usually becomes a production bug.",
|
|
219
|
+
rule: "Rule",
|
|
220
|
+
},
|
|
221
|
+
stack: { title: "The tools of the house", lede: "The stack, and what each piece is for in here." },
|
|
222
|
+
architecture: {
|
|
223
|
+
title: "How the pieces fit together",
|
|
224
|
+
modulesTitle: "Where everything lives",
|
|
225
|
+
modulesLede: "A map of the folders that matter.",
|
|
226
|
+
},
|
|
227
|
+
setup: {
|
|
228
|
+
title: "Let's run it on your machine",
|
|
229
|
+
lede: "From zero to running, step by step.",
|
|
230
|
+
prereq: "Have installed",
|
|
231
|
+
commandsTitle: "Everyday commands",
|
|
232
|
+
commandsLede: "The scripts you will use all the time.",
|
|
233
|
+
envTitle: "Environment variables",
|
|
234
|
+
envLede: "Ask the team for the values and never commit your .env.",
|
|
235
|
+
required: "required",
|
|
236
|
+
optional: "optional",
|
|
237
|
+
copy: "Copy",
|
|
238
|
+
copied: "Copied!",
|
|
239
|
+
},
|
|
240
|
+
conventions: {
|
|
241
|
+
title: "How we write code around here",
|
|
242
|
+
lede: "Following the house style makes reviews faster (and happier).",
|
|
243
|
+
},
|
|
244
|
+
tours: {
|
|
245
|
+
kicker: "Guided tour",
|
|
246
|
+
intro: "Open the files in your editor and follow along, one stop at a time.",
|
|
247
|
+
stops: "Stops",
|
|
248
|
+
stop: (i, total) => `Stop ${i} of ${total}`,
|
|
249
|
+
},
|
|
250
|
+
gotchas: { title: "Mind the banana peels", lede: "Things that caught someone off guard before. Not you, though." },
|
|
251
|
+
decisions: {
|
|
252
|
+
title: "Why is it like this?",
|
|
253
|
+
lede: "Decisions that explain the shape of the code.",
|
|
254
|
+
context: "The context",
|
|
255
|
+
decision: "What we decided",
|
|
256
|
+
},
|
|
257
|
+
mission: { title: "Your first mission", lede: "Small, safe tasks to start contributing. Check them off as you go." },
|
|
258
|
+
finale: {
|
|
259
|
+
title: "You're ready!",
|
|
260
|
+
lede: "You now know the why, the what and the how of this project. Welcome to the team 💜",
|
|
261
|
+
recap: "What you've seen",
|
|
262
|
+
tasks: "{done} of {total} first-mission tasks done",
|
|
263
|
+
restart: "Watch again",
|
|
264
|
+
index: "Revisit a chapter",
|
|
265
|
+
},
|
|
266
|
+
ui: {
|
|
267
|
+
prev: "Back",
|
|
268
|
+
next: "Next",
|
|
269
|
+
overview: "Index",
|
|
270
|
+
theme: "Toggle light/dark theme",
|
|
271
|
+
keys: "use ← → to navigate",
|
|
272
|
+
resume: "Pick up where you left off (slide {n})?",
|
|
273
|
+
resumeYes: "Continue",
|
|
274
|
+
resumeNo: "From the start",
|
|
275
|
+
close: "Close",
|
|
276
|
+
chapters: "Chapters",
|
|
277
|
+
generated: "Generated on",
|
|
278
|
+
by: "by",
|
|
279
|
+
minutes: "min",
|
|
280
|
+
},
|
|
281
|
+
pending: {
|
|
282
|
+
title: "Onboarding has not been generated yet",
|
|
283
|
+
body: "The file exists but your AI has not filled it in yet. Open the AI you use in this project (Claude Code, Cursor, Copilot…) and send:",
|
|
284
|
+
},
|
|
285
|
+
missing: { title: "Onboarding file not found", body: "Run this command at the project root to create it:" },
|
|
286
|
+
invalid: { title: "The onboarding file has an error" },
|
|
287
|
+
};
|
|
288
|
+
function copyFor(language) {
|
|
289
|
+
return language && !language.toLowerCase().startsWith("pt") ? en : pt;
|
|
290
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Evidence, OnboardingData } from "../types";
|
|
2
|
+
export declare function escapeHtml(value: unknown): string;
|
|
3
|
+
/** Texto escapado com quebras de linha e `código` inline. */
|
|
4
|
+
export declare function text(value: unknown): string;
|
|
5
|
+
export declare function chunk<T>(items: T[], size: number): T[][];
|
|
6
|
+
/** Matiz estável (0–359) derivado de um texto, para avatares e logos. */
|
|
7
|
+
export declare function hueFrom(value: string): number;
|
|
8
|
+
export declare function initials(value: string): string;
|
|
9
|
+
export declare function pad(n: number): string;
|
|
10
|
+
export declare function repoBase(data: OnboardingData): string | undefined;
|
|
11
|
+
export declare function evidenceChip(ref: Evidence, data: OnboardingData, extraClass?: string): string;
|
|
12
|
+
export declare function evidenceList(refs: Evidence[] | undefined, data: OnboardingData): string;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.escapeHtml = escapeHtml;
|
|
4
|
+
exports.text = text;
|
|
5
|
+
exports.chunk = chunk;
|
|
6
|
+
exports.hueFrom = hueFrom;
|
|
7
|
+
exports.initials = initials;
|
|
8
|
+
exports.pad = pad;
|
|
9
|
+
exports.repoBase = repoBase;
|
|
10
|
+
exports.evidenceChip = evidenceChip;
|
|
11
|
+
exports.evidenceList = evidenceList;
|
|
12
|
+
const load_1 = require("../load");
|
|
13
|
+
const validate_1 = require("../validate");
|
|
14
|
+
function escapeHtml(value) {
|
|
15
|
+
return String(value ?? "")
|
|
16
|
+
.replace(/&/g, "&")
|
|
17
|
+
.replace(/</g, "<")
|
|
18
|
+
.replace(/>/g, ">")
|
|
19
|
+
.replace(/"/g, """)
|
|
20
|
+
.replace(/'/g, "'");
|
|
21
|
+
}
|
|
22
|
+
/** Texto escapado com quebras de linha e `código` inline. */
|
|
23
|
+
function text(value) {
|
|
24
|
+
return escapeHtml(value)
|
|
25
|
+
.replace(/`([^`\n]+)`/g, "<code>$1</code>")
|
|
26
|
+
.replace(/\n/g, "<br>");
|
|
27
|
+
}
|
|
28
|
+
function chunk(items, size) {
|
|
29
|
+
const pages = [];
|
|
30
|
+
for (let i = 0; i < items.length; i += size)
|
|
31
|
+
pages.push(items.slice(i, i + size));
|
|
32
|
+
return pages;
|
|
33
|
+
}
|
|
34
|
+
/** Matiz estável (0–359) derivado de um texto, para avatares e logos. */
|
|
35
|
+
function hueFrom(value) {
|
|
36
|
+
let hash = 0;
|
|
37
|
+
for (const ch of value)
|
|
38
|
+
hash = (hash * 31 + ch.charCodeAt(0)) >>> 0;
|
|
39
|
+
return hash % 360;
|
|
40
|
+
}
|
|
41
|
+
function initials(value) {
|
|
42
|
+
const words = value.replace(/[^\p{L}\p{N}\s.-]/gu, " ").trim().split(/[\s.-]+/).filter(Boolean);
|
|
43
|
+
if (words.length === 0)
|
|
44
|
+
return "?";
|
|
45
|
+
if (words.length === 1)
|
|
46
|
+
return words[0].slice(0, 2).toUpperCase();
|
|
47
|
+
return (words[0][0] + words[1][0]).toUpperCase();
|
|
48
|
+
}
|
|
49
|
+
function pad(n) {
|
|
50
|
+
return String(n).padStart(2, "0");
|
|
51
|
+
}
|
|
52
|
+
function repoBase(data) {
|
|
53
|
+
const url = data.project?.repoUrl?.trim();
|
|
54
|
+
if (!url || !/^https?:\/\//.test(url))
|
|
55
|
+
return undefined;
|
|
56
|
+
return url.replace(/\.git$/, "").replace(/\/$/, "");
|
|
57
|
+
}
|
|
58
|
+
const FILE_ICON = '<svg viewBox="0 0 16 16" aria-hidden="true" width="12" height="12"><path fill="none" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round" d="M4 1.75h5.25L12.5 5v9.25H4z M9 1.75V5.25h3.5"/></svg>';
|
|
59
|
+
function evidenceChip(ref, data, extraClass = "") {
|
|
60
|
+
const { file, line } = (0, validate_1.parseEvidence)(ref);
|
|
61
|
+
const label = `${FILE_ICON}<span>${escapeHtml(ref)}</span>`;
|
|
62
|
+
const base = repoBase(data);
|
|
63
|
+
if (!base)
|
|
64
|
+
return `<span class="ev ${extraClass}">${label}</span>`;
|
|
65
|
+
const commit = data.meta?.commit?.trim() || "HEAD";
|
|
66
|
+
const href = `${base}/blob/${encodeURIComponent(commit)}/${file.split("/").map(encodeURIComponent).join("/")}${line ? `#L${line}` : ""}`;
|
|
67
|
+
return `<a class="ev ${extraClass}" href="${escapeHtml(href)}" target="_blank" rel="noopener">${label}</a>`;
|
|
68
|
+
}
|
|
69
|
+
function evidenceList(refs, data) {
|
|
70
|
+
const list = (0, load_1.filled)(refs);
|
|
71
|
+
if (list.length === 0)
|
|
72
|
+
return "";
|
|
73
|
+
return `<div class="evidence">${list.map((ref) => evidenceChip(ref, data)).join("")}</div>`;
|
|
74
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { OnboardingData } from "../types";
|
|
2
|
+
export { escapeHtml } from "./html";
|
|
3
|
+
export declare function renderMissingPage(): string;
|
|
4
|
+
export declare function renderErrorPage(message: string): string;
|
|
5
|
+
export declare function renderOnboarding(data: OnboardingData): string;
|