@nan0web/ui 1.10.0 → 1.12.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/README.md +69 -3
- package/package.json +65 -29
- package/src/App/Command/DepsCommand.js +3 -4
- package/src/Frame/Props.js +12 -18
- package/src/InterfaceTemplate/InterfaceTemplate.js +9 -7
- package/src/Model/index.js +61 -6
- package/src/StdIn.js +2 -6
- package/src/cli.js +1 -0
- package/src/core/GeneratorRunner.js +67 -7
- package/src/core/InputAdapter.js +22 -5
- package/src/core/Intent.js +230 -18
- package/src/core/Message/Message.js +4 -7
- package/src/core/Message/OutputMessage.js +4 -9
- package/src/core/StreamEntry.js +20 -28
- package/src/core/index.js +4 -0
- package/src/domain/Content.js +198 -0
- package/src/domain/Document.js +25 -0
- package/src/domain/FooterModel.js +37 -19
- package/src/domain/HeaderModel.js +47 -21
- package/src/domain/HeroModel.js +24 -22
- package/src/domain/LayoutModel.js +43 -0
- package/src/domain/ModelAsApp.js +46 -0
- package/src/domain/SandboxModel.js +19 -16
- package/src/domain/app/GalleryCommand.js +53 -0
- package/src/domain/app/GalleryRenderIntent.js +77 -0
- package/src/domain/app/SnapshotAuditor.js +399 -0
- package/src/domain/app/SnapshotRunner.js +264 -0
- package/src/domain/app/UIApp.js +78 -0
- package/src/domain/components/BreadcrumbModel.js +10 -6
- package/src/domain/components/FeatureGridModel.js +62 -0
- package/src/domain/components/MarkdownModel.js +24 -0
- package/src/domain/components/ShellModel.js +243 -0
- package/src/domain/components/TableModel.js +10 -6
- package/src/domain/components/ToastModel.js +10 -6
- package/src/domain/components/index.js +3 -1
- package/src/domain/index.js +14 -4
- package/src/index.js +23 -2
- package/src/inspect.js +2 -0
- package/src/test/ScenarioAdapter.js +59 -0
- package/src/test/ScenarioTest.js +51 -0
- package/src/test/ScenarioTest.story.js +56 -0
- package/src/testing/CrashReporter.js +56 -0
- package/src/testing/GalleryGenerator.js +15 -71
- package/src/testing/LogicInspector.js +4 -4
- package/src/testing/SnapshotRunner.js +22 -0
- package/src/testing/SpecAdapter.js +114 -0
- package/src/testing/SpecRunner.js +121 -0
- package/src/testing/VisualAdapter.js +24 -19
- package/src/testing/index.js +5 -1
- package/src/testing/verifySnapshot.js +17 -0
- package/types/App/Command/DepsCommand.d.ts +0 -2
- package/types/Model/index.d.ts +56 -62
- package/types/StdIn.d.ts +3 -3
- package/types/cli.d.ts +1 -0
- package/types/core/GeneratorRunner.d.ts +14 -1
- package/types/core/InputAdapter.d.ts +50 -6
- package/types/core/Intent.d.ts +280 -32
- package/types/core/Message/Message.d.ts +2 -2
- package/types/core/Message/OutputMessage.d.ts +0 -2
- package/types/core/index.d.ts +4 -0
- package/types/domain/Content.d.ts +344 -0
- package/types/domain/Document.d.ts +40 -0
- package/types/domain/FooterModel.d.ts +22 -12
- package/types/domain/HeaderModel.d.ts +36 -13
- package/types/domain/HeroModel.d.ts +19 -17
- package/types/domain/LayoutModel.d.ts +34 -0
- package/types/domain/ModelAsApp.d.ts +23 -0
- package/types/domain/SandboxModel.d.ts +10 -0
- package/types/domain/app/GalleryCommand.d.ts +55 -0
- package/types/domain/app/GalleryRenderIntent.d.ts +31 -0
- package/types/domain/app/SnapshotAuditor.d.ts +99 -0
- package/types/domain/app/SnapshotRunner.d.ts +45 -0
- package/types/domain/app/UIApp.d.ts +60 -0
- package/types/domain/components/BreadcrumbModel.d.ts +6 -8
- package/types/domain/components/FeatureGridModel.d.ts +50 -0
- package/types/domain/components/MarkdownModel.d.ts +19 -0
- package/types/domain/components/ShellModel.d.ts +56 -0
- package/types/domain/components/TableModel.d.ts +4 -0
- package/types/domain/components/ToastModel.d.ts +4 -0
- package/types/domain/components/index.d.ts +3 -0
- package/types/domain/index.d.ts +10 -4
- package/types/index.d.ts +21 -1
- package/types/inspect.d.ts +2 -0
- package/types/test/ScenarioAdapter.d.ts +43 -0
- package/types/test/ScenarioTest.d.ts +24 -0
- package/types/test/ScenarioTest.story.d.ts +1 -0
- package/types/testing/CrashReporter.d.ts +13 -0
- package/types/testing/SnapshotRunner.d.ts +7 -0
- package/types/testing/SpecAdapter.d.ts +58 -0
- package/types/testing/SpecRunner.d.ts +41 -0
- package/types/testing/VisualAdapter.d.ts +0 -6
- package/types/testing/index.d.ts +5 -1
- package/types/testing/verifySnapshot.d.ts +14 -0
- package/src/testing/SnapshotInspector.js +0 -84
- package/types/App/Command/Options.d.ts +0 -43
- package/types/App/Command/index.d.ts +0 -8
- package/types/App/User/Command/Options.d.ts +0 -34
- package/types/core/Message/InputMessage.d.ts +0 -71
- package/types/domain/components/HeroModel.d.ts +0 -24
- package/types/domain/components/ShowcaseAppModel.d.ts +0 -32
- package/types/testing/SnapshotInspector.d.ts +0 -17
package/src/core/Intent.js
CHANGED
|
@@ -38,17 +38,33 @@ import { IntentErrorModel } from './IntentErrorModel.js'
|
|
|
38
38
|
* @typedef {Object} ProgressIntent
|
|
39
39
|
* @property {'progress'} type
|
|
40
40
|
* @property {number} [value] - Progress value (0-1).
|
|
41
|
-
* @property {
|
|
41
|
+
* @property {number} [total] - Absolute total (if value is absolute).
|
|
42
|
+
* @property {string} [id] - Progress ID for tracking by Adapter to calculate speed/eta.
|
|
42
43
|
* @property {string} message - Status message from Model (i18n static field value).
|
|
44
|
+
* @property {ProgressOptions} [options] - Additional options for progress intent.
|
|
43
45
|
*/
|
|
44
46
|
|
|
45
47
|
/**
|
|
46
|
-
*
|
|
48
|
+
* @typedef {'info' | 'warn' | 'error' | 'success'} ShowLevel
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/** @typedef {ShowLevel} LogLevel */
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Model emits a show message. No response expected.
|
|
47
55
|
* Message MUST come from the Model (i18n static field value).
|
|
56
|
+
* @typedef {Object} ShowIntent
|
|
57
|
+
* @property {'show'} type
|
|
58
|
+
* @property {ShowLevel} level
|
|
59
|
+
* @property {string} message - Show message from Model (i18n static field value).
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Model emits a log message intended for debugging/developer (Not UI).
|
|
48
64
|
* @typedef {Object} LogIntent
|
|
49
65
|
* @property {'log'} type
|
|
50
|
-
* @property {
|
|
51
|
-
* @property {string} message -
|
|
66
|
+
* @property {LogLevel} level
|
|
67
|
+
* @property {string} message - Internal log message.
|
|
52
68
|
*/
|
|
53
69
|
|
|
54
70
|
/**
|
|
@@ -67,9 +83,33 @@ import { IntentErrorModel } from './IntentErrorModel.js'
|
|
|
67
83
|
* @property {object} props - Static props for the component.
|
|
68
84
|
*/
|
|
69
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Contextual data and attachments for the AI subagent.
|
|
88
|
+
* @typedef {Object} AgentContext
|
|
89
|
+
* @property {string[]} [instructions] - List of instructions or guidelines (e.g. ['Use 1-char emojis only']).
|
|
90
|
+
* @property {Record<string, string>} [files] - Hash map of file paths to their string contents.
|
|
91
|
+
* @property {Record<string, any>} [data] - Any arbitrary JSON data (e.g. parsed errors, ASTs, metadata) useful for the task.
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Model delegates a task to an AI subagent. The Adapter should launch the agent
|
|
96
|
+
* with the provided task and context, and return the result. If the agent is skipped,
|
|
97
|
+
* it returns { success: false } but allows user to generate a prompt.
|
|
98
|
+
* @typedef {Object} AgentIntent
|
|
99
|
+
* @property {'agent'} type
|
|
100
|
+
* @property {string} task - The instructional task for the AI agent.
|
|
101
|
+
* @property {AgentContext} context - Contextual data, files, and instructions for the task.
|
|
102
|
+
* @property {() => string} toPrompt - Helper to format task and context as an LLM prompt.
|
|
103
|
+
*/
|
|
104
|
+
|
|
70
105
|
/**
|
|
71
106
|
* Union of all possible yielded intents.
|
|
72
|
-
* @typedef {AskIntent | ProgressIntent | LogIntent | RenderIntent
|
|
107
|
+
* @typedef {(AskIntent | ProgressIntent | LogIntent | ShowIntent | RenderIntent | AgentIntent | ResultIntent) & {
|
|
108
|
+
* $value?: any;
|
|
109
|
+
* $success?: boolean;
|
|
110
|
+
* $files?: Record<string, string>;
|
|
111
|
+
* $message?: string;
|
|
112
|
+
* }} Intent
|
|
73
113
|
*/
|
|
74
114
|
|
|
75
115
|
// ─── Response Types (Adapter → Model) ───
|
|
@@ -79,7 +119,26 @@ import { IntentErrorModel } from './IntentErrorModel.js'
|
|
|
79
119
|
* The value MUST conform to the type described in the requested FieldSchema.
|
|
80
120
|
* @typedef {Object} AskResponse
|
|
81
121
|
* @property {*} value - The value matching schema.type (collected from user / LLM / test fixture).
|
|
82
|
-
* @property {boolean}
|
|
122
|
+
* @property {boolean} cancelled - Whether the user cancelled this interaction (e.g. pressed ESC).
|
|
123
|
+
* @property {string} [action] - The action identifier (e.g., 'submit', 'exit', 'back').
|
|
124
|
+
* @property {any} [body] - Additional payload or form data.
|
|
125
|
+
* @property {any} [form] - The form model instance (if applicable).
|
|
126
|
+
* @property {number} [index] - The selected index for lists/tables.
|
|
127
|
+
*/
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Response to an AgentIntent.
|
|
131
|
+
* The underlying Adapter (Orchestrator) is responsible for communicating with the LLM,
|
|
132
|
+
* enforcing output formats (e.g. Unified Diff or Tool Calls like `updateFile`),
|
|
133
|
+
* and resolving common LLM hallucinations (like Grok truncating code with `// ...`).
|
|
134
|
+
*
|
|
135
|
+
* The Model (e.g. IconsAuditor) receives this clean, resolved response and does not
|
|
136
|
+
* need to parse Markdown or interpret diffs itself.
|
|
137
|
+
*
|
|
138
|
+
* @typedef {Object} AgentResponse
|
|
139
|
+
* @property {boolean} success - True if the agent successfully processed the task.
|
|
140
|
+
* @property {Record<string, string>} [files] - Hash map of fully resolved, updated file contents.
|
|
141
|
+
* @property {string} [message] - Optional summary or explanation returned by the AI.
|
|
83
142
|
*/
|
|
84
143
|
|
|
85
144
|
// ─── Abort Support ───
|
|
@@ -101,10 +160,14 @@ import { IntentErrorModel } from './IntentErrorModel.js'
|
|
|
101
160
|
|
|
102
161
|
/**
|
|
103
162
|
* Union of all possible responses an Adapter can send back via iterator.next().
|
|
104
|
-
* @typedef {AskResponse | AbortResponse | undefined} IntentResponse
|
|
163
|
+
* @typedef {AskResponse | AgentResponse | AbortResponse | undefined} IntentResponse
|
|
164
|
+
*/
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @typedef {'ask' | 'show' | 'progress' | 'render' | 'agent'} IntentType
|
|
105
168
|
*/
|
|
106
169
|
|
|
107
|
-
export const INTENT_TYPES = /** @type {const} */ (['ask', 'progress', 'log', 'render'])
|
|
170
|
+
export const INTENT_TYPES = /** @type {const} */ (['ask', 'progress', 'show', 'log', 'render', 'agent'])
|
|
108
171
|
|
|
109
172
|
/**
|
|
110
173
|
* Detects if a value is a Model-as-Schema class (has static fields with `help`).
|
|
@@ -113,7 +176,7 @@ export const INTENT_TYPES = /** @type {const} */ (['ask', 'progress', 'log', 're
|
|
|
113
176
|
*/
|
|
114
177
|
export function isModelSchema(schema) {
|
|
115
178
|
if (typeof schema !== 'function') return false
|
|
116
|
-
return Object.
|
|
179
|
+
return Object.getOwnPropertyNames(schema).some((key) => {
|
|
117
180
|
const meta = schema[key]
|
|
118
181
|
return meta && typeof meta === 'object' && 'help' in meta
|
|
119
182
|
})
|
|
@@ -142,13 +205,16 @@ export function validateIntent(intent) {
|
|
|
142
205
|
}
|
|
143
206
|
// Accept both: plain schema {help: '...'} and Model-as-Schema class/instance
|
|
144
207
|
const isModel = !!intent.model
|
|
145
|
-
if (
|
|
208
|
+
if (
|
|
209
|
+
!isModel &&
|
|
210
|
+
(!intent.schema || typeof intent.schema !== 'object' || !('help' in intent.schema))
|
|
211
|
+
) {
|
|
146
212
|
throw IntentErrorModel.error('ask_missing_schema_help')
|
|
147
213
|
}
|
|
148
214
|
}
|
|
149
|
-
if (intent.type === 'progress' || intent.type === 'log') {
|
|
150
|
-
const
|
|
151
|
-
if (!
|
|
215
|
+
if (intent.type === 'progress' || intent.type === 'show' || intent.type === 'log') {
|
|
216
|
+
const isComponentShow = (intent.type === 'show' || intent.type === 'log') && intent.component
|
|
217
|
+
if (!isComponentShow && typeof intent.message !== 'string') {
|
|
152
218
|
throw IntentErrorModel.error('intent_missing_message', { type: intent.type })
|
|
153
219
|
}
|
|
154
220
|
}
|
|
@@ -157,6 +223,11 @@ export function validateIntent(intent) {
|
|
|
157
223
|
throw IntentErrorModel.error('render_missing_component')
|
|
158
224
|
}
|
|
159
225
|
}
|
|
226
|
+
if (intent.type === 'agent') {
|
|
227
|
+
if (typeof intent.task !== 'string' || !intent.task) {
|
|
228
|
+
throw IntentErrorModel.error('agent_missing_task')
|
|
229
|
+
}
|
|
230
|
+
}
|
|
160
231
|
return true
|
|
161
232
|
}
|
|
162
233
|
|
|
@@ -171,14 +242,155 @@ export function validateIntent(intent) {
|
|
|
171
242
|
* @param {object | Function} schema - Field descriptor or Model-as-Schema class.
|
|
172
243
|
* @returns {AskIntent}
|
|
173
244
|
*/
|
|
174
|
-
export
|
|
245
|
+
export function ask(field, schema) {
|
|
175
246
|
if (isModelSchema(schema)) {
|
|
176
247
|
return { type: 'ask', field, schema, model: true }
|
|
177
248
|
}
|
|
178
249
|
return { type: 'ask', field, schema }
|
|
179
250
|
}
|
|
180
251
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
252
|
+
/**
|
|
253
|
+
* @typedef {Object} ProgressOptions
|
|
254
|
+
* @property {number} [total] - Absolute total steps.
|
|
255
|
+
* @property {string} [id] - Progress tracking ID.
|
|
256
|
+
* @property {number} [width] - Width of the progress bar in terminal characters.
|
|
257
|
+
* @property {number} [fps] - Frames per second update rate limit.
|
|
258
|
+
* @property {string} [format] - Custom format string (e.g. '{time} {bar} {percent} {title}').
|
|
259
|
+
* @property {number} [columns] - Number of columns (terminal width).
|
|
260
|
+
* @property {boolean} [forceOneLine] - Prevent wrapping and truncate instead.
|
|
261
|
+
* @property {boolean|'success'|'error'} [stop] - Set to true to stop, or 'success'/'error' to stop with a status icon (for spinners).
|
|
262
|
+
*/
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Create a progress intent.
|
|
266
|
+
* @param {string} message - Status message from Model (i18n static field value).
|
|
267
|
+
* @param {number} [value=0] - Progress value (current step or percentage).
|
|
268
|
+
* @param {ProgressOptions|number|string} [optionsOrTotalOrId] - Options object, or absolute total steps (number), or progress tracking ID (string).
|
|
269
|
+
* @param {string} [id='default'] - Progress ID (if total is provided).
|
|
270
|
+
* @returns {ProgressIntent}
|
|
271
|
+
*/
|
|
272
|
+
export function progress(message, value = 0, optionsOrTotalOrId, id) {
|
|
273
|
+
/** @type {ProgressOptions} */
|
|
274
|
+
let options = {}
|
|
275
|
+
|
|
276
|
+
if (typeof optionsOrTotalOrId === 'object' && optionsOrTotalOrId !== null) {
|
|
277
|
+
options = optionsOrTotalOrId
|
|
278
|
+
} else {
|
|
279
|
+
// legacy support
|
|
280
|
+
if (typeof optionsOrTotalOrId === 'number') {
|
|
281
|
+
options.total = optionsOrTotalOrId
|
|
282
|
+
if (typeof id === 'string') options.id = id
|
|
283
|
+
} else if (typeof optionsOrTotalOrId === 'string') {
|
|
284
|
+
options.id = optionsOrTotalOrId
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
type: 'progress',
|
|
290
|
+
message,
|
|
291
|
+
value,
|
|
292
|
+
total: options.total,
|
|
293
|
+
id: options.id || 'default',
|
|
294
|
+
options
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export function log(level, message, data = {}) {
|
|
299
|
+
return { type: 'log', level, message, ...data }
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Create a render intent.
|
|
304
|
+
* @param {string} component - Component name (e.g. 'App.Layout.Header').
|
|
305
|
+
* @param {object} [props] - Static props for the component.
|
|
306
|
+
* @returns {RenderIntent}
|
|
307
|
+
*/
|
|
308
|
+
export function render(component, props = {}) {
|
|
309
|
+
return { type: 'render', component, props }
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Create a result intent.
|
|
314
|
+
* @param {*} data - The raw result data.
|
|
315
|
+
* @returns {ResultIntent}
|
|
316
|
+
*/
|
|
317
|
+
export function result(data) {
|
|
318
|
+
return { type: 'result', data }
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* @typedef {Object} ShowData
|
|
323
|
+
* @property {any} [component]
|
|
324
|
+
* @property {import('@nan0web/types').Model} [model]
|
|
325
|
+
*/
|
|
326
|
+
/**
|
|
327
|
+
* Create a show intent.
|
|
328
|
+
* @param {string | any} message Message to display.
|
|
329
|
+
* @param {ShowLevel|ShowData} [level='info'] Level of message or additional data then `level = 'info'`.
|
|
330
|
+
* @param {ShowData} [data={}] Additional data to display.
|
|
331
|
+
* @returns {ShowIntent}
|
|
332
|
+
*/
|
|
333
|
+
export function show(message, level = 'info', data = {}) {
|
|
334
|
+
if ('string' === typeof level) {
|
|
335
|
+
return { type: 'show', level, message, ...data }
|
|
336
|
+
}
|
|
337
|
+
return { type: 'show', level: 'info', message, ...level, ...data }
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Create an agent intent to delegate a task to an AI subagent.
|
|
342
|
+
* @param {string} task - The instructional task for the AI agent.
|
|
343
|
+
* @param {AgentContext} [context={}] - Contextual data (files, errors, docs).
|
|
344
|
+
* @returns {AgentIntent}
|
|
345
|
+
*/
|
|
346
|
+
export function agent(task, context = {}) {
|
|
347
|
+
return {
|
|
348
|
+
type: 'agent',
|
|
349
|
+
task,
|
|
350
|
+
context,
|
|
351
|
+
toPrompt() {
|
|
352
|
+
let ctxStr = ''
|
|
353
|
+
if (this.context.data) {
|
|
354
|
+
try {
|
|
355
|
+
ctxStr = JSON.stringify(this.context.data, null, 2)
|
|
356
|
+
} catch (e) {
|
|
357
|
+
ctxStr = String(this.context.data)
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Format input files for the LLM using the boundary concept
|
|
362
|
+
const filesStr = Object.entries(this.context.files || {})
|
|
363
|
+
.map(([path, content]) => `---boundary:${path}---\n${content}\n---boundary---`)
|
|
364
|
+
.join('\n\n')
|
|
365
|
+
|
|
366
|
+
const outputRules = `
|
|
367
|
+
[Output Format Rules]
|
|
368
|
+
You must return your code modifications using the following strictly parsable boundary format. Do NOT use markdown code blocks (\`\`\`) or JSON for your code outputs.
|
|
369
|
+
|
|
370
|
+
To replace an ENTIRE file:
|
|
371
|
+
---boundary:path/to/file.js---
|
|
372
|
+
<full new file content here>
|
|
373
|
+
---boundary---
|
|
374
|
+
|
|
375
|
+
To replace a SPECIFIC SNIPPET (e.g. replacing 3 lines starting at line 33):
|
|
376
|
+
---boundary:path/to/file.js:33:3---
|
|
377
|
+
<new snippet content here>
|
|
378
|
+
---boundary---
|
|
379
|
+
`
|
|
380
|
+
const inst = Array.isArray(this.context.instructions)
|
|
381
|
+
? this.context.instructions.join('\n')
|
|
382
|
+
: this.context.instructions
|
|
383
|
+
|
|
384
|
+
return [
|
|
385
|
+
`[Subagent Task]`,
|
|
386
|
+
this.task,
|
|
387
|
+
inst ? `\n[Instructions]\n${inst}` : '',
|
|
388
|
+
ctxStr ? `\n[Context]\n${ctxStr}` : '',
|
|
389
|
+
filesStr ? `\n[Files]\n${filesStr}` : '',
|
|
390
|
+
outputRules,
|
|
391
|
+
]
|
|
392
|
+
.filter(Boolean)
|
|
393
|
+
.join('\n')
|
|
394
|
+
},
|
|
395
|
+
}
|
|
396
|
+
}
|
|
@@ -43,11 +43,6 @@ export default class UiMessage extends Message {
|
|
|
43
43
|
NAVIGATION: 'navigation',
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
/** @type {string} */
|
|
47
|
-
type = ''
|
|
48
|
-
/** @type {string} */
|
|
49
|
-
id = ''
|
|
50
|
-
|
|
51
46
|
/**
|
|
52
47
|
* Creates a UiMessage.
|
|
53
48
|
*
|
|
@@ -56,9 +51,11 @@ export default class UiMessage extends Message {
|
|
|
56
51
|
constructor(input = {}) {
|
|
57
52
|
super(input)
|
|
58
53
|
|
|
59
|
-
const { type
|
|
54
|
+
const { type, id } = input
|
|
55
|
+
/** @type {string} */
|
|
60
56
|
this.id = id || `ui-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
|
|
61
|
-
|
|
57
|
+
/** @type {string} */
|
|
58
|
+
this.type = type ? String(type) : ''
|
|
62
59
|
|
|
63
60
|
if (!('body' in input) && 'content' in input) {
|
|
64
61
|
this.body = Array.isArray(input.content) ? input.content : [input.content]
|
|
@@ -14,15 +14,6 @@ export default class OutputMessage extends UiMessage {
|
|
|
14
14
|
CRITICAL: 3,
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
/** @type {string[]} */
|
|
18
|
-
body
|
|
19
|
-
/** @type {Object} */
|
|
20
|
-
meta = {}
|
|
21
|
-
/** @type {Error|null} */
|
|
22
|
-
error = null
|
|
23
|
-
/** @type {number} */
|
|
24
|
-
priority = OutputMessage.PRIORITY.NORMAL
|
|
25
|
-
|
|
26
17
|
/**
|
|
27
18
|
* Creates an OutputMessage.
|
|
28
19
|
*
|
|
@@ -41,14 +32,18 @@ export default class OutputMessage extends UiMessage {
|
|
|
41
32
|
|
|
42
33
|
const contentSource = 'body' in input ? body : 'content' in input ? content : []
|
|
43
34
|
|
|
35
|
+
/** @type {string[]} */
|
|
44
36
|
this.body = Array.isArray(contentSource)
|
|
45
37
|
? contentSource
|
|
46
38
|
: contentSource
|
|
47
39
|
? [String(contentSource)]
|
|
48
40
|
: []
|
|
49
41
|
|
|
42
|
+
/** @type {Object} */
|
|
50
43
|
this.meta = meta
|
|
44
|
+
/** @type {Error|null} */
|
|
51
45
|
this.error = error instanceof Error ? error : error ? new Error(String(error)) : null
|
|
46
|
+
/** @type {number} */
|
|
52
47
|
this.priority = Number(priority)
|
|
53
48
|
|
|
54
49
|
if (!this.type && this.error) {
|
package/src/core/StreamEntry.js
CHANGED
|
@@ -2,30 +2,6 @@
|
|
|
2
2
|
* Represents an entry in a stream with value, completion status, cancellation status, and error message.
|
|
3
3
|
*/
|
|
4
4
|
export default class StreamEntry {
|
|
5
|
-
/**
|
|
6
|
-
* The value of the stream entry.
|
|
7
|
-
* @type {any}
|
|
8
|
-
*/
|
|
9
|
-
value = undefined
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Indicates if the stream entry is done (completed).
|
|
13
|
-
* @type {boolean}
|
|
14
|
-
*/
|
|
15
|
-
done = false
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Indicates if the stream entry has been cancelled.
|
|
19
|
-
* @type {boolean}
|
|
20
|
-
*/
|
|
21
|
-
cancelled = false
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Error message associated with the stream entry.
|
|
25
|
-
* @type {string}
|
|
26
|
-
*/
|
|
27
|
-
error = ''
|
|
28
|
-
|
|
29
5
|
/**
|
|
30
6
|
* Creates a new StreamEntry instance.
|
|
31
7
|
* @param {Object} [input={}] - Input object to initialize the stream entry.
|
|
@@ -36,14 +12,30 @@ export default class StreamEntry {
|
|
|
36
12
|
*/
|
|
37
13
|
constructor(input = {}) {
|
|
38
14
|
const {
|
|
39
|
-
value =
|
|
40
|
-
done =
|
|
41
|
-
cancelled =
|
|
42
|
-
error =
|
|
15
|
+
value = undefined,
|
|
16
|
+
done = false,
|
|
17
|
+
cancelled = false,
|
|
18
|
+
error = '',
|
|
43
19
|
} = input
|
|
20
|
+
/**
|
|
21
|
+
* The value of the stream entry.
|
|
22
|
+
* @type {any}
|
|
23
|
+
*/
|
|
44
24
|
this.value = value
|
|
25
|
+
/**
|
|
26
|
+
* Indicates if the stream entry is done (completed).
|
|
27
|
+
* @type {boolean}
|
|
28
|
+
*/
|
|
45
29
|
this.done = Boolean(done)
|
|
30
|
+
/**
|
|
31
|
+
* Indicates if the stream entry has been cancelled.
|
|
32
|
+
* @type {boolean}
|
|
33
|
+
*/
|
|
46
34
|
this.cancelled = Boolean(cancelled)
|
|
35
|
+
/**
|
|
36
|
+
* Error message associated with the stream entry.
|
|
37
|
+
* @type {string}
|
|
38
|
+
*/
|
|
47
39
|
this.error = String(error)
|
|
48
40
|
}
|
|
49
41
|
|
package/src/core/index.js
CHANGED
|
@@ -42,7 +42,11 @@ export { default as Flow } from './Flow.js'
|
|
|
42
42
|
|
|
43
43
|
// OLMUI Generator Engine — Intent-based Model→Adapter contract
|
|
44
44
|
export { validateIntent, ask, progress, log, render, result, INTENT_TYPES, isModelSchema } from './Intent.js'
|
|
45
|
+
/** @typedef {import('./Intent.js').Intent} Intent */
|
|
46
|
+
/** @typedef {import('./Intent.js').AskResponse} AskResponse */
|
|
47
|
+
/** @typedef {import('./InputAdapter.js').AskOptions} AskOptions */
|
|
45
48
|
export { IntentErrorModel } from './IntentErrorModel.js'
|
|
46
49
|
export { runGenerator } from './GeneratorRunner.js'
|
|
47
50
|
|
|
48
51
|
export { MaskHandler } from './MaskHandler.js'
|
|
52
|
+
export { LayoutModel } from '../domain/LayoutModel.js'
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { Model } from '@nan0web/types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} HTML5Elements
|
|
5
|
+
* @property {string|ContentData[]} [a]
|
|
6
|
+
* @property {string|ContentData[]} [abbr]
|
|
7
|
+
* @property {string|ContentData[]} [address]
|
|
8
|
+
* @property {string|ContentData[]} [area]
|
|
9
|
+
* @property {string|ContentData[]} [article]
|
|
10
|
+
* @property {string|ContentData[]} [aside]
|
|
11
|
+
* @property {string|ContentData[]} [audio]
|
|
12
|
+
* @property {string|ContentData[]} [b]
|
|
13
|
+
* @property {string|ContentData[]} [base]
|
|
14
|
+
* @property {string|ContentData[]} [bdi]
|
|
15
|
+
* @property {string|ContentData[]} [bdo]
|
|
16
|
+
* @property {string|ContentData[]} [blockquote]
|
|
17
|
+
* @property {string|ContentData[]} [body]
|
|
18
|
+
* @property {boolean|object} [br]
|
|
19
|
+
* @property {string|ContentData[]} [canvas]
|
|
20
|
+
* @property {string|ContentData[]} [caption]
|
|
21
|
+
* @property {string|ContentData[]} [cite]
|
|
22
|
+
* @property {string|ContentData[]} [code]
|
|
23
|
+
* @property {string|ContentData[]} [col]
|
|
24
|
+
* @property {string|ContentData[]} [colgroup]
|
|
25
|
+
* @property {string|ContentData[]} [data]
|
|
26
|
+
* @property {string|ContentData[]} [datalist]
|
|
27
|
+
* @property {string|ContentData[]} [dd]
|
|
28
|
+
* @property {string|ContentData[]} [del]
|
|
29
|
+
* @property {string|ContentData[]} [details]
|
|
30
|
+
* @property {string|ContentData[]} [dfn]
|
|
31
|
+
* @property {string|ContentData[]} [dialog]
|
|
32
|
+
* @property {string|ContentData[]} [div]
|
|
33
|
+
* @property {string|ContentData[]} [dl]
|
|
34
|
+
* @property {string|ContentData[]} [dt]
|
|
35
|
+
* @property {string|ContentData[]} [em]
|
|
36
|
+
* @property {string|ContentData[]} [embed]
|
|
37
|
+
* @property {string|ContentData[]} [fieldset]
|
|
38
|
+
* @property {string|ContentData[]} [figcaption]
|
|
39
|
+
* @property {string|ContentData[]} [figure]
|
|
40
|
+
* @property {any} [footer]
|
|
41
|
+
* @property {string|ContentData[]} [form]
|
|
42
|
+
* @property {string|ContentData[]} [h1]
|
|
43
|
+
* @property {string|ContentData[]} [h2]
|
|
44
|
+
* @property {string|ContentData[]} [h3]
|
|
45
|
+
* @property {string|ContentData[]} [h4]
|
|
46
|
+
* @property {string|ContentData[]} [h5]
|
|
47
|
+
* @property {string|ContentData[]} [h6]
|
|
48
|
+
* @property {string|ContentData[]} [head]
|
|
49
|
+
* @property {any} [header]
|
|
50
|
+
* @property {string|ContentData[]} [hgroup]
|
|
51
|
+
* @property {boolean|object} [hr]
|
|
52
|
+
* @property {string|ContentData[]} [html]
|
|
53
|
+
* @property {string|ContentData[]} [i]
|
|
54
|
+
* @property {string|ContentData[]} [iframe]
|
|
55
|
+
* @property {string|ContentData[]} [img]
|
|
56
|
+
* @property {string|ContentData[]} [ins]
|
|
57
|
+
* @property {string|ContentData[]} [kbd]
|
|
58
|
+
* @property {string|ContentData[]} [label]
|
|
59
|
+
* @property {string|ContentData[]} [legend]
|
|
60
|
+
* @property {string|ContentData[]} [li]
|
|
61
|
+
* @property {string|ContentData[]} [link]
|
|
62
|
+
* @property {string|ContentData[]} [main]
|
|
63
|
+
* @property {string|ContentData[]} [map]
|
|
64
|
+
* @property {string|ContentData[]} [mark]
|
|
65
|
+
* @property {string|ContentData[]} [meta]
|
|
66
|
+
* @property {string|ContentData[]} [meter]
|
|
67
|
+
* @property {boolean|any} [input]
|
|
68
|
+
* @property {boolean|any} [button]
|
|
69
|
+
* @property {boolean|any} [select]
|
|
70
|
+
* @property {string|ContentData[]} [nav]
|
|
71
|
+
* @property {string|ContentData[]} [noscript]
|
|
72
|
+
* @property {string|ContentData[]} [object]
|
|
73
|
+
* @property {string|ContentData[]} [ol]
|
|
74
|
+
* @property {string|ContentData[]} [optgroup]
|
|
75
|
+
* @property {string|ContentData[]} [option]
|
|
76
|
+
* @property {string|ContentData[]} [output]
|
|
77
|
+
* @property {string|ContentData[]} [p]
|
|
78
|
+
* @property {string|ContentData[]} [picture]
|
|
79
|
+
* @property {string|ContentData[]} [pre]
|
|
80
|
+
* @property {string|ContentData[]} [progress]
|
|
81
|
+
* @property {string|ContentData[]} [q]
|
|
82
|
+
* @property {string|ContentData[]} [rp]
|
|
83
|
+
* @property {string|ContentData[]} [rt]
|
|
84
|
+
* @property {string|ContentData[]} [ruby]
|
|
85
|
+
* @property {string|ContentData[]} [s]
|
|
86
|
+
* @property {string|ContentData[]} [samp]
|
|
87
|
+
* @property {string|ContentData[]} [script]
|
|
88
|
+
* @property {string|ContentData[]} [section]
|
|
89
|
+
* @property {string|ContentData[]} [slot]
|
|
90
|
+
* @property {string|ContentData[]} [small]
|
|
91
|
+
* @property {string|ContentData[]} [source]
|
|
92
|
+
* @property {string|ContentData[]} [span]
|
|
93
|
+
* @property {string|ContentData[]} [strong]
|
|
94
|
+
* @property {string|ContentData[]} [style]
|
|
95
|
+
* @property {string|ContentData[]} [sub]
|
|
96
|
+
* @property {string|ContentData[]} [summary]
|
|
97
|
+
* @property {string|ContentData[]} [sup]
|
|
98
|
+
* @property {string|ContentData[]} [table]
|
|
99
|
+
* @property {string|ContentData[]} [tbody]
|
|
100
|
+
* @property {string|ContentData[]} [td]
|
|
101
|
+
* @property {string|ContentData[]} [template]
|
|
102
|
+
* @property {string|ContentData[]} [textarea]
|
|
103
|
+
* @property {string|ContentData[]} [tfoot]
|
|
104
|
+
* @property {string|ContentData[]} [th]
|
|
105
|
+
* @property {string|ContentData[]} [thead]
|
|
106
|
+
* @property {string|ContentData[]} [time]
|
|
107
|
+
* @property {string|ContentData[]} [title]
|
|
108
|
+
* @property {string|ContentData[]} [tr]
|
|
109
|
+
* @property {string|ContentData[]} [track]
|
|
110
|
+
* @property {string|ContentData[]} [u]
|
|
111
|
+
* @property {string|ContentData[]} [ul]
|
|
112
|
+
* @property {string|ContentData[]} [var]
|
|
113
|
+
* @property {string|ContentData[]} [video]
|
|
114
|
+
* @property {string|ContentData[]} [wbr]
|
|
115
|
+
* @property {string|ContentData[]} [svg]
|
|
116
|
+
* @property {string|ContentData[]} [path]
|
|
117
|
+
* @property {string|ContentData[]} [circle]
|
|
118
|
+
* @property {string|ContentData[]} [rect]
|
|
119
|
+
* @property {string|ContentData[]} [line]
|
|
120
|
+
* @property {string|ContentData[]} [polyline]
|
|
121
|
+
* @property {string|ContentData[]} [polygon]
|
|
122
|
+
* @property {string|ContentData[]} [g]
|
|
123
|
+
* @property {string|ContentData[]} [defs]
|
|
124
|
+
* @property {string|ContentData[]} [symbol]
|
|
125
|
+
* @property {string|ContentData[]} [use]
|
|
126
|
+
* @property {string|ContentData[]} [text]
|
|
127
|
+
*/
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @typedef {Object} CoreUIElements
|
|
131
|
+
* @property {import('./components/AccordionModel.js').AccordionModel} [accordion]
|
|
132
|
+
* @property {import('./components/AutocompleteModel.js').AutocompleteModel} [autocomplete]
|
|
133
|
+
* @property {import('./components/BannerModel.js').BannerModel} [banner]
|
|
134
|
+
* @property {import('./components/BreadcrumbModel.js').BreadcrumbModel} [breadcrumb]
|
|
135
|
+
* @property {import('./components/ButtonModel.js').ButtonModel} [button]
|
|
136
|
+
* @property {import('./components/CommentModel.js').CommentModel} [comment]
|
|
137
|
+
* @property {import('./components/ConfirmModel.js').ConfirmModel} [confirm]
|
|
138
|
+
* @property {import('./components/EmptyStateModel.js').EmptyStateModel} [emptyState]
|
|
139
|
+
* @property {import('./components/FAQModel.js').FAQModel} [faq]
|
|
140
|
+
* @property {import('./components/FeatureGridModel.js').FeatureGridModel} [featureGrid]
|
|
141
|
+
* @property {import('./components/GalleryModel.js').GalleryModel} [gallery]
|
|
142
|
+
* @property {import('./HeaderModel.js').HeaderModel} [header]
|
|
143
|
+
* @property {import('./FooterModel.js').FooterModel} [footer]
|
|
144
|
+
* @property {import('./components/InputModel.js').InputModel} [input]
|
|
145
|
+
* @property {import('./components/MarkdownModel.js').MarkdownModel} [markdown]
|
|
146
|
+
* @property {import('./components/PriceModel.js').PriceModel} [price]
|
|
147
|
+
* @property {import('./components/PricingModel.js').PricingModel} [pricing]
|
|
148
|
+
* @property {import('./components/PricingSectionModel.js').PricingSectionModel} [pricingSection]
|
|
149
|
+
* @property {import('./components/ProfileDropdownModel.js').ProfileDropdownModel} [profileDropdown]
|
|
150
|
+
* @property {import('./components/SelectModel.js').SelectModel} [select]
|
|
151
|
+
* @property {import('./components/ShellModel.js').ShellModel} [shell]
|
|
152
|
+
* @property {import('./components/SpinnerModel.js').SpinnerModel} [spinner]
|
|
153
|
+
* @property {import('./components/StatsItemModel.js').StatsItemModel} [statsItem]
|
|
154
|
+
* @property {import('./components/StatsModel.js').StatsModel} [stats]
|
|
155
|
+
* @property {import('./components/TableModel.js').TableModel} [tableUI]
|
|
156
|
+
* @property {import('./components/TabsModel.js').TabsModel} [tabs]
|
|
157
|
+
* @property {import('./components/TestimonialModel.js').TestimonialModel} [testimonial]
|
|
158
|
+
* @property {import('./components/TimelineItemModel.js').TimelineItemModel} [timelineItem]
|
|
159
|
+
* @property {import('./components/TimelineModel.js').TimelineModel} [timeline]
|
|
160
|
+
* @property {import('./components/ToastModel.js').ToastModel} [toast]
|
|
161
|
+
* @property {import('./components/TreeModel.js').TreeModel} [tree]
|
|
162
|
+
* @property {ContentData[]} [sortable] - Інтерактивний Drag-n-Drop контейнер
|
|
163
|
+
*/
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @typedef {Partial<Content & HTML5Elements & CoreUIElements> & Record<string, any>} ContentData
|
|
167
|
+
*/
|
|
168
|
+
|
|
169
|
+
export class Content extends Model {
|
|
170
|
+
static content = { type: 'string', help: 'Content' }
|
|
171
|
+
static children = { type: 'array', model: Content, help: 'Children' }
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @param {ContentData | string} [data={}]
|
|
175
|
+
* @param {import('@nan0web/types').ModelOptions} [options={}]
|
|
176
|
+
*/
|
|
177
|
+
constructor(data = {}, options = {}) {
|
|
178
|
+
if ('string' === typeof data) {
|
|
179
|
+
data = { content: data }
|
|
180
|
+
}
|
|
181
|
+
super(data, options)
|
|
182
|
+
|
|
183
|
+
// ── Base Fields ──
|
|
184
|
+
/** @type {string|undefined} Content */ this.content
|
|
185
|
+
/** @type {Array<Content>|undefined} Children */ this.children
|
|
186
|
+
|
|
187
|
+
// ── Hydration ──
|
|
188
|
+
if (Array.isArray(this.children)) {
|
|
189
|
+
this.children = this.children.map((child) => new Content(child, options))
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const { content, children, ...rest } = /** @type {any} */ (data)
|
|
193
|
+
|
|
194
|
+
for (const [key, value] of Object.entries(rest)) {
|
|
195
|
+
/** @type {any} */ (this)[key] = value
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Model } from '@nan0web/types'
|
|
2
|
+
import { Content } from './Content.js'
|
|
3
|
+
import Navigation from './Navigation.js'
|
|
4
|
+
import { Language } from '@nan0web/i18n'
|
|
5
|
+
|
|
6
|
+
export class Document extends Model {
|
|
7
|
+
static title = { type: 'string', help: 'Title' }
|
|
8
|
+
static content = { type: 'array', model: Content, help: 'Content' }
|
|
9
|
+
static $content = { type: 'array', model: Content, help: 'Layout configuration' }
|
|
10
|
+
static nav = { type: 'any', model: Navigation, help: 'Navigation config or reference' }
|
|
11
|
+
static langs = { type: 'array', model: Language, help: 'Supported languages array' }
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @param {Partial<Document>} [data]
|
|
15
|
+
* @param {import('@nan0web/types').ModelOptions} [options]
|
|
16
|
+
*/
|
|
17
|
+
constructor(data = {}, options = {}) {
|
|
18
|
+
super(data, options)
|
|
19
|
+
/** @type {string} Title */ this.title
|
|
20
|
+
/** @type {Array<Content>} Content */ this.content
|
|
21
|
+
/** @type {Array<Content>} Layout configuration */ this.$content
|
|
22
|
+
/** @type {Navigation|string|Array<Navigation>} Navigation config */ this.nav
|
|
23
|
+
/** @type {Array<Language>} Supported languages */ this.langs
|
|
24
|
+
}
|
|
25
|
+
}
|