@nan0web/ui 1.0.4 → 1.3.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 (95) hide show
  1. package/README.md +19 -14
  2. package/package.json +6 -4
  3. package/src/App/Command/DepsCommand.js +25 -0
  4. package/src/App/Core/CoreApp.js +18 -17
  5. package/src/App/Core/UI.js +12 -19
  6. package/src/App/Core/Widget.js +6 -10
  7. package/src/App/Core/index.js +3 -3
  8. package/src/App/Scenario.js +4 -4
  9. package/src/App/User/Command/Message.js +2 -29
  10. package/src/App/User/Command/index.js +3 -4
  11. package/src/App/User/UserApp.js +30 -23
  12. package/src/App/User/UserUI.js +2 -2
  13. package/src/App/User/index.js +2 -2
  14. package/src/App/index.js +5 -10
  15. package/src/Component/Process/Input.js +10 -17
  16. package/src/Component/Process/Process.js +3 -5
  17. package/src/Component/Process/index.js +2 -2
  18. package/src/Component/SortableList/SortableList.js +100 -0
  19. package/src/Component/SortableList/index.js +3 -0
  20. package/src/Component/Welcome/Input.js +2 -4
  21. package/src/Component/Welcome/Welcome.js +5 -9
  22. package/src/Component/Welcome/index.js +2 -2
  23. package/src/Component/index.js +5 -3
  24. package/src/Frame/Frame.js +163 -146
  25. package/src/Frame/Props.js +20 -20
  26. package/src/Locale.js +17 -18
  27. package/src/Model/User/User.js +3 -6
  28. package/src/Model/index.js +1 -1
  29. package/src/README.md.js +84 -94
  30. package/src/StdIn.js +8 -12
  31. package/src/StdOut.js +23 -27
  32. package/src/View/RenderOptions.js +1 -1
  33. package/src/View/View.js +42 -38
  34. package/src/core/Error/CancelError.js +2 -2
  35. package/src/core/Error/index.js +3 -5
  36. package/src/core/Flow.js +347 -0
  37. package/src/core/Form/Form.js +35 -33
  38. package/src/core/Form/Input.js +29 -14
  39. package/src/core/Form/Message.js +3 -6
  40. package/src/core/Form/index.js +4 -8
  41. package/src/core/InputAdapter.js +4 -6
  42. package/src/core/Message/Message.js +9 -12
  43. package/src/core/Message/OutputMessage.js +19 -17
  44. package/src/core/Message/index.js +2 -2
  45. package/src/core/OutputAdapter.js +12 -10
  46. package/src/core/Stream.js +4 -3
  47. package/src/core/StreamEntry.js +2 -2
  48. package/src/core/UiAdapter.js +57 -29
  49. package/src/core/index.js +38 -9
  50. package/src/functions.js +8 -15
  51. package/src/index.js +21 -32
  52. package/types/App/Command/DepsCommand.d.ts +16 -0
  53. package/types/App/Command/Options.d.ts +37 -40
  54. package/types/App/Command/index.d.ts +6 -6
  55. package/types/App/Core/CoreApp.d.ts +2 -2
  56. package/types/App/Core/UI.d.ts +6 -7
  57. package/types/App/Core/Widget.d.ts +4 -4
  58. package/types/App/Core/index.d.ts +3 -3
  59. package/types/App/Scenario.d.ts +1 -1
  60. package/types/App/User/Command/Message.d.ts +2 -16
  61. package/types/App/User/Command/Options.d.ts +29 -29
  62. package/types/App/User/Command/index.d.ts +2 -3
  63. package/types/App/User/UserApp.d.ts +5 -5
  64. package/types/App/User/index.d.ts +2 -2
  65. package/types/App/index.d.ts +4 -4
  66. package/types/Component/Process/Process.d.ts +2 -2
  67. package/types/Component/Process/index.d.ts +2 -2
  68. package/types/Component/SortableList/SortableList.d.ts +58 -0
  69. package/types/Component/SortableList/index.d.ts +2 -0
  70. package/types/Component/Welcome/Input.d.ts +1 -1
  71. package/types/Component/Welcome/Welcome.d.ts +1 -1
  72. package/types/Component/Welcome/index.d.ts +2 -2
  73. package/types/Component/index.d.ts +5 -3
  74. package/types/Frame/Frame.d.ts +1 -1
  75. package/types/Frame/Props.d.ts +1 -1
  76. package/types/Model/index.d.ts +1 -1
  77. package/types/StdIn.d.ts +2 -2
  78. package/types/StdOut.d.ts +1 -1
  79. package/types/View/View.d.ts +7 -7
  80. package/types/core/Error/index.d.ts +1 -1
  81. package/types/core/Flow.d.ts +320 -0
  82. package/types/core/Form/Form.d.ts +2 -2
  83. package/types/core/Form/Input.d.ts +15 -4
  84. package/types/core/Form/Message.d.ts +1 -1
  85. package/types/core/Form/index.d.ts +3 -3
  86. package/types/core/InputAdapter.d.ts +2 -2
  87. package/types/core/Intent.d.ts +65 -68
  88. package/types/core/Message/InputMessage.d.ts +65 -65
  89. package/types/core/Message/Message.d.ts +1 -1
  90. package/types/core/Message/OutputMessage.d.ts +1 -1
  91. package/types/core/Message/index.d.ts +2 -2
  92. package/types/core/Stream.d.ts +1 -2
  93. package/types/core/UiAdapter.d.ts +22 -4
  94. package/types/core/index.d.ts +5 -2
  95. package/types/index.d.ts +10 -10
@@ -16,8 +16,9 @@
16
16
  * @property {string} type - Input type (text, email, number, select, etc.).
17
17
  * @property {boolean} required - Whether the field is required.
18
18
  * @property {string} placeholder - Placeholder text.
19
- * @property {Array<string>} options - Select options (if type is 'select').
20
- * @property {Function|null} validation - Custom validation function.
19
+ * @property {InputOptions} options - Select options (if type is 'select').
20
+ * @property {Function} validation - Custom validation function.
21
+ * @property {string} mask - Mask pattern (e.g. '###-###').
21
22
  * @property {*} defaultValue - Default value.
22
23
  */
23
24
  export default class FormInput {
@@ -27,7 +28,8 @@ export default class FormInput {
27
28
  /** @type {boolean} */ required = false
28
29
  /** @type {string} */ placeholder = ''
29
30
  /** @type {InputOptions} */ options = []
30
- /** @type {import("@nan0web/co").ValidateFn|null} */ validation = null
31
+ /** @type {Function} */ validation = () => true
32
+ /** @type {string} */ mask = ''
31
33
  /** @type {*} */ defaultValue = null
32
34
 
33
35
  /**
@@ -39,7 +41,14 @@ export default class FormInput {
39
41
  NUMBER: 'number',
40
42
  SELECT: 'select',
41
43
  CHECKBOX: 'checkbox',
42
- TEXTAREA: 'textarea'
44
+ TEXTAREA: 'textarea',
45
+ PASSWORD: 'password',
46
+ SECRET: 'secret',
47
+ MASK: 'mask',
48
+ CONFIRM: 'confirm',
49
+ TOGGLE: 'toggle',
50
+ MULTISELECT: 'multiselect',
51
+ AUTOCOMPLETE: 'autocomplete',
43
52
  }
44
53
 
45
54
  /**
@@ -52,7 +61,8 @@ export default class FormInput {
52
61
  * @param {boolean} [props.required=false] - Is required.
53
62
  * @param {string} [props.placeholder=''] - Placeholder.
54
63
  * @param {InputOptions} [props.options=[]] - Select options or async function to retrieve data with the search and page.
55
- * @param {Function} [props.validation=null] - Custom validation.
64
+ * @param {Function} [props.validation] - Custom validation.
65
+ * @param {string} [props.mask=''] - Mask pattern.
56
66
  * @param {*} [props.defaultValue=null] - Default value.
57
67
  */
58
68
  constructor(props) {
@@ -64,7 +74,8 @@ export default class FormInput {
64
74
  placeholder = this.placeholder,
65
75
  options = [],
66
76
  validation = this.validation,
67
- defaultValue = this.defaultValue
77
+ mask = '',
78
+ defaultValue = this.defaultValue,
68
79
  } = props
69
80
 
70
81
  if (!name) {
@@ -78,6 +89,7 @@ export default class FormInput {
78
89
  this.placeholder = String(placeholder)
79
90
  this.options = options
80
91
  this.validation = validation
92
+ this.mask = String(mask)
81
93
  this.defaultValue = defaultValue
82
94
 
83
95
  this.requireValidType()
@@ -85,12 +97,14 @@ export default class FormInput {
85
97
 
86
98
  requireValidType() {
87
99
  if (!Object.values(FormInput.TYPES).includes(this.type)) {
88
- throw new TypeError([
89
- "FormInput.type is invalid!",
90
- ["Provided", this.type].join(": "),
91
- "Available types:",
92
- ...Object.values(FormInput.TYPES).map(t => ` - ${t}`)
93
- ].join("\n"))
100
+ throw new TypeError(
101
+ [
102
+ 'FormInput.type is invalid!',
103
+ ['Provided', this.type].join(': '),
104
+ 'Available types:',
105
+ ...Object.values(FormInput.TYPES).map((t) => ` - ${t}`),
106
+ ].join('\n'),
107
+ )
94
108
  }
95
109
  }
96
110
 
@@ -107,7 +121,8 @@ export default class FormInput {
107
121
  required: this.required,
108
122
  placeholder: this.placeholder,
109
123
  options: this.options,
110
- defaultValue: this.defaultValue
124
+ mask: this.mask,
125
+ defaultValue: this.defaultValue,
111
126
  }
112
127
  }
113
128
 
@@ -117,7 +132,7 @@ export default class FormInput {
117
132
  */
118
133
  static from(input) {
119
134
  if (input instanceof FormInput) return input
120
- if ("string" === typeof input) {
135
+ if (typeof input === 'string') {
121
136
  return new FormInput({ name: input, label: input })
122
137
  }
123
138
  return new FormInput(input)
@@ -1,4 +1,4 @@
1
- import UiMessage from "../Message/Message.js"
1
+ import UiMessage from '../Message/Message.js'
2
2
 
3
3
  /**
4
4
  * FormMessage – specialized UiMessage for forms.
@@ -15,10 +15,7 @@ export default class FormMessage extends UiMessage {
15
15
  */
16
16
  constructor(input = {}) {
17
17
  super(input)
18
- const {
19
- data = {},
20
- schema = {},
21
- } = input
18
+ const { data = {}, schema = {} } = input
22
19
 
23
20
  // Store data and schema for easy access
24
21
  this.data = data
@@ -81,4 +78,4 @@ export default class FormMessage extends UiMessage {
81
78
 
82
79
  return { isValid: Object.keys(errors).length === 0, errors }
83
80
  }
84
- }
81
+ }
@@ -1,11 +1,7 @@
1
- import FormInput from "./Input.js"
2
- import FormMessage from "./Message.js"
3
- import Form from "./Form.js"
1
+ import FormInput from './Input.js'
2
+ import FormMessage from './Message.js'
3
+ import Form from './Form.js'
4
4
 
5
- export {
6
- FormInput,
7
- FormMessage,
8
- Form,
9
- }
5
+ export { FormInput, FormMessage, Form }
10
6
 
11
7
  export default Form
@@ -1,6 +1,6 @@
1
- import Event from "@nan0web/event/oop"
2
- import CancelError from "./Error/CancelError.js"
3
- import UiMessage from "./Message/Message.js"
1
+ import Event from '@nan0web/event/oop'
2
+ import CancelError from './Error/CancelError.js'
3
+ import UiMessage from './Message/Message.js'
4
4
 
5
5
  /**
6
6
  * Abstract input adapter for UI implementations.
@@ -20,9 +20,7 @@ export default class InputAdapter extends Event {
20
20
  * @returns {void}
21
21
  */
22
22
  start() {
23
- this.emit('input',
24
- UiMessage.from({ body: "Adapter started" })
25
- )
23
+ this.emit('input', UiMessage.from({ body: 'Adapter started' }))
26
24
  }
27
25
 
28
26
  /**
@@ -1,4 +1,4 @@
1
- import { Message } from "@nan0web/co"
1
+ import { Message } from '@nan0web/co'
2
2
 
3
3
  /**
4
4
  * @typedef {Object} MessageBodySchema
@@ -40,13 +40,13 @@ export default class UiMessage extends Message {
40
40
  SUCCESS: 'success',
41
41
  WARNING: 'warning',
42
42
  COMMAND: 'command',
43
- NAVIGATION: 'navigation'
43
+ NAVIGATION: 'navigation',
44
44
  }
45
45
 
46
46
  /** @type {string} */
47
- type = ""
47
+ type = ''
48
48
  /** @type {string} */
49
- id = ""
49
+ id = ''
50
50
 
51
51
  /**
52
52
  * Creates a UiMessage.
@@ -56,10 +56,7 @@ export default class UiMessage extends Message {
56
56
  constructor(input = {}) {
57
57
  super(input)
58
58
 
59
- const {
60
- type = this.type,
61
- id = this.id,
62
- } = input
59
+ const { type = this.type, id = this.id } = input
63
60
  this.id = id || `ui-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
64
61
  this.type = String(type)
65
62
 
@@ -96,7 +93,7 @@ export default class UiMessage extends Message {
96
93
  for (const [field, schema] of entries) {
97
94
  const value = body[field]
98
95
  const fn = schema?.validate
99
- if ("function" === typeof fn) {
96
+ if ('function' === typeof fn) {
100
97
  const ok = fn.apply(body, [value])
101
98
  if (ok !== true) {
102
99
  result.set(field, String(ok))
@@ -105,7 +102,7 @@ export default class UiMessage extends Message {
105
102
  }
106
103
  const required = schema?.required ?? false
107
104
  if (required && !value) {
108
- result.set(field, "Required")
105
+ result.set(field, 'Required')
109
106
  continue
110
107
  }
111
108
  if (schema?.pattern && schema.pattern instanceof RegExp) {
@@ -116,10 +113,10 @@ export default class UiMessage extends Message {
116
113
  }
117
114
  if (schema?.options) {
118
115
  if (!Array.isArray(schema.options)) {
119
- throw new Error("Schema options must be an array of possible values")
116
+ throw new Error('Schema options must be an array of possible values')
120
117
  }
121
118
  if (!schema.options.includes(value)) {
122
- result.set(field, "Enumeration must have one value")
119
+ result.set(field, 'Enumeration must have one value')
123
120
  continue
124
121
  }
125
122
  }
@@ -1,4 +1,4 @@
1
- import UiMessage from "./Message.js"
1
+ import UiMessage from './Message.js'
2
2
 
3
3
  /**
4
4
  * OutputMessage – message sent from the system to the UI.
@@ -11,7 +11,7 @@ export default class OutputMessage extends UiMessage {
11
11
  LOW: 0,
12
12
  NORMAL: 1,
13
13
  HIGH: 2,
14
- CRITICAL: 3
14
+ CRITICAL: 3,
15
15
  }
16
16
 
17
17
  /** @type {string[]} */
@@ -36,19 +36,19 @@ export default class OutputMessage extends UiMessage {
36
36
  body = [],
37
37
  meta = {},
38
38
  error = null,
39
- priority = OutputMessage.PRIORITY.NORMAL
39
+ priority = OutputMessage.PRIORITY.NORMAL,
40
40
  } = input
41
41
 
42
- const contentSource = 'body' in input ? body :
43
- 'content' in input ? content : []
42
+ const contentSource = 'body' in input ? body : 'content' in input ? content : []
44
43
 
45
- this.body = Array.isArray(contentSource) ?
46
- contentSource :
47
- (contentSource ? [String(contentSource)] : [])
44
+ this.body = Array.isArray(contentSource)
45
+ ? contentSource
46
+ : contentSource
47
+ ? [String(contentSource)]
48
+ : []
48
49
 
49
50
  this.meta = meta
50
- this.error = error instanceof Error ? error :
51
- error ? new Error(String(error)) : null
51
+ this.error = error instanceof Error ? error : error ? new Error(String(error)) : null
52
52
  this.priority = Number(priority)
53
53
 
54
54
  if (!this.type && this.error) {
@@ -91,7 +91,7 @@ export default class OutputMessage extends UiMessage {
91
91
  let combinedError = this.error
92
92
  let combinedPriority = this.priority
93
93
 
94
- messages.forEach(msg => {
94
+ messages.forEach((msg) => {
95
95
  if (msg instanceof OutputMessage) {
96
96
  combinedContent.push(...msg.content)
97
97
  combinedMeta = { ...combinedMeta, ...msg.meta }
@@ -105,7 +105,7 @@ export default class OutputMessage extends UiMessage {
105
105
  meta: combinedMeta,
106
106
  error: combinedError,
107
107
  priority: combinedPriority,
108
- type: this.type
108
+ type: this.type,
109
109
  })
110
110
  }
111
111
 
@@ -122,11 +122,13 @@ export default class OutputMessage extends UiMessage {
122
122
  type: this.type,
123
123
  id: this.id,
124
124
  time: this.time.toISOString(),
125
- error: this.error ? {
126
- message: this.error.message,
127
- stack: this.error.stack
128
- } : null,
129
- priority: this.priority
125
+ error: this.error
126
+ ? {
127
+ message: this.error.message,
128
+ stack: this.error.stack,
129
+ }
130
+ : null,
131
+ priority: this.priority,
130
132
  }
131
133
  }
132
134
 
@@ -1,5 +1,5 @@
1
- import UiMessage from "./Message.js"
2
- import OutputMessage from "./OutputMessage.js"
1
+ import UiMessage from './Message.js'
2
+ import OutputMessage from './OutputMessage.js'
3
3
 
4
4
  export { UiMessage, OutputMessage }
5
5
 
@@ -16,7 +16,7 @@ class OutputAdapter extends Event {
16
16
  * @throws {Error} If not overridden by a subclass.
17
17
  */
18
18
  render(message) {
19
- throw new Error("render() must be implemented by subclass")
19
+ throw new Error('render() must be implemented by subclass')
20
20
  }
21
21
 
22
22
  /**
@@ -27,14 +27,16 @@ class OutputAdapter extends Event {
27
27
  * @returns {void}
28
28
  */
29
29
  progress(progress, metadata = {}) {
30
- this.render(OutputMessage.from({
31
- content: [],
32
- metadata: {
33
- ...metadata,
34
- progress,
35
- elementType: "progress"
36
- }
37
- }))
30
+ this.render(
31
+ OutputMessage.from({
32
+ content: [],
33
+ metadata: {
34
+ ...metadata,
35
+ progress,
36
+ elementType: 'progress',
37
+ },
38
+ }),
39
+ )
38
40
  }
39
41
 
40
42
  /**
@@ -47,4 +49,4 @@ class OutputAdapter extends Event {
47
49
  }
48
50
  }
49
51
 
50
- export default OutputAdapter
52
+ export default OutputAdapter
@@ -1,6 +1,4 @@
1
- import StreamEntry from "./StreamEntry.js"
2
-
3
- export { StreamEntry } // Export if needed
1
+ import StreamEntry from './StreamEntry.js'
4
2
 
5
3
  /**
6
4
  * Agnostic UI stream for processing progress using async generators.
@@ -18,6 +16,9 @@ export default class UIStream {
18
16
  static createProcessor(signal, processorFn) {
19
17
  return async function* () {
20
18
  try {
19
+ if (signal.aborted) {
20
+ throw new DOMException('Aborted', 'AbortError')
21
+ }
21
22
  const result = await processorFn()
22
23
  yield result
23
24
  } catch (/** @type {any} */ error) {
@@ -24,7 +24,7 @@ export default class StreamEntry {
24
24
  * Error message associated with the stream entry.
25
25
  * @type {string}
26
26
  */
27
- error = ""
27
+ error = ''
28
28
 
29
29
  /**
30
30
  * Creates a new StreamEntry instance.
@@ -56,4 +56,4 @@ export default class StreamEntry {
56
56
  if (input instanceof StreamEntry) return input
57
57
  return new StreamEntry(input)
58
58
  }
59
- }
59
+ }
@@ -1,8 +1,10 @@
1
- import EventProcessor from "@nan0web/event/oop"
2
- import CancelError from "./Error/CancelError.js"
3
- import UIMessage from "./Message/Message.js"
4
- import UIForm from "./Form/Form.js"
5
- import FormInput from "./Form/Input.js"
1
+ import EventProcessor from '@nan0web/event/oop'
2
+ import CancelError from './Error/CancelError.js'
3
+ import UIMessage from './Message/Message.js'
4
+ import UIForm from './Form/Form.js'
5
+ import FormInput from './Form/Input.js'
6
+ import OutputAdapter from './OutputAdapter.js'
7
+ import OutputMessage from './Message/OutputMessage.js'
6
8
 
7
9
  /**
8
10
  * Unified UI Adapter that handles both input and output operations.
@@ -34,7 +36,7 @@ export default class UiAdapter extends EventProcessor {
34
36
  * @returns {void}
35
37
  */
36
38
  start() {
37
- this.emit('input', UIMessage.from({ body: "Adapter started" }))
39
+ this.emit('input', UIMessage.from({ body: 'Adapter started' }))
38
40
  }
39
41
 
40
42
  /**
@@ -81,6 +83,30 @@ export default class UiAdapter extends EventProcessor {
81
83
  throw new Error('select() method must be implemented in subclass')
82
84
  }
83
85
 
86
+ /**
87
+ * Process a UIForm and return its result.
88
+ *
89
+ * This default implementation follows an **agnostic UI** approach:
90
+ * it simply returns the form instance (with optional initial state merged)
91
+ * without UI interaction. Concrete adapters (CLI, Web, etc.) can override
92
+ * this method to render the form, collect user input and return a richer
93
+ * result object (`{ form, cancelled }`).
94
+ *
95
+ * @param {UIForm} form - The UIForm instance to process.
96
+ * @param {object} [initialState={}] - Pre‑filled values for the form.
97
+ * @returns {Promise<{ form: UIForm, cancelled?: boolean }>} Form processing result.
98
+ */
99
+ async processForm(form, initialState = {}) {
100
+ // Merge any provided initial state into the form's internal state.
101
+ if (initialState && typeof initialState === 'object') {
102
+ form.state = { ...form.state, ...initialState }
103
+ }
104
+ // In the agnostic baseline we do not perform any interactive I/O.
105
+ // Sub‑classes may provide a UI (render, ask, etc.) and return
106
+ // `{ form, cancelled: true/false, ... }`.
107
+ return { form }
108
+ }
109
+
84
110
  /**
85
111
  * Ensures a message's body is fully and validly filled.
86
112
  * Generates a form from the message's static Body schema,
@@ -96,22 +122,21 @@ export default class UiAdapter extends EventProcessor {
96
122
  */
97
123
  async requireInput(msg) {
98
124
  if (!msg) {
99
- throw new Error("Message instance is required")
125
+ throw new Error('Message instance is required')
100
126
  }
101
127
  if (!(msg instanceof UIMessage)) {
102
- throw new TypeError("Message must be an instance of UIMessage")
128
+ throw new TypeError('Message must be an instance of UIMessage')
103
129
  }
104
130
  /** @type {Map<string,string>} */
105
131
  let errors = msg.validate()
106
132
  while (errors.size > 0) {
107
- const form = generateForm(
108
- /** @type {any} */ (msg.constructor).Body,
109
- { initialState: msg.body }
110
- )
111
-
112
- const formResult = await this.processForm ? this.processForm(form, msg.body) : {} // Assume method exists or handle differently, but error indicates missing method; perhaps remove if not used
113
- if (formResult.escaped) {
114
- throw new CancelError("User cancelled form")
133
+ const form = generateForm(/** @type {any} */ (msg.constructor).Body, {
134
+ initialState: msg.body,
135
+ })
136
+
137
+ const formResult = (await this.processForm) ? this.processForm(form, msg.body) : {} // Assume method exists or handle differently, but error indicates missing method; perhaps remove if not used
138
+ if (formResult.cancelled) {
139
+ throw new CancelError('User cancelled form')
115
140
  }
116
141
 
117
142
  const updatedBody = { ...msg.body, ...formResult.form.state }
@@ -119,10 +144,13 @@ export default class UiAdapter extends EventProcessor {
119
144
 
120
145
  if (updatedErrors.size > 0) {
121
146
  if (this.output) {
122
- await this.output.render("Alert", {
123
- variant: "error",
124
- content: Array.from(updatedErrors.values()).join("\n")
125
- })
147
+ this.output.render(
148
+ new OutputMessage({
149
+ type: 'Alert',
150
+ variant: 'error',
151
+ body: Array.from(updatedErrors.values()).join('\n'),
152
+ }),
153
+ )
126
154
  }
127
155
  errors = updatedErrors
128
156
  continue
@@ -142,8 +170,8 @@ export default class UiAdapter extends EventProcessor {
142
170
  * @throws {Error} If not implemented in subclass.
143
171
  */
144
172
  render(message) {
145
- throw new Error("render() must be implemented by subclass")
146
- this.emit("rendered", message)
173
+ throw new Error('render() must be implemented by subclass')
174
+ this.emit('rendered', message)
147
175
  }
148
176
  }
149
177
 
@@ -157,14 +185,14 @@ export default class UiAdapter extends EventProcessor {
157
185
  * @returns {UIForm} Form instance ready for input.
158
186
  */
159
187
  export function generateForm(BodyClass, options = {}) {
160
- const { initialState = {}, t = v => v } = options
188
+ const { initialState = {}, t = (v) => v } = options
161
189
  const fields = []
162
190
 
163
191
  for (const [name, schema] of Object.entries(BodyClass)) {
164
- if (typeof schema !== "object" || schema === null || !name || !schema.help) continue
192
+ if (typeof schema !== 'object' || schema === null || !name || !schema.help) continue
165
193
 
166
194
  const label = t(schema.help) || name
167
- const placeholder = t(schema.placeholder || schema.defaultValue || "")
195
+ const placeholder = t(schema.placeholder || schema.defaultValue || '')
168
196
  const isRequired = !!schema.required
169
197
 
170
198
  fields.push(
@@ -174,12 +202,12 @@ export function generateForm(BodyClass, options = {}) {
174
202
  type: schema.type || FormInput.TYPES.TEXT,
175
203
  required: isRequired,
176
204
  placeholder,
177
- options: schema.options ? schema.options.map(o => String(o)) : [],
205
+ options: schema.options ? schema.options.map((o) => String(o)) : [],
178
206
  validation: schema.validate
179
207
  ? (value) => {
180
- const res = schema.validate(value)
181
- return res === true ? true : typeof res === "string" ? res : `Invalid ${name}`
182
- }
208
+ const res = schema.validate(value)
209
+ return res === true ? true : typeof res === 'string' ? res : `Invalid ${name}`
210
+ }
183
211
  : () => true,
184
212
  }),
185
213
  )
package/src/core/index.js CHANGED
@@ -1,12 +1,41 @@
1
- export { default as InputAdapter } from "./InputAdapter.js"
2
- export { default as OutputAdapter } from "./OutputAdapter.js"
3
- export { default as UIStream } from "./Stream.js"
1
+ export { default as InputAdapter } from './InputAdapter.js'
2
+ export { default as OutputAdapter } from './OutputAdapter.js'
4
3
 
5
- export { default as UiMessage } from "./Message/Message.js"
6
- export { default as FormMessage } from "./Form/Message.js"
7
- export { default as FormInput } from "./Form/Input.js"
8
- export { default as UIForm } from "./Form/Form.js"
4
+ import UIStream from './Stream.js'
5
+ export { UIStream, UIStream as UiStream }
6
+ import StreamEntry from './StreamEntry.js'
7
+ export { StreamEntry, StreamEntry as UiStreamEntry }
9
8
 
10
- export { default as Error, CancelError } from "./Error/index.js"
9
+ export { default as UiMessage } from './Message/Message.js'
10
+ export { default as FormMessage } from './Form/Message.js'
11
+ export { default as FormInput } from './Form/Input.js'
11
12
 
12
- export { default as UiAdapter } from "./UiAdapter.js"
13
+ import UIForm from './Form/Form.js'
14
+ export { UIForm, UIForm as UiForm }
15
+
16
+ export { default as Error, CancelError } from './Error/index.js'
17
+
18
+ export { default as UiAdapter } from './UiAdapter.js'
19
+
20
+ // Flow — Yield-Based Universal UI Architecture
21
+ export {
22
+ runFlow,
23
+ flow,
24
+ View,
25
+ Prompt,
26
+ Stream,
27
+ Alert,
28
+ Toast,
29
+ Badge,
30
+ Text,
31
+ Table,
32
+ Input,
33
+ Select,
34
+ Confirm,
35
+ Multiselect,
36
+ Mask,
37
+ Password,
38
+ Spinner,
39
+ Progress,
40
+ } from './Flow.js'
41
+ export { default as Flow } from './Flow.js'
package/src/functions.js CHANGED
@@ -1,19 +1,18 @@
1
- import { empty } from "@nan0web/types"
1
+ import { empty } from '@nan0web/types'
2
2
 
3
3
  export const spaces = (options = {}) => {
4
4
  const { cols = [], padding = 1, aligns = [] } = options
5
- return (row) => (
5
+ return (row) =>
6
6
  row.map((str, i) => {
7
- const pad = " ".repeat(cols[i] - str.length + padding)
8
- return aligns[i] === "r" ? pad + str : str + pad
7
+ const pad = ' '.repeat(cols[i] - str.length + padding)
8
+ return aligns[i] === 'r' ? pad + str : str + pad
9
9
  })
10
- )
11
10
  }
12
11
 
13
12
  export const weight = (arr) => {
14
- return (Fn = v => v) => {
13
+ return (Fn = (v) => v) => {
15
14
  const cols = []
16
- arr.forEach(m => {
15
+ arr.forEach((m) => {
17
16
  Fn(m).forEach((str, i) => {
18
17
  if (undefined === cols[i]) cols[i] = 0
19
18
  cols[i] = Math.max(str.length, cols[i])
@@ -23,16 +22,10 @@ export const weight = (arr) => {
23
22
  }
24
23
  }
25
24
 
26
-
27
25
  export const table = (options = {}) => {
28
- const {
29
- Fn = v => v,
30
- cols = [],
31
- padding = 1,
32
- aligns = []
33
- } = options
26
+ const { Fn = (v) => v, cols = [], padding = 1, aligns = [] } = options
34
27
  return (arr) => {
35
28
  const options = { cols: empty(cols) ? weight(arr)(Fn) : cols, padding, aligns }
36
- return arr.map(row => spaces(options)(row))
29
+ return arr.map((row) => spaces(options)(row))
37
30
  }
38
31
  }