redmine-context 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +449 -0
  3. package/dist/bundle/index.d.ts +5 -0
  4. package/dist/bundle/index.js +5 -0
  5. package/dist/bundle/json.d.ts +90 -0
  6. package/dist/bundle/json.js +266 -0
  7. package/dist/bundle/markdown.d.ts +75 -0
  8. package/dist/bundle/markdown.js +294 -0
  9. package/dist/bundle/search-list.d.ts +43 -0
  10. package/dist/bundle/search-list.js +53 -0
  11. package/dist/bundle/stable-stringify.d.ts +26 -0
  12. package/dist/bundle/stable-stringify.js +50 -0
  13. package/dist/cache/contract.d.ts +157 -0
  14. package/dist/cache/contract.js +0 -0
  15. package/dist/cache/disk-index.d.ts +82 -0
  16. package/dist/cache/disk-index.js +220 -0
  17. package/dist/cache/disk.d.ts +133 -0
  18. package/dist/cache/disk.js +313 -0
  19. package/dist/cache/gc.d.ts +78 -0
  20. package/dist/cache/gc.js +123 -0
  21. package/dist/cache/get-or-compute.d.ts +36 -0
  22. package/dist/cache/get-or-compute.js +52 -0
  23. package/dist/cache/index.d.ts +9 -0
  24. package/dist/cache/index.js +8 -0
  25. package/dist/cache/keys.d.ts +76 -0
  26. package/dist/cache/keys.js +78 -0
  27. package/dist/cache/memory.d.ts +48 -0
  28. package/dist/cache/memory.js +110 -0
  29. package/dist/cache-first.d.ts +127 -0
  30. package/dist/cache-first.js +227 -0
  31. package/dist/client/errors.d.ts +33 -0
  32. package/dist/client/errors.js +49 -0
  33. package/dist/client/http.d.ts +110 -0
  34. package/dist/client/http.js +207 -0
  35. package/dist/client/index.d.ts +5 -0
  36. package/dist/client/index.js +5 -0
  37. package/dist/client/issues.d.ts +71 -0
  38. package/dist/client/issues.js +100 -0
  39. package/dist/client/search.d.ts +58 -0
  40. package/dist/client/search.js +81 -0
  41. package/dist/config/credentials.d.ts +247 -0
  42. package/dist/config/credentials.js +427 -0
  43. package/dist/config/doctor.d.ts +123 -0
  44. package/dist/config/doctor.js +260 -0
  45. package/dist/config/index.d.ts +6 -0
  46. package/dist/config/index.js +6 -0
  47. package/dist/config/keyring.d.ts +96 -0
  48. package/dist/config/keyring.js +158 -0
  49. package/dist/config/login.d.ts +97 -0
  50. package/dist/config/login.js +189 -0
  51. package/dist/config/settings.d.ts +94 -0
  52. package/dist/config/settings.js +140 -0
  53. package/dist/contract.d.ts +173 -0
  54. package/dist/contract.js +27 -0
  55. package/dist/core.d.ts +1 -0
  56. package/dist/core.js +8 -0
  57. package/dist/extract/audio-extractor.d.ts +105 -0
  58. package/dist/extract/audio-extractor.js +156 -0
  59. package/dist/extract/audio.d.ts +126 -0
  60. package/dist/extract/audio.js +184 -0
  61. package/dist/extract/dispatcher.d.ts +132 -0
  62. package/dist/extract/dispatcher.js +115 -0
  63. package/dist/extract/download.d.ts +111 -0
  64. package/dist/extract/download.js +261 -0
  65. package/dist/extract/duration.d.ts +106 -0
  66. package/dist/extract/duration.js +148 -0
  67. package/dist/extract/ffmpeg.d.ts +56 -0
  68. package/dist/extract/ffmpeg.js +95 -0
  69. package/dist/extract/gguf.d.ts +137 -0
  70. package/dist/extract/gguf.js +215 -0
  71. package/dist/extract/index.d.ts +19 -0
  72. package/dist/extract/index.js +19 -0
  73. package/dist/extract/magic.d.ts +80 -0
  74. package/dist/extract/magic.js +282 -0
  75. package/dist/extract/ooxml.d.ts +131 -0
  76. package/dist/extract/ooxml.js +336 -0
  77. package/dist/extract/pdf.d.ts +147 -0
  78. package/dist/extract/pdf.js +322 -0
  79. package/dist/extract/queue.d.ts +167 -0
  80. package/dist/extract/queue.js +217 -0
  81. package/dist/extract/subprocess.d.ts +145 -0
  82. package/dist/extract/subprocess.js +181 -0
  83. package/dist/extract/tesseract.d.ts +153 -0
  84. package/dist/extract/tesseract.js +321 -0
  85. package/dist/extract/video-extractor.d.ts +84 -0
  86. package/dist/extract/video-extractor.js +89 -0
  87. package/dist/extract/video.d.ts +198 -0
  88. package/dist/extract/video.js +418 -0
  89. package/dist/extract/which.d.ts +63 -0
  90. package/dist/extract/which.js +72 -0
  91. package/dist/extract/whisper-extract.d.ts +211 -0
  92. package/dist/extract/whisper-extract.js +323 -0
  93. package/dist/extract/whisper.d.ts +50 -0
  94. package/dist/extract/whisper.js +67 -0
  95. package/dist/extract/zip.d.ts +50 -0
  96. package/dist/extract/zip.js +165 -0
  97. package/dist/extract-issue-attachments.d.ts +82 -0
  98. package/dist/extract-issue-attachments.js +156 -0
  99. package/dist/fetch-attachment-text.d.ts +113 -0
  100. package/dist/fetch-attachment-text.js +155 -0
  101. package/dist/fetch-issue-bundle.d.ts +81 -0
  102. package/dist/fetch-issue-bundle.js +93 -0
  103. package/dist/fetch-issue-search.d.ts +74 -0
  104. package/dist/fetch-issue-search.js +120 -0
  105. package/dist/index.d.ts +21 -0
  106. package/dist/index.js +67 -0
  107. package/dist/normalize/collections.d.ts +52 -0
  108. package/dist/normalize/collections.js +170 -0
  109. package/dist/normalize/helpers.d.ts +35 -0
  110. package/dist/normalize/helpers.js +59 -0
  111. package/dist/normalize/index.d.ts +2 -0
  112. package/dist/normalize/index.js +2 -0
  113. package/dist/normalize/issue.d.ts +34 -0
  114. package/dist/normalize/issue.js +154 -0
  115. package/dist/surfaces/cli/commands.d.ts +61 -0
  116. package/dist/surfaces/cli/commands.js +262 -0
  117. package/dist/surfaces/cli/main.d.ts +32 -0
  118. package/dist/surfaces/cli/main.js +210 -0
  119. package/dist/surfaces/cli/prompts.d.ts +66 -0
  120. package/dist/surfaces/cli/prompts.js +147 -0
  121. package/dist/surfaces/cli/tty.d.ts +27 -0
  122. package/dist/surfaces/cli/tty.js +35 -0
  123. package/dist/surfaces/cli/types.d.ts +39 -0
  124. package/dist/surfaces/cli/types.js +7 -0
  125. package/dist/surfaces/mcp/server.d.ts +171 -0
  126. package/dist/surfaces/mcp/server.js +427 -0
  127. package/dist/surfaces/tui/app.d.ts +55 -0
  128. package/dist/surfaces/tui/app.js +180 -0
  129. package/dist/surfaces/tui/attachment-status.d.ts +79 -0
  130. package/dist/surfaces/tui/attachment-status.js +113 -0
  131. package/dist/surfaces/tui/components/breadcrumb.d.ts +7 -0
  132. package/dist/surfaces/tui/components/breadcrumb.js +31 -0
  133. package/dist/surfaces/tui/components/gradient-text.d.ts +23 -0
  134. package/dist/surfaces/tui/components/gradient-text.js +75 -0
  135. package/dist/surfaces/tui/components/scroll-view.d.ts +25 -0
  136. package/dist/surfaces/tui/components/scroll-view.js +77 -0
  137. package/dist/surfaces/tui/components/spinner.d.ts +12 -0
  138. package/dist/surfaces/tui/components/spinner.js +39 -0
  139. package/dist/surfaces/tui/components/text-input.d.ts +45 -0
  140. package/dist/surfaces/tui/components/text-input.js +114 -0
  141. package/dist/surfaces/tui/format-file-size.d.ts +24 -0
  142. package/dist/surfaces/tui/format-file-size.js +43 -0
  143. package/dist/surfaces/tui/glyphs.d.ts +47 -0
  144. package/dist/surfaces/tui/glyphs.js +84 -0
  145. package/dist/surfaces/tui/hooks/use-auth-guard.d.ts +39 -0
  146. package/dist/surfaces/tui/hooks/use-auth-guard.js +135 -0
  147. package/dist/surfaces/tui/hooks/use-doctor-status.d.ts +64 -0
  148. package/dist/surfaces/tui/hooks/use-doctor-status.js +123 -0
  149. package/dist/surfaces/tui/hooks/use-escape-interceptor.d.ts +25 -0
  150. package/dist/surfaces/tui/hooks/use-escape-interceptor.js +65 -0
  151. package/dist/surfaces/tui/hooks/use-exit-guard.d.ts +18 -0
  152. package/dist/surfaces/tui/hooks/use-exit-guard.js +66 -0
  153. package/dist/surfaces/tui/hooks/use-export-bundle.d.ts +62 -0
  154. package/dist/surfaces/tui/hooks/use-export-bundle.js +100 -0
  155. package/dist/surfaces/tui/hooks/use-issue-detail.d.ts +61 -0
  156. package/dist/surfaces/tui/hooks/use-issue-detail.js +132 -0
  157. package/dist/surfaces/tui/hooks/use-issue-search.d.ts +71 -0
  158. package/dist/surfaces/tui/hooks/use-issue-search.js +168 -0
  159. package/dist/surfaces/tui/hooks/use-list-navigation.d.ts +44 -0
  160. package/dist/surfaces/tui/hooks/use-list-navigation.js +82 -0
  161. package/dist/surfaces/tui/hooks/use-media-binaries.d.ts +24 -0
  162. package/dist/surfaces/tui/hooks/use-media-binaries.js +44 -0
  163. package/dist/surfaces/tui/hooks/use-my-issues.d.ts +66 -0
  164. package/dist/surfaces/tui/hooks/use-my-issues.js +151 -0
  165. package/dist/surfaces/tui/hooks/use-onboarding-callbacks.d.ts +11 -0
  166. package/dist/surfaces/tui/hooks/use-onboarding-callbacks.js +104 -0
  167. package/dist/surfaces/tui/hooks/use-terminal-width.d.ts +52 -0
  168. package/dist/surfaces/tui/hooks/use-terminal-width.js +90 -0
  169. package/dist/surfaces/tui/index.d.ts +50 -0
  170. package/dist/surfaces/tui/index.js +158 -0
  171. package/dist/surfaces/tui/instance.d.ts +37 -0
  172. package/dist/surfaces/tui/instance.js +36 -0
  173. package/dist/surfaces/tui/job-registry.d.ts +113 -0
  174. package/dist/surfaces/tui/job-registry.js +123 -0
  175. package/dist/surfaces/tui/job-status.d.ts +45 -0
  176. package/dist/surfaces/tui/job-status.js +81 -0
  177. package/dist/surfaces/tui/navigation.d.ts +74 -0
  178. package/dist/surfaces/tui/navigation.js +87 -0
  179. package/dist/surfaces/tui/palettes.d.ts +38 -0
  180. package/dist/surfaces/tui/palettes.js +244 -0
  181. package/dist/surfaces/tui/screen.d.ts +30 -0
  182. package/dist/surfaces/tui/screen.js +51 -0
  183. package/dist/surfaces/tui/screens/about.d.ts +2 -0
  184. package/dist/surfaces/tui/screens/about.js +35 -0
  185. package/dist/surfaces/tui/screens/appearance.d.ts +2 -0
  186. package/dist/surfaces/tui/screens/appearance.js +74 -0
  187. package/dist/surfaces/tui/screens/config.d.ts +2 -0
  188. package/dist/surfaces/tui/screens/config.js +82 -0
  189. package/dist/surfaces/tui/screens/doctor.d.ts +2 -0
  190. package/dist/surfaces/tui/screens/doctor.js +109 -0
  191. package/dist/surfaces/tui/screens/export.d.ts +2 -0
  192. package/dist/surfaces/tui/screens/export.js +168 -0
  193. package/dist/surfaces/tui/screens/home-selection.d.ts +70 -0
  194. package/dist/surfaces/tui/screens/home-selection.js +80 -0
  195. package/dist/surfaces/tui/screens/home.d.ts +2 -0
  196. package/dist/surfaces/tui/screens/home.js +200 -0
  197. package/dist/surfaces/tui/screens/issue-detail.d.ts +6 -0
  198. package/dist/surfaces/tui/screens/issue-detail.js +182 -0
  199. package/dist/surfaces/tui/screens/jobs.d.ts +7 -0
  200. package/dist/surfaces/tui/screens/jobs.js +89 -0
  201. package/dist/surfaces/tui/screens/loaded-issue-context.d.ts +49 -0
  202. package/dist/surfaces/tui/screens/loaded-issue-context.js +57 -0
  203. package/dist/surfaces/tui/screens/onboarding/api-key.d.ts +2 -0
  204. package/dist/surfaces/tui/screens/onboarding/api-key.js +69 -0
  205. package/dist/surfaces/tui/screens/onboarding/login.d.ts +2 -0
  206. package/dist/surfaces/tui/screens/onboarding/login.js +50 -0
  207. package/dist/surfaces/tui/screens/onboarding/mode.d.ts +2 -0
  208. package/dist/surfaces/tui/screens/onboarding/mode.js +42 -0
  209. package/dist/surfaces/tui/screens/onboarding/onboarding-context.d.ts +221 -0
  210. package/dist/surfaces/tui/screens/onboarding/onboarding-context.js +131 -0
  211. package/dist/surfaces/tui/screens/onboarding/success.d.ts +2 -0
  212. package/dist/surfaces/tui/screens/onboarding/success.js +41 -0
  213. package/dist/surfaces/tui/screens/onboarding/url.d.ts +24 -0
  214. package/dist/surfaces/tui/screens/onboarding/url.js +84 -0
  215. package/dist/surfaces/tui/screens/onboarding/validating.d.ts +1 -0
  216. package/dist/surfaces/tui/screens/onboarding/validating.js +86 -0
  217. package/dist/surfaces/tui/screens/welcome.d.ts +6 -0
  218. package/dist/surfaces/tui/screens/welcome.js +95 -0
  219. package/dist/surfaces/tui/status-color.d.ts +21 -0
  220. package/dist/surfaces/tui/status-color.js +23 -0
  221. package/dist/surfaces/tui/symbols.d.ts +231 -0
  222. package/dist/surfaces/tui/symbols.js +14 -0
  223. package/dist/surfaces/tui/terminal-colors.d.ts +29 -0
  224. package/dist/surfaces/tui/terminal-colors.js +39 -0
  225. package/dist/surfaces/tui/theme.d.ts +156 -0
  226. package/dist/surfaces/tui/theme.js +86 -0
  227. package/dist/surfaces/tui/truncate.d.ts +31 -0
  228. package/dist/surfaces/tui/truncate.js +81 -0
  229. package/package.json +93 -0
@@ -0,0 +1,123 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Registro leve de jobs da sessão (#34/M2-11) — fonte de dados do painel de
4
+ * jobs (`./screens/jobs.tsx`, tecla `t` na home/detalhe).
5
+ *
6
+ * Mesmo padrão de `./screens/home-selection.tsx`/`./screens/loaded-issue-context.tsx`
7
+ * (ver o JSDoc de qualquer um dos dois para as alternativas descartadas —
8
+ * singleton de módulo vazaria estado entre testes; guardar isto dentro de
9
+ * `NavigationValue` acoplaria o roteador a um detalhe de produtores
10
+ * específicos): instanciado uma vez em `./app.tsx`, ACIMA da pilha de
11
+ * navegação, para que jobs sobrevivam ao push/pop de telas — iniciar uma
12
+ * exportação (`./screens/export.tsx`), navegar para outra tela e voltar ainda
13
+ * deve mostrar o job (em andamento ou já concluído) no painel.
14
+ *
15
+ * ## Contrato para produtores futuros (M3/M4 — extração de anexos)
16
+ *
17
+ * Qualquer operação assíncrona da sessão que o usuário deva acompanhar
18
+ * (exportação hoje; extração de imagem/PDF no M3, áudio/vídeo no M4) é um
19
+ * "produtor": chama {@link JobRegistryValue.registerJob} uma vez ao iniciar
20
+ * e {@link JobRegistryValue.updateJobStatus} a cada transição de estado.
21
+ * Regras do contrato:
22
+ *
23
+ * 1. `id` é escolhido pelo PRODUTOR, não pelo registro — deve ser único por
24
+ * EXECUÇÃO (ex.: `extract-${attachmentId}-${Date.now()}`), permitindo que
25
+ * tentativas repetidas do mesmo tipo de job apareçam como entradas
26
+ * distintas no painel (histórico da sessão, não um contador único por
27
+ * recurso). `registerJob` faz upsert por `id`: registrar de novo com um
28
+ * `id` já existente substitui a entrada em vez de duplicá-la.
29
+ * 2. Transições de `status` esperadas: `pending` (enfileirado, ainda não
30
+ * iniciado) → `processing` (em execução) → `done` | `failed` (estado
31
+ * terminal). Produtores sem fase de fila (ex.: exportação, #33 — dispara
32
+ * e já começa a gravar) podem registrar direto em `processing`.
33
+ * 3. `startedAt` (epoch ms, `Date.now()`) é OPCIONAL — quando presente, é o
34
+ * instante em que o job entrou em `processing` (não o momento do
35
+ * registro em si, que pode ser antes se o job nasceu `pending`). Hoje o
36
+ * painel (`./screens/jobs.tsx`) não renderiza duração a partir disso —
37
+ * campo já capturado para uma UI futura sem exigir novo campo no
38
+ * contrato.
39
+ * 4. `cancelable`/`onCancel`: só produtores que sabem interromper a
40
+ * operação de fato devem marcar `cancelable: true` E fornecer
41
+ * `onCancel` (chamado pelo painel ao usuário pressionar Ctrl+C com o job
42
+ * selecionado). Exportação (#33) NÃO é cancelável — nada a abortar no
43
+ * meio de um `writeFile` — o painel mostra "(não cancelável)" para ela.
44
+ * A infraestrutura de cancelamento REAL (abortar uma extração em
45
+ * andamento) é do M4; até lá, nenhum produtor real marca `cancelable`.
46
+ * 5. Jobs terminais (`done`/`failed`) permanecem no registro até o fim da
47
+ * sessão (histórico "concluídas da sessão" da AC) — nenhuma limpeza
48
+ * automática hoje.
49
+ */
50
+ import { createContext, useCallback, useContext, useState } from 'react';
51
+ /** Teto de jobs retidos: além disso, os CONCLUÍDOS mais antigos são descartados (GC). */
52
+ const MAX_JOBS = 50;
53
+ /** Estados terminais — não aceitam transição de volta (ver validação no updateJob). */
54
+ const TERMINAL = ['done', 'failed'];
55
+ const JobRegistryContext = createContext(undefined);
56
+ /**
57
+ * Provider do registro de jobs — instanciado uma vez em `./app.tsx`,
58
+ * envolvendo toda a árvore de telas (ver o JSDoc do módulo).
59
+ */
60
+ export function JobRegistryProvider({ children }) {
61
+ const [jobs, setJobs] = useState([]);
62
+ // Identidades ESTÁVEIS (useCallback sem deps, padrão do repo) — evita
63
+ // resubscrições desnecessárias em quem as usa como dependência de efeito
64
+ // (ex.: `./screens/export.tsx`).
65
+ const registerJob = useCallback((job) => {
66
+ setJobs((current) => {
67
+ const index = current.findIndex((existing) => existing.id === job.id);
68
+ let next;
69
+ if (index === -1) {
70
+ next = [...current, job];
71
+ }
72
+ else {
73
+ next = current.slice();
74
+ next[index] = job;
75
+ }
76
+ // GC: acima do teto, descarta os CONCLUÍDOS mais antigos (em andamento
77
+ // nunca são descartados) — sessões longas com muitas extrações (M3/M4)
78
+ // não acumulam sem bound.
79
+ if (next.length > MAX_JOBS) {
80
+ const overflow = next.length - MAX_JOBS;
81
+ let removed = 0;
82
+ next = next.filter((j) => {
83
+ if (removed < overflow && TERMINAL.includes(j.status)) {
84
+ removed += 1;
85
+ return false;
86
+ }
87
+ return true;
88
+ });
89
+ }
90
+ return next;
91
+ });
92
+ }, []);
93
+ const updateJob = useCallback((id, patch) => {
94
+ setJobs((current) => current.map((job) => {
95
+ if (job.id !== id)
96
+ return job;
97
+ if (TERMINAL.includes(job.status) && patch.status !== undefined && patch.status !== job.status) {
98
+ // Estado terminal não regride — ignora a transição inválida inteira.
99
+ return job;
100
+ }
101
+ return { ...job, ...patch };
102
+ }));
103
+ }, []);
104
+ const updateJobStatus = useCallback((id, status) => {
105
+ updateJob(id, { status });
106
+ }, [updateJob]);
107
+ const value = { jobs, registerJob, updateJob, updateJobStatus };
108
+ return _jsx(JobRegistryContext.Provider, { value: value, children: children });
109
+ }
110
+ /**
111
+ * Hook do registro de jobs, consumido tanto por produtores (`./screens/export.tsx`,
112
+ * futuramente extração no M3/M4) quanto pelo painel (`./screens/jobs.tsx`).
113
+ *
114
+ * @throws {Error} Se chamado fora de um `<JobRegistryProvider>` — indica erro
115
+ * de composição (tela renderizada sem passar pelo roteador).
116
+ */
117
+ export function useJobRegistry() {
118
+ const value = useContext(JobRegistryContext);
119
+ if (value === undefined) {
120
+ throw new Error('useJobRegistry() usado fora de <JobRegistryProvider>.');
121
+ }
122
+ return value;
123
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Mapeamento visual do status de job (#34/M2-11) — ícone (com fallback ASCII
3
+ * via `./symbols.ts`) e cor (token do tema) para cada {@link JobStatus},
4
+ * consumido por `./screens/jobs.tsx`. Mesmo padrão de `./attachment-status.ts`,
5
+ * extraído para um módulo à parte para poder ser testado sem montar a tela.
6
+ *
7
+ * `processing` não tem um ícone estático — o painel usa o `Spinner` já
8
+ * existente (`./components/spinner.tsx`) para esse estado, então
9
+ * {@link jobStatusIcon} devolve `undefined` para ele (ver a doc da função).
10
+ */
11
+ import type { JobStatus } from './job-registry.js';
12
+ import type { Theme } from './theme.js';
13
+ /**
14
+ * Cor (token do tema) do ícone/rótulo de status — nunca uma cor literal
15
+ * (varredura anti-hardcode, `tests/surfaces/tui/no-hardcoded-colors.test.ts`).
16
+ *
17
+ * `pending` = `muted` (neutro, aguardando), `processing` = `primary`
18
+ * (operação ativa, mesma cor do `Spinner`), `done` = `success`, `failed` =
19
+ * `danger`.
20
+ *
21
+ * @param theme - Tema ativo (via `useTheme()` no chamador).
22
+ * @param status - Status do job.
23
+ * @returns O token de cor do tema mais adequado ao status.
24
+ */
25
+ export declare function jobStatusColor(theme: Theme, status: JobStatus): string;
26
+ /**
27
+ * Ícone estático do status, ou `undefined` para `processing` — nesse caso o
28
+ * painel (`./screens/jobs.tsx`) renderiza o `Spinner` animado existente em
29
+ * vez de um ícone parado.
30
+ *
31
+ * @param status - Status do job.
32
+ * @returns O símbolo (de `./symbols.ts`, com fallback ASCII automático) ou
33
+ * `undefined`.
34
+ * @example
35
+ * jobStatusIcon('done') // symbols.tick
36
+ * jobStatusIcon('processing') // undefined — usar <Spinner /> no lugar
37
+ */
38
+ export declare function jobStatusIcon(status: JobStatus): string | undefined;
39
+ /**
40
+ * Rótulo textual (pt-BR) do status, exibido ao lado do label do job.
41
+ *
42
+ * @param status - Status do job.
43
+ * @returns O rótulo a exibir (ex.: `[concluído]`).
44
+ */
45
+ export declare function jobStatusLabel(status: JobStatus): string;
@@ -0,0 +1,81 @@
1
+ import { symbols } from './symbols.js';
2
+ /**
3
+ * Cor (token do tema) do ícone/rótulo de status — nunca uma cor literal
4
+ * (varredura anti-hardcode, `tests/surfaces/tui/no-hardcoded-colors.test.ts`).
5
+ *
6
+ * `pending` = `muted` (neutro, aguardando), `processing` = `primary`
7
+ * (operação ativa, mesma cor do `Spinner`), `done` = `success`, `failed` =
8
+ * `danger`.
9
+ *
10
+ * @param theme - Tema ativo (via `useTheme()` no chamador).
11
+ * @param status - Status do job.
12
+ * @returns O token de cor do tema mais adequado ao status.
13
+ */
14
+ export function jobStatusColor(theme, status) {
15
+ switch (status) {
16
+ case 'pending':
17
+ return theme.muted;
18
+ case 'processing':
19
+ return theme.primary;
20
+ case 'done':
21
+ return theme.success;
22
+ case 'failed':
23
+ return theme.danger;
24
+ default: {
25
+ // Exaustividade: se a união ganhar um novo membro sem atualizar este
26
+ // switch, o typecheck falha aqui (nunca em runtime).
27
+ const exhaustiveCheck = status;
28
+ return exhaustiveCheck;
29
+ }
30
+ }
31
+ }
32
+ /**
33
+ * Ícone estático do status, ou `undefined` para `processing` — nesse caso o
34
+ * painel (`./screens/jobs.tsx`) renderiza o `Spinner` animado existente em
35
+ * vez de um ícone parado.
36
+ *
37
+ * @param status - Status do job.
38
+ * @returns O símbolo (de `./symbols.ts`, com fallback ASCII automático) ou
39
+ * `undefined`.
40
+ * @example
41
+ * jobStatusIcon('done') // symbols.tick
42
+ * jobStatusIcon('processing') // undefined — usar <Spinner /> no lugar
43
+ */
44
+ export function jobStatusIcon(status) {
45
+ switch (status) {
46
+ case 'pending':
47
+ return symbols.circle;
48
+ case 'processing':
49
+ return undefined;
50
+ case 'done':
51
+ return symbols.tick;
52
+ case 'failed':
53
+ return symbols.cross;
54
+ default: {
55
+ const exhaustiveCheck = status;
56
+ return exhaustiveCheck;
57
+ }
58
+ }
59
+ }
60
+ /**
61
+ * Rótulo textual (pt-BR) do status, exibido ao lado do label do job.
62
+ *
63
+ * @param status - Status do job.
64
+ * @returns O rótulo a exibir (ex.: `[concluído]`).
65
+ */
66
+ export function jobStatusLabel(status) {
67
+ switch (status) {
68
+ case 'pending':
69
+ return 'pendente';
70
+ case 'processing':
71
+ return 'processando';
72
+ case 'done':
73
+ return 'concluído';
74
+ case 'failed':
75
+ return 'falhou';
76
+ default: {
77
+ const exhaustiveCheck = status;
78
+ return exhaustiveCheck;
79
+ }
80
+ }
81
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Contexto de navegação da TUI (M2-01, evoluído em M2-04 para uma PILHA de
3
+ * telas).
4
+ *
5
+ * Expõe a pilha de telas navegadas — a base (índice 0) é sempre a tela
6
+ * inicial, o topo (`stack.at(-1)`) é a tela renderizada — e as operações que
7
+ * a manipulam: `push` (avança, empilha), `pop` (volta, usado pelo Esc
8
+ * global em `app.tsx`) e `replace` (troca o topo sem aumentar a
9
+ * profundidade). O breadcrumb fixo (`components/breadcrumb.tsx`) lê `stack`
10
+ * direto para renderizar o caminho "Início › Atalhos".
11
+ *
12
+ * `navigate()` é mantido como alias de `push()` — nome original da API do
13
+ * M2-01 — para que as telas existentes (`screens/welcome.tsx`) não
14
+ * precisassem ser reescritas junto desta evolução.
15
+ *
16
+ * O roteador (`app.tsx`) é o único lugar que instancia o `Provider`; o
17
+ * estado real da pilha vive em {@link useNavigationStack}, extraído para cá
18
+ * (em vez de inline no roteador) para poder ser testado isoladamente, sem
19
+ * depender de handlers de teclado.
20
+ */
21
+ import { type ReactNode } from 'react';
22
+ import type { ScreenName } from './screen.js';
23
+ /** Valor exposto pelo contexto de navegação. */
24
+ export interface NavigationValue {
25
+ /** Pilha de telas navegadas — a base (índice 0) é sempre a tela inicial. */
26
+ stack: readonly ScreenName[];
27
+ /** Tela atualmente roteada — sempre o topo da pilha (`stack.at(-1)`). */
28
+ current: ScreenName;
29
+ /** Empilha uma nova tela (navegação "para frente"). */
30
+ push(next: ScreenName): void;
31
+ /** Alias de {@link push} — nome original da API do M2-01, mantido por compat. */
32
+ navigate(next: ScreenName): void;
33
+ /** Desempilha a tela atual, voltando à anterior. Nada faz na raiz (pilha com 1 item). */
34
+ pop(): void;
35
+ /** Substitui o topo da pilha por outra tela, sem alterar a profundidade. */
36
+ replace(next: ScreenName): void;
37
+ /** Zera a pilha para uma única tela (fim de fluxo multi-tela). */
38
+ resetTo(next: ScreenName): void;
39
+ /**
40
+ * Desempilha repetidamente até `target` ficar no topo — usado para
41
+ * retomar a navegação após um fluxo empilhado por cima de uma tela de
42
+ * origem (ex.: re-autenticação, M2-13/#36: `onboarding-login` é
43
+ * empilhado sobre a tela de dados que recebeu um 401; ao logar de novo,
44
+ * `popTo(origin)` desfaz esse empilhamento de uma vez, mesmo que o login
45
+ * tenha passado por telas intermediárias como `onboarding-api-key`).
46
+ *
47
+ * Fix do review #119: se `target` não estiver na pilha (ex.: abandono do
48
+ * re-auth via Esc, `../app.tsx`, numa sessão onde a origem já não é mais
49
+ * alcançável), cai em {@link resetTo} — zera a pilha para `target` — em
50
+ * vez de silenciosamente não fazer nada (o que deixaria o usuário preso
51
+ * numa tela de onboarding sem saída visível).
52
+ */
53
+ popTo(target: ScreenName): void;
54
+ }
55
+ /** Provider do contexto de navegação — usado apenas pelo roteador (`app.tsx`). */
56
+ export declare function NavigationProvider({ value, children, }: {
57
+ value: NavigationValue;
58
+ children: ReactNode;
59
+ }): import("react").JSX.Element;
60
+ /**
61
+ * Hook de navegação consumido pelas telas.
62
+ *
63
+ * @throws {Error} Se chamado fora de um `<NavigationProvider>` — indica erro
64
+ * de composição (tela renderizada sem passar pelo roteador).
65
+ */
66
+ export declare function useNavigation(): NavigationValue;
67
+ /**
68
+ * Estado real da pilha de navegação — hook interno usado apenas pelo
69
+ * roteador (`app.tsx`), que instancia o `NavigationProvider` com o valor
70
+ * retornado aqui.
71
+ *
72
+ * @param initial Tela na base da pilha (única tela quando o app abre).
73
+ */
74
+ export declare function useNavigationStack(initial: ScreenName): NavigationValue;
@@ -0,0 +1,87 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Contexto de navegação da TUI (M2-01, evoluído em M2-04 para uma PILHA de
4
+ * telas).
5
+ *
6
+ * Expõe a pilha de telas navegadas — a base (índice 0) é sempre a tela
7
+ * inicial, o topo (`stack.at(-1)`) é a tela renderizada — e as operações que
8
+ * a manipulam: `push` (avança, empilha), `pop` (volta, usado pelo Esc
9
+ * global em `app.tsx`) e `replace` (troca o topo sem aumentar a
10
+ * profundidade). O breadcrumb fixo (`components/breadcrumb.tsx`) lê `stack`
11
+ * direto para renderizar o caminho "Início › Atalhos".
12
+ *
13
+ * `navigate()` é mantido como alias de `push()` — nome original da API do
14
+ * M2-01 — para que as telas existentes (`screens/welcome.tsx`) não
15
+ * precisassem ser reescritas junto desta evolução.
16
+ *
17
+ * O roteador (`app.tsx`) é o único lugar que instancia o `Provider`; o
18
+ * estado real da pilha vive em {@link useNavigationStack}, extraído para cá
19
+ * (em vez de inline no roteador) para poder ser testado isoladamente, sem
20
+ * depender de handlers de teclado.
21
+ */
22
+ import { createContext, useCallback, useContext, useState } from 'react';
23
+ const NavigationContext = createContext(undefined);
24
+ /** Provider do contexto de navegação — usado apenas pelo roteador (`app.tsx`). */
25
+ export function NavigationProvider({ value, children, }) {
26
+ return _jsx(NavigationContext.Provider, { value: value, children: children });
27
+ }
28
+ /**
29
+ * Hook de navegação consumido pelas telas.
30
+ *
31
+ * @throws {Error} Se chamado fora de um `<NavigationProvider>` — indica erro
32
+ * de composição (tela renderizada sem passar pelo roteador).
33
+ */
34
+ export function useNavigation() {
35
+ const value = useContext(NavigationContext);
36
+ if (value === undefined) {
37
+ throw new Error('useNavigation() usado fora de <NavigationProvider>.');
38
+ }
39
+ return value;
40
+ }
41
+ /**
42
+ * Estado real da pilha de navegação — hook interno usado apenas pelo
43
+ * roteador (`app.tsx`), que instancia o `NavigationProvider` com o valor
44
+ * retornado aqui.
45
+ *
46
+ * @param initial Tela na base da pilha (única tela quando o app abre).
47
+ */
48
+ export function useNavigationStack(initial) {
49
+ const [stack, setStack] = useState([initial]);
50
+ const push = useCallback((next) => {
51
+ setStack((current) => [...current, next]);
52
+ }, []);
53
+ const pop = useCallback(() => {
54
+ setStack((current) => (current.length > 1 ? current.slice(0, -1) : current));
55
+ }, []);
56
+ const replace = useCallback((next) => {
57
+ setStack((current) => [...current.slice(0, -1), next]);
58
+ }, []);
59
+ // Zera a pilha para uma única tela — fim de fluxos multi-tela (ex.: splash
60
+ // do onboarding, logout): sem isso, Esc "voltaria" para telas do fluxo
61
+ // encerrado que continuam empilhadas por baixo.
62
+ const resetTo = useCallback((next) => {
63
+ setStack(() => [next]);
64
+ }, []);
65
+ // Corta a pilha logo após a última ocorrência de `target` — equivalente a
66
+ // chamar `pop()` repetidamente até `target` virar o topo, mas em uma única
67
+ // atualização de estado (evita re-renders intermediários).
68
+ //
69
+ // Fix do review #119: quando `target` NÃO está na pilha, o comportamento
70
+ // passa a ser o mesmo de `resetTo(target)` (zera a pilha para uma única
71
+ // tela) em vez de um no-op silencioso — evita deixar o usuário preso numa
72
+ // tela sem rota de volta visível (ex.: abandono do re-auth, `../app.tsx`).
73
+ const popTo = useCallback((target) => {
74
+ setStack((current) => {
75
+ const index = current.lastIndexOf(target);
76
+ if (index === -1) {
77
+ return [target];
78
+ }
79
+ return current.slice(0, index + 1);
80
+ });
81
+ }, []);
82
+ // Reason: a pilha sempre tem ao menos 1 item (inicializada com `initial`,
83
+ // `pop()` nunca a esvazia) — o fallback só existe para satisfazer
84
+ // `noUncheckedIndexedAccess` do TypeScript, nunca é de fato alcançado.
85
+ const current = stack[stack.length - 1] ?? initial;
86
+ return { stack, current, push, navigate: push, pop, replace, resetTo, popTo };
87
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Kits de PALETAS de cores da TUI (#190).
3
+ *
4
+ * Cada paleta é um {@link Theme} COMPLETO em truecolor (hex), curado a partir de
5
+ * esquemas de cor consagrados. Trocar de paleta é escolher um `id` aqui — o
6
+ * `ThemeProvider` (`./theme.tsx`) recebe o `Theme` resolvido. Este arquivo é a
7
+ * ÚNICA fonte de literais hex (junto de `theme.tsx`); as telas nunca declaram cor
8
+ * literal (varredura `no-hardcoded-colors.test.ts`), sempre via `useTheme()`.
9
+ *
10
+ * Os valores hex são bem conhecidos dos respectivos projetos de tema. A
11
+ * degradação para texto puro (NO_COLOR/CI/não-TTY) é decidida ANTES de a TUI
12
+ * subir (`shouldRenderTui`), então aqui só existem terminais com cor.
13
+ */
14
+ import type { Theme } from './theme.js';
15
+ /** Uma paleta nomeada: metadados + o {@link Theme} completo. */
16
+ export interface Palette {
17
+ /** Identificador estável (persistido em settings.json). */
18
+ readonly id: string;
19
+ /** Rótulo legível exibido no seletor. */
20
+ readonly label: string;
21
+ /** Tema completo (truecolor) desta paleta. */
22
+ readonly theme: Theme;
23
+ }
24
+ /**
25
+ * Lista ORDENADA de paletas (a ordem é a do seletor). A primeira é o default
26
+ * quando não há escolha persistida ({@link DEFAULT_PALETTE_ID}).
27
+ */
28
+ export declare const PALETTES: readonly Palette[];
29
+ /** Id da paleta default (a primeira da lista) — usada sem escolha persistida. */
30
+ export declare const DEFAULT_PALETTE_ID: string;
31
+ /**
32
+ * Resolve uma paleta por `id`, com fallback para a default se o `id` for
33
+ * desconhecido (ex.: settings.json com uma paleta removida numa versão futura).
34
+ *
35
+ * @param id - Id persistido (ou `undefined`).
36
+ * @returns A paleta correspondente, ou a default.
37
+ */
38
+ export declare function resolvePalette(id: string | undefined): Palette;