@nan0web/ui 1.0.3 → 1.0.4

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 (41) hide show
  1. package/README.md +18 -20
  2. package/package.json +15 -16
  3. package/src/App/Core/UI.js +0 -46
  4. package/src/App/Core/Widget.js +4 -6
  5. package/src/App/User/Command/Message.js +23 -37
  6. package/src/App/User/Command/index.js +3 -8
  7. package/src/App/User/UserApp.js +30 -10
  8. package/src/README.md.js +33 -33
  9. package/src/StdIn.js +12 -13
  10. package/src/View/View.js +5 -5
  11. package/src/core/Form/Form.js +8 -6
  12. package/src/core/Form/Input.js +1 -1
  13. package/src/core/Form/Message.js +6 -5
  14. package/src/core/InputAdapter.js +2 -2
  15. package/src/core/Message/Message.js +109 -19
  16. package/src/core/Message/OutputMessage.js +7 -7
  17. package/src/core/Message/index.js +3 -4
  18. package/src/core/Stream.js +10 -10
  19. package/src/core/UiAdapter.js +189 -0
  20. package/src/core/index.js +3 -6
  21. package/src/index.js +4 -4
  22. package/types/App/Core/UI.d.ts +0 -10
  23. package/types/App/Core/Widget.d.ts +6 -7
  24. package/types/App/User/Command/Message.d.ts +15 -28
  25. package/types/App/User/Command/index.d.ts +3 -4
  26. package/types/App/User/UserApp.d.ts +14 -7
  27. package/types/StdIn.d.ts +13 -13
  28. package/types/View/View.d.ts +6 -6
  29. package/types/core/Form/Form.d.ts +2 -5
  30. package/types/core/Form/Input.d.ts +1 -1
  31. package/types/core/Form/Message.d.ts +5 -10
  32. package/types/core/Intent.d.ts +91 -0
  33. package/types/core/Message/Message.d.ts +58 -15
  34. package/types/core/Message/OutputMessage.d.ts +3 -3
  35. package/types/core/Message/index.d.ts +3 -4
  36. package/types/core/Stream.d.ts +5 -4
  37. package/types/core/UiAdapter.d.ts +104 -0
  38. package/types/core/index.d.ts +2 -3
  39. package/types/index.d.ts +4 -4
  40. package/src/App/User/Command/Options.js +0 -48
  41. package/src/core/Message/InputMessage.js +0 -119
package/README.md CHANGED
@@ -41,8 +41,7 @@ yarn add @nan0web/ui
41
41
 
42
42
  UI communication is built around messages:
43
43
 
44
- - **`UIMessage`** – abstract message base class
45
- - **`InputMessage`** – user input message (value, options)
44
+ - **`UiMessage`** – abstract message base class
46
45
  - **`OutputMessage`** – system output (content, error, priority)
47
46
 
48
47
  Messages are simple, serializable data containers. They help build
@@ -51,14 +50,14 @@ decoupled communication systems between UI components.
51
50
  How to create input and output messages?
52
51
  ```js
53
52
  import { InputMessage, OutputMessage } from '@nan0web/ui'
54
- const input = InputMessage.from({ value: 'Hello User' })
53
+ const input = UiMessage.from({ body: 'Hello User' })
55
54
  const output = OutputMessage.from({ content: ['Welcome to @nan0web/ui'] })
56
- console.info(input.value) // ← Message { body: "Hello User", head: {} }
57
- console.info(output.content[0]) // ← Welcome to @nan0web/ui
55
+ console.info(input) // ← Message { body: "Hello User", head: {}, id: "....", type: "" }
56
+ console.info(String(output)) // ← Welcome to @nan0web/ui
58
57
  ```
59
58
  ### Forms
60
59
 
61
- `UIForm` supports field definitions, data management, and schema validation.
60
+ `UiForm` supports field definitions, data management, and schema validation.
62
61
  Every form includes a title, fields, and current state.
63
62
 
64
63
  Field types include:
@@ -70,10 +69,10 @@ Field types include:
70
69
  - `checkbox`
71
70
  - `textarea`
72
71
 
73
- How to define and validate a UIForm?
72
+ How to define and validate a UiForm?
74
73
  ```js
75
- import { UIForm } from '@nan0web/ui'
76
- const form = new UIForm({
74
+ import { UiForm } from '@nan0web/ui'
75
+ const form = new UiForm({
77
76
  title: "Contact Form",
78
77
  fields: [
79
78
  FormInput.from({ name: "email", label: "Email Address", type: "email", required: true }),
@@ -84,9 +83,9 @@ const form = new UIForm({
84
83
  message: "Hello!"
85
84
  }
86
85
  })
87
- const result = form.validate()
88
- console.info(result.isValid) // ← false
89
- console.info(result.errors.email) // ← Invalid email format
86
+ const errors = form.validate()
87
+ console.info(errors.size) // ← 1
88
+ console.info(errors.get("email")) // ← Invalid email format
90
89
  ```
91
90
  ### Components
92
91
 
@@ -164,19 +163,18 @@ with minimal setup.
164
163
 
165
164
  How to test UI components with assertions?
166
165
  ```js
167
- import { Welcome, InputMessage } from '@nan0web/ui'
166
+ import { Welcome } from '@nan0web/ui'
168
167
  const output = Welcome({ user: { name: "Test" } })
169
- const input = InputMessage.from({ value: "test" })
170
- console.log(output[0].join("")) // ← Welcome Test!
168
+ console.info(output) // Welcome Test!
171
169
  ```
172
170
  ## Playground Demos
173
171
 
174
172
  The library includes rich playground demos:
175
173
 
176
- - [Registration Form](./playground/registration.form.js)
177
- - [Currency Exchange](./playground/currency.exchange.js)
178
- - [Mobile Top-up](./playground/topup.telephone.js)
179
- - [Language Selector](./playground/language.form.js)
174
+ - [Registration Form](./play/registration.form.js)
175
+ - [Currency Exchange](./play/currency.exchange.js)
176
+ - [Mobile Top-up](./play/topup.telephone.js)
177
+ - [Language Selector](./play/language.form.js)
180
178
 
181
179
  Run to explore live functionality:
182
180
 
@@ -186,7 +184,7 @@ How to run the playground?
186
184
  git clone https://github.com/nan0web/ui.git
187
185
  cd ui
188
186
  npm install
189
- npm run playground
187
+ npm run play
190
188
  ```
191
189
 
192
190
  ## API Documentation
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nan0web/ui",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "NaN•Web UI. One application logic (algorithm) and many UI.",
5
5
  "main": "src/index.js",
6
6
  "types": "types/index.d.ts",
@@ -12,7 +12,7 @@
12
12
  ],
13
13
  "scripts": {
14
14
  "build": "tsc",
15
- "playground": "node playground/main.js",
15
+ "play": "node play/main.js",
16
16
  "test": "node --test --test-timeout=3333 \"src/**/*.test.js\"",
17
17
  "test:nan0test": "node --test --test-timeout=3333 \"src/**/*.test.js\" | nan0test parse --fail",
18
18
  "test:coverage": "node --experimental-test-coverage --test-coverage-include=\"src/**/*.js\" --test-coverage-exclude=\"src/**/*.test.js\" --test \"src/**/*.test.js\"",
@@ -20,6 +20,8 @@
20
20
  "test:docs": "node --test src/README.md.js",
21
21
  "test:release": "node --test \"releases/**/*.test.js\"",
22
22
  "test:status": "nan0test status --hide-name",
23
+ "test:play": "node --test --test-timeout=3333 \"play/**/*.test.js\"",
24
+ "test:all": "npm run test && npm run test:docs && npm run test:play && npm run build",
23
25
  "precommit": "npm test",
24
26
  "prepush": "npm test",
25
27
  "prepare": "husky",
@@ -52,23 +54,20 @@
52
54
  "license": "ISC",
53
55
  "packageManager": "pnpm@10.11.0",
54
56
  "devDependencies": {
55
- "@nan0web/co": "workspace:*",
56
- "@nan0web/event": "workspace:*",
57
- "@nan0web/i18n": "workspace:*",
58
- "@nan0web/release": "workspace:*",
59
- "@nan0web/test": "workspace:*",
60
- "@nan0web/types": "workspace:*",
61
- "@nan0web/ui-cli": "workspace:*",
57
+ "@nan0web/event": "^1.0.0",
58
+ "@nan0web/i18n": "^1.0.1",
59
+ "@nan0web/release": "1.0.1",
60
+ "@nan0web/test": "1.1.0",
61
+ "@nan0web/ui-cli": "1.0.2",
62
62
  "@vitest/coverage-v8": "^3.2.4",
63
- "vitest": "^3.2.4",
64
- "husky": "^9.1.7"
63
+ "husky": "^9.1.7",
64
+ "vitest": "^3.2.4"
65
65
  },
66
66
  "dependencies": {
67
+ "@nan0web/co": "^2.0.0",
68
+ "@nan0web/event": "^1.0.0",
69
+ "@nan0web/types": "^1.2.0",
67
70
  "string-width": "^7.2.0"
68
71
  },
69
- "peerDependencies": {
70
- "@nan0web/co": "^1.0.0",
71
- "@nan0web/event": "^1.0.0",
72
- "@nan0web/types": "^1.0.0"
73
- }
72
+ "peerDependencies": {}
74
73
  }
@@ -35,52 +35,6 @@ class UI extends Widget {
35
35
  throw new Error("convertInput must be implemented by subclass")
36
36
  }
37
37
 
38
- /**
39
- * Process input, run commands on app, and output results.
40
- * Supports progress callback.
41
- * @emits {start} Emitted when processing begins
42
- * @emits {data} Emitted for each command being processed
43
- * @emits {end} Emitted when all commands have been processed
44
- * @param {any} rawInput - Raw input to process
45
- * @returns {Promise<any[]>} Results of command processing
46
- */
47
- async process(rawInput) {
48
- const commands = this.convertInput(rawInput).filter(notEmpty)
49
- const results = []
50
- let count = commands.length
51
-
52
- const proc = this.view.get("UIProcess")
53
- if (proc) {
54
- this.show(proc)
55
- }
56
-
57
- if (0 === count && this.app.selectCommand) {
58
- const answer = await this.app.selectCommand(this)
59
- if (answer) {
60
- const [input] = this.convertInput(rawInput)
61
- const cmd = new Message({
62
- argv: [answer],
63
- opts: input?.opts ?? {},
64
- })
65
- commands.push(cmd)
66
- ++count
67
- } else {
68
- this.emit("end", { commands, results })
69
- return results
70
- }
71
- }
72
- this.emit("start", { commands })
73
- for (let i = 0; i < count; i++) {
74
- const command = commands[i]
75
- this.emit("data", { i, count, command })
76
- const result = await this.app.processCommand(command, this)
77
- results.push(result)
78
- }
79
- this.emit("end", { commands, results })
80
- // this.output(results)
81
- return results
82
- }
83
-
84
38
  /**
85
39
  * Sets up event handlers for UI process events.
86
40
  * @param {ComponentFn} UIProcess - Process view component
@@ -1,7 +1,7 @@
1
1
  import EventProcessor from "@nan0web/event/oop"
2
2
  import View from "../../View/View.js"
3
- import InputMessage from "../../core/Message/InputMessage.js"
4
3
  import { StreamEntry } from "@nan0web/db"
4
+ import { UiMessage } from "../../core/index.js"
5
5
 
6
6
  /** @typedef {import("./UI.js").ComponentFn} ComponentFn */
7
7
 
@@ -10,7 +10,7 @@ import { StreamEntry } from "@nan0web/db"
10
10
  * Widget is a view with ability to input data in a specific format.
11
11
  * Input and output data are typed classes.
12
12
  */
13
- class Widget extends EventProcessor {
13
+ export default class Widget extends EventProcessor {
14
14
  /** @type {View} The view associated with this widget */
15
15
  view
16
16
 
@@ -25,8 +25,8 @@ class Widget extends EventProcessor {
25
25
 
26
26
  /**
27
27
  * Ask user for input data of specific class.
28
- * @param {InputMessage} input - instance of InputMessage or similar
29
- * @returns {Promise<InputMessage | null>} instance of InputMessage or null
28
+ * @param {UiMessage} input - instance of UiMessage or similar
29
+ * @returns {Promise<UiMessage | null>} instance of UiMessage or null
30
30
  */
31
31
  async ask(input) {
32
32
  return await this.view.ask(input)
@@ -63,5 +63,3 @@ class Widget extends EventProcessor {
63
63
  return this.view.render(viewFn)(outputData)
64
64
  }
65
65
  }
66
-
67
- export default Widget
@@ -1,44 +1,30 @@
1
- import { CommandMessage } from "../../Command/index.js"
2
- import UserAppCommandOptions from "./Options.js"
1
+ import Message from "@nan0web/co"
2
+ import UIMessage from "../../../core/Message/Message.js"
3
3
 
4
- /**
5
- * Extends Command.Message to include user-specific command options.
6
- */
7
- class UserAppCommandMessage extends CommandMessage {
8
- /**
9
- * Creates a new UserAppCommandMessage instance.
10
- * @param {object} props - Command message properties
11
- * @param {string[]} [props.args=[]] - Command arguments
12
- * @param {Partial<UserAppCommandOptions>} [props.opts={}] - User-specific options
13
- */
14
- constructor(props = {}) {
15
- super(props)
4
+ class DepsCommandParams {
5
+ fix = false
6
+ static fix = {
7
+ help: "Fix dependencies",
8
+ defaultValue: false
16
9
  }
17
-
18
- /** @returns {UserAppCommandOptions} */
19
- get opts() {
20
- return UserAppCommandOptions.from(super.opts)
21
- }
22
-
23
- /**
24
- * @param {Partial<UserAppCommandOptions>} value
25
- */
26
- set opts(value) {
27
- super.opts = UserAppCommandOptions.from(value)
10
+ constructor(input = {}) {
11
+ const {
12
+ fix = this.fix
13
+ } = input
28
14
  }
15
+ }
29
16
 
30
- /**
31
- * Parses an array of strings into a UserAppCommandMessage.
32
- * @param {string[] | string} value - Arguments to parse
33
- * @returns {UserAppCommandMessage} Parsed command message
34
- */
35
- static parse(value = []) {
36
- if ("string" === typeof value) {
37
- value = value.split(" ")
38
- }
39
- const result = super.parse(value)
40
- return new this(result)
17
+ export class DepsCommand extends UIMessage {
18
+ static Body = DepsCommandParams
19
+ /** @type {DepsCommandParams} */
20
+ body
21
+ constructor(input = {}) {
22
+ const {
23
+ body = new DepsCommandParams()
24
+ } = UIMessage.parseBody(input, DepsCommandParams)
25
+ super(input)
26
+ this.body = body
41
27
  }
42
28
  }
43
29
 
44
- export default UserAppCommandMessage
30
+ export default DepsCommand
@@ -1,11 +1,6 @@
1
- import Command from "../../Command/index.js"
2
1
  import CommandMessage from "./Message.js"
3
- import CommandOptions from "./Options.js"
2
+ import DepsCommand from "./Message.js"
4
3
 
5
- export { CommandMessage, CommandOptions }
4
+ export { CommandMessage, DepsCommand }
6
5
 
7
- export default {
8
- ...Command,
9
- Message: CommandMessage,
10
- Options: CommandOptions,
11
- }
6
+ export default CommandMessage
@@ -3,14 +3,17 @@ import { notEmpty } from "@nan0web/types"
3
3
  import CoreApp from "../Core/CoreApp.js"
4
4
  import User from "../../Model/User/User.js"
5
5
  import UserUI from "./UserUI.js"
6
- import InputMessage from "../../core/Message/InputMessage.js"
6
+ import UserAppCommandMessage from "./Command/Message.js"
7
+ import DepsCommand from "./Command/Message.js"
8
+ import UIStream from "../../core/Stream.js"
9
+ import { UiMessage } from "../../core/index.js"
7
10
 
8
11
  /**
9
12
  * UserApp requires user name and shows Welcome view.
10
13
  * If user.name is provided in command input, ignores user input.
11
14
  * User can change user data to see another Welcome view.
12
15
  */
13
- class UserApp extends CoreApp {
16
+ export default class UserApp extends CoreApp {
14
17
  /**
15
18
  * Creates a new UserApp instance.
16
19
  * @param {Partial<CoreApp>} [props={}] - UserApp properties
@@ -19,16 +22,35 @@ class UserApp extends CoreApp {
19
22
  super(props)
20
23
  this.registerCommand("setUser", this.setUser.bind(this))
21
24
  this.registerCommand("welcome", this.welcome.bind(this))
25
+ this.registerCommand("deps", this.handleDeps.bind(this)) // Register new command
26
+ }
27
+
28
+ /**
29
+ * Handle deps command with async generator for stream processing.
30
+ * @param {DepsCommand} cmd - Command message with deps parameters
31
+ * @param {UserUI} ui - UI instance
32
+ * @returns {Promise<Object>} Command output
33
+ */
34
+ async handleDeps(cmd, ui) {
35
+ // Example: Use async generator to stream deps processing
36
+ const processorFn = async () => new UIStream.StreamEntry({ value: { message: `Deps command executed with fix: ${cmd.body.fix}` }, done: true })
37
+ const generatorFn = UIStream.createProcessor(new AbortController().signal, processorFn)
38
+ await UIStream.process(new AbortController().signal, generatorFn,
39
+ (progress, item) => ui.output && ui.output(item.value), // Fix to output the value
40
+ (error) => ui.output && ui.output({ error }), // Assume ui has output method
41
+ (item) => ui.output && ui.output(item.value) // Fix complete callback
42
+ )
43
+ return { completed: true }
22
44
  }
23
45
 
24
46
  /**
25
47
  * Set user data from params.
26
- * @param {Message} cmd - Command message with user data
48
+ * @param {UserAppCommandMessage} cmd - Command message with user data
27
49
  * @param {UserUI} ui - UI instance
28
50
  * @returns {Promise<{ message: string }>} Welcome message
29
51
  */
30
52
  async setUser(cmd, ui) {
31
- this.state.user = User.from(cmd.opts.user)
53
+ this.state.user = User.from(cmd.body.user) // cmd is UserAppCommandMessage, has user
32
54
  const frame = await this.welcome(cmd, ui)
33
55
  return {
34
56
  message: String(frame)
@@ -37,22 +59,20 @@ class UserApp extends CoreApp {
37
59
 
38
60
  /**
39
61
  * Show welcome message for current user.
40
- * @param {Message} cmd - Command message
62
+ * @param {UserAppCommandMessage} cmd - Command message
41
63
  * @param {UserUI} ui - UI instance
42
64
  * @returns {Promise<string[][]>} Welcome view output
43
65
  */
44
66
  async welcome(cmd, ui) {
45
- if (cmd.opts.user) {
46
- const user = User.from(cmd.opts.user)
67
+ if (cmd.body.user) { // cmd is UserAppCommandMessage, has user
68
+ const user = User.from(cmd.body.user)
47
69
  return ui.render("Welcome", { user })
48
70
  }
49
71
  if (notEmpty(this.user)) {
50
72
  return ui.render("Welcome", { user: this.user })
51
73
  }
52
- const answer = await ui.ask(InputMessage.from("What is your name?"))
74
+ const answer = await ui.ask(UiMessage.from("What is your name?"))
53
75
  this.user = User.from(answer?.value)
54
76
  return ui.render("Welcome", { user: this.user })
55
77
  }
56
78
  }
57
-
58
- export default UserApp
package/src/README.md.js CHANGED
@@ -8,17 +8,13 @@ import {
8
8
  runSpawn,
9
9
  } from "@nan0web/test"
10
10
  import {
11
- App,
12
- Component,
13
11
  Frame,
14
- InputMessage,
15
12
  Model,
16
13
  OutputMessage,
17
- UIMessage,
18
- UIForm,
19
- UIStream,
20
14
  View,
21
15
  FormInput,
16
+ UiMessage,
17
+ UiForm,
22
18
  } from "./index.js"
23
19
  import { Welcome } from "./Component/index.js"
24
20
 
@@ -108,8 +104,7 @@ function testRender() {
108
104
  *
109
105
  * UI communication is built around messages:
110
106
  *
111
- * - **`UIMessage`** – abstract message base class
112
- * - **`InputMessage`** – user input message (value, options)
107
+ * - **`UiMessage`** – abstract message base class
113
108
  * - **`OutputMessage`** – system output (content, error, priority)
114
109
  *
115
110
  * Messages are simple, serializable data containers. They help build
@@ -118,19 +113,22 @@ function testRender() {
118
113
  it("How to create input and output messages?", () => {
119
114
  //import { InputMessage, OutputMessage } from '@nan0web/ui'
120
115
 
121
- const input = InputMessage.from({ value: 'Hello User' })
116
+ const input = UiMessage.from({ body: 'Hello User' })
122
117
  const output = OutputMessage.from({ content: ['Welcome to @nan0web/ui'] })
123
- console.info(input.value) // ← Message { body: "Hello User", head: {} }
124
- console.info(output.content[0]) // ← Welcome to @nan0web/ui
125
- assert.deepStrictEqual({ ...console.output()[0][1] }, { body: "Hello User", head: {} })
126
- assert.equal(console.output()[1][1], 'Welcome to @nan0web/ui')
118
+ console.info(input) // ← Message { body: "Hello User", head: {}, id: "....", type: "" }
119
+ console.info(String(output)) // ← Welcome to @nan0web/ui
120
+ assert.deepStrictEqual(console.output()[0][1].body, "Hello User")
121
+ assert.deepStrictEqual(console.output()[0][1].head, {})
122
+ assert.deepStrictEqual(console.output()[0][1].type, "")
123
+ assert.ok(console.output()[0][1].id)
124
+ assert.ok(console.output()[1][1].endsWith('Welcome to @nan0web/ui'))
127
125
  })
128
126
 
129
127
  /**
130
128
  * @docs
131
129
  * ### Forms
132
130
  *
133
- * `UIForm` supports field definitions, data management, and schema validation.
131
+ * `UiForm` supports field definitions, data management, and schema validation.
134
132
  * Every form includes a title, fields, and current state.
135
133
  *
136
134
  * Field types include:
@@ -142,10 +140,10 @@ function testRender() {
142
140
  * - `checkbox`
143
141
  * - `textarea`
144
142
  */
145
- it("How to define and validate a UIForm?", () => {
146
- //import { UIForm } from '@nan0web/ui'
143
+ it("How to define and validate a UiForm?", () => {
144
+ //import { UiForm } from '@nan0web/ui'
147
145
 
148
- const form = new UIForm({
146
+ const form = new UiForm({
149
147
  title: "Contact Form",
150
148
  fields: [
151
149
  FormInput.from({ name: "email", label: "Email Address", type: "email", required: true }),
@@ -157,12 +155,12 @@ function testRender() {
157
155
  }
158
156
  })
159
157
 
160
- const result = form.validate()
161
- console.info(result.isValid) // ← false
162
- console.info(result.errors.email) // ← Invalid email format
158
+ const errors = form.validate()
159
+ console.info(errors.size) // ← 1
160
+ console.info(errors.get("email")) // ← Invalid email format
163
161
 
164
- assert.equal(result.isValid, false)
165
- assert.equal(result.errors.email, "Invalid email format")
162
+ assert.equal(console.output()[0][1], 1)
163
+ assert.equal(console.output()[1][1], "Invalid email format")
166
164
  })
167
165
 
168
166
  /**
@@ -277,13 +275,15 @@ function testRender() {
277
275
  * with minimal setup.
278
276
  */
279
277
  it("How to test UI components with assertions?", () => {
280
- //import { Welcome, InputMessage } from '@nan0web/ui'
278
+ //import { Welcome } from '@nan0web/ui'
281
279
 
282
280
  const output = Welcome({ user: { name: "Test" } })
283
- const input = InputMessage.from({ value: "test" })
284
- console.log(output[0].join("")) // ← Welcome Test!
285
- assert.equal(console.output()[0][1], "Welcome Test!")
286
- assert.ok(input instanceof InputMessage)
281
+ console.info(output) // Welcome Test!
282
+ assert.deepStrictEqual(console.output()[0][1], [
283
+ ["Welcome", " ", "Test", "!"],
284
+ ["What can we do today great?"],
285
+ [""],
286
+ ])
287
287
  })
288
288
 
289
289
  /**
@@ -292,10 +292,10 @@ function testRender() {
292
292
  *
293
293
  * The library includes rich playground demos:
294
294
  *
295
- * - [Registration Form](./playground/registration.form.js)
296
- * - [Currency Exchange](./playground/currency.exchange.js)
297
- * - [Mobile Top-up](./playground/topup.telephone.js)
298
- * - [Language Selector](./playground/language.form.js)
295
+ * - [Registration Form](./play/registration.form.js)
296
+ * - [Currency Exchange](./play/currency.exchange.js)
297
+ * - [Mobile Top-up](./play/topup.telephone.js)
298
+ * - [Language Selector](./play/language.form.js)
299
299
  *
300
300
  * Run to explore live functionality:
301
301
  */
@@ -306,10 +306,10 @@ function testRender() {
306
306
  * git clone https://github.com/nan0web/ui.git
307
307
  * cd ui
308
308
  * npm install
309
- * npm run playground
309
+ * npm run play
310
310
  * ```
311
311
  */
312
- assert.ok(String(pkg.scripts?.playground).includes("node playground"))
312
+ assert.ok(String(pkg.scripts?.play).includes("node play"))
313
313
  const response = await runSpawn("git", ["remote", "get-url", "origin"])
314
314
  assert.ok(response.code === 0, "git command fails (e.g., not in a git repo)")
315
315
  assert.ok(response.text.trim().endsWith(":nan0web/ui.git"))
package/src/StdIn.js CHANGED
@@ -1,20 +1,20 @@
1
1
  import EventProcessor from "@nan0web/event/oop"
2
2
  import { typeOf } from "@nan0web/types"
3
- import InputMessage from "./core/Message/InputMessage.js"
3
+ import { UiMessage } from "./core/index.js"
4
4
 
5
5
  class Processor extends EventProcessor { }
6
6
 
7
7
  /**
8
8
  * Handles standard input stream with message buffering.
9
9
  */
10
- class StdIn extends EventProcessor {
10
+ export default class StdIn extends EventProcessor {
11
11
  /** @type {number} Read interval in milliseconds */
12
12
  static READ_INTERVAL = 99
13
13
 
14
14
  /** @type {string[]} Messages to ignore */
15
15
  static IGNORE_MESSAGES = ["", "undefined"]
16
16
 
17
- /** @type {InputMessage[]} Input message buffer */
17
+ /** @type {UiMessage[]} Input message buffer */
18
18
  stream = []
19
19
 
20
20
  /** @type {Processor} Input processor */
@@ -24,7 +24,7 @@ class StdIn extends EventProcessor {
24
24
  * Creates a new StdIn instance.
25
25
  * @param {object} props - StdIn properties
26
26
  * @param {Processor} [props.processor] - Input processor
27
- * @param {InputMessage[]} [props.stream=[]] - Initial input stream
27
+ * @param {UiMessage[]} [props.stream=[]] - Initial input stream
28
28
  */
29
29
  constructor(props = {}) {
30
30
  super()
@@ -58,13 +58,13 @@ class StdIn extends EventProcessor {
58
58
  /**
59
59
  * Reads a message from the input stream.
60
60
  * Waits until messages are available if stream is empty.
61
- * @returns {Promise<InputMessage>} Next input message
61
+ * @returns {Promise<UiMessage>} Next input message
62
62
  */
63
63
  async read() {
64
64
  while (this.ended) {
65
65
  await new Promise(resolve => setTimeout(resolve, StdIn.READ_INTERVAL))
66
66
  }
67
- return this.stream.shift() ?? new InputMessage()
67
+ return this.stream.shift() ?? new UiMessage()
68
68
  }
69
69
 
70
70
  /**
@@ -83,16 +83,16 @@ class StdIn extends EventProcessor {
83
83
  }
84
84
 
85
85
  /**
86
- * Decodes a message into an InputMessage instance.
87
- * @param {InputMessage | string[] | any} message - Message to decode
88
- * @returns {InputMessage} Decoded input message
86
+ * Decodes a message into an UiMessage instance.
87
+ * @param {UiMessage | string[] | any} message - Message to decode
88
+ * @returns {UiMessage} Decoded input message
89
89
  */
90
90
  decode(message) {
91
- if (message instanceof InputMessage) return message
91
+ if (message instanceof UiMessage) return message
92
92
  if (Array.isArray(message) && message.every(typeOf(String))) {
93
- return new InputMessage({ value: message })
93
+ return new UiMessage({ value: message })
94
94
  }
95
- return new InputMessage(message)
95
+ return new UiMessage(message)
96
96
  }
97
97
 
98
98
  /**
@@ -106,4 +106,3 @@ class StdIn extends EventProcessor {
106
106
  }
107
107
  }
108
108
 
109
- export default StdIn
package/src/View/View.js CHANGED
@@ -3,13 +3,13 @@ import Frame, { FrameRenderMethod } from "../Frame/Frame.js"
3
3
  import Locale from "../Locale.js"
4
4
  import StdOut from "../StdOut.js"
5
5
  import StdIn from "../StdIn.js"
6
- import InputMessage from "../core/Message/InputMessage.js"
7
6
  import RenderOptions from "./RenderOptions.js"
7
+ import UiMessage from "../core/Message/Message.js"
8
8
 
9
9
  /**
10
10
  * @typedef {Object} ComponentFn
11
11
  * @property {string} name
12
- * @property {(input: InputMessage) => Promise<any>} ask
12
+ * @property {(input: UiMessage) => Promise<any>} ask
13
13
  * @property {Function} bind
14
14
  */
15
15
 
@@ -256,8 +256,8 @@ export default class View {
256
256
  }
257
257
 
258
258
  /**
259
- * @param {InputMessage} input
260
- * @returns {Promise<InputMessage | null>}
259
+ * @param {UiMessage} input
260
+ * @returns {Promise<UiMessage | null>}
261
261
  */
262
262
  async ask(input) {
263
263
  const name = input.constructor.name.replace(/Input$/, "")
@@ -268,7 +268,7 @@ export default class View {
268
268
  let result = null
269
269
  do {
270
270
  const answer = await this.stdin.read()
271
- result = /** @type {typeof InputMessage} */ (input.constructor).from(answer)
271
+ result = /** @type {typeof UiMessage} */ (input.constructor).from(answer)
272
272
  } while (!result.isValid && !result.escaped)
273
273
  return result.escaped ? null : result
274
274
  }