@trismegisto/spine 3.3.6 → 3.3.8

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 CHANGED
@@ -237,7 +237,7 @@ Desligados sem configuração; o app liga cada um em `provideUi({ ..., features
237
237
 
238
238
  ```ts
239
239
  features: {
240
- aiAgent: { agent: 'meu-app', http: '/agents/graphql', ws: 'wss://exemplo.com/agents/graphql', title: 'Agente do app' },
240
+ aiAgent: { http: '/agents/graphql', ws: 'wss://exemplo.com/agents/graphql', title: 'Agente do app' },
241
241
  mcpConnect: { servers: [{ name: 'hefesto', url: '/hefesto/mcp', label: 'Hefesto', readScopes: 'deploy.*.read', writeScopes: 'deploy.*' }] },
242
242
  accountActivity: true,
243
243
  idleTimeout: { max: 60 }, // ou true; { min, max } em minutos
@@ -250,7 +250,7 @@ features: {
250
250
 
251
251
  | Recurso | Onde | O que faz | Depende de |
252
252
  | --- | --- | --- | --- |
253
- | `aiAgent` | Botão flutuante no canto inferior direito | Conversa com o agente de IA do app, num painel não modal que pode ser expandido. `http` e `ws` são os endereços do gateway de agentes e `agent` é o id do agente. Sem o recurso, não aparece |
253
+ | `aiAgent` | Botão flutuante no canto inferior direito | Conversa com o agente de IA do app, num painel não modal que pode ser expandido. `http` e `ws` são os endereços do gateway de agentes; o agente ele descobre pelo client do token. Quem conversa pode baixar a transcrição em PDF. Sem o recurso, não aparece |
254
254
  | `mcpConnect` | Sessões › Conectar um agente de IA | Cria um token de aplicação pessoal (`x-app-token`) e mostra, uma única vez, o `.mcp.json` pronto para o Claude Code com os servidores do app; "Leitura e escrita" põe `writeScopes` em `x-mcp-scopes` (sem isso, `readScopes` ou nenhum header: o MCP já é só leitura). URL relativa vira absoluta pela origem da página. O token nunca vai para o console nem para o storage | `SPINE_ADMIN_API` |
255
255
  | `accountActivity` | MFA › Atividade da conta | Entradas e tentativas que falharam desta conta, 90 dias, com IP, navegador, aplicativo, método e motivo; filtro "Só falhas" e "Carregar mais" | API da conta (`GET /activity`) |
256
256
  | `idleTimeout` | Conta › Encerrar por inatividade | Nunca/5/15/30/60 min; aviso com contagem 60 s antes e saída pelo `SpineLogoutService`. Conta a atividade de todas as abas. `{ min, max }` limitam a escolha (com `max`, "Nunca" some) | — |
@@ -13,7 +13,7 @@ import { TextareaAddonDirective, TextareaComponent } from '@trismegisto/angular/
13
13
  import { HermesToastService } from '@trismegisto/angular/toast';
14
14
  import { TraceComponent, TraceItemComponent } from '@trismegisto/angular/trace';
15
15
  import { SPINE_MESSAGES } from '@trismegisto/spine';
16
- import { v as SpineAiAgentService, w as SpineMarkdownComponent } from './trismegisto-spine-layout-trismegisto-spine-layout-B47sMo_4.mjs';
16
+ import { v as SpineAiAgentService, w as SpineMarkdownComponent } from './trismegisto-spine-layout-trismegisto-spine-layout-UM7CC7Sj.mjs';
17
17
 
18
18
  /** A conversa com o agente do app, dentro do painel flutuante. O estado fica no `SpineAiAgentService`. */
19
19
  class SpineAiAgentChatComponent {
@@ -73,7 +73,7 @@ class SpineAiAgentChatComponent {
73
73
  <hm-message-scroller class="spine-agent__messages" [label]="text.conversation">
74
74
  @for (entry of agent.entries(); track entry.id; let last = $last) {
75
75
  <hm-message
76
- [author]="entry.role === 'person' ? text.you : agent.title"
76
+ [author]="entry.role === 'person' ? text.you : agent.title()"
77
77
  [align]="entry.role === 'person' ? 'end' : 'start'"
78
78
  [avatarUrl]="entry.role === 'person' ? picture : last ? agent.avatarNow() : agent.avatar()"
79
79
  >
@@ -187,7 +187,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
187
187
  <hm-message-scroller class="spine-agent__messages" [label]="text.conversation">
188
188
  @for (entry of agent.entries(); track entry.id; let last = $last) {
189
189
  <hm-message
190
- [author]="entry.role === 'person' ? text.you : agent.title"
190
+ [author]="entry.role === 'person' ? text.you : agent.title()"
191
191
  [align]="entry.role === 'person' ? 'end' : 'start'"
192
192
  [avatarUrl]="entry.role === 'person' ? picture : last ? agent.avatarNow() : agent.avatar()"
193
193
  >
@@ -291,4 +291,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
291
291
  }], propDecorators: { scroller: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MessageScrollerComponent), { isSignal: true }] }] } });
292
292
 
293
293
  export { SpineAiAgentChatComponent };
294
- //# sourceMappingURL=trismegisto-spine-layout-ai-agent-chat.component-C5JljPYD.mjs.map
294
+ //# sourceMappingURL=trismegisto-spine-layout-ai-agent-chat.component-Brh0_LS9.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trismegisto-spine-layout-ai-agent-chat.component-Brh0_LS9.mjs","sources":["../../../projects/ui/layout/src/ai-agent/ai-agent-chat.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, inject, signal, viewChild } from '@angular/core';\nimport { FormControl, ReactiveFormsModule } from '@angular/forms';\nimport { AuthorizationService } from '@orfeu/angular/core';\nimport { BubbleComponent } from '@trismegisto/angular/bubble';\nimport { ButtonComponent } from '@trismegisto/angular/button';\nimport { IconButtonComponent } from '@trismegisto/angular/icon-button';\nimport { MessageComponent } from '@trismegisto/angular/message';\nimport { MessageScrollerComponent } from '@trismegisto/angular/message-scroller';\nimport { PromptSuggestionsComponent } from '@trismegisto/angular/prompt-suggestions';\nimport { TextareaAddonDirective, TextareaComponent } from '@trismegisto/angular/textarea';\nimport { HermesToastService } from '@trismegisto/angular/toast';\nimport { TraceComponent, TraceItemComponent } from '@trismegisto/angular/trace';\nimport { SPINE_MESSAGES } from '@trismegisto/spine';\nimport { SpineMarkdownComponent } from '../docs/markdown.component';\nimport { SpineAgentEntry, SpineAiAgentService } from './ai-agent.service';\n\n/** A conversa com o agente do app, dentro do painel flutuante. O estado fica no `SpineAiAgentService`. */\n@Component({\n selector: 'spine-ai-agent-chat',\n imports: [\n BubbleComponent, ButtonComponent, IconButtonComponent, MessageComponent, MessageScrollerComponent,\n PromptSuggestionsComponent, ReactiveFormsModule, SpineMarkdownComponent, TextareaAddonDirective, TextareaComponent,\n TraceComponent, TraceItemComponent,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"spine-agent\">\n @if (agent.entries().length) {\n <hm-message-scroller class=\"spine-agent__messages\" [label]=\"text.conversation\">\n @for (entry of agent.entries(); track entry.id; let last = $last) {\n <hm-message\n [author]=\"entry.role === 'person' ? text.you : agent.title()\"\n [align]=\"entry.role === 'person' ? 'end' : 'start'\"\n [avatarUrl]=\"entry.role === 'person' ? picture : last ? agent.avatarNow() : agent.avatar()\"\n >\n @if (entry.role === 'person') {\n <hm-bubble variant=\"received\"><span class=\"spine-agent__plain\">{{ entry.text }}</span></hm-bubble>\n } @else {\n <div class=\"spine-agent__answer\">\n @if (entry.steps.length) {\n <hm-trace class=\"spine-agent__steps\" [label]=\"text.steps\">\n @for (step of entry.steps; track step.id) {\n <hm-trace-item [heading]=\"step.title\" [status]=\"step.status\" [detail]=\"step.detail ?? ''\" />\n }\n </hm-trace>\n }\n @if (entry.error) {\n <p class=\"spine-agent__error\" role=\"alert\">{{ entry.text }}</p>\n @if (last && !agent.running()) {\n <hm-button size=\"small\" emphasis=\"medium\" icon=\"refresh\" (clicked)=\"agent.retry()\">{{ text.retry }}</hm-button>\n }\n } @else if (entry.text) {\n <hm-bubble class=\"spine-agent__bubble\" variant=\"emphasis\"><spine-markdown [content]=\"entry.text\" /></hm-bubble>\n @if (!(last && agent.running())) {\n <div class=\"spine-agent__actions\">\n <hm-icon-button emphasis=\"subtle\" size=\"small\" icon=\"copy\" [label]=\"text.copy\" (clicked)=\"copy(entry)\" />\n <hm-icon-button\n emphasis=\"subtle\"\n size=\"small\"\n icon=\"thumbs-up\"\n [solid]=\"entry.rating === 'up'\"\n [label]=\"text.rateUp\"\n (clicked)=\"rateUp(entry)\"\n />\n <hm-icon-button\n emphasis=\"subtle\"\n size=\"small\"\n icon=\"thumbs-down\"\n [solid]=\"entry.rating === 'down'\"\n [label]=\"text.rateDown\"\n (clicked)=\"correcting.set(entry.id)\"\n />\n </div>\n @if (correcting() === entry.id) {\n <form class=\"spine-agent__correction\" (submit)=\"$event.preventDefault(); sendCorrection(entry)\">\n <hm-textarea\n [label]=\"text.correctionLabel\"\n [placeholder]=\"text.correctionPlaceholder\"\n [rows]=\"2\"\n [formControl]=\"correction\"\n />\n <div class=\"spine-agent__correction-actions\">\n <hm-button size=\"small\" emphasis=\"minimal\" (clicked)=\"correcting.set(null)\">{{ text.correctionCancel }}</hm-button>\n <hm-button size=\"small\" emphasis=\"medium\" (clicked)=\"sendCorrection(entry)\">{{ text.correctionSend }}</hm-button>\n </div>\n </form>\n }\n }\n } @else if (!entry.steps.length) {\n <hm-bubble [typing]=\"true\" />\n }\n </div>\n }\n </hm-message>\n }\n </hm-message-scroller>\n } @else {\n <div class=\"spine-agent__empty\">\n @if (agent.image() ?? agent.avatar(); as image) {\n <img class=\"spine-agent__portrait\" [src]=\"image\" alt=\"\" (error)=\"agent.imageFailed(image)\" />\n }\n <p class=\"spine-agent__intro\">{{ text.empty }}</p>\n <hm-prompt-suggestions [suggestions]=\"agent.suggestions()\" (chosen)=\"choose($event)\" />\n </div>\n }\n\n <hm-textarea\n class=\"spine-agent__input\"\n [label]=\"text.placeholder\"\n [labelHidden]=\"true\"\n [placeholder]=\"text.placeholder\"\n [rows]=\"2\"\n [maxRows]=\"6\"\n [autoGrow]=\"true\"\n submitKey=\"enter\"\n [formControl]=\"draft\"\n (submitted)=\"send()\"\n >\n <hm-icon-button\n hmTextareaActionsEnd\n class=\"spine-agent__send\"\n emphasis=\"filled\"\n size=\"medium\"\n icon=\"send-arrow\"\n [label]=\"text.send\"\n [disabled]=\"agent.running()\"\n (clicked)=\"send()\"\n />\n </hm-textarea>\n <p class=\"spine-agent__note\">{{ text.note }}</p>\n </div>\n `,\n styles: [`\n :host { display: block; height: 100%; }\n .spine-agent { display: flex; flex-direction: column; gap: var(--hm-space-sm); height: 100%; min-height: 0; }\n .spine-agent__messages { flex: 1; min-height: 0; }\n .spine-agent__empty {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n gap: var(--hm-space-sm);\n min-height: 0;\n overflow-y: auto;\n }\n .spine-agent__intro { margin: 0; color: var(--hm-muted); }\n .spine-agent__portrait {\n align-self: center;\n /* Em painel baixo, é a imagem que cede: o texto e as sugestões continuam à vista. */\n flex: 0 1 auto;\n min-block-size: 0;\n max-inline-size: 60%;\n max-block-size: calc(var(--hm-space-3xl) * 5);\n object-fit: contain;\n }\n .spine-agent__plain { white-space: pre-wrap; overflow-wrap: anywhere; }\n .spine-agent__answer { display: flex; flex-direction: column; align-items: flex-start; gap: var(--hm-space-xs); min-width: 0; width: 100%; }\n .spine-agent__steps { width: 100%; }\n /* O balão acompanha o texto até a largura toda; a tabela maior que isso rola dentro dele. */\n .spine-agent__bubble { inline-size: 100%; max-inline-size: 100%; }\n .spine-agent__answer spine-markdown { display: block; max-inline-size: 100%; overflow-x: auto; }\n /* O avatar já diz de quem é a mensagem. O nome sai só da tela: leitor de tela continua lendo. */\n :host ::ng-deep .spine-agent__messages .hm-message__header {\n position: absolute;\n inline-size: 1px;\n block-size: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n }\n /* A tabela herda o tom do balão: só o título ganha um véu do texto, que funciona nos dois temas. */\n :host ::ng-deep .spine-agent__bubble .spine-md th { background: color-mix(in srgb, var(--hm-text) 8%, transparent); }\n :host ::ng-deep .spine-agent__bubble .spine-md :is(th, td) { border-color: color-mix(in srgb, var(--hm-text) 18%, transparent); }\n .spine-agent__input { --hm-radius-control: calc(var(--hm-radius) * 3); }\n .spine-agent__send { --hm-radius-control: var(--hm-radius-pill); }\n .spine-agent__error { margin: 0; color: var(--hm-danger); }\n .spine-agent__actions { display: flex; gap: var(--hm-space-micro); }\n .spine-agent__correction { display: flex; flex-direction: column; gap: var(--hm-space-xs); inline-size: 100%; }\n .spine-agent__correction-actions { display: flex; justify-content: flex-end; gap: var(--hm-space-xs); }\n .spine-agent__note { margin: 0; color: var(--hm-muted); font-size: var(--hm-font-size-xs); }\n `],\n})\nexport class SpineAiAgentChatComponent {\n protected readonly agent = inject(SpineAiAgentService);\n protected readonly text = inject(SPINE_MESSAGES).aiAgentChat;\n protected readonly draft = new FormControl('', { nonNullable: true });\n protected readonly correction = new FormControl('', { nonNullable: true });\n /** A resposta que a pessoa está corrigindo. */\n protected readonly correcting = signal<number | null>(null);\n protected readonly picture = inject(AuthorizationService).profile?.picture ?? null;\n private readonly scroller = viewChild(MessageScrollerComponent);\n private readonly toast = inject(HermesToastService);\n\n protected send(): void {\n const value = this.draft.value;\n if (!value.trim() || this.agent.running()) return;\n this.draft.setValue('');\n this.ask(value);\n }\n\n protected choose(suggestion: string): void {\n this.ask(suggestion);\n }\n\n protected async copy(entry: SpineAgentEntry): Promise<void> {\n try {\n await navigator.clipboard.writeText(entry.text);\n this.toast.success(this.text.copied);\n } catch {\n this.toast.error(this.text.copyError);\n }\n }\n\n protected async rateUp(entry: SpineAgentEntry): Promise<void> {\n this.correcting.set(null);\n if (!(await this.agent.rate(entry.id, 'up'))) this.toast.error(this.text.rateError);\n }\n\n protected async sendCorrection(entry: SpineAgentEntry): Promise<void> {\n const sent = await this.agent.rate(entry.id, 'down', this.correction.value.trim());\n if (!sent) {\n this.toast.error(this.text.rateError);\n return;\n }\n this.correcting.set(null);\n this.correction.setValue('');\n this.toast.success(this.text.rateThanks);\n }\n\n private ask(text: string): void {\n this.agent.send(text);\n queueMicrotask(() => this.scroller()?.scrollToEnd());\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,438 @@
1
+ import { Marked } from 'marked';
2
+
3
+ const PAGE_WIDTH = 595;
4
+ const PAGE_HEIGHT = 842;
5
+ const MARGIN = 48;
6
+ const CONTENT_WIDTH = PAGE_WIDTH - 2 * MARGIN;
7
+ const BOTTOM = PAGE_HEIGHT - MARGIN - 14;
8
+ const BODY_SIZE = 10;
9
+ const BODY_LEADING = 14;
10
+ const TABLE_SIZE = 8.5;
11
+ const TABLE_LEADING = 11;
12
+ const TABLE_PADDING = 4;
13
+ const INK = 0.13;
14
+ const MUTED = 0.42;
15
+ const RULE = 0.8;
16
+ const SHADE = 0.95;
17
+ // Larguras das fontes padrão (AFM da Adobe), de 32 a 126, em milésimos do corpo.
18
+ const REGULAR = [
19
+ 278, 278, 355, 556, 556, 889, 667, 191, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556,
20
+ 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778,
21
+ 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 333, 556, 556, 500, 556, 556, 278, 556,
22
+ 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584,
23
+ ];
24
+ const BOLD = [
25
+ 278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556,
26
+ 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778,
27
+ 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 333, 556, 611, 556, 611, 556, 333, 611,
28
+ 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584,
29
+ ];
30
+ const COURIER_WIDTH = 600;
31
+ // O trecho de 0x80 a 0x9F do WinAnsi; o resto até 0xFF coincide com o Unicode.
32
+ const WIN_ANSI = {
33
+ '€': 0x80, '‚': 0x82, 'ƒ': 0x83, '„': 0x84, '…': 0x85, '†': 0x86, '‡': 0x87, 'ˆ': 0x88, '‰': 0x89, 'Š': 0x8a, '‹': 0x8b,
34
+ 'Œ': 0x8c, 'Ž': 0x8e, '‘': 0x91, '’': 0x92, '“': 0x93, '”': 0x94, '•': 0x95, '–': 0x96, '—': 0x97, '˜': 0x98, '™': 0x99,
35
+ 'š': 0x9a, '›': 0x9b, 'œ': 0x9c, 'ž': 0x9e, 'Ÿ': 0x9f,
36
+ };
37
+ const REPLACEMENTS = {
38
+ '→': '->', '←': '<-', '⇒': '=>', '≥': '>=', '≤': '<=', '≠': '!=', '✓': 'OK', '✔': 'OK', '✅': 'OK', '✗': 'X', '✘': 'X', '❌': 'X', '⚠': '!',
39
+ '\u00a0': ' ', '\u2009': ' ', '\u202f': ' ', '\u200b': '',
40
+ };
41
+ const WIDE_PUNCTUATION = { '—': 1000, '…': 1000, '•': 350, '‰': 1000, '™': 1000, '‘': 222, '’': 222, '“': 333, '”': 333 };
42
+ const ENTITIES = { '&amp;': '&', '&lt;': '<', '&gt;': '>', '&quot;': '"', '&#39;': "'" };
43
+ /** Só o que as fontes padrão desenham: troca o que tem equivalente, tira emoji e marca o resto com `?`. */
44
+ function printable(raw) {
45
+ let result = '';
46
+ for (const char of raw.normalize('NFC')) {
47
+ const code = char.codePointAt(0);
48
+ if (REPLACEMENTS[char] !== undefined)
49
+ result += REPLACEMENTS[char];
50
+ else if (char === '\t')
51
+ result += ' ';
52
+ else if (code < 32 || code === 127)
53
+ continue;
54
+ else if (code <= 255 && !(code >= 0x80 && code <= 0x9f))
55
+ result += char;
56
+ else if (WIN_ANSI[char])
57
+ result += char;
58
+ // Modificadores e seletores de variação de emoji somem junto com ele.
59
+ else if (code >= 0x1f000 || (code >= 0x2600 && code <= 0x27bf) || code === 0xfe0f || code === 0x200d)
60
+ continue;
61
+ else
62
+ result += '?';
63
+ }
64
+ return result;
65
+ }
66
+ function charWidth(char, font) {
67
+ if (font === 'F3')
68
+ return COURIER_WIDTH;
69
+ const table = font === 'F2' ? BOLD : REGULAR;
70
+ const direct = table[char.charCodeAt(0) - 32];
71
+ if (direct !== undefined)
72
+ return direct;
73
+ if (WIDE_PUNCTUATION[char])
74
+ return WIDE_PUNCTUATION[char];
75
+ // Acentuada: a largura é a da letra base.
76
+ return table[char.normalize('NFD').charCodeAt(0) - 32] ?? 556;
77
+ }
78
+ function textWidth(text, font, size) {
79
+ let total = 0;
80
+ for (const char of text)
81
+ total += charWidth(char, font);
82
+ return (total * size) / 1000;
83
+ }
84
+ /** Quebra em linhas no espaço disponível. Palavra maior que a linha é cortada, para nunca passar da borda. */
85
+ function wrap(runs, width, size) {
86
+ const lines = [];
87
+ let line = [];
88
+ let used = 0;
89
+ const flush = () => {
90
+ lines.push(line);
91
+ line = [];
92
+ used = 0;
93
+ };
94
+ const push = (text, font) => {
95
+ const last = line[line.length - 1];
96
+ if (last && last.font === font)
97
+ last.text += text;
98
+ else
99
+ line.push({ text, font });
100
+ used += textWidth(text, font, size);
101
+ };
102
+ for (const run of runs) {
103
+ for (const piece of run.text.split(/(\n|\s+)/)) {
104
+ if (!piece)
105
+ continue;
106
+ if (piece === '\n') {
107
+ flush();
108
+ }
109
+ else if (!piece.trim()) {
110
+ if (used > 0)
111
+ push(' ', run.font);
112
+ }
113
+ else {
114
+ let word = piece;
115
+ if (used + textWidth(word, run.font, size) > width && used > 0)
116
+ flush();
117
+ while (textWidth(word, run.font, size) > width) {
118
+ let end = 1;
119
+ while (end < word.length && textWidth(word.slice(0, end + 1), run.font, size) <= width)
120
+ end += 1;
121
+ push(word.slice(0, end), run.font);
122
+ flush();
123
+ word = word.slice(end);
124
+ }
125
+ push(word, run.font);
126
+ }
127
+ }
128
+ }
129
+ flush();
130
+ return lines.map((current) => {
131
+ // O espaço que sobrou no fim da linha não conta.
132
+ const last = current[current.length - 1];
133
+ if (last)
134
+ last.text = last.text.replace(/ +$/, '');
135
+ return current.filter((run) => run.text);
136
+ });
137
+ }
138
+ function escapePdfText(text) {
139
+ let result = '';
140
+ for (const char of text) {
141
+ const code = WIN_ANSI[char] ?? char.charCodeAt(0);
142
+ if (char === '(' || char === ')' || char === '\\')
143
+ result += `\\${char}`;
144
+ else if (code < 32 || code > 126)
145
+ result += `\\${code.toString(8).padStart(3, '0')}`;
146
+ else
147
+ result += char;
148
+ }
149
+ return result;
150
+ }
151
+ function inlineRuns(tokens, font = 'F1') {
152
+ const runs = [];
153
+ for (const token of tokens ?? []) {
154
+ const item = token;
155
+ switch (token.type) {
156
+ case 'strong':
157
+ runs.push(...inlineRuns(item.tokens, 'F2'));
158
+ break;
159
+ case 'em':
160
+ case 'del':
161
+ runs.push(...inlineRuns(item.tokens, font));
162
+ break;
163
+ case 'codespan':
164
+ runs.push({ text: printable(decodeEntities(item['text'])), font: 'F3' });
165
+ break;
166
+ case 'br':
167
+ runs.push({ text: '\n', font });
168
+ break;
169
+ case 'link': {
170
+ const label = inlineRuns(item.tokens, font);
171
+ runs.push(...label);
172
+ if (label.map((run) => run.text).join('') !== item['href'])
173
+ runs.push({ text: ` (${printable(item['href'])})`, font });
174
+ break;
175
+ }
176
+ case 'image':
177
+ runs.push({ text: printable(item['text'] || item['href']), font });
178
+ break;
179
+ default:
180
+ if (item.tokens?.length)
181
+ runs.push(...inlineRuns(item.tokens, font));
182
+ else
183
+ runs.push({ text: printable(decodeEntities(String(item['text'] ?? item.raw ?? ''))), font });
184
+ }
185
+ }
186
+ return runs;
187
+ }
188
+ function decodeEntities(text) {
189
+ return text.replace(/&(amp|lt|gt|quot|#39);/g, (entity) => ENTITIES[entity] ?? entity);
190
+ }
191
+ class PdfDocument {
192
+ constructor() {
193
+ this.pages = [[]];
194
+ this.y = MARGIN;
195
+ }
196
+ get page() {
197
+ return this.pages[this.pages.length - 1];
198
+ }
199
+ newPage() {
200
+ this.pages.push([]);
201
+ this.y = MARGIN;
202
+ }
203
+ /** Garante espaço para o que vem a seguir, ou passa para a próxima página. */
204
+ reserve(height) {
205
+ if (this.y + height > BOTTOM)
206
+ this.newPage();
207
+ }
208
+ rect(x, top, width, height, gray) {
209
+ this.page.push(`${gray} g ${x.toFixed(2)} ${(PAGE_HEIGHT - top - height).toFixed(2)} ${width.toFixed(2)} ${height.toFixed(2)} re f`);
210
+ }
211
+ rule(x1, top1, x2, top2) {
212
+ this.page.push(`${RULE} G 0.5 w ${x1.toFixed(2)} ${(PAGE_HEIGHT - top1).toFixed(2)} m ${x2.toFixed(2)} ${(PAGE_HEIGHT - top2).toFixed(2)} l S`);
213
+ }
214
+ /** `baseline` conta do alto da página. */
215
+ write(line, x, baseline, size, gray = INK) {
216
+ let cursor = x;
217
+ for (const run of line) {
218
+ this.page.push(`BT /${run.font} ${size} Tf ${gray} g 1 0 0 1 ${cursor.toFixed(2)} ${(PAGE_HEIGHT - baseline).toFixed(2)} Tm (${escapePdfText(run.text)}) Tj ET`);
219
+ cursor += textWidth(run.text, run.font, size);
220
+ }
221
+ }
222
+ paragraph(runs, x, options = {}) {
223
+ const size = options.size ?? BODY_SIZE;
224
+ const leading = options.leading ?? BODY_LEADING;
225
+ const width = PAGE_WIDTH - MARGIN - x;
226
+ for (const line of wrap(runs, width - (options.shade ? 2 * TABLE_PADDING : 0), size)) {
227
+ this.reserve(leading);
228
+ if (options.shade)
229
+ this.rect(MARGIN, this.y, CONTENT_WIDTH, leading, SHADE);
230
+ this.write(line, options.shade ? x + TABLE_PADDING : x, this.y + leading - 3.5, size, options.gray);
231
+ this.y += leading;
232
+ }
233
+ }
234
+ blocks(tokens, x) {
235
+ for (const token of tokens) {
236
+ const item = token;
237
+ switch (token.type) {
238
+ case 'space':
239
+ break;
240
+ case 'heading': {
241
+ const size = item['depth'] <= 2 ? 12 : 11;
242
+ this.reserve(size + BODY_LEADING * 2);
243
+ this.y += 4;
244
+ this.paragraph(inlineRuns(item.tokens, 'F2'), x, { size, leading: size + 4 });
245
+ this.y += 2;
246
+ break;
247
+ }
248
+ case 'paragraph':
249
+ case 'text':
250
+ this.paragraph(item.tokens?.length ? inlineRuns(item.tokens) : [{ text: printable(decodeEntities(String(item['text'] ?? ''))), font: 'F1' }], x);
251
+ this.y += token.type === 'paragraph' ? 5 : 0;
252
+ break;
253
+ case 'list':
254
+ this.list(token, x);
255
+ this.y += 5;
256
+ break;
257
+ case 'code':
258
+ this.code(String(item['text'] ?? ''), x);
259
+ break;
260
+ case 'table':
261
+ this.table(token);
262
+ break;
263
+ case 'blockquote':
264
+ this.blocks(item.tokens ?? [], x + 12);
265
+ break;
266
+ case 'hr':
267
+ this.reserve(10);
268
+ this.rule(x, this.y + 4, PAGE_WIDTH - MARGIN, this.y + 4);
269
+ this.y += 10;
270
+ break;
271
+ default:
272
+ this.paragraph([{ text: printable(String(item.raw ?? '')), font: 'F1' }], x);
273
+ }
274
+ }
275
+ }
276
+ list(list, x) {
277
+ list.items.forEach((item, index) => {
278
+ const marker = list.ordered ? `${Number(list.start || 1) + index}.` : '•';
279
+ this.reserve(BODY_LEADING);
280
+ this.write([{ text: marker, font: 'F1' }], x + 2, this.y + BODY_LEADING - 3.5, BODY_SIZE);
281
+ this.blocks(item.tokens, x + 16);
282
+ });
283
+ }
284
+ code(text, x) {
285
+ const size = 8.5;
286
+ this.y += 2;
287
+ for (const source of text.split('\n')) {
288
+ for (const line of wrap([{ text: printable(source) || ' ', font: 'F3' }], PAGE_WIDTH - MARGIN - x - 2 * TABLE_PADDING, size)) {
289
+ this.reserve(TABLE_LEADING);
290
+ this.rect(x, this.y, PAGE_WIDTH - MARGIN - x, TABLE_LEADING, SHADE);
291
+ this.write(line, x + TABLE_PADDING, this.y + TABLE_LEADING - 2.5, size);
292
+ this.y += TABLE_LEADING;
293
+ }
294
+ }
295
+ this.y += 6;
296
+ }
297
+ table(table) {
298
+ const header = table.header.map((cell) => inlineRuns(cell.tokens, 'F2'));
299
+ const rows = table.rows.map((row) => row.map((cell) => inlineRuns(cell.tokens)));
300
+ const columns = Math.max(header.length, ...rows.map((row) => row.length));
301
+ const widths = this.columnWidths([header, ...rows], columns);
302
+ const inner = (column) => Math.max(widths[column] - 2 * TABLE_PADDING, 8);
303
+ const wrapRow = (row) => widths.map((_, column) => wrap(row[column] ?? [], inner(column), TABLE_SIZE));
304
+ const headerLines = wrapRow(header);
305
+ const drawRow = (cells, from, count, shaded) => {
306
+ const height = count * TABLE_LEADING + 2 * TABLE_PADDING - 2;
307
+ if (shaded)
308
+ this.rect(MARGIN, this.y, CONTENT_WIDTH, height, SHADE);
309
+ let x = MARGIN;
310
+ cells.forEach((lines, column) => {
311
+ lines.slice(from, from + count).forEach((line, index) => {
312
+ this.write(line, x + TABLE_PADDING, this.y + TABLE_PADDING + (index + 1) * TABLE_LEADING - 4, TABLE_SIZE);
313
+ });
314
+ x += widths[column];
315
+ });
316
+ this.y += height;
317
+ this.rule(MARGIN, this.y, PAGE_WIDTH - MARGIN, this.y);
318
+ };
319
+ const lineCount = (cells) => Math.max(1, ...cells.map((lines) => lines.length));
320
+ const drawHeader = () => drawRow(headerLines, 0, lineCount(headerLines), true);
321
+ const headerHeight = lineCount(headerLines) * TABLE_LEADING + 2 * TABLE_PADDING;
322
+ this.y += 2;
323
+ this.reserve(headerHeight + 2 * TABLE_LEADING + 2 * TABLE_PADDING);
324
+ drawHeader();
325
+ for (const row of rows) {
326
+ const cells = wrapRow(row);
327
+ const total = lineCount(cells);
328
+ let done = 0;
329
+ while (done < total) {
330
+ const fits = Math.floor((BOTTOM - this.y - 2 * TABLE_PADDING) / TABLE_LEADING);
331
+ // Linha que cabe inteira na página seguinte não é partida; só a que é maior que uma página.
332
+ if (fits < 1 || (done === 0 && fits < total && total * TABLE_LEADING < BOTTOM - MARGIN - headerHeight * 2)) {
333
+ this.newPage();
334
+ drawHeader();
335
+ continue;
336
+ }
337
+ const count = Math.min(fits, total - done);
338
+ drawRow(cells, done, count, false);
339
+ done += count;
340
+ }
341
+ }
342
+ this.y += 8;
343
+ }
344
+ /** Proporcional ao conteúdo, com um mínimo por coluna, para coluna curta não roubar nem sobrar demais. */
345
+ columnWidths(rows, columns) {
346
+ const natural = Array.from({ length: columns }, (_, column) => Math.max(24, ...rows.map((row) => (row[column] ?? []).reduce((sum, run) => sum + textWidth(run.text, run.font, TABLE_SIZE), 0) + 2 * TABLE_PADDING)));
347
+ const total = natural.reduce((sum, width) => sum + width, 0);
348
+ if (total <= CONTENT_WIDTH) {
349
+ const spare = (CONTENT_WIDTH - total) / columns;
350
+ return natural.map((width) => width + spare);
351
+ }
352
+ const floor = Math.min(36, CONTENT_WIDTH / columns);
353
+ const capped = natural.map((width) => Math.max(floor, (width / total) * CONTENT_WIDTH));
354
+ const scale = CONTENT_WIDTH / capped.reduce((sum, width) => sum + width, 0);
355
+ return capped.map((width) => width * scale);
356
+ }
357
+ serialize(title, pageLabel) {
358
+ const total = this.pages.length;
359
+ this.pages.forEach((content, index) => {
360
+ const text = printable(`${pageLabel} ${index + 1} / ${total}`);
361
+ const x = PAGE_WIDTH - MARGIN - textWidth(text, 'F1', 8);
362
+ content.push(`BT /F1 8 Tf ${MUTED} g 1 0 0 1 ${x.toFixed(2)} 28.00 Tm (${escapePdfText(text)}) Tj ET`);
363
+ });
364
+ // 1 catálogo, 2 árvore, 3 a 5 fontes, 6 metadados; depois, página e conteúdo de cada uma.
365
+ const firstPage = 7;
366
+ const objects = [
367
+ '<< /Type /Catalog /Pages 2 0 R >>',
368
+ `<< /Type /Pages /Count ${total} /Kids [${this.pages.map((_, index) => `${firstPage + index * 2} 0 R`).join(' ')}] >>`,
369
+ '<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>',
370
+ '<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>',
371
+ '<< /Type /Font /Subtype /Type1 /BaseFont /Courier /Encoding /WinAnsiEncoding >>',
372
+ `<< /Title (${escapePdfText(printable(title))}) >>`,
373
+ ];
374
+ this.pages.forEach((content, index) => {
375
+ const stream = content.join('\n');
376
+ objects.push(`<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${PAGE_WIDTH} ${PAGE_HEIGHT}] /Resources << /Font << /F1 3 0 R /F2 4 0 R /F3 5 0 R >> >> /Contents ${firstPage + index * 2 + 1} 0 R >>`, `<< /Length ${stream.length} >>\nstream\n${stream}\nendstream`);
377
+ });
378
+ // O texto já saiu escapado: o arquivo é todo ASCII, então a posição em caracteres é a posição em bytes.
379
+ let body = '%PDF-1.4\n';
380
+ const offsets = [];
381
+ objects.forEach((object, index) => {
382
+ offsets.push(body.length);
383
+ body += `${index + 1} 0 obj\n${object}\nendobj\n`;
384
+ });
385
+ const xref = body.length;
386
+ const count = objects.length + 1;
387
+ body += `xref\n0 ${count}\n0000000000 65535 f \n${offsets.map((offset) => `${String(offset).padStart(10, '0')} 00000 n \n`).join('')}`;
388
+ body += `trailer\n<< /Size ${count} /Root 1 0 R /Info 6 0 R >>\nstartxref\n${xref}\n%%EOF\n`;
389
+ return new TextEncoder().encode(body);
390
+ }
391
+ }
392
+ function buildTranscriptPdf(transcript) {
393
+ const parser = new Marked({ gfm: true, breaks: false });
394
+ const pdf = new PdfDocument();
395
+ pdf.paragraph([{ text: printable(transcript.title), font: 'F2' }], MARGIN, { size: 16, leading: 22 });
396
+ pdf.paragraph([{ text: printable(transcript.subtitle), font: 'F1' }], MARGIN, { size: 9, leading: 13, gray: MUTED });
397
+ pdf.y += 6;
398
+ pdf.rule(MARGIN, pdf.y, PAGE_WIDTH - MARGIN, pdf.y);
399
+ pdf.y += 10;
400
+ for (const entry of transcript.entries) {
401
+ const label = entry.role === 'person' ? transcript.personLabel : transcript.agentLabel;
402
+ // O rótulo não fica sozinho no pé da página.
403
+ pdf.reserve(BODY_LEADING * 3);
404
+ pdf.paragraph([{ text: printable(label), font: 'F2' }], MARGIN, { size: 9, leading: 13, gray: MUTED });
405
+ if (entry.role === 'person') {
406
+ // A pergunta é o que a pessoa escreveu, sem interpretar como Markdown.
407
+ pdf.paragraph([{ text: entry.text.split(/\r?\n/).map(printable).join('\n'), font: 'F1' }], MARGIN, { shade: true });
408
+ pdf.y += 8;
409
+ }
410
+ else {
411
+ if (entry.error)
412
+ pdf.paragraph([{ text: printable(entry.text), font: 'F1' }], MARGIN, { gray: MUTED });
413
+ else
414
+ pdf.blocks(parser.lexer(entry.text), MARGIN);
415
+ if (entry.steps.length) {
416
+ pdf.y += 2;
417
+ pdf.reserve(13 * 2);
418
+ pdf.paragraph([{ text: printable(transcript.queriesLabel), font: 'F2' }], MARGIN, { size: 8.5, leading: 12, gray: MUTED });
419
+ for (const step of entry.steps) {
420
+ const failed = step.failed ? ` (${transcript.failedLabel})` : '';
421
+ const detail = step.detail ? `: ${step.detail}` : '';
422
+ pdf.paragraph([{ text: printable(`• ${step.title}${failed}${detail}`), font: 'F1' }], MARGIN, { size: 8.5, leading: 12, gray: MUTED });
423
+ }
424
+ }
425
+ pdf.y += 12;
426
+ }
427
+ }
428
+ return pdf.serialize(transcript.title, transcript.pageLabel);
429
+ }
430
+ /** `dudu-conversa-2026-11-05.pdf`: sem acento nem espaço, para qualquer sistema aceitar. */
431
+ function transcriptFileName(title, word, now) {
432
+ const slug = (value) => value.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
433
+ const day = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
434
+ return `${[slug(title), slug(word)].filter(Boolean).join('-')}-${day}.pdf`;
435
+ }
436
+
437
+ export { buildTranscriptPdf, transcriptFileName };
438
+ //# sourceMappingURL=trismegisto-spine-layout-ai-agent-transcript-BNcgbrV_.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trismegisto-spine-layout-ai-agent-transcript-BNcgbrV_.mjs","sources":["../../../projects/ui/layout/src/ai-agent/ai-agent-transcript.ts"],"sourcesContent":["import { Marked, Token, Tokens } from 'marked';\n\n/**\n * Transcrição da conversa em PDF, gerada no navegador e carregada só no clique. Um gerador pequeno em vez de uma\n * biblioteca: a conversa é texto e tabela, e as fontes padrão do PDF cobrem o português sem embutir arquivo nenhum.\n * O que não existe nessas fontes (WinAnsi) vira um equivalente em texto ou `?`, nunca erro.\n */\nexport interface SpineTranscriptStep {\n title: string;\n detail?: string;\n failed?: boolean;\n}\n\nexport interface SpineTranscriptEntry {\n role: 'person' | 'agent';\n text: string;\n steps: readonly SpineTranscriptStep[];\n error: boolean;\n}\n\nexport interface SpineTranscript {\n title: string;\n /** Linha abaixo do título: aplicação, pessoa e data. */\n subtitle: string;\n personLabel: string;\n agentLabel: string;\n queriesLabel: string;\n failedLabel: string;\n pageLabel: string;\n entries: readonly SpineTranscriptEntry[];\n}\n\ntype Font = 'F1' | 'F2' | 'F3';\n\ninterface Run {\n text: string;\n font: Font;\n}\n\ntype Line = Run[];\n\nconst PAGE_WIDTH = 595;\nconst PAGE_HEIGHT = 842;\nconst MARGIN = 48;\nconst CONTENT_WIDTH = PAGE_WIDTH - 2 * MARGIN;\nconst BOTTOM = PAGE_HEIGHT - MARGIN - 14;\nconst BODY_SIZE = 10;\nconst BODY_LEADING = 14;\nconst TABLE_SIZE = 8.5;\nconst TABLE_LEADING = 11;\nconst TABLE_PADDING = 4;\nconst INK = 0.13;\nconst MUTED = 0.42;\nconst RULE = 0.8;\nconst SHADE = 0.95;\n\n// Larguras das fontes padrão (AFM da Adobe), de 32 a 126, em milésimos do corpo.\nconst REGULAR = [\n 278, 278, 355, 556, 556, 889, 667, 191, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556,\n 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778,\n 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 333, 556, 556, 500, 556, 556, 278, 556,\n 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584,\n];\nconst BOLD = [\n 278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556,\n 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778,\n 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 333, 556, 611, 556, 611, 556, 333, 611,\n 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584,\n];\nconst COURIER_WIDTH = 600;\n\n// O trecho de 0x80 a 0x9F do WinAnsi; o resto até 0xFF coincide com o Unicode.\nconst WIN_ANSI: Readonly<Record<string, number>> = {\n '€': 0x80, '‚': 0x82, 'ƒ': 0x83, '„': 0x84, '…': 0x85, '†': 0x86, '‡': 0x87, 'ˆ': 0x88, '‰': 0x89, 'Š': 0x8a, '‹': 0x8b,\n 'Œ': 0x8c, 'Ž': 0x8e, '‘': 0x91, '’': 0x92, '“': 0x93, '”': 0x94, '•': 0x95, '–': 0x96, '—': 0x97, '˜': 0x98, '™': 0x99,\n 'š': 0x9a, '›': 0x9b, 'œ': 0x9c, 'ž': 0x9e, 'Ÿ': 0x9f,\n};\nconst REPLACEMENTS: Readonly<Record<string, string>> = {\n '→': '->', '←': '<-', '⇒': '=>', '≥': '>=', '≤': '<=', '≠': '!=', '✓': 'OK', '✔': 'OK', '✅': 'OK', '✗': 'X', '✘': 'X', '❌': 'X', '⚠': '!',\n '\\u00a0': ' ', '\\u2009': ' ', '\\u202f': ' ', '\\u200b': '',\n};\nconst WIDE_PUNCTUATION: Readonly<Record<string, number>> = { '—': 1000, '…': 1000, '•': 350, '‰': 1000, '™': 1000, '‘': 222, '’': 222, '“': 333, '”': 333 };\nconst ENTITIES: Readonly<Record<string, string>> = { '&amp;': '&', '&lt;': '<', '&gt;': '>', '&quot;': '\"', '&#39;': \"'\" };\n\n/** Só o que as fontes padrão desenham: troca o que tem equivalente, tira emoji e marca o resto com `?`. */\nfunction printable(raw: string): string {\n let result = '';\n for (const char of raw.normalize('NFC')) {\n const code = char.codePointAt(0)!;\n if (REPLACEMENTS[char] !== undefined) result += REPLACEMENTS[char];\n else if (char === '\\t') result += ' ';\n else if (code < 32 || code === 127) continue;\n else if (code <= 255 && !(code >= 0x80 && code <= 0x9f)) result += char;\n else if (WIN_ANSI[char]) result += char;\n // Modificadores e seletores de variação de emoji somem junto com ele.\n else if (code >= 0x1f000 || (code >= 0x2600 && code <= 0x27bf) || code === 0xfe0f || code === 0x200d) continue;\n else result += '?';\n }\n return result;\n}\n\nfunction charWidth(char: string, font: Font): number {\n if (font === 'F3') return COURIER_WIDTH;\n const table = font === 'F2' ? BOLD : REGULAR;\n const direct = table[char.charCodeAt(0) - 32];\n if (direct !== undefined) return direct;\n if (WIDE_PUNCTUATION[char]) return WIDE_PUNCTUATION[char];\n // Acentuada: a largura é a da letra base.\n return table[char.normalize('NFD').charCodeAt(0) - 32] ?? 556;\n}\n\nfunction textWidth(text: string, font: Font, size: number): number {\n let total = 0;\n for (const char of text) total += charWidth(char, font);\n return (total * size) / 1000;\n}\n\n/** Quebra em linhas no espaço disponível. Palavra maior que a linha é cortada, para nunca passar da borda. */\nfunction wrap(runs: readonly Run[], width: number, size: number): Line[] {\n const lines: Line[] = [];\n let line: Line = [];\n let used = 0;\n const flush = () => {\n lines.push(line);\n line = [];\n used = 0;\n };\n const push = (text: string, font: Font) => {\n const last = line[line.length - 1];\n if (last && last.font === font) last.text += text;\n else line.push({ text, font });\n used += textWidth(text, font, size);\n };\n for (const run of runs) {\n for (const piece of run.text.split(/(\\n|\\s+)/)) {\n if (!piece) continue;\n if (piece === '\\n') {\n flush();\n } else if (!piece.trim()) {\n if (used > 0) push(' ', run.font);\n } else {\n let word = piece;\n if (used + textWidth(word, run.font, size) > width && used > 0) flush();\n while (textWidth(word, run.font, size) > width) {\n let end = 1;\n while (end < word.length && textWidth(word.slice(0, end + 1), run.font, size) <= width) end += 1;\n push(word.slice(0, end), run.font);\n flush();\n word = word.slice(end);\n }\n push(word, run.font);\n }\n }\n }\n flush();\n return lines.map((current) => {\n // O espaço que sobrou no fim da linha não conta.\n const last = current[current.length - 1];\n if (last) last.text = last.text.replace(/ +$/, '');\n return current.filter((run) => run.text);\n });\n}\n\nfunction escapePdfText(text: string): string {\n let result = '';\n for (const char of text) {\n const code = WIN_ANSI[char] ?? char.charCodeAt(0);\n if (char === '(' || char === ')' || char === '\\\\') result += `\\\\${char}`;\n else if (code < 32 || code > 126) result += `\\\\${code.toString(8).padStart(3, '0')}`;\n else result += char;\n }\n return result;\n}\n\nfunction inlineRuns(tokens: readonly Token[] | undefined, font: Font = 'F1'): Run[] {\n const runs: Run[] = [];\n for (const token of tokens ?? []) {\n const item = token as Tokens.Generic;\n switch (token.type) {\n case 'strong':\n runs.push(...inlineRuns(item.tokens, 'F2'));\n break;\n case 'em':\n case 'del':\n runs.push(...inlineRuns(item.tokens, font));\n break;\n case 'codespan':\n runs.push({ text: printable(decodeEntities(item['text'])), font: 'F3' });\n break;\n case 'br':\n runs.push({ text: '\\n', font });\n break;\n case 'link': {\n const label = inlineRuns(item.tokens, font);\n runs.push(...label);\n if (label.map((run) => run.text).join('') !== item['href']) runs.push({ text: ` (${printable(item['href'])})`, font });\n break;\n }\n case 'image':\n runs.push({ text: printable(item['text'] || item['href']), font });\n break;\n default:\n if (item.tokens?.length) runs.push(...inlineRuns(item.tokens, font));\n else runs.push({ text: printable(decodeEntities(String(item['text'] ?? item.raw ?? ''))), font });\n }\n }\n return runs;\n}\n\nfunction decodeEntities(text: string): string {\n return text.replace(/&(amp|lt|gt|quot|#39);/g, (entity) => ENTITIES[entity] ?? entity);\n}\n\nclass PdfDocument {\n readonly pages: string[][] = [[]];\n y = MARGIN;\n\n private get page(): string[] {\n return this.pages[this.pages.length - 1];\n }\n\n newPage(): void {\n this.pages.push([]);\n this.y = MARGIN;\n }\n\n /** Garante espaço para o que vem a seguir, ou passa para a próxima página. */\n reserve(height: number): void {\n if (this.y + height > BOTTOM) this.newPage();\n }\n\n rect(x: number, top: number, width: number, height: number, gray: number): void {\n this.page.push(`${gray} g ${x.toFixed(2)} ${(PAGE_HEIGHT - top - height).toFixed(2)} ${width.toFixed(2)} ${height.toFixed(2)} re f`);\n }\n\n rule(x1: number, top1: number, x2: number, top2: number): void {\n this.page.push(`${RULE} G 0.5 w ${x1.toFixed(2)} ${(PAGE_HEIGHT - top1).toFixed(2)} m ${x2.toFixed(2)} ${(PAGE_HEIGHT - top2).toFixed(2)} l S`);\n }\n\n /** `baseline` conta do alto da página. */\n write(line: readonly Run[], x: number, baseline: number, size: number, gray = INK): void {\n let cursor = x;\n for (const run of line) {\n this.page.push(`BT /${run.font} ${size} Tf ${gray} g 1 0 0 1 ${cursor.toFixed(2)} ${(PAGE_HEIGHT - baseline).toFixed(2)} Tm (${escapePdfText(run.text)}) Tj ET`);\n cursor += textWidth(run.text, run.font, size);\n }\n }\n\n paragraph(runs: readonly Run[], x: number, options: { size?: number; leading?: number; gray?: number; shade?: boolean } = {}): void {\n const size = options.size ?? BODY_SIZE;\n const leading = options.leading ?? BODY_LEADING;\n const width = PAGE_WIDTH - MARGIN - x;\n for (const line of wrap(runs, width - (options.shade ? 2 * TABLE_PADDING : 0), size)) {\n this.reserve(leading);\n if (options.shade) this.rect(MARGIN, this.y, CONTENT_WIDTH, leading, SHADE);\n this.write(line, options.shade ? x + TABLE_PADDING : x, this.y + leading - 3.5, size, options.gray);\n this.y += leading;\n }\n }\n\n blocks(tokens: readonly Token[], x: number): void {\n for (const token of tokens) {\n const item = token as Tokens.Generic;\n switch (token.type) {\n case 'space':\n break;\n case 'heading': {\n const size = (item['depth'] as number) <= 2 ? 12 : 11;\n this.reserve(size + BODY_LEADING * 2);\n this.y += 4;\n this.paragraph(inlineRuns(item.tokens, 'F2'), x, { size, leading: size + 4 });\n this.y += 2;\n break;\n }\n case 'paragraph':\n case 'text':\n this.paragraph(item.tokens?.length ? inlineRuns(item.tokens) : [{ text: printable(decodeEntities(String(item['text'] ?? ''))), font: 'F1' }], x);\n this.y += token.type === 'paragraph' ? 5 : 0;\n break;\n case 'list':\n this.list(token as Tokens.List, x);\n this.y += 5;\n break;\n case 'code':\n this.code(String(item['text'] ?? ''), x);\n break;\n case 'table':\n this.table(token as Tokens.Table);\n break;\n case 'blockquote':\n this.blocks(item.tokens ?? [], x + 12);\n break;\n case 'hr':\n this.reserve(10);\n this.rule(x, this.y + 4, PAGE_WIDTH - MARGIN, this.y + 4);\n this.y += 10;\n break;\n default:\n this.paragraph([{ text: printable(String(item.raw ?? '')), font: 'F1' }], x);\n }\n }\n }\n\n private list(list: Tokens.List, x: number): void {\n list.items.forEach((item, index) => {\n const marker = list.ordered ? `${Number(list.start || 1) + index}.` : '•';\n this.reserve(BODY_LEADING);\n this.write([{ text: marker, font: 'F1' }], x + 2, this.y + BODY_LEADING - 3.5, BODY_SIZE);\n this.blocks(item.tokens, x + 16);\n });\n }\n\n private code(text: string, x: number): void {\n const size = 8.5;\n this.y += 2;\n for (const source of text.split('\\n')) {\n for (const line of wrap([{ text: printable(source) || ' ', font: 'F3' }], PAGE_WIDTH - MARGIN - x - 2 * TABLE_PADDING, size)) {\n this.reserve(TABLE_LEADING);\n this.rect(x, this.y, PAGE_WIDTH - MARGIN - x, TABLE_LEADING, SHADE);\n this.write(line, x + TABLE_PADDING, this.y + TABLE_LEADING - 2.5, size);\n this.y += TABLE_LEADING;\n }\n }\n this.y += 6;\n }\n\n private table(table: Tokens.Table): void {\n const header = table.header.map((cell) => inlineRuns(cell.tokens, 'F2'));\n const rows = table.rows.map((row) => row.map((cell) => inlineRuns(cell.tokens)));\n const columns = Math.max(header.length, ...rows.map((row) => row.length));\n const widths = this.columnWidths([header, ...rows], columns);\n const inner = (column: number) => Math.max(widths[column] - 2 * TABLE_PADDING, 8);\n const wrapRow = (row: readonly Run[][]) => widths.map((_, column) => wrap(row[column] ?? [], inner(column), TABLE_SIZE));\n const headerLines = wrapRow(header);\n\n const drawRow = (cells: readonly Line[][], from: number, count: number, shaded: boolean) => {\n const height = count * TABLE_LEADING + 2 * TABLE_PADDING - 2;\n if (shaded) this.rect(MARGIN, this.y, CONTENT_WIDTH, height, SHADE);\n let x = MARGIN;\n cells.forEach((lines, column) => {\n lines.slice(from, from + count).forEach((line, index) => {\n this.write(line, x + TABLE_PADDING, this.y + TABLE_PADDING + (index + 1) * TABLE_LEADING - 4, TABLE_SIZE);\n });\n x += widths[column];\n });\n this.y += height;\n this.rule(MARGIN, this.y, PAGE_WIDTH - MARGIN, this.y);\n };\n const lineCount = (cells: readonly Line[][]) => Math.max(1, ...cells.map((lines) => lines.length));\n const drawHeader = () => drawRow(headerLines, 0, lineCount(headerLines), true);\n const headerHeight = lineCount(headerLines) * TABLE_LEADING + 2 * TABLE_PADDING;\n\n this.y += 2;\n this.reserve(headerHeight + 2 * TABLE_LEADING + 2 * TABLE_PADDING);\n drawHeader();\n for (const row of rows) {\n const cells = wrapRow(row);\n const total = lineCount(cells);\n let done = 0;\n while (done < total) {\n const fits = Math.floor((BOTTOM - this.y - 2 * TABLE_PADDING) / TABLE_LEADING);\n // Linha que cabe inteira na página seguinte não é partida; só a que é maior que uma página.\n if (fits < 1 || (done === 0 && fits < total && total * TABLE_LEADING < BOTTOM - MARGIN - headerHeight * 2)) {\n this.newPage();\n drawHeader();\n continue;\n }\n const count = Math.min(fits, total - done);\n drawRow(cells, done, count, false);\n done += count;\n }\n }\n this.y += 8;\n }\n\n /** Proporcional ao conteúdo, com um mínimo por coluna, para coluna curta não roubar nem sobrar demais. */\n private columnWidths(rows: readonly (readonly Run[][])[], columns: number): number[] {\n const natural = Array.from({ length: columns }, (_, column) => Math.max(\n 24,\n ...rows.map((row) => (row[column] ?? []).reduce((sum, run) => sum + textWidth(run.text, run.font, TABLE_SIZE), 0) + 2 * TABLE_PADDING),\n ));\n const total = natural.reduce((sum, width) => sum + width, 0);\n if (total <= CONTENT_WIDTH) {\n const spare = (CONTENT_WIDTH - total) / columns;\n return natural.map((width) => width + spare);\n }\n const floor = Math.min(36, CONTENT_WIDTH / columns);\n const capped = natural.map((width) => Math.max(floor, (width / total) * CONTENT_WIDTH));\n const scale = CONTENT_WIDTH / capped.reduce((sum, width) => sum + width, 0);\n return capped.map((width) => width * scale);\n }\n\n serialize(title: string, pageLabel: string): Uint8Array {\n const total = this.pages.length;\n this.pages.forEach((content, index) => {\n const text = printable(`${pageLabel} ${index + 1} / ${total}`);\n const x = PAGE_WIDTH - MARGIN - textWidth(text, 'F1', 8);\n content.push(`BT /F1 8 Tf ${MUTED} g 1 0 0 1 ${x.toFixed(2)} 28.00 Tm (${escapePdfText(text)}) Tj ET`);\n });\n\n // 1 catálogo, 2 árvore, 3 a 5 fontes, 6 metadados; depois, página e conteúdo de cada uma.\n const firstPage = 7;\n const objects: string[] = [\n '<< /Type /Catalog /Pages 2 0 R >>',\n `<< /Type /Pages /Count ${total} /Kids [${this.pages.map((_, index) => `${firstPage + index * 2} 0 R`).join(' ')}] >>`,\n '<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>',\n '<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>',\n '<< /Type /Font /Subtype /Type1 /BaseFont /Courier /Encoding /WinAnsiEncoding >>',\n `<< /Title (${escapePdfText(printable(title))}) >>`,\n ];\n this.pages.forEach((content, index) => {\n const stream = content.join('\\n');\n objects.push(\n `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${PAGE_WIDTH} ${PAGE_HEIGHT}] /Resources << /Font << /F1 3 0 R /F2 4 0 R /F3 5 0 R >> >> /Contents ${firstPage + index * 2 + 1} 0 R >>`,\n `<< /Length ${stream.length} >>\\nstream\\n${stream}\\nendstream`,\n );\n });\n\n // O texto já saiu escapado: o arquivo é todo ASCII, então a posição em caracteres é a posição em bytes.\n let body = '%PDF-1.4\\n';\n const offsets: number[] = [];\n objects.forEach((object, index) => {\n offsets.push(body.length);\n body += `${index + 1} 0 obj\\n${object}\\nendobj\\n`;\n });\n const xref = body.length;\n const count = objects.length + 1;\n body += `xref\\n0 ${count}\\n0000000000 65535 f \\n${offsets.map((offset) => `${String(offset).padStart(10, '0')} 00000 n \\n`).join('')}`;\n body += `trailer\\n<< /Size ${count} /Root 1 0 R /Info 6 0 R >>\\nstartxref\\n${xref}\\n%%EOF\\n`;\n return new TextEncoder().encode(body);\n }\n}\n\nexport function buildTranscriptPdf(transcript: SpineTranscript): Uint8Array {\n const parser = new Marked({ gfm: true, breaks: false });\n const pdf = new PdfDocument();\n\n pdf.paragraph([{ text: printable(transcript.title), font: 'F2' }], MARGIN, { size: 16, leading: 22 });\n pdf.paragraph([{ text: printable(transcript.subtitle), font: 'F1' }], MARGIN, { size: 9, leading: 13, gray: MUTED });\n pdf.y += 6;\n pdf.rule(MARGIN, pdf.y, PAGE_WIDTH - MARGIN, pdf.y);\n pdf.y += 10;\n\n for (const entry of transcript.entries) {\n const label = entry.role === 'person' ? transcript.personLabel : transcript.agentLabel;\n // O rótulo não fica sozinho no pé da página.\n pdf.reserve(BODY_LEADING * 3);\n pdf.paragraph([{ text: printable(label), font: 'F2' }], MARGIN, { size: 9, leading: 13, gray: MUTED });\n if (entry.role === 'person') {\n // A pergunta é o que a pessoa escreveu, sem interpretar como Markdown.\n pdf.paragraph([{ text: entry.text.split(/\\r?\\n/).map(printable).join('\\n'), font: 'F1' }], MARGIN, { shade: true });\n pdf.y += 8;\n } else {\n if (entry.error) pdf.paragraph([{ text: printable(entry.text), font: 'F1' }], MARGIN, { gray: MUTED });\n else pdf.blocks(parser.lexer(entry.text), MARGIN);\n if (entry.steps.length) {\n pdf.y += 2;\n pdf.reserve(13 * 2);\n pdf.paragraph([{ text: printable(transcript.queriesLabel), font: 'F2' }], MARGIN, { size: 8.5, leading: 12, gray: MUTED });\n for (const step of entry.steps) {\n const failed = step.failed ? ` (${transcript.failedLabel})` : '';\n const detail = step.detail ? `: ${step.detail}` : '';\n pdf.paragraph([{ text: printable(`• ${step.title}${failed}${detail}`), font: 'F1' }], MARGIN, { size: 8.5, leading: 12, gray: MUTED });\n }\n }\n pdf.y += 12;\n }\n }\n return pdf.serialize(transcript.title, transcript.pageLabel);\n}\n\n/** `dudu-conversa-2026-11-05.pdf`: sem acento nem espaço, para qualquer sistema aceitar. */\nexport function transcriptFileName(title: string, word: string, now: Date): string {\n const slug = (value: string) => value.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');\n const day = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;\n return `${[slug(title), slug(word)].filter(Boolean).join('-')}-${day}.pdf`;\n}\n"],"names":[],"mappings":"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"}