@nan0web/ui 1.9.0 → 1.11.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 (108) hide show
  1. package/README.md +97 -12
  2. package/package.json +54 -25
  3. package/src/App/Command/DepsCommand.js +3 -4
  4. package/src/Frame/Props.js +12 -18
  5. package/src/InterfaceTemplate/InterfaceTemplate.js +9 -7
  6. package/src/Model/index.js +86 -2
  7. package/src/StdIn.js +2 -6
  8. package/src/cli.js +1 -0
  9. package/src/core/Form/Form.js +8 -7
  10. package/src/core/Form/Message.js +1 -1
  11. package/src/core/GeneratorRunner.js +77 -7
  12. package/src/core/InputAdapter.js +3 -1
  13. package/src/core/Intent.js +214 -16
  14. package/src/core/IntentErrorModel.js +6 -1
  15. package/src/core/Message/Message.js +4 -7
  16. package/src/core/Message/OutputMessage.js +4 -9
  17. package/src/core/Stream.js +16 -5
  18. package/src/core/StreamEntry.js +20 -28
  19. package/src/core/index.js +2 -1
  20. package/src/domain/Content.js +196 -0
  21. package/src/domain/Document.js +17 -0
  22. package/src/domain/FooterModel.js +37 -19
  23. package/src/domain/HeaderModel.js +47 -21
  24. package/src/domain/HeroModel.js +24 -22
  25. package/src/domain/LayoutModel.js +43 -0
  26. package/src/domain/ModelAsApp.js +46 -0
  27. package/src/domain/SandboxModel.js +19 -16
  28. package/src/domain/app/GalleryCommand.js +53 -0
  29. package/src/domain/app/GalleryRenderIntent.js +77 -0
  30. package/src/domain/app/SnapshotAuditor.js +401 -0
  31. package/src/domain/app/SnapshotRunner.js +264 -0
  32. package/src/domain/app/UIApp.js +78 -0
  33. package/src/domain/components/BreadcrumbModel.js +10 -6
  34. package/src/domain/components/FeatureGridModel.js +62 -0
  35. package/src/domain/components/MarkdownModel.js +24 -0
  36. package/src/domain/components/ShellModel.js +243 -0
  37. package/src/domain/components/TableModel.js +10 -6
  38. package/src/domain/components/ToastModel.js +10 -6
  39. package/src/domain/components/index.js +3 -1
  40. package/src/domain/index.js +14 -4
  41. package/src/index.js +21 -2
  42. package/src/inspect.js +2 -0
  43. package/src/test/ScenarioAdapter.js +59 -0
  44. package/src/test/ScenarioTest.js +51 -0
  45. package/src/test/ScenarioTest.story.js +56 -0
  46. package/src/testing/CrashReporter.js +56 -0
  47. package/src/testing/GalleryGenerator.js +29 -0
  48. package/src/testing/LogicInspector.js +55 -0
  49. package/src/testing/SnapshotRunner.js +22 -0
  50. package/src/testing/SpecAdapter.js +115 -0
  51. package/src/testing/SpecRunner.js +121 -0
  52. package/src/testing/VisualAdapter.js +46 -0
  53. package/src/testing/index.js +7 -0
  54. package/src/testing/verifySnapshot.js +17 -0
  55. package/types/App/Command/DepsCommand.d.ts +0 -2
  56. package/types/Model/index.d.ts +56 -4
  57. package/types/StdIn.d.ts +3 -3
  58. package/types/cli.d.ts +1 -0
  59. package/types/core/Form/Form.d.ts +2 -2
  60. package/types/core/GeneratorRunner.d.ts +18 -1
  61. package/types/core/InputAdapter.d.ts +2 -1
  62. package/types/core/Intent.d.ts +232 -26
  63. package/types/core/IntentErrorModel.d.ts +4 -0
  64. package/types/core/Message/Message.d.ts +2 -2
  65. package/types/core/Message/OutputMessage.d.ts +0 -2
  66. package/types/core/index.d.ts +2 -1
  67. package/types/domain/Content.d.ts +340 -0
  68. package/types/domain/Document.d.ts +21 -0
  69. package/types/domain/FooterModel.d.ts +22 -12
  70. package/types/domain/HeaderModel.d.ts +36 -13
  71. package/types/domain/HeroModel.d.ts +19 -17
  72. package/types/domain/LayoutModel.d.ts +34 -0
  73. package/types/domain/ModelAsApp.d.ts +23 -0
  74. package/types/domain/SandboxModel.d.ts +10 -0
  75. package/types/domain/app/GalleryCommand.d.ts +55 -0
  76. package/types/domain/app/GalleryRenderIntent.d.ts +31 -0
  77. package/types/domain/app/SnapshotAuditor.d.ts +99 -0
  78. package/types/domain/app/SnapshotRunner.d.ts +45 -0
  79. package/types/domain/app/UIApp.d.ts +60 -0
  80. package/types/domain/components/BreadcrumbModel.d.ts +6 -8
  81. package/types/domain/components/FeatureGridModel.d.ts +50 -0
  82. package/types/domain/components/MarkdownModel.d.ts +19 -0
  83. package/types/domain/components/ShellModel.d.ts +56 -0
  84. package/types/domain/components/TableModel.d.ts +4 -0
  85. package/types/domain/components/ToastModel.d.ts +4 -0
  86. package/types/domain/components/index.d.ts +3 -0
  87. package/types/domain/index.d.ts +10 -4
  88. package/types/index.d.ts +19 -1
  89. package/types/inspect.d.ts +2 -0
  90. package/types/test/ScenarioAdapter.d.ts +43 -0
  91. package/types/test/ScenarioTest.d.ts +24 -0
  92. package/types/test/ScenarioTest.story.d.ts +1 -0
  93. package/types/testing/CrashReporter.d.ts +13 -0
  94. package/types/testing/GalleryGenerator.d.ts +1 -0
  95. package/types/testing/LogicInspector.d.ts +22 -0
  96. package/types/testing/SnapshotRunner.d.ts +7 -0
  97. package/types/testing/SpecAdapter.d.ts +57 -0
  98. package/types/testing/SpecRunner.d.ts +41 -0
  99. package/types/testing/VisualAdapter.d.ts +9 -0
  100. package/types/testing/index.d.ts +7 -0
  101. package/types/testing/verifySnapshot.d.ts +14 -0
  102. package/src/README.md.js +0 -436
  103. package/types/App/Command/Options.d.ts +0 -43
  104. package/types/App/Command/index.d.ts +0 -8
  105. package/types/App/User/Command/Options.d.ts +0 -34
  106. package/types/core/Message/InputMessage.d.ts +0 -71
  107. package/types/domain/components/HeroModel.d.ts +0 -24
  108. package/types/domain/components/ShowcaseAppModel.d.ts +0 -32
@@ -0,0 +1,196 @@
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 {string|ContentData[]} [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 {string|ContentData[]} [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('./components/InputModel.js').InputModel} [input]
143
+ * @property {import('./components/MarkdownModel.js').MarkdownModel} [markdown]
144
+ * @property {import('./components/PriceModel.js').PriceModel} [price]
145
+ * @property {import('./components/PricingModel.js').PricingModel} [pricing]
146
+ * @property {import('./components/PricingSectionModel.js').PricingSectionModel} [pricingSection]
147
+ * @property {import('./components/ProfileDropdownModel.js').ProfileDropdownModel} [profileDropdown]
148
+ * @property {import('./components/SelectModel.js').SelectModel} [select]
149
+ * @property {import('./components/ShellModel.js').ShellModel} [shell]
150
+ * @property {import('./components/SpinnerModel.js').SpinnerModel} [spinner]
151
+ * @property {import('./components/StatsItemModel.js').StatsItemModel} [statsItem]
152
+ * @property {import('./components/StatsModel.js').StatsModel} [stats]
153
+ * @property {import('./components/TableModel.js').TableModel} [tableUI]
154
+ * @property {import('./components/TabsModel.js').TabsModel} [tabs]
155
+ * @property {import('./components/TestimonialModel.js').TestimonialModel} [testimonial]
156
+ * @property {import('./components/TimelineItemModel.js').TimelineItemModel} [timelineItem]
157
+ * @property {import('./components/TimelineModel.js').TimelineModel} [timeline]
158
+ * @property {import('./components/ToastModel.js').ToastModel} [toast]
159
+ * @property {import('./components/TreeModel.js').TreeModel} [tree]
160
+ * @property {ContentData[]} [sortable] - Інтерактивний Drag-n-Drop контейнер
161
+ */
162
+
163
+ /**
164
+ * @typedef {Partial<Content & HTML5Elements & CoreUIElements> & Record<string, any>} ContentData
165
+ */
166
+
167
+ export class Content extends Model {
168
+ static content = { type: 'string', help: 'Content' }
169
+ static children = { type: 'array', model: Content, help: 'Children' }
170
+
171
+ /**
172
+ * @param {ContentData | string} [data={}]
173
+ * @param {import('@nan0web/types').ModelOptions} [options={}]
174
+ */
175
+ constructor(data = {}, options = {}) {
176
+ if ('string' === typeof data) {
177
+ data = { content: data }
178
+ }
179
+ super(data, options)
180
+
181
+ // ── Base Fields ──
182
+ /** @type {string|undefined} Content */ this.content
183
+ /** @type {Array<Content>|undefined} Children */ this.children
184
+
185
+ // ── Hydration ──
186
+ if (Array.isArray(this.children)) {
187
+ this.children = this.children.map((child) => new Content(child, options))
188
+ }
189
+
190
+ const { content, children, ...rest } = /** @type {any} */ (data)
191
+
192
+ for (const [key, value] of Object.entries(rest)) {
193
+ /** @type {any} */ (this)[key] = value
194
+ }
195
+ }
196
+ }
@@ -0,0 +1,17 @@
1
+ import { Model } from '@nan0web/types'
2
+ import { Content } from './Content.js'
3
+
4
+ export class Document extends Model {
5
+ static title = { type: 'string', help: 'Title' }
6
+ static content = { type: 'array', model: Content, help: 'Content' }
7
+ /**
8
+ *
9
+ * @param {Partial<Document>} [data]
10
+ * @param {import('@nan0web/types').ModelOptions} [options]
11
+ */
12
+ constructor(data = {}, options = {}) {
13
+ super(data, options)
14
+ /** @type {string} Title */ this.title
15
+ /** @type {Array<Content>} Content */ this.content
16
+ }
17
+ }
@@ -1,30 +1,31 @@
1
1
  import { Model } from '@nan0web/types'
2
+ import { Language } from '@nan0web/i18n'
2
3
  import Navigation from './Navigation.js'
3
4
 
4
5
  /**
5
- * FooterModel — OLMUI Model-as-Schema
6
- * Universal footer structure: copyright, version, license, navigation, sharing, languages.
6
+ * FooterModel — OLMUI Component Model
7
+ * Universal footer structure.
7
8
  */
8
- export default class FooterModel extends Model {
9
+ export class FooterModel extends Model {
9
10
  static $id = '@nan0web/ui/FooterModel'
10
11
 
11
12
  static copyright = {
12
- help: 'Copyright text',
13
- placeholder: '© 2026 Company',
13
+ help: 'Copyright notice text',
14
+ placeholder: '© 2026 My Site',
14
15
  default: '',
15
16
  }
16
17
  static version = {
17
- help: 'Application version string',
18
+ help: 'App version to display',
18
19
  placeholder: '1.0.0',
19
20
  default: '',
20
21
  }
21
22
  static license = {
22
- help: 'License type',
23
+ help: 'License name or link',
23
24
  placeholder: 'ISC',
24
25
  default: '',
25
26
  }
26
27
  static nav = {
27
- help: 'Footer navigation links',
28
+ help: 'Terms of Service, Privacy Policy, etc.',
28
29
  type: 'Navigation[]',
29
30
  hint: Navigation,
30
31
  default: [],
@@ -35,23 +36,40 @@ export default class FooterModel extends Model {
35
36
  hint: Navigation,
36
37
  default: [],
37
38
  }
39
+ static lang = {
40
+ help: 'Active language',
41
+ default: null,
42
+ }
38
43
  static langs = {
39
- help: 'Available languages for switcher',
44
+ help: 'Languages for switcher',
40
45
  type: 'Language[]',
46
+ hint: Language,
41
47
  default: [],
42
48
  }
43
49
 
44
50
  /**
45
- * @param {Partial<FooterModel> | Record<string, any>} data Model input data.
46
- * @param {object} [options] Extended options (db, etc.)
51
+ * @param {Partial<FooterModel>} data
47
52
  */
48
- constructor(data = {}, options = {}) {
49
- super(data, options)
50
- /** @type {string} Copyright text */ this.copyright
51
- /** @type {string} Application version string */ this.version
52
- /** @type {string} License type */ this.license
53
- /** @type {Navigation[]} Footer navigation links */ this.nav
54
- /** @type {Navigation[]} Social sharing links */ this.share
55
- /** @type {any[]} Available languages for switcher */ this.langs
53
+ constructor(data = {}) {
54
+ super(data)
55
+ /** @type {string} */ this.copyright
56
+ /** @type {string} */ this.version
57
+ /** @type {string} */ this.license
58
+ /** @type {Navigation[]} */
59
+ this.nav = this.#map(this.nav)
60
+ /** @type {Navigation[]} */
61
+ this.share = this.#map(this.share)
62
+ /** @type {Language|null} */
63
+ this.lang = this.lang && !(this.lang instanceof Language) ? new Language(this.lang) : this.lang
64
+ /** @type {Language[]} */
65
+ this.langs = Array.isArray(this.langs)
66
+ ? this.langs.map((l) => (l instanceof Language ? l : new Language(l)))
67
+ : []
68
+ }
69
+
70
+ #map(arr) {
71
+ return Array.isArray(arr)
72
+ ? arr.map((i) => (i instanceof Navigation ? i : new Navigation(i)))
73
+ : []
56
74
  }
57
75
  }
@@ -2,49 +2,75 @@ import { Model } from '@nan0web/types'
2
2
  import Navigation from './Navigation.js'
3
3
 
4
4
  /**
5
- * HeaderModel — OLMUI Model-as-Schema
6
- * Universal header structure: logo, navigation, language switcher, actions.
5
+ * HeaderModel — OLMUI Component Model
6
+ * Universal header structure with logo, navigation, and language controls.
7
7
  */
8
- export default class HeaderModel extends Model {
8
+ export class HeaderModel extends Model {
9
9
  static $id = '@nan0web/ui/HeaderModel'
10
10
 
11
11
  static title = {
12
- help: 'Site or app title displayed in the header',
13
- placeholder: 'My App',
12
+ help: 'Header title text',
13
+ placeholder: 'My Site',
14
14
  default: '',
15
15
  }
16
16
  static logo = {
17
- help: 'Logo image URL or icon name',
18
- placeholder: 'https://...',
17
+ help: 'Primary logo path',
18
+ placeholder: 'logo.svg',
19
19
  default: '',
20
20
  }
21
+ static logoDark = {
22
+ help: 'Dark theme logo path',
23
+ placeholder: 'logo-dark.svg',
24
+ default: '',
25
+ }
26
+ static nav = {
27
+ help: 'Main navigation links',
28
+ type: 'Navigation[]',
29
+ hint: Navigation,
30
+ default: [],
31
+ }
21
32
  static actions = {
22
- help: 'Header action links (CTA, Sign In, etc.)',
33
+ help: 'CTA or specific header actions',
34
+ type: 'Navigation[]',
35
+ hint: Navigation,
36
+ default: [],
37
+ }
38
+ static share = {
39
+ help: 'Social sharing or utility links',
23
40
  type: 'Navigation[]',
24
41
  hint: Navigation,
25
42
  default: [],
26
43
  }
27
44
  static lang = {
28
- help: 'Currently active language',
29
- type: 'Language',
45
+ help: 'Active language code or object',
30
46
  default: null,
31
47
  }
32
48
  static langs = {
33
- help: 'Available languages for switcher',
34
- type: 'Language[]',
49
+ help: 'Available languages',
35
50
  default: [],
36
51
  }
37
52
 
38
53
  /**
39
- * @param {Partial<HeaderModel> | Record<string, any>} data Model input data.
40
- * @param {object} [options] Extended options (db, etc.)
54
+ * @param {Partial<HeaderModel>} data
41
55
  */
42
- constructor(data = {}, options = {}) {
43
- super(data, options)
44
- /** @type {string} Site or app title displayed in the header */ this.title
45
- /** @type {string} Logo image URL or icon name */ this.logo
46
- /** @type {Navigation[]} Header action links (CTA, Sign In, etc.) */ this.actions
47
- /** @type {any|null} Currently active language */ this.lang
48
- /** @type {any[]} Available languages for switcher */ this.langs
56
+ constructor(data = {}) {
57
+ super(data)
58
+ /** @type {string} */ this.title
59
+ /** @type {string} */ this.logo
60
+ /** @type {string} */ this.logoDark
61
+ /** @type {Navigation[]} */
62
+ this.nav = this.#map(this.nav)
63
+ /** @type {Navigation[]} */
64
+ this.actions = this.#map(this.actions)
65
+ /** @type {Navigation[]} */
66
+ this.share = this.#map(this.share)
67
+ /** @type {any} */
68
+ this.lang = this.lang
69
+ /** @type {any[]} */
70
+ this.langs = Array.isArray(this.langs) ? this.langs : []
71
+ }
72
+
73
+ #map(arr) {
74
+ return Array.isArray(arr) ? arr.map(i => i instanceof Navigation ? i : new Navigation(i)) : []
49
75
  }
50
76
  }
@@ -2,47 +2,49 @@ import { Model } from '@nan0web/types'
2
2
  import Navigation from './Navigation.js'
3
3
 
4
4
  /**
5
- * HeroModel — OLMUI Model-as-Schema
6
- * Universal hero/banner section for landing pages.
7
- * Uses Navigation[] for actions instead of a single CTA.
5
+ * HeroModel — OLMUI Component Model
6
+ * Represents the top presentation section of a page.
8
7
  */
9
- export default class HeroModel extends Model {
8
+ export class HeroModel extends Model {
10
9
  static $id = '@nan0web/ui/HeroModel'
11
10
 
11
+ static badge = {
12
+ help: 'Top small badge text or icon',
13
+ placeholder: 'v1.4.0 out now',
14
+ default: '',
15
+ }
12
16
  static title = {
13
- help: 'Hero main headline',
14
- placeholder: 'Welcome to Our Platform',
17
+ help: 'Hero heading',
18
+ placeholder: 'Build Amazing Apps',
15
19
  default: '',
16
20
  required: true,
17
21
  }
18
- static description = {
19
- help: 'Hero sub-headline or description text',
20
- placeholder: 'Build something amazing...',
22
+ static subtitle = {
23
+ help: 'Hero secondary text',
24
+ placeholder: 'The open architecture for the next web.',
21
25
  default: '',
22
26
  }
23
- static image = {
24
- help: 'Hero background or feature image URL',
25
- placeholder: 'https://...',
26
- hint: 'image',
27
- upload: true,
27
+ static code = {
28
+ help: 'Code snippet or secondary markup',
28
29
  default: '',
29
30
  }
30
31
  static actions = {
31
- help: 'Call-to-action buttons (multiple CTA support)',
32
+ help: 'CTA buttons',
32
33
  type: 'Navigation[]',
33
- hint: Navigation,
34
+ model: Navigation,
34
35
  default: [],
35
36
  }
36
37
 
37
38
  /**
38
- * @param {Partial<HeroModel> | Record<string, any>} data Model input data.
39
- * @param {object} [options] Extended options (db, etc.)
39
+ * @param {Partial<HeroModel | Record<string, any>>} [data={}]
40
+ * @param {import('@nan0web/types').ModelOptions} [options={}]
40
41
  */
41
42
  constructor(data = {}, options = {}) {
42
43
  super(data, options)
43
- /** @type {string} Hero main headline */ this.title
44
- /** @type {string} Hero sub-headline or description text */ this.description
45
- /** @type {string} Hero background or feature image URL */ this.image
46
- /** @type {Navigation[]} Call-to-action buttons (multiple CTA support) */ this.actions
44
+ /** @type {string} Top small badge text ior icon */ this.badge
45
+ /** @type {string} Hero heading */ this.title
46
+ /** @type {string} Hero secondary text */ this.subtitle
47
+ /** @type {string} Code snippet or secondary markup */ this.code
48
+ /** @type {Navigation[]} CTA buttons */ this.actions
47
49
  }
48
50
  }
@@ -0,0 +1,43 @@
1
+ import { Model } from '@nan0web/types'
2
+
3
+ /**
4
+ * @typedef {'flow'|'sticky-bottom'|'sticky-top'|'dialog-modal'|'spatial-xyz'} LayoutType
5
+ */
6
+
7
+ /**
8
+ * Universal Layout Interface for OLMUI.
9
+ * Defines the semantic spatial placement of a component in any renderer.
10
+ */
11
+ export class LayoutModel extends Model {
12
+ static type = {
13
+ help: 'Base layout type',
14
+ default: 'flow',
15
+ options: [
16
+ { value: 'flow', label: 'Inline Flow' },
17
+ { value: 'sticky-bottom', label: 'Sticky Bottom' },
18
+ { value: 'sticky-top', label: 'Sticky Top' },
19
+ { value: 'dialog-modal', label: 'Modal / Dialog' },
20
+ { value: 'spatial-xyz', label: 'Spatial XYZ Coordinates' }
21
+ ]
22
+ }
23
+
24
+ static coordinates = {
25
+ help: 'Physical, screen, or audio coordinates if type requires it',
26
+ default: null,
27
+ type: 'object'
28
+ }
29
+
30
+ /**
31
+ * Creates a new LayoutModel instance to define spatial placement.
32
+ * @param {Partial<LayoutModel> | Record<string, any>} [data] Input model data.
33
+ * @param {object} [options] Model options.
34
+ */
35
+ constructor(data = {}, options = {}) {
36
+ super(data, options)
37
+ /** @type {LayoutType} Base layout type */
38
+ this.type
39
+
40
+ /** @type {object | null} Configuration or specific layout parameters */
41
+ this.coordinates
42
+ }
43
+ }
@@ -0,0 +1,46 @@
1
+ import { Model } from '@nan0web/types'
2
+ import { result } from '../core/Intent.js'
3
+ import { InputAdapter } from '../core/InputAdapter.js'
4
+
5
+ /** @typedef {import('@nan0web/types').ModelOptions & { adapter: InputAdapter }} ModelAsAppOptions */
6
+
7
+ /**
8
+ * The model with a run generator.
9
+ */
10
+ export class ModelAsApp extends Model {
11
+ /** @type {ModelAsAppOptions} */
12
+ #appOptions = {
13
+ t: (key) => key,
14
+ plugins: {},
15
+ adapter: null,
16
+ }
17
+ /**
18
+ * @param {Partial<ModelAsApp> | Record<string, any>} [data={}]
19
+ * @param {ModelAsAppOptions} [options={}]
20
+ */
21
+ constructor(data = {}, options = {}) {
22
+ super(data, options)
23
+ this.#appOptions = {
24
+ ...options,
25
+ t:
26
+ options.t ||
27
+ ((key, props = {}) =>
28
+ String(key)
29
+ .replace(/{(\w+)}/g, (_, x) => props[x] ?? `{${x}}`)
30
+ .replace(/_/g, ' ')),
31
+ plugins: options.plugins || {},
32
+ adapter: options.adapter || new InputAdapter(),
33
+ }
34
+ }
35
+
36
+ /** @returns {ModelAsAppOptions} */
37
+ get _() {
38
+ return this.#appOptions
39
+ }
40
+ /**
41
+ * @returns {AsyncGenerator<import('@nan0web/ui').Intent, import('@nan0web/ui').ResultIntent, any>}
42
+ */
43
+ async *run() {
44
+ return result({})
45
+ }
46
+ }
@@ -1,5 +1,6 @@
1
1
  import { Model } from '@nan0web/types'
2
2
  import { BreadcrumbModel } from './components/BreadcrumbModel.js'
3
+ import { show, result, ask } from '../core/Intent.js'
3
4
 
4
5
  /**
5
6
  * SandboxModel — OLMUI Model-as-Schema
@@ -8,8 +9,19 @@ import { BreadcrumbModel } from './components/BreadcrumbModel.js'
8
9
  export class SandboxModel extends Model {
9
10
  static $id = '@nan0web/ui/SandboxModel'
10
11
 
12
+ static UI = {
13
+ breadcrumb: '\n{path}',
14
+ componentsHelp: 'List of registered UI components available for inspection',
15
+ selectedComponentHelp: 'The component currently being inspected in the sandbox',
16
+ selectedComponentPlaceholder: 'Button',
17
+ themeFormatHelp: 'The file format chosen to export the custom theme configuration',
18
+ selectComponentHelp: 'Select a component to inspect',
19
+ configurePropertiesHelp: 'Configure {component} properties',
20
+ exportFormatHelp: 'Choose export format',
21
+ }
22
+
11
23
  static components = {
12
- help: 'List of registered UI components available for inspection',
24
+ help: SandboxModel.UI.componentsHelp,
13
25
  type: 'string[]',
14
26
  default: [],
15
27
  }
@@ -52,13 +64,10 @@ export class SandboxModel extends Model {
52
64
 
53
65
  if (!this.selectedComponent) {
54
66
  // Show breadcrumb
55
- yield {
56
- type: 'log',
57
- level: 'info',
58
- message: `\n${nav}`,
67
+ yield show(this._.t(SandboxModel.UI.breadcrumb, { path: String(nav) }), 'info', {
59
68
  component: 'Breadcrumbs',
60
69
  model: nav,
61
- }
70
+ })
62
71
 
63
72
  const response = yield {
64
73
  type: 'ask',
@@ -79,13 +88,10 @@ export class SandboxModel extends Model {
79
88
  // 2. Component Configuration Mode
80
89
  let configResponse
81
90
  try {
82
- yield {
83
- type: 'log',
84
- level: 'info',
85
- message: `\n${nav}`,
91
+ yield show(this._.t(SandboxModel.UI.breadcrumb, { path: String(nav) }), 'info', {
86
92
  component: 'Breadcrumbs',
87
93
  model: nav,
88
- }
94
+ })
89
95
 
90
96
  configResponse = yield {
91
97
  type: 'ask',
@@ -107,13 +113,10 @@ export class SandboxModel extends Model {
107
113
  // 3. Theme Export Mode
108
114
  try {
109
115
  nav.push('Export', 'export')
110
- yield {
111
- type: 'log',
112
- level: 'info',
113
- message: `\n${nav}`,
116
+ yield show(this._.t(SandboxModel.UI.breadcrumb, { path: String(nav) }), 'info', {
114
117
  component: 'Breadcrumbs',
115
118
  model: nav,
116
- }
119
+ })
117
120
 
118
121
  const themeResponse = yield {
119
122
  type: 'ask',