@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.
- package/package.json +90 -85
- package/src/core/GeneratorRunner.js +213 -0
- package/src/core/Intent.js +168 -0
- package/src/core/IntentErrorModel.js +94 -0
- package/src/core/MaskHandler.js +125 -0
- package/src/core/index.js +7 -0
- package/src/domain/Navigation.js +59 -0
- package/src/domain/SandboxModel.js +193 -0
- package/src/domain/ShowcaseAppModel.js +90 -0
- package/src/domain/components/AutocompleteModel.js +58 -0
- package/src/domain/components/BreadcrumbModel.js +265 -0
- package/src/domain/components/ButtonModel.js +92 -0
- package/src/domain/components/ConfirmModel.js +64 -0
- package/src/domain/components/InputModel.js +142 -0
- package/src/domain/components/SelectModel.js +59 -0
- package/src/domain/components/SpinnerModel.js +58 -0
- package/src/domain/components/TableModel.js +60 -0
- package/src/domain/components/ToastModel.js +77 -0
- package/src/domain/components/TreeModel.js +53 -0
- package/src/domain/components/index.js +11 -0
- package/src/domain/index.js +17 -0
- package/src/format.js +21 -0
- package/src/index.js +6 -0
- package/types/core/GeneratorRunner.d.ts +51 -0
- package/types/core/Intent.d.ts +227 -85
- package/types/core/IntentErrorModel.d.ts +55 -0
- package/types/core/MaskHandler.d.ts +33 -0
- package/types/core/index.d.ts +4 -0
- package/types/domain/Navigation.d.ts +50 -0
- package/types/domain/SandboxModel.d.ts +59 -0
- package/types/domain/ShowcaseAppModel.d.ts +64 -0
- package/types/domain/components/AutocompleteModel.d.ts +47 -0
- package/types/domain/components/BreadcrumbModel.d.ts +164 -0
- package/types/domain/components/ButtonModel.d.ts +81 -0
- package/types/domain/components/ConfirmModel.d.ts +54 -0
- package/types/domain/components/InputModel.d.ts +121 -0
- package/types/domain/components/SelectModel.d.ts +48 -0
- package/types/domain/components/SpinnerModel.d.ts +45 -0
- package/types/domain/components/TableModel.d.ts +44 -0
- package/types/domain/components/ToastModel.d.ts +62 -0
- package/types/domain/components/TreeModel.d.ts +49 -0
- package/types/domain/components/index.d.ts +10 -0
- package/types/domain/index.d.ts +4 -0
- package/types/format.d.ts +5 -0
- 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
|
+
};
|
package/types/core/Intent.d.ts
CHANGED
|
@@ -1,88 +1,230 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
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
|
-
*
|
|
7
|
-
*
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* }
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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;
|