@nan0web/ui 1.12.2 → 3.1.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 (160) hide show
  1. package/README.md +18 -355
  2. package/package.json +36 -22
  3. package/src/Component/index.js +1 -5
  4. package/src/Model/Element.js +183 -0
  5. package/src/Model/index.js +2 -2
  6. package/src/Theme/AppTheme.js +19 -0
  7. package/src/Theme/CustomTheme.js +32 -0
  8. package/src/Theme/DarkLightTheme.js +34 -0
  9. package/src/Theme/Theme.js +25 -0
  10. package/src/Theme/atoms/Avatar.js +20 -0
  11. package/src/Theme/atoms/Badge.js +28 -0
  12. package/src/Theme/atoms/Button.js +88 -0
  13. package/src/Theme/atoms/Checkbox.js +26 -0
  14. package/src/Theme/atoms/Input.js +28 -0
  15. package/src/Theme/atoms/Radio.js +26 -0
  16. package/src/Theme/atoms/Select.js +16 -0
  17. package/src/Theme/atoms/TextArea.js +17 -0
  18. package/src/Theme/atoms/Typography.js +26 -0
  19. package/src/Theme/atoms/index.js +11 -0
  20. package/src/Theme/createTheme.js +22 -0
  21. package/src/Theme/index.js +20 -0
  22. package/src/Theme/molecules/Card.js +24 -0
  23. package/src/Theme/molecules/index.js +3 -0
  24. package/src/Theme/organisms/Modal.js +24 -0
  25. package/src/Theme/organisms/index.js +3 -0
  26. package/src/Theme/presets/HighContrastTheme.js +65 -0
  27. package/src/Theme/presets/NightTheme.js +66 -0
  28. package/src/Theme/presets/index.js +4 -0
  29. package/src/Theme/tokens.js +115 -0
  30. package/src/core/InputAdapter.js +1 -2
  31. package/src/core/Intent.js +22 -8
  32. package/src/core/Message/Message.js +3 -0
  33. package/src/core/OutputAdapter.js +9 -13
  34. package/src/core/index.js +7 -4
  35. package/src/core/resolvePositionalArgs.js +51 -0
  36. package/src/domain/Content.js +5 -5
  37. package/src/domain/Document.js +1 -1
  38. package/src/domain/HeroModel.js +1 -1
  39. package/src/domain/ModelAsApp.js +310 -20
  40. package/src/domain/ModelAsApp.story.js +117 -0
  41. package/src/domain/app/GalleryCommand.js +9 -8
  42. package/src/domain/app/{GalleryRenderIntent.js → GalleryRenderCommand.js} +20 -20
  43. package/src/domain/app/IntentAuditor.js +53 -0
  44. package/src/domain/app/JsIntentAuditor.js +145 -0
  45. package/src/domain/app/PyIntentAuditor.js +144 -0
  46. package/src/domain/app/SnapshotAuditor.js +82 -86
  47. package/src/domain/app/SnapshotRunner.js +1 -1
  48. package/src/domain/app/UIApp.js +12 -21
  49. package/src/domain/components/ShellModel.js +2 -2
  50. package/src/index.js +38 -10
  51. package/src/inspect.js +4 -0
  52. package/src/testing/SnapshotRunner.js +2 -1
  53. package/src/utils/format.js +21 -0
  54. package/src/utils/processI18n.js +27 -0
  55. package/src/utils/resolveContext.js +79 -0
  56. package/types/Component/index.d.ts +1 -5
  57. package/types/Model/Element.d.ts +87 -0
  58. package/types/Model/index.d.ts +2 -2
  59. package/types/Theme/AppTheme.d.ts +14 -0
  60. package/types/Theme/CustomTheme.d.ts +21 -0
  61. package/types/Theme/DarkLightTheme.d.ts +16 -0
  62. package/types/Theme/Theme.d.ts +18 -0
  63. package/types/Theme/atoms/Avatar.d.ts +14 -0
  64. package/types/Theme/atoms/Badge.d.ts +22 -0
  65. package/types/Theme/atoms/Button.d.ts +144 -0
  66. package/types/Theme/atoms/Checkbox.d.ts +20 -0
  67. package/types/Theme/atoms/Input.d.ts +22 -0
  68. package/types/Theme/atoms/Radio.d.ts +20 -0
  69. package/types/Theme/atoms/Select.d.ts +15 -0
  70. package/types/Theme/atoms/TextArea.d.ts +17 -0
  71. package/types/Theme/atoms/Typography.d.ts +47 -0
  72. package/types/Theme/atoms/index.d.ts +10 -0
  73. package/types/Theme/createTheme.d.ts +7 -0
  74. package/types/Theme/index.d.ts +10 -0
  75. package/types/Theme/molecules/Card.d.ts +18 -0
  76. package/types/Theme/molecules/index.d.ts +2 -0
  77. package/types/Theme/organisms/Modal.d.ts +18 -0
  78. package/types/Theme/organisms/index.d.ts +2 -0
  79. package/types/Theme/presets/HighContrastTheme.d.ts +2 -0
  80. package/types/Theme/presets/NightTheme.d.ts +2 -0
  81. package/types/Theme/presets/index.d.ts +3 -0
  82. package/types/Theme/tokens.d.ts +119 -0
  83. package/types/core/Intent.d.ts +10 -7
  84. package/types/core/Message/Message.d.ts +3 -0
  85. package/types/core/OutputAdapter.d.ts +2 -4
  86. package/types/core/index.d.ts +5 -2
  87. package/types/core/resolvePositionalArgs.d.ts +24 -0
  88. package/types/docs/README.md.d.ts +1 -0
  89. package/types/domain/Content.d.ts +2 -2
  90. package/types/domain/Document.d.ts +4 -3
  91. package/types/domain/FooterModel.d.ts +2 -1
  92. package/types/domain/HeroModel.d.ts +2 -2
  93. package/types/domain/ModelAsApp.d.ts +49 -5
  94. package/types/domain/ModelAsApp.story.d.ts +1 -0
  95. package/types/domain/app/GalleryCommand.d.ts +6 -37
  96. package/types/domain/app/GalleryRenderCommand.d.ts +27 -0
  97. package/types/domain/app/IntentAuditor.d.ts +23 -0
  98. package/types/domain/app/JsIntentAuditor.d.ts +22 -0
  99. package/types/domain/app/PyIntentAuditor.d.ts +22 -0
  100. package/types/domain/app/SnapshotAuditor.d.ts +34 -25
  101. package/types/domain/app/SnapshotRunner.d.ts +2 -2
  102. package/types/domain/app/UIApp.d.ts +14 -11
  103. package/types/domain/components/ShellModel.d.ts +1 -5
  104. package/types/index.d.ts +10 -10
  105. package/types/inspect.d.ts +4 -0
  106. package/types/testing/verifySnapshot.d.ts +1 -1
  107. package/types/utils/format.d.ts +5 -0
  108. package/types/utils/processI18n.d.ts +8 -0
  109. package/types/utils/resolveContext.d.ts +21 -0
  110. package/src/App/Command/DepsCommand.js +0 -24
  111. package/src/App/Core/CoreApp.js +0 -125
  112. package/src/App/Core/UI.js +0 -63
  113. package/src/App/Core/Widget.js +0 -61
  114. package/src/App/Core/index.js +0 -11
  115. package/src/App/Scenario.js +0 -45
  116. package/src/App/User/Command/Message.js +0 -3
  117. package/src/App/User/Command/index.js +0 -5
  118. package/src/App/User/UserApp.js +0 -85
  119. package/src/App/User/UserUI.js +0 -20
  120. package/src/App/User/index.js +0 -9
  121. package/src/App/index.js +0 -14
  122. package/src/Component/Process/Input.js +0 -63
  123. package/src/Component/Process/Process.js +0 -24
  124. package/src/Component/Process/index.js +0 -5
  125. package/src/Component/Welcome/Input.js +0 -48
  126. package/src/Component/Welcome/Welcome.js +0 -22
  127. package/src/Component/Welcome/index.js +0 -5
  128. package/src/Frame/Frame.js +0 -608
  129. package/src/Frame/Props.js +0 -96
  130. package/src/StdIn.js +0 -100
  131. package/src/StdOut.js +0 -95
  132. package/src/View/RenderOptions.js +0 -48
  133. package/src/View/View.js +0 -306
  134. package/src/core/Message/index.js +0 -6
  135. package/types/App/Command/DepsCommand.d.ts +0 -14
  136. package/types/App/Core/CoreApp.d.ts +0 -70
  137. package/types/App/Core/UI.d.ts +0 -38
  138. package/types/App/Core/Widget.d.ts +0 -39
  139. package/types/App/Core/index.d.ts +0 -10
  140. package/types/App/Scenario.d.ts +0 -26
  141. package/types/App/User/Command/Message.d.ts +0 -2
  142. package/types/App/User/Command/index.d.ts +0 -3
  143. package/types/App/User/UserApp.d.ts +0 -41
  144. package/types/App/User/UserUI.d.ts +0 -9
  145. package/types/App/User/index.d.ts +0 -8
  146. package/types/App/index.d.ts +0 -12
  147. package/types/Component/Process/Input.d.ts +0 -48
  148. package/types/Component/Process/Process.d.ts +0 -13
  149. package/types/Component/Process/index.d.ts +0 -4
  150. package/types/Component/Welcome/Input.d.ts +0 -34
  151. package/types/Component/Welcome/Welcome.d.ts +0 -13
  152. package/types/Component/Welcome/index.d.ts +0 -4
  153. package/types/Frame/Frame.d.ts +0 -186
  154. package/types/Frame/Props.d.ts +0 -77
  155. package/types/StdIn.d.ts +0 -62
  156. package/types/StdOut.d.ts +0 -52
  157. package/types/View/RenderOptions.d.ts +0 -29
  158. package/types/View/View.d.ts +0 -124
  159. package/types/core/Message/index.d.ts +0 -4
  160. package/types/domain/app/GalleryRenderIntent.d.ts +0 -31
@@ -0,0 +1,24 @@
1
+ import { tokens } from '../tokens.js'
2
+
3
+ /**
4
+ * Theme definition for Modal organism.
5
+ *
6
+ * @typedef {Object} ModalTheme
7
+ * @property {string} overlayBackground
8
+ * @property {string} borderRadius
9
+ * @property {string} boxShadow
10
+ * @property {string} padding
11
+ * @property {string} backgroundColor
12
+ */
13
+
14
+ /**
15
+ * Modal organism theme.
16
+ * @type {ModalTheme}
17
+ */
18
+ export default {
19
+ overlayBackground: 'rgba(0, 0, 0, 0.5)',
20
+ borderRadius: tokens.radius.lg,
21
+ boxShadow: tokens.shadow.lg,
22
+ padding: tokens.space.xl,
23
+ backgroundColor: tokens.color.background,
24
+ }
@@ -0,0 +1,3 @@
1
+ import Modal from './Modal.js'
2
+
3
+ export { Modal }
@@ -0,0 +1,65 @@
1
+ import { createTheme } from '../createTheme.js'
2
+ import { tokens } from '../tokens.js'
3
+
4
+ /**
5
+ * High contrast theme preset.
6
+ * Designed for maximum readability by increasing contrast between text and background.
7
+ */
8
+ export default createTheme({
9
+ atoms: {
10
+ Button: {
11
+ fontFamily: tokens.font.family,
12
+ color: tokens.color.text,
13
+ background: tokens.color.primary,
14
+ shadow: tokens.shadow.sm,
15
+ hoverBackground: 'color-mix(in srgb, var(--color-primary) 80%, black)',
16
+ borderColor: tokens.border.color.default,
17
+ borderRadius: tokens.radius.sm,
18
+ borderWidth: tokens.border.width.sm,
19
+ fontSize: tokens.font.size.base,
20
+ paddingX: tokens.space.lg,
21
+ paddingY: tokens.space.md,
22
+ solid: {
23
+ primary: { background: '#000000', color: '#ffffff', border: '#000000' },
24
+ secondary: { background: '#333333', color: '#ffffff', border: '#333333' },
25
+ success: { background: '#006600', color: '#ffffff', border: '#006600' },
26
+ warning: { background: '#996600', color: '#000000', border: '#996600' },
27
+ danger: { background: '#990000', color: '#ffffff', border: '#990000' },
28
+ info: { background: '#006699', color: '#ffffff', border: '#006699' },
29
+ light: { background: '#dddddd', color: '#000000', border: '#dddddd' },
30
+ dark: { background: '#222222', color: '#ffffff', border: '#222222' },
31
+ link: { background: 'transparent', color: '#000099', border: 'transparent' },
32
+ },
33
+ outline: {
34
+ primary: { background: '#ffffff', color: '#000000', border: '#000000' },
35
+ secondary: { background: '#ffffff', color: '#333333', border: '#333333' },
36
+ success: { background: '#ffffff', color: '#006600', border: '#006600' },
37
+ warning: { background: '#ffffff', color: '#996600', border: '#996600' },
38
+ danger: { background: '#ffffff', color: '#990000', border: '#990000' },
39
+ info: { background: '#ffffff', color: '#006699', border: '#006699' },
40
+ light: { background: '#ffffff', color: '#000000', border: '#000000' },
41
+ dark: { background: '#ffffff', color: '#222222', border: '#222222' },
42
+ },
43
+ size: {
44
+ sm: {
45
+ fontSize: '0.875rem',
46
+ paddingX: '0.5rem',
47
+ paddingY: '0.25rem',
48
+ },
49
+ md: {
50
+ fontSize: '1rem',
51
+ paddingX: '1rem',
52
+ paddingY: '0.5rem',
53
+ },
54
+ },
55
+ animation: {
56
+ transition: 'background 0.15s ease, transform 0.1s ease',
57
+ hoverAdjust: 20,
58
+ activeAdjust: -30,
59
+ focusScale: 1.02,
60
+ activeScale: 0.98,
61
+ disabledOpacity: 0.65,
62
+ },
63
+ },
64
+ },
65
+ })
@@ -0,0 +1,66 @@
1
+ import { createTheme } from '../createTheme.js'
2
+ import { tokens } from '../tokens.js'
3
+
4
+ /**
5
+ * Night (dark) theme preset.
6
+ * Optimized for low-light environments with deep background colors and vibrant text.
7
+ * Overrides solid button styles while keeping contrast high.
8
+ */
9
+ export default createTheme({
10
+ atoms: {
11
+ Button: {
12
+ fontFamily: tokens.font.family,
13
+ color: tokens.color.text,
14
+ background: tokens.color.primary,
15
+ shadow: tokens.shadow.sm,
16
+ hoverBackground: 'color-mix(in srgb, var(--color-primary) 80%, black)',
17
+ borderColor: tokens.border.color.default,
18
+ borderRadius: tokens.radius.sm,
19
+ borderWidth: tokens.border.width.sm,
20
+ fontSize: tokens.font.size.base,
21
+ paddingX: tokens.space.lg,
22
+ paddingY: tokens.space.md,
23
+ solid: {
24
+ primary: { background: '#0d6efd', color: '#ffffff', border: '#0d6efd' },
25
+ secondary: { background: '#6c757d', color: '#ffffff', border: '#6c757d' },
26
+ success: { background: '#198754', color: '#ffffff', border: '#198754' },
27
+ warning: { background: '#ffc107', color: '#000000', border: '#ffc107' },
28
+ danger: { background: '#dc3545', color: '#ffffff', border: '#dc3545' },
29
+ info: { background: '#0dcaf0', color: '#000000', border: '#0dcaf0' },
30
+ light: { background: '#aaaaaa', color: '#000000', border: '#aaaaaa' },
31
+ dark: { background: '#212529', color: '#ffffff', border: '#212529' },
32
+ link: { background: 'transparent', color: '#6ea8fe', border: 'transparent' },
33
+ },
34
+ outline: {
35
+ primary: { background: 'transparent', color: '#0d6efd', border: '#0d6efd' },
36
+ secondary: { background: 'transparent', color: '#6c757d', border: '#6c757d' },
37
+ success: { background: 'transparent', color: '#198754', border: '#198754' },
38
+ warning: { background: 'transparent', color: '#ffc107', border: '#ffc107' },
39
+ danger: { background: 'transparent', color: '#dc3545', border: '#dc3545' },
40
+ info: { background: 'transparent', color: '#0dcaf0', border: '#0dcaf0' },
41
+ light: { background: 'transparent', color: '#aaaaaa', border: '#aaaaaa' },
42
+ dark: { background: 'transparent', color: '#212529', border: '#212529' },
43
+ },
44
+ size: {
45
+ sm: {
46
+ fontSize: '0.875rem',
47
+ paddingX: '0.5rem',
48
+ paddingY: '0.25rem',
49
+ },
50
+ md: {
51
+ fontSize: '1rem',
52
+ paddingX: '1rem',
53
+ paddingY: '0.5rem',
54
+ },
55
+ },
56
+ animation: {
57
+ transition: 'background 0.15s ease, transform 0.1s ease',
58
+ hoverAdjust: 20,
59
+ activeAdjust: -30,
60
+ focusScale: 1.02,
61
+ activeScale: 0.98,
62
+ disabledOpacity: 0.65,
63
+ },
64
+ },
65
+ },
66
+ })
@@ -0,0 +1,4 @@
1
+ import HighContrastTheme from './HighContrastTheme.js'
2
+ import NightTheme from './NightTheme.js'
3
+
4
+ export { HighContrastTheme, NightTheme }
@@ -0,0 +1,115 @@
1
+ // src/tokens.js
2
+ export const tokens = {
3
+ // Spacing
4
+ space: {
5
+ xs: '0.25rem',
6
+ sm: '0.5rem',
7
+ md: '0.75rem',
8
+ lg: '1rem',
9
+ xl: '1.5rem',
10
+ },
11
+
12
+ // Radius
13
+ radius: {
14
+ none: '0',
15
+ sm: '0.125rem',
16
+ md: '0.25rem',
17
+ lg: '0.5rem',
18
+ full: '9999px',
19
+ },
20
+
21
+ // Border
22
+ border: {
23
+ width: {
24
+ sm: '1px',
25
+ md: '2px',
26
+ },
27
+ color: {
28
+ default: 'var(--color-border)',
29
+ muted: 'var(--color-border-muted)',
30
+ error: 'var(--color-error)',
31
+ },
32
+ },
33
+
34
+ // Color
35
+ color: {
36
+ text: 'var(--color-text)',
37
+ background: 'var(--color-background)',
38
+ primary: 'var(--color-primary)',
39
+ success: 'var(--color-success)',
40
+ warning: 'var(--color-warning)',
41
+ error: 'var(--color-error)',
42
+ },
43
+
44
+ // Typography
45
+ font: {
46
+ family: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
47
+ size: {
48
+ xs: '0.75rem',
49
+ sm: '0.875rem',
50
+ base: '1rem',
51
+ lg: '1.125rem',
52
+ xl: '1.25rem',
53
+ },
54
+ weight: {
55
+ normal: '400',
56
+ medium: '500',
57
+ bold: '700',
58
+ },
59
+ },
60
+
61
+ // Shadows
62
+ shadow: {
63
+ sm: '0 1px 2px rgba(0,0,0,0.05)',
64
+ md: '0 4px 6px rgba(0,0,0,0.1)',
65
+ lg: '0 10px 25px rgba(0,0,0,0.15)',
66
+ },
67
+
68
+ // Breakpoints (Bootstrap 5)
69
+ breakpoint: {
70
+ xs: '0', // extra‑small
71
+ sm: '576px',
72
+ md: '768px',
73
+ lg: '992px',
74
+ xl: '1200px',
75
+ xxl: '1400px',
76
+ },
77
+
78
+ // Container max‑widths (Bootstrap 5)
79
+ container: {
80
+ sm: '540px',
81
+ md: '720px',
82
+ lg: '960px',
83
+ xl: '1140px',
84
+ xxl: '1320px',
85
+ },
86
+
87
+ // Z‑index scale (Bootstrap)
88
+ zIndex: {
89
+ dropdown: 1000,
90
+ sticky: 1020,
91
+ fixed: 1030,
92
+ modalBackdrop: 1040,
93
+ modal: 1050,
94
+ popover: 1060,
95
+ tooltip: 1070,
96
+ },
97
+
98
+ // Opacity scale
99
+ opacity: {
100
+ 0: '0',
101
+ 10: '0.1',
102
+ 25: '0.25',
103
+ 50: '0.5',
104
+ 75: '0.75',
105
+ 90: '0.9',
106
+ 100: '1',
107
+ },
108
+
109
+ // Transition durations
110
+ transition: {
111
+ fast: '150ms',
112
+ medium: '300ms',
113
+ slow: '500ms',
114
+ },
115
+ }
@@ -1,6 +1,5 @@
1
1
  import Event from '@nan0web/event/oop'
2
2
  import CancelError from './Error/CancelError.js'
3
- import UiMessage from './Message/Message.js'
4
3
 
5
4
  /**
6
5
  * @typedef {Object} AskOptions
@@ -32,7 +31,7 @@ export class InputAdapter extends Event {
32
31
  * @returns {void}
33
32
  */
34
33
  start() {
35
- this.emit('input', UiMessage.from({ body: 'Adapter started' }))
34
+ this.emit('input', { body: 'Adapter started' })
36
35
  }
37
36
 
38
37
  /**
@@ -48,7 +48,7 @@ import { IntentErrorModel } from './IntentErrorModel.js'
48
48
  * @typedef {'info' | 'warn' | 'error' | 'success'} ShowLevel
49
49
  */
50
50
 
51
- /** @typedef {ShowLevel} LogLevel */
51
+ /** @typedef {ShowLevel | 'debug'} LogLevel */
52
52
 
53
53
  /**
54
54
  * Model emits a show message. No response expected.
@@ -168,7 +168,14 @@ import { IntentErrorModel } from './IntentErrorModel.js'
168
168
  * @typedef {'ask' | 'show' | 'progress' | 'render' | 'agent'} IntentType
169
169
  */
170
170
 
171
- export const INTENT_TYPES = /** @type {const} */ (['ask', 'progress', 'show', 'log', 'render', 'agent'])
171
+ export const INTENT_TYPES = /** @type {const} */ ([
172
+ 'ask',
173
+ 'progress',
174
+ 'show',
175
+ 'log',
176
+ 'render',
177
+ 'agent',
178
+ ])
172
179
 
173
180
  /**
174
181
  * Detects if a value is a Model-as-Schema class (has static fields with `help`).
@@ -286,16 +293,23 @@ export function progress(message, value = 0, optionsOrTotalOrId, id) {
286
293
  }
287
294
  }
288
295
 
289
- return {
290
- type: 'progress',
291
- message,
292
- value,
296
+ return {
297
+ type: 'progress',
298
+ message,
299
+ value,
293
300
  total: options.total,
294
- id: options.id || 'default',
295
- options
301
+ id: options.id || 'default',
302
+ options,
296
303
  }
297
304
  }
298
305
 
306
+ /**
307
+ * Lgs a message if level is greater or equal the current view level.
308
+ * @param {LogLevel} level
309
+ * @param {string} message
310
+ * @param {object} [data={}]
311
+ * @returns {LogIntent}
312
+ */
299
313
  export function log(level, message, data = {}) {
300
314
  return { type: 'log', level, message, ...data }
301
315
  }
@@ -17,6 +17,9 @@ import { Message } from '@nan0web/co'
17
17
  *
18
18
  * @class UiMessage
19
19
  * @extends Message
20
+ * @property {Record<string, any>} head - Message head.
21
+ * @property {boolean} isValid - True if message is valid.
22
+ * @property {Date} time - Creation timestamp.
20
23
  *
21
24
  * @example
22
25
  * class UserLoginMessage extends UiMessage {
@@ -1,6 +1,4 @@
1
1
  import Event from '@nan0web/event/oop'
2
- import OutputMessage from './Message/OutputMessage.js'
3
- import FormMessage from './Form/Message.js'
4
2
 
5
3
  /**
6
4
  * Abstract output adapter for UI implementations.
@@ -12,7 +10,7 @@ class OutputAdapter extends Event {
12
10
  /**
13
11
  * Renders a message to the user.
14
12
  *
15
- * @param {OutputMessage|FormMessage} message - Message to render.
13
+ * @param {object} message - Message to render.
16
14
  * @throws {Error} If not overridden by a subclass.
17
15
  */
18
16
  render(message) {
@@ -27,16 +25,14 @@ class OutputAdapter extends Event {
27
25
  * @returns {void}
28
26
  */
29
27
  progress(progress, metadata = {}) {
30
- this.render(
31
- OutputMessage.from({
32
- content: [],
33
- metadata: {
34
- ...metadata,
35
- progress,
36
- elementType: 'progress',
37
- },
38
- }),
39
- )
28
+ this.render({
29
+ content: [],
30
+ metadata: {
31
+ ...metadata,
32
+ progress,
33
+ elementType: 'progress',
34
+ },
35
+ })
40
36
  }
41
37
 
42
38
  /**
package/src/core/index.js CHANGED
@@ -6,16 +6,16 @@ export { UIStream, UIStream as UiStream }
6
6
  import StreamEntry from './StreamEntry.js'
7
7
  export { StreamEntry, StreamEntry as UiStreamEntry }
8
8
 
9
- export { default as UiMessage } from './Message/Message.js'
10
- export { default as FormMessage } from './Form/Message.js'
11
9
  export { default as FormInput } from './Form/Input.js'
12
10
 
13
11
  import UIForm from './Form/Form.js'
14
12
  export { UIForm, UIForm as UiForm }
15
13
 
16
- export { default as Error, CancelError } from './Error/index.js'
14
+ export { default as UiMessage } from './Message/Message.js'
15
+ export { default as FormMessage } from './Form/Message.js'
16
+ export { default as OutputMessage } from './Message/OutputMessage.js'
17
17
 
18
- export { default as UiAdapter } from './UiAdapter.js'
18
+ export { default as Error, CancelError } from './Error/index.js'
19
19
 
20
20
  // Flow — Yield-Based Universal UI Architecture
21
21
  export {
@@ -43,6 +43,9 @@ export { default as Flow } from './Flow.js'
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
45
  /** @typedef {import('./Intent.js').Intent} Intent */
46
+ /** @typedef {import('./Intent.js').ShowLevel} ShowLevel */
47
+ /** @typedef {import('./Intent.js').ShowIntent} ShowIntent */
48
+ /** @typedef {import('./Intent.js').ShowData} ShowData */
46
49
  /** @typedef {import('./Intent.js').AskResponse} AskResponse */
47
50
  /** @typedef {import('./InputAdapter.js').AskOptions} AskOptions */
48
51
  export { IntentErrorModel } from './IntentErrorModel.js'
@@ -0,0 +1,51 @@
1
+ import { Model, getMetadata } from '@nan0web/types'
2
+
3
+ /**
4
+ * Resolves positional CLI arguments into named model fields.
5
+ *
6
+ * Scans a Model class for `static` field descriptors with `positional: true`.
7
+ * The order of positional fields follows the declaration order of static properties
8
+ * (guaranteed by JavaScript spec for non-integer keys).
9
+ *
10
+ * @example
11
+ * class MyModel {
12
+ * static source = { help: 'Source path', default: '.', positional: true }
13
+ * static target = { help: 'Target path', default: 'out', positional: true }
14
+ * static quiet = { help: 'Quiet mode', default: false, type: 'boolean' }
15
+ * }
16
+ *
17
+ * const data = resolvePositionalArgs(MyModel, ['src/', 'dist/'])
18
+ * // → { source: 'src/', target: 'dist/' }
19
+ *
20
+ * @param {typeof Model} ModelClass - The Model class with static field descriptors.
21
+ * @param {string[]} args - Positional arguments from the CLI (e.g., process.argv positionals).
22
+ * @param {Object} [existing={}] - Existing named options (take priority over positionals).
23
+ * @returns {Object} Merged data object with positional args resolved to named fields.
24
+ */
25
+ export function resolvePositionalArgs(ModelClass, args = [], existing = {}) {
26
+ if (!args || !args.length) return { ...existing }
27
+
28
+ const metadata = getMetadata(ModelClass)
29
+ const positionalFields = []
30
+ for (const [key, descriptor] of Object.entries(metadata)) {
31
+ if (descriptor && typeof descriptor === 'object' && descriptor.positional === true) {
32
+ positionalFields.push(key)
33
+ }
34
+ }
35
+
36
+ const result = { ...existing }
37
+ for (let i = 0; i < positionalFields.length && i < args.length; i++) {
38
+ const field = positionalFields[i]
39
+ // Named options take priority over positionals
40
+ if (result[field] === undefined || result[field] === null) {
41
+ result[field] = args[i]
42
+ }
43
+ }
44
+
45
+ // Preserve remaining positional arguments for subcommands
46
+ if (args.length > positionalFields.length) {
47
+ result._positionals = args.slice(positionalFields.length)
48
+ }
49
+
50
+ return result
51
+ }
@@ -1,6 +1,6 @@
1
1
  import { Model } from '@nan0web/types'
2
2
 
3
- /**
3
+ /**
4
4
  * @typedef {Object} HTML5Elements
5
5
  * @property {string|ContentData[]} [a]
6
6
  * @property {string|ContentData[]} [abbr]
@@ -126,7 +126,7 @@ import { Model } from '@nan0web/types'
126
126
  * @property {string|ContentData[]} [text]
127
127
  */
128
128
 
129
- /**
129
+ /**
130
130
  * @typedef {Object} CoreUIElements
131
131
  * @property {import('./components/AccordionModel.js').AccordionModel} [accordion]
132
132
  * @property {import('./components/AutocompleteModel.js').AutocompleteModel} [autocomplete]
@@ -162,8 +162,8 @@ import { Model } from '@nan0web/types'
162
162
  * @property {ContentData[]} [sortable] - Інтерактивний Drag-n-Drop контейнер
163
163
  */
164
164
 
165
- /**
166
- * @typedef {Partial<Content & HTML5Elements & CoreUIElements> & Record<string, any>} ContentData
165
+ /**
166
+ * @typedef {Partial<Content & HTML5Elements & CoreUIElements> & Record<string, any>} ContentData
167
167
  */
168
168
 
169
169
  export class Content extends Model {
@@ -172,7 +172,7 @@ export class Content extends Model {
172
172
 
173
173
  /**
174
174
  * @param {ContentData | string} [data={}]
175
- * @param {import('@nan0web/types').ModelOptions} [options={}]
175
+ * @param {Partial<import('@nan0web/types').ModelOptions>} [options={}]
176
176
  */
177
177
  constructor(data = {}, options = {}) {
178
178
  if ('string' === typeof data) {
@@ -12,7 +12,7 @@ export class Document extends Model {
12
12
  /**
13
13
  *
14
14
  * @param {Partial<Document>} [data]
15
- * @param {import('@nan0web/types').ModelOptions} [options]
15
+ * @param {Partial<import('@nan0web/types').ModelOptions>} [options]
16
16
  */
17
17
  constructor(data = {}, options = {}) {
18
18
  super(data, options)
@@ -37,7 +37,7 @@ export class HeroModel extends Model {
37
37
 
38
38
  /**
39
39
  * @param {Partial<HeroModel | Record<string, any>>} [data={}]
40
- * @param {import('@nan0web/types').ModelOptions} [options={}]
40
+ * @param {Partial<import('@nan0web/types').ModelOptions>} [options={}]
41
41
  */
42
42
  constructor(data = {}, options = {}) {
43
43
  super(data, options)