@nan0web/ui 1.12.3 → 3.1.1

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 (136) hide show
  1. package/package.json +29 -20
  2. package/src/Component/index.js +1 -5
  3. package/src/Model/Element.js +183 -0
  4. package/src/Theme/AppTheme.js +19 -0
  5. package/src/Theme/CustomTheme.js +32 -0
  6. package/src/Theme/DarkLightTheme.js +34 -0
  7. package/src/Theme/Theme.js +25 -0
  8. package/src/Theme/atoms/Avatar.js +20 -0
  9. package/src/Theme/atoms/Badge.js +28 -0
  10. package/src/Theme/atoms/Button.js +88 -0
  11. package/src/Theme/atoms/Checkbox.js +26 -0
  12. package/src/Theme/atoms/Input.js +28 -0
  13. package/src/Theme/atoms/Radio.js +26 -0
  14. package/src/Theme/atoms/Select.js +16 -0
  15. package/src/Theme/atoms/TextArea.js +17 -0
  16. package/src/Theme/atoms/Typography.js +26 -0
  17. package/src/Theme/atoms/index.js +11 -0
  18. package/src/Theme/createTheme.js +22 -0
  19. package/src/Theme/index.js +20 -0
  20. package/src/Theme/molecules/Card.js +24 -0
  21. package/src/Theme/molecules/index.js +3 -0
  22. package/src/Theme/organisms/Modal.js +24 -0
  23. package/src/Theme/organisms/index.js +3 -0
  24. package/src/Theme/presets/HighContrastTheme.js +65 -0
  25. package/src/Theme/presets/NightTheme.js +66 -0
  26. package/src/Theme/presets/index.js +4 -0
  27. package/src/Theme/tokens.js +115 -0
  28. package/src/core/InputAdapter.js +1 -2
  29. package/src/core/Intent.js +22 -8
  30. package/src/core/Message/Message.js +3 -0
  31. package/src/core/OutputAdapter.js +9 -13
  32. package/src/core/index.js +7 -4
  33. package/src/domain/ModelAsApp.js +1 -1
  34. package/src/domain/app/IntentAuditor.js +53 -0
  35. package/src/domain/app/JsIntentAuditor.js +145 -0
  36. package/src/domain/app/PyIntentAuditor.js +144 -0
  37. package/src/domain/app/SnapshotAuditor.js +8 -8
  38. package/src/domain/components/ShellModel.js +2 -2
  39. package/src/index.js +35 -9
  40. package/src/inspect.js +3 -0
  41. package/src/utils/format.js +21 -0
  42. package/src/utils/processI18n.js +27 -0
  43. package/src/utils/resolveContext.js +79 -0
  44. package/types/Component/index.d.ts +1 -5
  45. package/types/Model/Element.d.ts +87 -0
  46. package/types/Theme/AppTheme.d.ts +14 -0
  47. package/types/Theme/CustomTheme.d.ts +21 -0
  48. package/types/Theme/DarkLightTheme.d.ts +16 -0
  49. package/types/Theme/Theme.d.ts +18 -0
  50. package/types/Theme/atoms/Avatar.d.ts +14 -0
  51. package/types/Theme/atoms/Badge.d.ts +22 -0
  52. package/types/Theme/atoms/Button.d.ts +144 -0
  53. package/types/Theme/atoms/Checkbox.d.ts +20 -0
  54. package/types/Theme/atoms/Input.d.ts +22 -0
  55. package/types/Theme/atoms/Radio.d.ts +20 -0
  56. package/types/Theme/atoms/Select.d.ts +15 -0
  57. package/types/Theme/atoms/TextArea.d.ts +17 -0
  58. package/types/Theme/atoms/Typography.d.ts +47 -0
  59. package/types/Theme/atoms/index.d.ts +10 -0
  60. package/types/Theme/createTheme.d.ts +7 -0
  61. package/types/Theme/index.d.ts +10 -0
  62. package/types/Theme/molecules/Card.d.ts +18 -0
  63. package/types/Theme/molecules/index.d.ts +2 -0
  64. package/types/Theme/organisms/Modal.d.ts +18 -0
  65. package/types/Theme/organisms/index.d.ts +2 -0
  66. package/types/Theme/presets/HighContrastTheme.d.ts +2 -0
  67. package/types/Theme/presets/NightTheme.d.ts +2 -0
  68. package/types/Theme/presets/index.d.ts +3 -0
  69. package/types/Theme/tokens.d.ts +119 -0
  70. package/types/core/Intent.d.ts +10 -7
  71. package/types/core/Message/Message.d.ts +3 -0
  72. package/types/core/OutputAdapter.d.ts +2 -4
  73. package/types/core/index.d.ts +5 -2
  74. package/types/domain/Document.d.ts +2 -1
  75. package/types/domain/FooterModel.d.ts +2 -1
  76. package/types/domain/ModelAsApp.d.ts +48 -48
  77. package/types/domain/app/IntentAuditor.d.ts +23 -0
  78. package/types/domain/app/JsIntentAuditor.d.ts +22 -0
  79. package/types/domain/app/PyIntentAuditor.d.ts +22 -0
  80. package/types/domain/app/SnapshotAuditor.d.ts +5 -6
  81. package/types/domain/components/ShellModel.d.ts +1 -5
  82. package/types/index.d.ts +7 -9
  83. package/types/inspect.d.ts +3 -0
  84. package/types/utils/format.d.ts +5 -0
  85. package/types/utils/processI18n.d.ts +8 -0
  86. package/types/utils/resolveContext.d.ts +21 -0
  87. package/src/App/Command/DepsCommand.js +0 -24
  88. package/src/App/Core/CoreApp.js +0 -125
  89. package/src/App/Core/UI.js +0 -63
  90. package/src/App/Core/Widget.js +0 -61
  91. package/src/App/Core/index.js +0 -11
  92. package/src/App/Scenario.js +0 -45
  93. package/src/App/User/Command/Message.js +0 -3
  94. package/src/App/User/Command/index.js +0 -5
  95. package/src/App/User/UserApp.js +0 -85
  96. package/src/App/User/UserUI.js +0 -20
  97. package/src/App/User/index.js +0 -9
  98. package/src/App/index.js +0 -14
  99. package/src/Component/Process/Input.js +0 -63
  100. package/src/Component/Process/Process.js +0 -24
  101. package/src/Component/Process/index.js +0 -5
  102. package/src/Component/Welcome/Input.js +0 -48
  103. package/src/Component/Welcome/Welcome.js +0 -22
  104. package/src/Component/Welcome/index.js +0 -5
  105. package/src/Frame/Frame.js +0 -608
  106. package/src/Frame/Props.js +0 -96
  107. package/src/StdIn.js +0 -100
  108. package/src/StdOut.js +0 -95
  109. package/src/View/RenderOptions.js +0 -48
  110. package/src/View/View.js +0 -306
  111. package/src/core/Message/index.js +0 -6
  112. package/types/App/Command/DepsCommand.d.ts +0 -14
  113. package/types/App/Core/CoreApp.d.ts +0 -70
  114. package/types/App/Core/UI.d.ts +0 -38
  115. package/types/App/Core/Widget.d.ts +0 -39
  116. package/types/App/Core/index.d.ts +0 -10
  117. package/types/App/Scenario.d.ts +0 -26
  118. package/types/App/User/Command/Message.d.ts +0 -2
  119. package/types/App/User/Command/index.d.ts +0 -3
  120. package/types/App/User/UserApp.d.ts +0 -41
  121. package/types/App/User/UserUI.d.ts +0 -9
  122. package/types/App/User/index.d.ts +0 -8
  123. package/types/App/index.d.ts +0 -12
  124. package/types/Component/Process/Input.d.ts +0 -48
  125. package/types/Component/Process/Process.d.ts +0 -13
  126. package/types/Component/Process/index.d.ts +0 -4
  127. package/types/Component/Welcome/Input.d.ts +0 -34
  128. package/types/Component/Welcome/Welcome.d.ts +0 -13
  129. package/types/Component/Welcome/index.d.ts +0 -4
  130. package/types/Frame/Frame.d.ts +0 -186
  131. package/types/Frame/Props.d.ts +0 -77
  132. package/types/StdIn.d.ts +0 -62
  133. package/types/StdOut.d.ts +0 -52
  134. package/types/View/RenderOptions.d.ts +0 -29
  135. package/types/View/View.d.ts +0 -124
  136. package/types/core/Message/index.d.ts +0 -4
@@ -1,608 +0,0 @@
1
- import stringWidth from 'string-width'
2
- import { to, typeOf, empty } from '@nan0web/types'
3
- import FrameProps from './Props.js'
4
-
5
- export class FrameRenderMethod {
6
- static APPEND = 'append'
7
- static REPLACE = 'replace'
8
- static VISIBLE = 'visible'
9
- }
10
-
11
- /**
12
- * @link https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797 - ANSI escape codes
13
- */
14
- export default class Frame {
15
- /** @type {typeof FrameRenderMethod} */
16
- static RenderMethod = FrameRenderMethod
17
- static Props = FrameProps
18
- /** @type {string} End of line */
19
- static EOL = '\n'
20
- /** @type {string} Beginning of line */
21
- static BOL = '\r'
22
- /** @type {string} Beginning of frame */
23
- static BOF = '\x1b[0;0H'
24
- /** @type {string} Hide cursor */
25
- static HIDE_CURSOR = '\x1b[?25l'
26
- /** @type {string} Show cursor */
27
- static SHOW_CURSOR = '\x1b[?25h'
28
- /** @type {string} Tab */
29
- static TAB = '\t'
30
- /** @type {string} Bold */
31
- static BOLD = '\x1b[1m'
32
- /** @type {string} Italic */
33
- static ITALIC = '\x1b[3m'
34
- /** @type {string} Underline */
35
- static UNDERLINE = '\x1b[4m'
36
- /** @type {string} Strikethrough */
37
- static STRIKETHROUGH = '\x1b[9m'
38
- /** @type {string} Reset */
39
- static RESET = '\x1b[0m'
40
- /** @type {string} Clear line */
41
- static CLEAR_LINE = '\x1b[2K'
42
- /**
43
- * @example
44
- * ```js
45
- * new Frame([
46
- * ["Hello", "World"],
47
- * [["Hello", { color: "red", bgColor: "#009" }], "World"],
48
- * ["<b i fg=#900>Hello</b>", "<i>World</i>"],
49
- * ])
50
- * ```
51
- * @type {string[][]|any[][]}
52
- */
53
- value
54
- /** @type {FrameProps} */
55
- defaultProps
56
- /** @type {string} */
57
- imprint
58
- /** @type {number} */
59
- width
60
- /** @type {number} */
61
- height
62
- /** @type {string} */
63
- renderMethod
64
- /**
65
- * @param {object} [input]
66
- * @param {string[]|string[][]} [input.value]
67
- * @param {number} [input.width]
68
- * @param {number} [input.height]
69
- * @param {string} [input.imprint]
70
- * @param {string} [input.renderMethod]
71
- * @param {FrameProps} [input.defaultProps]
72
- */
73
- constructor(input = {}) {
74
- // if (typeOf(Array)(input)) {
75
- // input = { value: input }
76
- // }
77
- if (input instanceof Frame) {
78
- input = { ...input }
79
- }
80
- let {
81
- value = [],
82
- width = -1,
83
- height = -1,
84
- imprint = '',
85
- renderMethod = 'append',
86
- defaultProps = new FrameProps(),
87
- } = input
88
- if (value instanceof Frame) {
89
- value = value.value
90
- }
91
- if (!typeOf(Array)(value)) {
92
- throw new TypeError(
93
- [
94
- 'Frame constructor allows only string[] for rows or string[][] for rows with columns',
95
- 'Provided value:',
96
- JSON.stringify(value, null, 2),
97
- ].join('\n'),
98
- )
99
- }
100
- value = value.map((row) => {
101
- if (typeOf(Array)(row)) {
102
- return row.map(String)
103
- }
104
- return [row]
105
- })
106
- this.value = value.map((v) => (Array.isArray(v) ? v : [v]))
107
- this.imprint = String(imprint)
108
- this.width = width
109
- this.height = height
110
- this.renderMethod = renderMethod
111
- this.defaultProps = defaultProps
112
- }
113
- /**
114
- * Get whether the frame is empty.
115
- * @returns {boolean} True if the frame has no content.
116
- */
117
- get empty() {
118
- return empty(this.value)
119
- }
120
- /**
121
- * Calculate the visual width of a string.
122
- * @param {string} str
123
- * @returns {number} The visual width of the string.
124
- */
125
- lengthOf(str) {
126
- return stringWidth(str)
127
- }
128
- /**
129
- * Render the frame into a string representation.
130
- * @param {object} [options]
131
- * @param {string} [options.method] - Render method to use.
132
- * @param {FrameProps} [options.props] - Properties to apply during rendering.
133
- * @returns {string} The rendered frame as a string.
134
- */
135
- render(options = {}) {
136
- const { method = this.renderMethod, props = this.defaultProps } = options
137
- let rows = this.value.map((row) => {
138
- if (typeOf(Array)(row)) {
139
- row = row.join('')
140
- }
141
- return row
142
- })
143
- let spacesOn = ''
144
- if (Frame.BOF === rows[0]) {
145
- rows = rows.slice(1)
146
- spacesOn = 'top'
147
- } else if (Frame.BOF === rows[rows.length - 1]) {
148
- rows = rows.slice(0, -1)
149
- spacesOn = 'bottom'
150
- }
151
- if (this.height >= 0 && rows.length > this.height) {
152
- rows = rows.slice(0, this.height)
153
- }
154
- if (this.width >= 0 && rows.length > 0) {
155
- rows = rows.map((row) => {
156
- if (row.length > this.width) {
157
- row = row.slice(0, this.width)
158
- }
159
- return row
160
- })
161
- }
162
- let carret = ''
163
- if (method === Frame.RenderMethod.REPLACE) {
164
- const printedRows = rows.map(
165
- (row) => row + ' '.repeat(Math.max(0, this.width - this.lengthOf(row))),
166
- )
167
- const left = this.height >= 0 ? this.height - rows.length : 0
168
- const eraser = []
169
- for (let i = 0; i < left; i++) eraser.push(' '.repeat(this.width))
170
- carret = Frame.BOF
171
- if ('bottom' === spacesOn) {
172
- rows = left > 0 ? [...printedRows, ...eraser] : []
173
- } else {
174
- rows = left > 0 ? [...eraser, ...printedRows] : []
175
- }
176
- } else if (method === Frame.RenderMethod.APPEND) {
177
- rows = rows.map((row) => {
178
- const used = this.lengthOf(row)
179
- const left = Math.max(0, this.width - used)
180
- row = row + ' '.repeat(left)
181
- if (row.length > this.width) row = row.slice(0, this.width)
182
- return row
183
- })
184
- if (this.height >= 0 && rows.length > this.height) {
185
- rows = rows.slice(0, this.height)
186
- }
187
- if (spacesOn) {
188
- carret = Frame.BOF
189
- const left = this.height >= 0 ? this.height - rows.length : 0
190
- const eraser = []
191
- for (let i = 0; i < left; i++) eraser.push('')
192
- if (spacesOn === 'top') {
193
- rows = left > 0 ? [...eraser, ...rows] : []
194
- } else if (spacesOn === 'bottom') {
195
- rows = left > 0 ? [...rows, ...eraser] : []
196
- }
197
- }
198
- } else if (method === Frame.RenderMethod.VISIBLE) {
199
- // Move cursor up # lines (Math.max(0, Math.min(rows.length, height))) before rendering
200
- if (spacesOn) {
201
- let moveUpLines = Math.max(
202
- 0,
203
- Math.min(rows.length, this.height >= 0 ? this.height : rows.length),
204
- )
205
- if (moveUpLines > 0) {
206
- --moveUpLines
207
- }
208
- carret = Frame.cursorUp(moveUpLines)
209
- }
210
- rows = rows.map((row) => Frame.clearLine('\r') + row)
211
- } else {
212
- if (spacesOn) {
213
- carret = Frame.BOF
214
- const left = this.height >= 0 ? this.height - rows.length : 0
215
- const eraser = []
216
- for (let i = 0; i < left; i++) eraser.push('')
217
- if (spacesOn === 'top') {
218
- rows = left > 0 ? [...eraser, ...rows] : []
219
- } else if (spacesOn === 'bottom') {
220
- rows = left > 0 ? [...rows, ...eraser] : []
221
- }
222
- }
223
- }
224
- if ('bottom' === spacesOn) {
225
- this.imprint = rows.join('\n') + carret
226
- } else {
227
- this.imprint = carret + rows.join('\n')
228
- }
229
- return this.imprint
230
- }
231
- #render1(options = {}) {
232
- const { method = this.renderMethod, props = this.defaultProps } = options
233
-
234
- /**
235
- * Helper to apply CLI style codes.
236
- * @param {string} str
237
- * @param {object} style
238
- * @returns {string}
239
- */
240
- function applyStyle(str, style = {}) {
241
- let out = str
242
- let prefix = ''
243
- let suffix = Frame.RESET
244
-
245
- if (style.bold) prefix += Frame.BOLD
246
- if (style.italic) prefix += Frame.ITALIC
247
- if (style.underline) prefix += Frame.UNDERLINE
248
- if (style.strikethrough) prefix += Frame.STRIKETHROUGH
249
-
250
- // Color
251
- if (style.color) {
252
- const color = style.color
253
- if (/^#[0-9a-f]{3,6}$/i.test(color)) {
254
- // 24-bit color
255
- const hex = color.replace('#', '')
256
- const rgb =
257
- hex.length === 3
258
- ? [0, 1, 2].map((i) => parseInt(hex[i] + hex[i], 16))
259
- : [0, 2, 4].map((i) => parseInt(hex.slice(i, i + 2), 16))
260
- prefix += `\x1b[38;2;${rgb[0]};${rgb[1]};${rgb[2]}m`
261
- } else if (/^\d+$/.test(color)) {
262
- prefix += `\x1b[38;5;${color}m`
263
- } else {
264
- // Named color, map to 8-bit
265
- const map = {
266
- black: 30,
267
- red: 31,
268
- green: 32,
269
- yellow: 33,
270
- blue: 34,
271
- magenta: 35,
272
- cyan: 36,
273
- white: 37,
274
- gray: 90,
275
- grey: 90,
276
- brightRed: 91,
277
- brightGreen: 92,
278
- brightYellow: 93,
279
- brightBlue: 94,
280
- brightMagenta: 95,
281
- brightCyan: 96,
282
- brightWhite: 97,
283
- }
284
- if (map[color]) prefix += `\x1b[${map[color]}m`
285
- }
286
- }
287
- // BgColor
288
- if (style.bgColor) {
289
- const color = style.bgColor
290
- if (/^#[0-9a-f]{3,6}$/i.test(color)) {
291
- const hex = color.replace('#', '')
292
- const rgb =
293
- hex.length === 3
294
- ? [0, 1, 2].map((i) => parseInt(hex[i] + hex[i], 16))
295
- : [0, 2, 4].map((i) => parseInt(hex.slice(i, i + 2), 16))
296
- prefix += `\x1b[48;2;${rgb[0]};${rgb[1]};${rgb[2]}m`
297
- } else if (/^\d+$/.test(color)) {
298
- prefix += `\x1b[48;5;${color}m`
299
- } else {
300
- const map = {
301
- black: 40,
302
- red: 41,
303
- green: 42,
304
- yellow: 43,
305
- blue: 44,
306
- magenta: 45,
307
- cyan: 46,
308
- white: 47,
309
- gray: 100,
310
- grey: 100,
311
- brightRed: 101,
312
- brightGreen: 102,
313
- brightYellow: 103,
314
- brightBlue: 104,
315
- brightMagenta: 105,
316
- brightCyan: 106,
317
- brightWhite: 107,
318
- }
319
- if (map[color]) prefix += `\x1b[${map[color]}m`
320
- }
321
- }
322
- return prefix ? prefix + out + suffix : out
323
- }
324
-
325
- /**
326
- * Merge style objects, rightmost has priority.
327
- * @param {...object} styles
328
- * @returns {object}
329
- */
330
- function mergeStyles(...styles) {
331
- return Object.assign({}, ...styles)
332
- }
333
-
334
- /**
335
- * Parse cell for value and style.
336
- * @param {any} cell
337
- * @param {object} inherited
338
- * @returns {{text: string, style: object}}
339
- */
340
- function parseCell(cell, inherited = {}) {
341
- if (typeOf(Array)(cell)) {
342
- if (cell.length === 2 && typeOf(Object)(cell[1])) {
343
- return { text: String(cell[0]), style: mergeStyles(inherited, cell[1]) }
344
- }
345
- return { text: cell.map((c) => parseCell(c, inherited).text).join(''), style: inherited }
346
- }
347
- if (typeOf(Object)(cell)) {
348
- return { text: '', style: mergeStyles(inherited, cell) }
349
- }
350
- if (typeof cell === 'string' && cell.startsWith('<') && cell.endsWith('>')) {
351
- // Simple XML-like tag parser for <b>, <i>, <u>, <s>, <fg=...>, <bg=...>
352
- let text = cell
353
- let style = { ...inherited }
354
- const tagPattern = /<([bius]|fg|bg)(?:=([#\w\d]+))?>|<\/([bius]|fg|bg)>/gi
355
- let stack = []
356
- let result = ''
357
- let lastIndex = 0
358
- let m
359
- while ((m = tagPattern.exec(cell))) {
360
- result += cell.slice(lastIndex, m.index)
361
- lastIndex = tagPattern.lastIndex
362
- if (m[1]) {
363
- // Opening tag
364
- let tag = m[1]
365
- let val = m[2]
366
- let newStyle = { ...(stack.length ? stack[stack.length - 1] : style) }
367
- switch (tag) {
368
- case 'b':
369
- newStyle.bold = true
370
- break
371
- case 'i':
372
- newStyle.italic = true
373
- break
374
- case 'u':
375
- newStyle.underline = true
376
- break
377
- case 's':
378
- newStyle.strikethrough = true
379
- break
380
- case 'fg':
381
- newStyle.color = val
382
- break
383
- case 'bg':
384
- newStyle.bgColor = val
385
- break
386
- }
387
- stack.push(newStyle)
388
- } else if (m[3]) {
389
- // Closing tag
390
- stack.pop()
391
- }
392
- }
393
- result += cell.slice(lastIndex)
394
- let finalStyle = stack.length ? stack[stack.length - 1] : style
395
- return { text: result, style: finalStyle }
396
- }
397
- return { text: String(cell), style: inherited }
398
- }
399
-
400
- // Determine frame-level style
401
- let frameStyle = {}
402
- if (
403
- typeOf(Array)(this.value) &&
404
- this.value.length &&
405
- typeOf(Object)(this.value[this.value.length - 1])
406
- ) {
407
- frameStyle = this.value[this.value.length - 1]
408
- }
409
-
410
- let rows = this.value
411
- .filter((row) => !(typeOf(Object)(row) && !typeOf(Array)(row)))
412
- .map((row) => {
413
- let rowStyle = frameStyle
414
- let cells = row
415
- if (typeOf(Array)(row) && row.length && typeOf(Object)(row[row.length - 1])) {
416
- rowStyle = mergeStyles(frameStyle, row[row.length - 1])
417
- cells = row.slice(0, -1)
418
- }
419
- if (!typeOf(Array)(cells)) cells = [cells]
420
- let styled = cells.map((cell) => {
421
- const { text, style } = parseCell(cell, mergeStyles(props, rowStyle))
422
- return applyStyle(text, style)
423
- })
424
- return styled.join('')
425
- })
426
-
427
- if (method === FrameRenderMethod.REPLACE) {
428
- let emptyRows = rows.map((row) => ' '.repeat(this.lengthOf(row)))
429
- if (rows.length > this.height) {
430
- emptyRows = emptyRows.slice(0, this.height)
431
- rows = rows.slice(0, this.height)
432
- }
433
- rows = [...emptyRows, Frame.BOF, ...rows]
434
- } else if (method === FrameRenderMethod.APPEND) {
435
- rows = rows.map((row) => {
436
- const used = this.lengthOf(row)
437
- const left = this.width - used
438
- row = row + ' '.repeat(Math.max(0, left))
439
- if (stringWidth(row) > this.width) {
440
- let acc = ''
441
- let w = 0
442
- for (let ch of row) {
443
- let chW = stringWidth(ch)
444
- if (w + chW > this.width) break
445
- acc += ch
446
- w += chW
447
- }
448
- row = acc
449
- }
450
- return row
451
- })
452
- if (rows.length > this.height) {
453
- rows = rows.slice(0, this.height)
454
- }
455
- } else if (method === FrameRenderMethod.VISIBLE) {
456
- const moveUpLines = Math.max(
457
- 0,
458
- Math.min(rows.length, this.height >= 0 ? this.height : rows.length),
459
- )
460
- rows = [`\x1b[${moveUpLines}A${Frame.BOF}`, ...rows]
461
- if (rows.length > this.height && this.height >= 0) {
462
- rows = rows.slice(0, this.height + 1) // +1 for the cursor move line
463
- }
464
- } else {
465
- if (rows.length > this.height) {
466
- rows = rows.slice(0, this.height)
467
- }
468
- rows = [Frame.BOF, ...rows]
469
- }
470
-
471
- this.imprint = rows.join('\n')
472
- return this.imprint
473
- }
474
- /**
475
- * Convert the frame to its string representation.
476
- * @returns {string} The frame's imprint.
477
- */
478
- toString() {
479
- return this.imprint
480
- }
481
- /**
482
- * Transform each cell in the frame using a function.
483
- * @param {Function} fn - Function to apply to each cell.
484
- * @returns {Frame} A new Frame with transformed values.
485
- */
486
- transform(fn) {
487
- const value = this.value.map((row) => row.map(fn))
488
- return new Frame({ ...this, value })
489
- }
490
- /**
491
- * Set the window size for the frame.
492
- * @param {number} width - The width of the window.
493
- * @param {number} height - The height of the window.
494
- */
495
- setWindowSize(width, height) {
496
- this.width = Math.max(0, Number(width))
497
- this.height = Math.max(0, Number(height))
498
- this.render()
499
- }
500
- /**
501
- * Check if a value can be used to create a Frame instance.
502
- * @param {*} value - Value to check.
503
- * @returns {boolean} True if the value is valid for Frame creation.
504
- */
505
- static is(value) {
506
- try {
507
- new Frame(value)
508
- return true
509
- } catch {
510
- return false
511
- }
512
- }
513
- /**
514
- * Create a Frame instance from input.
515
- * @param {*} input - Input value to convert.
516
- * @returns {Frame} A new Frame instance.
517
- */
518
- static from(input) {
519
- if (input instanceof Frame) return input
520
- if (input?.value instanceof Frame) return new Frame(to(Object)(input.value))
521
- if ('string' === typeof input) input = [input]
522
- if (Array.isArray(input)) return new Frame({ value: input })
523
- return new Frame(input)
524
- }
525
- /**
526
- * Create a function to space columns based on options.
527
- * @param {object} options - Spacing options.
528
- * @param {number[]} [options.cols=[]] - Widths of the columns.
529
- * @param {number} [options.padding=1] - Padding between columns.
530
- * @param {string[]} [options.aligns=[]] - Alignment for each column ('l' or 'r').
531
- * @returns {Function} Function that spaces a row.
532
- */
533
- static spaces(options = {}) {
534
- const { cols = [], padding = 1, aligns = [] } = options
535
- return (row) =>
536
- row.map((str, i) => {
537
- const pad = ' '.repeat(cols[i] - str.length + padding)
538
- return aligns[i] === 'r' ? pad + str : str + pad
539
- })
540
- }
541
- /**
542
- *
543
- * @param {Array} arr
544
- * @returns {(v) => number[]}
545
- */
546
- static weight(arr) {
547
- return (Fn = (v) => v) => {
548
- const cols = []
549
- arr.forEach((m) => {
550
- Fn(m).forEach((str, i) => {
551
- if (undefined === cols[i]) cols[i] = 0
552
- cols[i] = Math.max(String(str).length, cols[i])
553
- })
554
- })
555
- return cols
556
- }
557
- }
558
- /**
559
- *
560
- * @param {object} options
561
- * @param {Function} [options.fn=(fn = v => v)] - Function to calculate weight.
562
- * @param {number[]} [options.cols=[]] - Widths of the columns.
563
- * @param {number} [options.padding=1] - The padding between columns.
564
- * @param {string[]} [options.aligns=[]] - The column aligns: l, r
565
- * @returns {(arr: []) => string[][]}
566
- */
567
- static table(options = {}) {
568
- const { fn = (v) => v, cols: initialCols = [], padding = 1, aligns = [] } = options
569
- return (arr) => {
570
- let cols = initialCols
571
- if (empty(cols)) {
572
- cols = Frame.weight(arr)(fn)
573
- }
574
- return arr.map((row) => Frame.spaces({ cols, padding, aligns })(row))
575
- }
576
- }
577
- /**
578
- * Move cursor up by specified lines.
579
- * @param {number} [lines=1] - Number of lines to move up.
580
- * @returns {string} ANSI escape code for cursor movement.
581
- */
582
- static cursorUp(lines = 1) {
583
- return `\x1b[${lines}A`
584
- }
585
- /**
586
- * Move cursor down by specified lines.
587
- * @param {number} [lines=1] - Number of lines to move down.
588
- * @returns {string} ANSI escape code for cursor movement.
589
- */
590
- static cursorDown(lines = 1) {
591
- return `\x1b[${lines}B`
592
- }
593
- /**
594
- * Clear the current line.
595
- * @param {string} [str="\r"] - String to append after clearing.
596
- * @returns {string} ANSI escape code for line clearing followed by the string.
597
- */
598
- static clearLine(str = '\r') {
599
- return Frame.CLEAR_LINE + str
600
- }
601
- /**
602
- * Clear the entire screen.
603
- * @returns {string} ANSI escape codes for screen clearing.
604
- */
605
- static clearScreen() {
606
- return '\x1b[2J\x1b[0;0H'
607
- }
608
- }
@@ -1,96 +0,0 @@
1
- import { typeOf, ObjectWithAlias } from '@nan0web/types'
2
-
3
- /**
4
- * Represents default styling properties for Frame rendering.
5
- * Every tag must be a separate value in the array of rows/columns.
6
- * If you want to apply the same props to multiple values, you can use an array of values.
7
- * If you want to apply different props to multiple values, you can use an object with the props.
8
- * If you want to apply props to a single value, you can use a string with the props in XML format.
9
- * Parser checks every atom for its beginning and end and if it's a tag, it applies the props to the value.
10
- *
11
- * @example
12
- * const defaultProps = new FrameProps({
13
- * color: "red",
14
- * bgColor: "blue",
15
- * bold: true,
16
- * italic: true,
17
- * underline: true,
18
- * strikethrough: true,
19
- * })
20
- * or by aliases:
21
- * const defaultProps = new FrameProps({
22
- * fg: "red",
23
- * bg: "blue",
24
- * b: true,
25
- * i: true,
26
- * u: true,
27
- * s: true,
28
- * })
29
- * from an array of strings:
30
- * const rows = [
31
- * ["Hello", "World"],
32
- * ["<fg=red>Hello</>", "<bg=blue>World</>"],
33
- * ["<b>Hello</b>", "<i>World</i>"],
34
- * ["<u>Hello</u>", "<s>World</s>"],
35
- * ["<b fg=red>Hello</b>", "<i bg=blue>World</i>"],
36
- * ["<b i>Hello</b>", "<i b>World</i>"],
37
- * ["<b i s>Some</b>", ["thing", {b: true, i: true, s: true}]],
38
- * [["Hello", "World", {b: true}]],
39
- * ]
40
- * const defaultProps = new FrameProps(rows)
41
- */
42
- class FrameProps extends ObjectWithAlias {
43
- /**
44
- * Property aliases for shorthand notation.
45
- * @type {Record<string, string>}
46
- */
47
- static ALIAS = {
48
- fg: 'color',
49
- bg: 'bgColor',
50
- b: 'bold',
51
- i: 'italic',
52
- u: 'underline',
53
- s: 'strikethrough',
54
- }
55
-
56
- /**
57
- * @param {object} props - Frame properties
58
- * @param {string} [props.color=""] - Text color
59
- * @param {string} [props.bgColor=""] - Background color
60
- * @param {boolean} [props.bold=false] - Bold text flag
61
- * @param {boolean} [props.italic=false] - Italic text flag
62
- * @param {boolean} [props.underline=false] - Underline text flag
63
- * @param {boolean} [props.strikethrough=false] - Strikethrough text flag
64
- */
65
- constructor(props = {}) {
66
- super()
67
- const {
68
- color = '',
69
- bgColor = '',
70
- bold = false,
71
- italic = false,
72
- underline = false,
73
- strikethrough = false,
74
- } = props
75
-
76
- /** @type {string} Text color */
77
- this.color = color
78
-
79
- /** @type {string} Background color */
80
- this.bgColor = bgColor
81
-
82
- /** @type {boolean} Bold text flag */
83
- this.bold = bold
84
-
85
- /** @type {boolean} Italic text flag */
86
- this.italic = italic
87
-
88
- /** @type {boolean} Underline text flag */
89
- this.underline = underline
90
-
91
- /** @type {boolean} Strikethrough text flag */
92
- this.strikethrough = strikethrough
93
- }
94
- }
95
-
96
- export default FrameProps