@arqel-dev/ai 0.8.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.
@@ -0,0 +1,268 @@
1
+ import { ReactElement } from 'react';
2
+
3
+ /**
4
+ * `<AiExtractInput>` — apresentational React component for the PHP
5
+ * `Arqel\Ai\Fields\AiExtractField` (component string `AiExtractInput`).
6
+ *
7
+ * Render contract (AI-010):
8
+ * - Read-only label `Source: {sourceField}` indicando o campo do
9
+ * formulário de onde o texto-fonte é lido.
10
+ * - Botão `Extract with AI` (label vem de `props.buttonLabel`); em
11
+ * loading o botão fica `disabled` + spinner inline.
12
+ * - Empty state `No extraction yet — click button to start` antes da
13
+ * primeira extração.
14
+ * - Após sucesso: lista preview `<dl>` com cada `targetField:
15
+ * extractedValue`; cada entry tem botão `Apply` individual e o
16
+ * toolbar mostra `Apply all`.
17
+ * - `Apply` chama `onPopulateField?.(targetField, value)` ou
18
+ * `onChange(extracted)` se `onPopulateField` for ausente.
19
+ * - Banner `role="alert"` quando o `fetch` falha (com `message` da
20
+ * response 422 quando disponível).
21
+ *
22
+ * Network:
23
+ * - URL = `props.extractUrl` override ou
24
+ * `/admin/${resource}/fields/${field}/extract`.
25
+ * - Body: `{ sourceText }` lido de `formData?.[sourceField] ?? ''`.
26
+ * - Resposta: `{ extracted: Record<string, unknown> }` (200) ou
27
+ * `{ message }` (422).
28
+ *
29
+ * SSR-safe: nada no render path toca `window`/`document`.
30
+ */
31
+
32
+ interface AiExtractInputFieldProps {
33
+ sourceField: string;
34
+ targetFields: string[];
35
+ buttonLabel: string;
36
+ usingJsonMode: boolean;
37
+ provider?: string | null;
38
+ }
39
+ type AiExtractValue = Record<string, unknown>;
40
+ interface AiExtractInputProps {
41
+ name: string;
42
+ value: AiExtractValue | null;
43
+ onChange?: (value: AiExtractValue) => void;
44
+ props: AiExtractInputFieldProps | undefined;
45
+ resource?: string;
46
+ field?: string;
47
+ formData?: Record<string, unknown>;
48
+ extractUrl?: string;
49
+ csrfToken?: string;
50
+ onPopulateField?: (targetField: string, value: unknown) => void;
51
+ }
52
+ declare function AiExtractInput(props: AiExtractInputProps): ReactElement;
53
+
54
+ /**
55
+ * `<AiImageInput>` — apresentational React component for the PHP
56
+ * `Arqel\Ai\Fields\AiImageField` (component string `AiImageInput`).
57
+ *
58
+ * Render contract (AI-011):
59
+ * - File input restrito por `accept={props.acceptedMimes.join(',')}`
60
+ * com drag-drop area visual (`<label>` clicável que dispara o
61
+ * `<input type="file">` escondido).
62
+ * - Preview da imagem após selecionar (img src vem de
63
+ * `URL.createObjectURL` — só é chamado dentro do change handler,
64
+ * mantendo o render path SSR-safe).
65
+ * - Validação client-side: rejeita arquivos > `props.maxFileSize`
66
+ * (em bytes) com banner de erro; nesse caso o botão "Analyze with
67
+ * AI" fica `disabled`.
68
+ * - Botão `Analyze with AI` (label de `props.buttonLabel`); spinner
69
+ * inline + `disabled` durante o `fetch`. Sem arquivo selecionado o
70
+ * botão também fica `disabled`.
71
+ * - Após sucesso: lista preview `<dl>` com `analysis_key: value` por
72
+ * entry; cada entry tem botão `Apply` individual; toolbar mostra
73
+ * `Apply all`.
74
+ * - `Apply` chama `onPopulateField?.(target, value)` usando o
75
+ * mapeamento `props.populateFields[analysisKey]` quando disponível.
76
+ * `Apply all` itera todas as analyses que têm mapping.
77
+ * - Banner `role="alert"` quando o `fetch` falha (mensagem da
78
+ * response 422 quando disponível).
79
+ *
80
+ * Network:
81
+ * - URL = `props.analyzeUrl` override ou
82
+ * `/admin/${resource}/fields/${field}/analyze-image`.
83
+ * - Body: `{ imageBase64 }` produzido por `FileReader.readAsDataURL`
84
+ * (data URI completo).
85
+ * - Resposta: `{ analyses, populateMapping }` (200) ou
86
+ * `{ message }` (422).
87
+ *
88
+ * SSR-safe: nada no render path toca `window`/`URL`/`FileReader` — só
89
+ * em handlers de evento.
90
+ */
91
+
92
+ interface AiImageInputFieldProps {
93
+ analyses: string[];
94
+ populateFields: Record<string, string>;
95
+ provider?: string | null;
96
+ acceptedMimes: string[];
97
+ maxFileSize: number;
98
+ buttonLabel: string;
99
+ }
100
+ interface AiImageInputProps {
101
+ name: string;
102
+ value: string | null;
103
+ onChange?: (value: string) => void;
104
+ props: AiImageInputFieldProps | undefined;
105
+ resource?: string;
106
+ field?: string;
107
+ analyzeUrl?: string;
108
+ csrfToken?: string;
109
+ onPopulateField?: (targetField: string, value: string) => void;
110
+ }
111
+ declare function AiImageInput(props: AiImageInputProps): ReactElement;
112
+
113
+ /**
114
+ * `<AiSelectInput>` — apresentational React component for the PHP
115
+ * `Arqel\Ai\Fields\AiSelectField` (component string `AiSelectInput`).
116
+ *
117
+ * Render contract (AI-009):
118
+ * - `<select>` controlled (or uncontrolled fallback) with a
119
+ * placeholder option + one entry per `props.options`,
120
+ * - "Classify with AI" button next to the select; disabled when
121
+ * `hasContextFields` is false (with a tooltip explaining why),
122
+ * - while a request is in-flight the button is disabled and an
123
+ * inline spinner is rendered,
124
+ * - on success a banner with `Suggested by AI` is rendered with two
125
+ * dismiss buttons: `Accept` and `Pick another` (both just hide the
126
+ * banner — the value is already applied),
127
+ * - if the response returns `key: null` and `fallbackOption` is
128
+ * configured, the fallback is applied and a `Used fallback`
129
+ * banner is shown,
130
+ * - if the response returns `key: null` and there is no fallback,
131
+ * an error banner `Could not classify` is rendered.
132
+ *
133
+ * Network:
134
+ * - URL = `props.classifyUrl` override or
135
+ * `/admin/${resource}/fields/${field}/classify`.
136
+ * - `fetch(url, { method: 'POST', credentials: 'same-origin', headers:
137
+ * { Content-Type, Accept, X-CSRF-TOKEN }, body: JSON.stringify({
138
+ * formData }) })`. The PHP controller runs the LLM classification
139
+ * and returns `{ key, label }`.
140
+ *
141
+ * SSR-safe: nothing in the render path touches `window`/`document`.
142
+ */
143
+
144
+ interface AiSelectInputFieldProps {
145
+ options: Record<string, string>;
146
+ classifyFromFields: string[];
147
+ provider?: string | null;
148
+ fallbackOption?: string | null;
149
+ hasContextFields: boolean;
150
+ }
151
+ interface AiSelectInputProps {
152
+ name: string;
153
+ value: string | null;
154
+ onChange?: (value: string | null) => void;
155
+ props: AiSelectInputFieldProps | undefined;
156
+ resource?: string;
157
+ field?: string;
158
+ formData?: Record<string, unknown>;
159
+ classifyUrl?: string;
160
+ csrfToken?: string;
161
+ }
162
+ declare function AiSelectInput(props: AiSelectInputProps): ReactElement;
163
+
164
+ /**
165
+ * `<AiTextInput>` — apresentational React component for the PHP
166
+ * `Arqel\Ai\Fields\AiTextField` (component string `AiTextInput`).
167
+ *
168
+ * Render contract (AI-007):
169
+ * - `<textarea>` displaying the current value (controlled if
170
+ * `onChange` is provided, otherwise uncontrolled with internal
171
+ * state seeded by `value`),
172
+ * - "Generate with AI" button (label from `props.buttonLabel`,
173
+ * fallback `Generate with AI`); after a successful generation the
174
+ * button label becomes `Regenerate`,
175
+ * - while a request is in-flight the button is disabled and shows a
176
+ * small inline spinner,
177
+ * - on failure a banner with a generic message + HTTP status code is
178
+ * rendered; the button re-enables so the user can retry,
179
+ * - optional `X / maxLength` counter when `props.maxLength` is set.
180
+ *
181
+ * Network:
182
+ * - URL = `props.generateUrl` (caller override) or
183
+ * `/admin/${resource}/fields/${field}/generate` (default route name
184
+ * `arqel.ai.generate`).
185
+ * - `fetch(url, { method: 'POST', credentials: 'same-origin', headers:
186
+ * { Content-Type, Accept, X-CSRF-TOKEN }, body: JSON.stringify({
187
+ * formData }) })`. The PHP controller resolves the prompt template
188
+ * server-side and returns `{ text }`.
189
+ *
190
+ * SSR-safe: nothing in the render path touches `window`/`document`;
191
+ * the network call only fires from the click handler (post-mount).
192
+ */
193
+
194
+ interface AiTextInputFieldProps {
195
+ provider?: string | null;
196
+ buttonLabel: string;
197
+ maxLength?: number | null;
198
+ hasContextFields: boolean;
199
+ }
200
+ interface AiTextInputProps {
201
+ name: string;
202
+ value: string;
203
+ onChange?: (value: string) => void;
204
+ props: AiTextInputFieldProps | undefined;
205
+ resource?: string;
206
+ field?: string;
207
+ formData?: Record<string, unknown>;
208
+ generateUrl?: string;
209
+ csrfToken?: string;
210
+ }
211
+ declare function AiTextInput(props: AiTextInputProps): ReactElement;
212
+
213
+ /**
214
+ * `<AiTranslateInput>` — apresentational React component for the PHP
215
+ * `Arqel\Ai\Fields\AiTranslateField` (component string `AiTranslateInput`).
216
+ *
217
+ * Render contract (AI-008):
218
+ * - Tab list (`role="tablist"`) com um `<button role="tab">` por
219
+ * idioma configurado em `props.languages`. A tab default vem de
220
+ * `props.defaultLanguage`.
221
+ * - Cada tab mostra um indicador visual (`data-missing="true"`)
222
+ * quando a tradução daquela língua está vazia/null.
223
+ * - Painel ativo: `<textarea>` controlado que edita
224
+ * `translations[activeLang]`. Mudanças disparam o `onChange` com o
225
+ * objeto completo `Record<lang, string>`.
226
+ * - Botão "Translate from {defaultLanguage}" presente em todas as
227
+ * tabs **não-default**; faz `POST` com `targetLanguages: [lang]`.
228
+ * - Botão "Translate all missing" no topo do componente; itera as
229
+ * línguas com tradução vazia e dispara um único `POST` com o
230
+ * array completo.
231
+ * - Loading per-language (`Set<string>`): o botão da tab/lang sendo
232
+ * traduzida fica `disabled` + spinner inline.
233
+ * - Banner `role="alert"` quando o `fetch` falha; o resto continua
234
+ * usável.
235
+ *
236
+ * Network:
237
+ * - URL = `props.translateUrl` (caller override) ou
238
+ * `/admin/${resource}/fields/${field}/translate` (default route
239
+ * name `arqel.ai.translate`).
240
+ * - Body: `{ sourceLanguage, targetLanguages: string[], sourceText }`.
241
+ * `sourceText` vem de `translations[defaultLanguage] ?? ''`.
242
+ * - Response: `{ translations: { [lang]: string } }` é mesclado em
243
+ * state local + `onChange` callback.
244
+ *
245
+ * SSR-safe: nada no render path toca `window`/`document`; o `fetch`
246
+ * só dispara dentro do click handler (post-mount).
247
+ */
248
+
249
+ interface AiTranslateInputFieldProps {
250
+ languages: string[];
251
+ defaultLanguage: string;
252
+ autoTranslate: boolean;
253
+ provider?: string | null;
254
+ }
255
+ type AiTranslateValue = Record<string, string>;
256
+ interface AiTranslateInputProps {
257
+ name: string;
258
+ value: AiTranslateValue | null;
259
+ onChange?: (value: AiTranslateValue) => void;
260
+ props: AiTranslateInputFieldProps | undefined;
261
+ resource?: string;
262
+ field?: string;
263
+ translateUrl?: string;
264
+ csrfToken?: string;
265
+ }
266
+ declare function AiTranslateInput(props: AiTranslateInputProps): ReactElement;
267
+
268
+ export { AiExtractInput, type AiExtractInputFieldProps, type AiExtractInputProps, type AiExtractValue, AiImageInput, type AiImageInputFieldProps, type AiImageInputProps, AiSelectInput, type AiSelectInputFieldProps, type AiSelectInputProps, AiTextInput, type AiTextInputFieldProps, type AiTextInputProps, AiTranslateInput, type AiTranslateInputFieldProps, type AiTranslateInputProps, type AiTranslateValue, AiTextInput as default };