@nan0web/ui 1.6.2 → 1.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.
Files changed (45) hide show
  1. package/package.json +90 -85
  2. package/src/core/GeneratorRunner.js +213 -0
  3. package/src/core/Intent.js +168 -0
  4. package/src/core/IntentErrorModel.js +94 -0
  5. package/src/core/MaskHandler.js +125 -0
  6. package/src/core/index.js +7 -0
  7. package/src/domain/Navigation.js +59 -0
  8. package/src/domain/SandboxModel.js +193 -0
  9. package/src/domain/ShowcaseAppModel.js +90 -0
  10. package/src/domain/components/AutocompleteModel.js +58 -0
  11. package/src/domain/components/BreadcrumbModel.js +265 -0
  12. package/src/domain/components/ButtonModel.js +92 -0
  13. package/src/domain/components/ConfirmModel.js +64 -0
  14. package/src/domain/components/InputModel.js +142 -0
  15. package/src/domain/components/SelectModel.js +59 -0
  16. package/src/domain/components/SpinnerModel.js +58 -0
  17. package/src/domain/components/TableModel.js +60 -0
  18. package/src/domain/components/ToastModel.js +77 -0
  19. package/src/domain/components/TreeModel.js +53 -0
  20. package/src/domain/components/index.js +11 -0
  21. package/src/domain/index.js +17 -0
  22. package/src/format.js +21 -0
  23. package/src/index.js +6 -0
  24. package/types/core/GeneratorRunner.d.ts +51 -0
  25. package/types/core/Intent.d.ts +227 -85
  26. package/types/core/IntentErrorModel.d.ts +55 -0
  27. package/types/core/MaskHandler.d.ts +33 -0
  28. package/types/core/index.d.ts +4 -0
  29. package/types/domain/Navigation.d.ts +50 -0
  30. package/types/domain/SandboxModel.d.ts +59 -0
  31. package/types/domain/ShowcaseAppModel.d.ts +64 -0
  32. package/types/domain/components/AutocompleteModel.d.ts +47 -0
  33. package/types/domain/components/BreadcrumbModel.d.ts +164 -0
  34. package/types/domain/components/ButtonModel.d.ts +81 -0
  35. package/types/domain/components/ConfirmModel.d.ts +54 -0
  36. package/types/domain/components/InputModel.d.ts +121 -0
  37. package/types/domain/components/SelectModel.d.ts +48 -0
  38. package/types/domain/components/SpinnerModel.d.ts +45 -0
  39. package/types/domain/components/TableModel.d.ts +44 -0
  40. package/types/domain/components/ToastModel.d.ts +62 -0
  41. package/types/domain/components/TreeModel.d.ts +49 -0
  42. package/types/domain/components/index.d.ts +10 -0
  43. package/types/domain/index.d.ts +4 -0
  44. package/types/format.d.ts +5 -0
  45. package/types/index.d.ts +4 -0
@@ -0,0 +1,60 @@
1
+ import { Model } from '@nan0web/core'
2
+
3
+ /**
4
+ * @typedef {Object} TableData
5
+ * @property {string[]} [columns]
6
+ * @property {string[][]} [rows]
7
+ */
8
+
9
+ /**
10
+ * Model-as-Schema for Table Data component.
11
+ * Displays tabular string data in rows and columns.
12
+ */
13
+ export class TableModel extends Model {
14
+ // ==========================================
15
+ // 1. MODEL AS SCHEMA (Static Definition)
16
+ // ==========================================
17
+
18
+ static columns = {
19
+ help: 'Array of column headers',
20
+ type: 'string[]',
21
+ default: ['Header 1', 'Header 2'],
22
+ }
23
+
24
+ static rows = {
25
+ help: '2D Array of table cells matching column length',
26
+ type: 'string[][]',
27
+ default: [
28
+ ['Cell 1.1', 'Cell 1.2'],
29
+ ['Cell 2.1', 'Cell 2.2'],
30
+ ],
31
+ }
32
+
33
+ /**
34
+ * @param {TableData | any} [data]
35
+ */
36
+ constructor(data = {}) {
37
+ super(data)
38
+ /** @type {string[]|undefined} */ this.columns
39
+ /** @type {string[][]|undefined} */ this.rows
40
+ }
41
+
42
+ // ==========================================
43
+ // 2. AGNOSTIC LOGIC (Async Generator)
44
+ // ==========================================
45
+
46
+ async *run() {
47
+ // Tables are naturally result or log displays.
48
+ // For an interactive flow, we could ask the user to 'select' a row,
49
+ // but by default a table simply presents data.
50
+ yield {
51
+ type: 'log',
52
+ level: 'info',
53
+ message: `Displaying table with ${this.rows?.length || 0} rows`,
54
+ component: 'Table',
55
+ model: /** @type {any} */ (this),
56
+ }
57
+
58
+ return { type: 'result', data: { rowsCount: this.rows?.length || 0 } }
59
+ }
60
+ }
@@ -0,0 +1,77 @@
1
+ import { Model } from '@nan0web/core'
2
+
3
+ /**
4
+ * @typedef {'success'|'error'|'info'|'warning'} ToastVariant
5
+ * @typedef {Object} ToastData
6
+ * @property {string} [message]
7
+ * @property {ToastVariant} [variant]
8
+ * @property {number} [duration]
9
+ * @property {boolean} [open]
10
+ */
11
+
12
+ /**
13
+ * Model-as-Schema for Toast notification component.
14
+ * Represents a transient message displayed to the user.
15
+ */
16
+ export class ToastModel extends Model {
17
+ // ==========================================
18
+ // 1. MODEL AS SCHEMA (Static Definition)
19
+ // ==========================================
20
+
21
+ static message = {
22
+ help: 'The message content of the toast',
23
+ default: 'Saved successfully!',
24
+ type: 'string',
25
+ }
26
+
27
+ static variant = {
28
+ help: 'Visual styling representing the message severity',
29
+ default: 'success',
30
+ options: ['success', 'error', 'info', 'warning'],
31
+ }
32
+
33
+ static duration = {
34
+ help: 'Time in ms before auto-dismissal. 0 to keep open indefinitely.',
35
+ default: 3000,
36
+ type: 'number',
37
+ }
38
+
39
+ static open = {
40
+ help: 'Controls visibility state',
41
+ default: true,
42
+ type: 'boolean',
43
+ }
44
+
45
+ /**
46
+ * @param {ToastData | any} [data]
47
+ */
48
+ constructor(data = {}) {
49
+ super(data)
50
+ /** @type {string|undefined} */ this.message
51
+ /** @type {ToastVariant|undefined} */ this.variant
52
+ /** @type {number|undefined} */ this.duration
53
+ /** @type {boolean|undefined} */ this.open
54
+ }
55
+
56
+ // ==========================================
57
+ // 2. AGNOSTIC LOGIC (Async Generator)
58
+ // ==========================================
59
+
60
+ async *run() {
61
+ // Maps naturally to the 'log' intent for OLMUI runners.
62
+ yield {
63
+ type: 'log',
64
+ level: this.variant === 'error' ? 'error' : this.variant === 'warning' ? 'warn' : 'info',
65
+ message: this.message,
66
+ component: 'Toast', // Hint for specific UI visual rendering
67
+ model: /** @type {any} */ (this),
68
+ }
69
+
70
+ // Wait exactly 'duration' ms before completing (unless duration is 0)
71
+ if (this.duration && this.duration > 0) {
72
+ await new Promise((resolve) => setTimeout(resolve, this.duration))
73
+ }
74
+
75
+ return { type: 'result', data: { closed: true } }
76
+ }
77
+ }
@@ -0,0 +1,53 @@
1
+ import { Model } from '@nan0web/core'
2
+
3
+ /**
4
+ * @typedef {Object} TreeNode
5
+ * @property {string} label
6
+ * @property {boolean} [expanded]
7
+ * @property {TreeNode[]} [children]
8
+ */
9
+
10
+ /**
11
+ * @typedef {Object} TreeData
12
+ * @property {TreeNode[]} [data]
13
+ */
14
+
15
+ /**
16
+ * Model-as-Schema for Tree component.
17
+ * Represents a hierarchical selection or navigation structure.
18
+ */
19
+ export class TreeModel extends Model {
20
+ // ==========================================
21
+ // 1. MODEL AS SCHEMA (Static Definition)
22
+ // ==========================================
23
+
24
+ static data = {
25
+ help: 'Tree nodes defining the hierarchy',
26
+ type: 'TreeNode[]',
27
+ default: [],
28
+ }
29
+
30
+ /**
31
+ * @param {TreeData | any} [data]
32
+ */
33
+ constructor(data = {}) {
34
+ super(data)
35
+ /** @type {TreeNode[]|undefined} */ this.data
36
+ }
37
+
38
+ // ==========================================
39
+ // 2. AGNOSTIC LOGIC (Async Generator)
40
+ // ==========================================
41
+
42
+ async *run() {
43
+ const response = yield {
44
+ type: 'ask',
45
+ field: 'selectedNode',
46
+ schema: { help: 'Select a node from the tree' },
47
+ component: 'Tree',
48
+ model: /** @type {any} */ (this),
49
+ }
50
+
51
+ return { type: 'result', data: { selected: response.value } }
52
+ }
53
+ }
@@ -0,0 +1,11 @@
1
+ // Exports for Component Models
2
+ export { BreadcrumbModel } from './BreadcrumbModel.js'
3
+ export { ButtonModel } from './ButtonModel.js'
4
+ export { ConfirmModel } from './ConfirmModel.js'
5
+ export { InputModel } from './InputModel.js'
6
+ export { SpinnerModel } from './SpinnerModel.js'
7
+ export { TableModel } from './TableModel.js'
8
+ export { ToastModel } from './ToastModel.js'
9
+ export { SelectModel } from './SelectModel.js'
10
+ export { AutocompleteModel } from './AutocompleteModel.js'
11
+ export { TreeModel } from './TreeModel.js'
@@ -0,0 +1,17 @@
1
+ // Domain Models — OLMUI Model-as-Schema
2
+ export { SandboxModel } from './SandboxModel.js'
3
+ export { ShowcaseAppModel } from './ShowcaseAppModel.js'
4
+ export { default as Navigation } from './Navigation.js'
5
+
6
+ // Component Models
7
+ export {
8
+ ButtonModel,
9
+ ConfirmModel,
10
+ InputModel,
11
+ SpinnerModel,
12
+ TableModel,
13
+ ToastModel,
14
+ SelectModel,
15
+ AutocompleteModel,
16
+ TreeModel,
17
+ } from './components/index.js'
package/src/format.js ADDED
@@ -0,0 +1,21 @@
1
+ export const format = {
2
+ currency: (value, currency = 'UAH', locale = 'uk-UA') => {
3
+ return new Intl.NumberFormat(locale, {
4
+ style: 'currency',
5
+ currency,
6
+ maximumFractionDigits: 0
7
+ }).format(value).replace(/,/g, ' ')
8
+ },
9
+ rate: (value, locale = 'uk-UA') => {
10
+ const rate = value < 1 ? value * 100 : value
11
+ return new Intl.NumberFormat(locale, {
12
+ style: 'percent',
13
+ maximumFractionDigits: 2
14
+ }).format(rate / 100)
15
+ },
16
+ number: (value, locale = 'uk-UA') => {
17
+ return new Intl.NumberFormat(locale, {
18
+ maximumFractionDigits: 2
19
+ }).format(value).replace(/,/g, ' ')
20
+ }
21
+ }
package/src/index.js CHANGED
@@ -10,6 +10,7 @@ import Component from './Component/index.js'
10
10
  import App from './App/index.js'
11
11
 
12
12
  export { Frame, FrameProps, Locale, StdIn, StdOut, View, RenderOptions, Model, Component, App }
13
+ export { format } from './format.js'
13
14
 
14
15
  // export default App
15
16
  export { default as FormMessage } from './core/Form/Message.js'
@@ -22,3 +23,8 @@ export { default as UiMessage } from './core/Message/Message.js'
22
23
  export { default as UiStream } from './core/Stream.js'
23
24
  export { default as Error, CancelError } from './core/Error/index.js'
24
25
  export { default as UiAdapter } from './core/UiAdapter.js'
26
+
27
+ // OLMUI Generator Engine
28
+ export { validateIntent, ask, progress, log, result, INTENT_TYPES, isModelSchema } from './core/Intent.js'
29
+ export { IntentErrorModel } from './core/IntentErrorModel.js'
30
+ export { runGenerator } from './core/GeneratorRunner.js'
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Runs an OLMUI async generator through the provided adapter handlers.
3
+ *
4
+ * This function is the SINGLE point of execution for all adapters.
5
+ * It guarantees:
6
+ * - Every yielded intent is validated (contract enforcement).
7
+ * - Every 'ask' intent gets a response or times out (if timeoutMs > 0).
8
+ * - External abort signals are respected.
9
+ * - The final result is returned.
10
+ *
11
+ * @template T
12
+ * @param {AsyncGenerator<import('./Intent.js').Intent, import('./Intent.js').ResultIntent, import('./Intent.js').IntentResponse>} generator
13
+ * The model's async generator (from Model.run()).
14
+ * @param {AdapterHandlers} handlers
15
+ * Platform-specific handlers for each intent type.
16
+ * @param {RunnerOptions} [options={}]
17
+ * Runner configuration (timeout, abort signal).
18
+ * @returns {Promise<T>}
19
+ * The final result data from the generator.
20
+ */
21
+ export function runGenerator<T>(generator: AsyncGenerator<import("./Intent.js").Intent, import("./Intent.js").ResultIntent, import("./Intent.js").IntentResponse>, handlers: AdapterHandlers, options?: RunnerOptions): Promise<T>;
22
+ export type AdapterHandlers = {
23
+ /**
24
+ * Handler for 'ask' intents. Must return { value: ... }.
25
+ */
26
+ ask: (intent: import("./Intent.js").AskIntent) => Promise<import("./Intent.js").AskResponse>;
27
+ /**
28
+ * Handler for 'progress' intents. Optional (defaults to no-op).
29
+ */
30
+ progress?: ((intent: import("./Intent.js").ProgressIntent) => void | Promise<void>) | undefined;
31
+ /**
32
+ * Handler for 'log' intents. Optional (defaults to no-op).
33
+ */
34
+ log?: ((intent: import("./Intent.js").LogIntent) => void | Promise<void>) | undefined;
35
+ /**
36
+ * Handler for the final 'result'. Optional (defaults to no-op).
37
+ */
38
+ result?: ((intent: import("./Intent.js").ResultIntent) => void | Promise<void>) | undefined;
39
+ };
40
+ export type RunnerOptions = {
41
+ /**
42
+ * Maximum milliseconds to wait for an adapter handler to respond.
43
+ * Default is 0 (disabled) — web forms may wait indefinitely.
44
+ * Set to a positive value for CLI/Chat adapters where hanging is unacceptable.
45
+ */
46
+ timeoutMs?: number | undefined;
47
+ /**
48
+ * External AbortSignal for cancellation from outside.
49
+ */
50
+ signal?: AbortSignal | undefined;
51
+ };
@@ -1,88 +1,230 @@
1
1
  /**
2
- * Intent represents the user's declared will to perform an action.
3
- * It is interface-agnostic and can be validated or executed without
4
- * any UI, CLI, or external dependencies.
2
+ * Detects if a value is a Model-as-Schema class (has static fields with `help`).
3
+ * @param {*} schema
4
+ * @returns {boolean}
5
+ */
6
+ export function isModelSchema(schema: any): boolean;
7
+ /**
8
+ * Validates that an object is a well-formed Intent.
9
+ * Throws ModelError if the intent is malformed (the "Judge").
5
10
  *
6
- * An Intent may be:
7
- * - Ready: all required data is valid → can be executed immediately
8
- * - Partial: some data missing or invalid → requires user action
9
- * - Invalid: cannot be fulfilled under any interface (e.g., restricted field)
11
+ * @param {*} intent
12
+ * @returns {intent is Intent}
13
+ */
14
+ export function validateIntent(intent: any): intent is Intent;
15
+ /**
16
+ * @typedef {Object} FieldSchema
17
+ * @property {string} help - Human-readable label / i18n key.
18
+ * @property {*} default - Default value for the field.
19
+ * @property {string} [type] - Field type hint ('text', 'number', 'text/markdown').
20
+ * @property {Array<{value: *, label: string}>} [options] - Enum options for select.
21
+ * @property {(val: *) => true | string} [validate] - Validator: true = ok, string = error key from Model.
22
+ * @property {boolean} [hidden] - If true, field is excluded from UI forms.
23
+ */
24
+ /**
25
+ * Model needs data from the environment (user input, LLM extraction, test fixture).
26
+ * Adapter MUST return an `AskResponse`.
27
+ * @typedef {Object} AskIntent
28
+ * @property {'ask'} type
29
+ * @property {string} field - Property name on the model.
30
+ * @property {FieldSchema | Function} schema - Field metadata or Model-as-Schema class constructor.
31
+ * @property {true} [model] - When true, schema is a Model-as-Schema class (full form).
32
+ */
33
+ /**
34
+ * Model informs about a long-running operation. No response expected.
35
+ * Message MUST come from the Model (i18n static field value).
36
+ * @typedef {Object} ProgressIntent
37
+ * @property {'progress'} type
38
+ * @property {number} [value] - Progress value (0-1).
39
+ * @property {string} [id] - Progress ID for tracking multiple parallel operations.
40
+ * @property {string} message - Status message from Model (i18n static field value).
41
+ */
42
+ /**
43
+ * Model emits a log message. No response expected.
44
+ * Message MUST come from the Model (i18n static field value).
45
+ * @typedef {Object} LogIntent
46
+ * @property {'log'} type
47
+ * @property {'info' | 'warn' | 'error' | 'success'} level
48
+ * @property {string} message - Log message from Model (i18n static field value).
49
+ */
50
+ /**
51
+ * Final return value from the generator.
52
+ * @typedef {Object} ResultIntent
53
+ * @property {'result'} type
54
+ * @property {*} data - The raw result data (JSON-serializable).
55
+ */
56
+ /**
57
+ * Union of all possible yielded intents.
58
+ * @typedef {AskIntent | ProgressIntent | LogIntent} Intent
59
+ */
60
+ /**
61
+ * Response to an AskIntent. Adapter provides the collected value.
62
+ * The value MUST conform to the type described in the requested FieldSchema.
63
+ * @typedef {Object} AskResponse
64
+ * @property {*} value - The value matching schema.type (collected from user / LLM / test fixture).
65
+ * @property {boolean} [cancelled] - Whether the user cancelled this interaction (e.g. pressed ESC).
66
+ */
67
+ /**
68
+ * Special response that Adapters can send to abort the generator.
69
+ *
70
+ * The `reason` is a KEY from the Model's static `abort` dictionary,
71
+ * not a freeform message. This enables proper i18n translation:
10
72
  *
11
- * @example
12
- * const intent = new Intent({
13
- * target: LoginMessage,
14
- * body: { username: "alice" }
15
- * })
16
- * if (!intent.isReady()) {
17
- * const validMsg = await handleIntent(intent, adapter)
18
- * }
19
- */
20
- declare class Intent extends Message {
21
- /**
22
- * Create a new Intent.
23
- *
24
- * @param {object} input
25
- * @param {typeof Message} input.target - message class this intent wants to create
26
- * @param {any} [input.context] - execution context
27
- * @param {object} [body] - partial or complete data for the target message
28
- */
29
- constructor({ target, context, ...body }?: { target: typeof Message; context?: any })
30
- /**
31
- * The target Message class this Intent aims to produce.
32
- * Must be a class with a static `.Body` schema.
33
- *
34
- * @type {typeof Message | null}
35
- */
36
- target: typeof Message | null
37
- /**
38
- * Optional context for execution (session, locale, state, etc.).
39
- *
40
- * @type {any}
41
- */
42
- context: any
43
- /**
44
- * Validates all fields in the Intent's body against the target's schema.
45
- *
46
- * Checks:
47
- * - Required fields presence and non-emptiness
48
- * - Pattern matching (RegExp)
49
- * - Value within allowed options (if defined)
50
- * - Custom validation logic (if `validate` function is defined)
51
- *
52
- * @returns {Map<string, string>} Map of field → error message, empty if valid
53
- */
54
- validateIntent(): Map<string, string>
55
- /**
56
- * Executes the Intent by creating a valid instance of the target message.
57
- *
58
- * If the Intent is not ready (has validation errors), returns null.
59
- *
60
- * @returns {Message | null} the created message, or null if invalid
61
- */
62
- execute(): Message | null
63
- /**
64
- * Checks if the Intent can be executed immediately, without user input.
65
- *
66
- * @returns {boolean} true if all required fields are valid
67
- */
68
- isReady(): boolean
69
- /**
70
- * Converts the Intent to a plain object for logging or inspection.
71
- *
72
- * @returns {object} serializable object with intent, body, and context
73
- */
74
- toObject(): object
75
- }
76
- declare namespace Intent {
77
- /**
78
- * Handles an Intent by fulfilling missing or invalid fields.
79
- *
80
- * @param {Intent} intent - the declared intent to handle
81
- * @param {object} inputAdapter - must have `.ask(prompt)`
82
- * @returns {Promise<Message | null>} resolved when intent is fulfilled
83
- * @throws {CancelError} if user cancels
84
- */
85
- function handleIntent(intent: Intent, inputAdapter: object): Promise<Message | null>
86
- }
87
- export default Intent
88
- import { Message } from '@nan0web/co'
73
+ * Model defines: static abort = { user_cancelled: 'Скасовано', timeout: 'Час вичерпано' }
74
+ * Adapter sends: { abort: true, reason: 'user_cancelled' }
75
+ * UI translates: t(Model.abort[reason])
76
+ *
77
+ * @typedef {Object} AbortResponse
78
+ * @property {true} abort - Signal to the model that execution was cancelled.
79
+ * @property {string} [reason] - Key from Model's static abort dictionary (not a freeform message).
80
+ */
81
+ /**
82
+ * Union of all possible responses an Adapter can send back via iterator.next().
83
+ * @typedef {AskResponse | AbortResponse | undefined} IntentResponse
84
+ */
85
+ export const INTENT_TYPES: readonly ["ask", "progress", "log"];
86
+ export function ask(field: string, schema: object | Function): AskIntent;
87
+ export function progress(message: any): {
88
+ type: string;
89
+ message: any;
90
+ };
91
+ export function log(level: any, message: any, data?: {}): {
92
+ type: string;
93
+ level: any;
94
+ message: any;
95
+ };
96
+ export function result(data: any): {
97
+ type: string;
98
+ data: any;
99
+ };
100
+ export type FieldSchema = {
101
+ /**
102
+ * - Human-readable label / i18n key.
103
+ */
104
+ help: string;
105
+ /**
106
+ * - Default value for the field.
107
+ */
108
+ default: any;
109
+ /**
110
+ * - Field type hint ('text', 'number', 'text/markdown').
111
+ */
112
+ type?: string | undefined;
113
+ /**
114
+ * - Enum options for select.
115
+ */
116
+ options?: {
117
+ value: any;
118
+ label: string;
119
+ }[] | undefined;
120
+ /**
121
+ * - Validator: true = ok, string = error key from Model.
122
+ */
123
+ validate?: ((val: any) => true | string) | undefined;
124
+ /**
125
+ * - If true, field is excluded from UI forms.
126
+ */
127
+ hidden?: boolean | undefined;
128
+ };
129
+ /**
130
+ * Model needs data from the environment (user input, LLM extraction, test fixture).
131
+ * Adapter MUST return an `AskResponse`.
132
+ */
133
+ export type AskIntent = {
134
+ type: "ask";
135
+ /**
136
+ * - Property name on the model.
137
+ */
138
+ field: string;
139
+ /**
140
+ * - Field metadata or Model-as-Schema class constructor.
141
+ */
142
+ schema: FieldSchema | Function;
143
+ /**
144
+ * - When true, schema is a Model-as-Schema class (full form).
145
+ */
146
+ model?: true | undefined;
147
+ };
148
+ /**
149
+ * Model informs about a long-running operation. No response expected.
150
+ * Message MUST come from the Model (i18n static field value).
151
+ */
152
+ export type ProgressIntent = {
153
+ type: "progress";
154
+ /**
155
+ * - Progress value (0-1).
156
+ */
157
+ value?: number | undefined;
158
+ /**
159
+ * - Progress ID for tracking multiple parallel operations.
160
+ */
161
+ id?: string | undefined;
162
+ /**
163
+ * - Status message from Model (i18n static field value).
164
+ */
165
+ message: string;
166
+ };
167
+ /**
168
+ * Model emits a log message. No response expected.
169
+ * Message MUST come from the Model (i18n static field value).
170
+ */
171
+ export type LogIntent = {
172
+ type: "log";
173
+ level: "info" | "warn" | "error" | "success";
174
+ /**
175
+ * - Log message from Model (i18n static field value).
176
+ */
177
+ message: string;
178
+ };
179
+ /**
180
+ * Final return value from the generator.
181
+ */
182
+ export type ResultIntent = {
183
+ type: "result";
184
+ /**
185
+ * - The raw result data (JSON-serializable).
186
+ */
187
+ data: any;
188
+ };
189
+ /**
190
+ * Union of all possible yielded intents.
191
+ */
192
+ export type Intent = AskIntent | ProgressIntent | LogIntent;
193
+ /**
194
+ * Response to an AskIntent. Adapter provides the collected value.
195
+ * The value MUST conform to the type described in the requested FieldSchema.
196
+ */
197
+ export type AskResponse = {
198
+ /**
199
+ * - The value matching schema.type (collected from user / LLM / test fixture).
200
+ */
201
+ value: any;
202
+ /**
203
+ * - Whether the user cancelled this interaction (e.g. pressed ESC).
204
+ */
205
+ cancelled?: boolean | undefined;
206
+ };
207
+ /**
208
+ * Special response that Adapters can send to abort the generator.
209
+ *
210
+ * The `reason` is a KEY from the Model's static `abort` dictionary,
211
+ * not a freeform message. This enables proper i18n translation:
212
+ *
213
+ * Model defines: static abort = { user_cancelled: 'Скасовано', timeout: 'Час вичерпано' }
214
+ * Adapter sends: { abort: true, reason: 'user_cancelled' }
215
+ * UI translates: t(Model.abort[reason])
216
+ */
217
+ export type AbortResponse = {
218
+ /**
219
+ * - Signal to the model that execution was cancelled.
220
+ */
221
+ abort: true;
222
+ /**
223
+ * - Key from Model's static abort dictionary (not a freeform message).
224
+ */
225
+ reason?: string | undefined;
226
+ };
227
+ /**
228
+ * Union of all possible responses an Adapter can send back via iterator.next().
229
+ */
230
+ export type IntentResponse = AskResponse | AbortResponse | undefined;