termcast 1.3.50 → 1.3.51
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.
- package/dist/apis/environment.d.ts +1 -0
- package/dist/apis/environment.d.ts.map +1 -1
- package/dist/apis/environment.js +5 -0
- package/dist/apis/environment.js.map +1 -1
- package/dist/app.d.ts +33 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +1125 -0
- package/dist/app.js.map +1 -0
- package/dist/cli.js +80 -0
- package/dist/cli.js.map +1 -1
- package/dist/components/detail.d.ts.map +1 -1
- package/dist/components/detail.js +20 -17
- package/dist/components/detail.js.map +1 -1
- package/dist/components/dropdown.d.ts.map +1 -1
- package/dist/components/dropdown.js +3 -2
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/footer.d.ts +6 -0
- package/dist/components/footer.d.ts.map +1 -1
- package/dist/components/footer.js +15 -6
- package/dist/components/footer.js.map +1 -1
- package/dist/components/form/checkbox.d.ts.map +1 -1
- package/dist/components/form/checkbox.js +1 -13
- package/dist/components/form/checkbox.js.map +1 -1
- package/dist/components/form/date-picker.js +2 -2
- package/dist/components/form/date-picker.js.map +1 -1
- package/dist/components/form/description.js +1 -1
- package/dist/components/form/description.js.map +1 -1
- package/dist/components/form/dropdown.d.ts.map +1 -1
- package/dist/components/form/dropdown.js +19 -3
- package/dist/components/form/dropdown.js.map +1 -1
- package/dist/components/form/file-picker.d.ts.map +1 -1
- package/dist/components/form/file-picker.js +22 -4
- package/dist/components/form/file-picker.js.map +1 -1
- package/dist/components/form/index.d.ts +3 -1
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +6 -4
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/form/password-field.js +3 -3
- package/dist/components/form/password-field.js.map +1 -1
- package/dist/components/form/text-area.d.ts.map +1 -1
- package/dist/components/form/text-area.js +29 -6
- package/dist/components/form/text-area.js.map +1 -1
- package/dist/components/form/text-field.js +3 -3
- package/dist/components/form/text-field.js.map +1 -1
- package/dist/components/heatmap.d.ts +80 -0
- package/dist/components/heatmap.d.ts.map +1 -0
- package/dist/components/heatmap.js +405 -0
- package/dist/components/heatmap.js.map +1 -0
- package/dist/components/list.d.ts +2 -0
- package/dist/components/list.d.ts.map +1 -1
- package/dist/components/list.js +80 -52
- package/dist/components/list.js.map +1 -1
- package/dist/components/markdown.d.ts +7 -0
- package/dist/components/markdown.d.ts.map +1 -0
- package/dist/components/markdown.js +19 -0
- package/dist/components/markdown.js.map +1 -0
- package/dist/components/metadata.d.ts.map +1 -1
- package/dist/components/metadata.js +4 -1
- package/dist/components/metadata.js.map +1 -1
- package/dist/components/progress-bar.d.ts +37 -0
- package/dist/components/progress-bar.d.ts.map +1 -0
- package/dist/components/progress-bar.js +34 -0
- package/dist/components/progress-bar.js.map +1 -0
- package/dist/components/table.d.ts +3 -2
- package/dist/components/table.d.ts.map +1 -1
- package/dist/components/table.js +78 -63
- package/dist/components/table.js.map +1 -1
- package/dist/diagram-parser.d.ts +17 -3
- package/dist/diagram-parser.d.ts.map +1 -1
- package/dist/diagram-parser.js +17 -3
- package/dist/diagram-parser.js.map +1 -1
- package/dist/examples/list-slot.d.ts +2 -0
- package/dist/examples/list-slot.d.ts.map +1 -0
- package/dist/examples/list-slot.js +14 -0
- package/dist/examples/list-slot.js.map +1 -0
- package/dist/examples/list-with-dropdown.js +2 -4
- package/dist/examples/list-with-dropdown.js.map +1 -1
- package/dist/examples/simple-heatmap.d.ts +2 -0
- package/dist/examples/simple-heatmap.d.ts.map +1 -0
- package/dist/examples/simple-heatmap.js +37 -0
- package/dist/examples/simple-heatmap.js.map +1 -0
- package/dist/examples/simple-progress-bar.d.ts +2 -0
- package/dist/examples/simple-progress-bar.d.ts.map +1 -0
- package/dist/examples/simple-progress-bar.js +36 -0
- package/dist/examples/simple-progress-bar.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/date-picker-widget.d.ts.map +1 -1
- package/dist/internal/date-picker-widget.js +5 -4
- package/dist/internal/date-picker-widget.js.map +1 -1
- package/dist/internal/navigation.d.ts.map +1 -1
- package/dist/internal/navigation.js +7 -2
- package/dist/internal/navigation.js.map +1 -1
- package/dist/internal/providers.d.ts.map +1 -1
- package/dist/internal/providers.js +42 -4
- package/dist/internal/providers.js.map +1 -1
- package/dist/logger.js +6 -1
- package/dist/logger.js.map +1 -1
- package/dist/state.d.ts +2 -0
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +31 -2
- package/dist/state.js.map +1 -1
- package/dist/theme.d.ts +1 -0
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +23 -1
- package/dist/theme.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +6 -1
- package/dist/utils.js.map +1 -1
- package/package.json +3 -3
- package/src/apis/environment.tsx +6 -0
- package/src/app.tsx +1487 -0
- package/src/assets/default-app-icon.png +0 -0
- package/src/cli.tsx +105 -0
- package/src/components/detail.tsx +32 -22
- package/src/components/dropdown.tsx +3 -2
- package/src/components/footer.tsx +37 -7
- package/src/components/form/checkbox.tsx +2 -17
- package/src/components/form/date-picker.tsx +2 -2
- package/src/components/form/description.tsx +1 -1
- package/src/components/form/dropdown.tsx +22 -3
- package/src/components/form/file-picker.tsx +33 -10
- package/src/components/form/index.tsx +10 -6
- package/src/components/form/password-field.tsx +3 -3
- package/src/components/form/text-area.tsx +31 -6
- package/src/components/form/text-field.tsx +3 -3
- package/src/components/heatmap.tsx +584 -0
- package/src/components/list.tsx +135 -72
- package/src/components/markdown.tsx +30 -0
- package/src/components/metadata.tsx +9 -2
- package/src/components/progress-bar.tsx +112 -0
- package/src/components/table.tsx +88 -71
- package/src/diagram-parser.tsx +17 -3
- package/src/examples/bar-graph-weekly.vitest.tsx +4 -4
- package/src/examples/detail-metadata-showcase.vitest.tsx +12 -12
- package/src/examples/form-basic.vitest.tsx +117 -16
- package/src/examples/graph-bar-chart.vitest.tsx +2 -2
- package/src/examples/graph-row.vitest.tsx +10 -10
- package/src/examples/internal/descendants-rerender.vitest.tsx +94 -46
- package/src/examples/internal/simple-scrollbox.vitest.tsx +38 -14
- package/src/examples/list-dropdown-default.vitest.tsx +78 -58
- package/src/examples/list-slot.tsx +38 -0
- package/src/examples/list-with-detail.vitest.tsx +8 -8
- package/src/examples/list-with-dropdown.tsx +2 -2
- package/src/examples/list-with-dropdown.vitest.tsx +16 -16
- package/src/examples/list-with-sections.vitest.tsx +45 -32
- package/src/examples/simple-detail-table.vitest.tsx +2 -2
- package/src/examples/simple-file-picker.vitest.tsx +1 -1
- package/src/examples/simple-grid.vitest.tsx +27 -53
- package/src/examples/simple-heatmap.tsx +63 -0
- package/src/examples/simple-heatmap.vitest.tsx +88 -0
- package/src/examples/simple-progress-bar.tsx +82 -0
- package/src/examples/simple-progress-bar.vitest.tsx +72 -0
- package/src/examples/table-edge-cases.vitest.tsx +1 -1
- package/src/index.tsx +19 -0
- package/src/internal/date-picker-widget.tsx +23 -12
- package/src/internal/navigation.tsx +7 -2
- package/src/internal/providers.tsx +48 -3
- package/src/logger.tsx +6 -1
- package/src/state.tsx +38 -2
- package/src/theme.tsx +26 -2
- package/src/utils.tsx +6 -1
package/src/components/table.tsx
CHANGED
|
@@ -79,7 +79,9 @@ export class TableRenderable extends Renderable {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
set rows(value: TableCellContent[][]) {
|
|
82
|
-
const
|
|
82
|
+
const newColCount = value[0]?.length || 0
|
|
83
|
+
const oldColCount = this._rows[0]?.length || 0
|
|
84
|
+
const structureChanged = value.length !== this._rows.length || newColCount !== oldColCount
|
|
83
85
|
this._rows = value
|
|
84
86
|
this._tableDirty = true
|
|
85
87
|
if (structureChanged) this._tableStructureDirty = true
|
|
@@ -164,7 +166,7 @@ export class TableRenderable extends Renderable {
|
|
|
164
166
|
this.remove(child.id)
|
|
165
167
|
}
|
|
166
168
|
|
|
167
|
-
const colCount = this._headers.length
|
|
169
|
+
const colCount = this._headers.length || this._rows[0]?.length || 0
|
|
168
170
|
if (colCount === 0 || this._rows.length === 0) return
|
|
169
171
|
|
|
170
172
|
const headingStyle =
|
|
@@ -193,31 +195,35 @@ export class TableRenderable extends Renderable {
|
|
|
193
195
|
headerFg: StyleDefinition['fg'],
|
|
194
196
|
stripeBg: StyleDefinition['fg'],
|
|
195
197
|
): void {
|
|
198
|
+
const hasHeaders = this._headers.length > 0
|
|
199
|
+
|
|
196
200
|
for (let col = 0; col < colCount; col++) {
|
|
197
201
|
const columnBox = new BoxRenderable(this.ctx, {
|
|
198
202
|
id: `${this.id}-col-${col}`,
|
|
199
203
|
flexDirection: 'column',
|
|
200
204
|
})
|
|
201
205
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
206
|
+
if (hasHeaders) {
|
|
207
|
+
const headerContent = this._headers[col] ?? ''
|
|
208
|
+
let headerStyledText = this.toStyledText(headerContent)
|
|
209
|
+
headerStyledText = this.styledHeaderChunks(headerStyledText, headingStyle, headerFg)
|
|
205
210
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
211
|
+
const headerBox = new BoxRenderable(this.ctx, {
|
|
212
|
+
id: `${this.id}-col-${col}-header-box`,
|
|
213
|
+
backgroundColor: headerBg,
|
|
214
|
+
})
|
|
215
|
+
headerBox.add(
|
|
216
|
+
new TextRenderable(this.ctx, {
|
|
217
|
+
id: `${this.id}-col-${col}-header`,
|
|
218
|
+
content: headerStyledText,
|
|
219
|
+
height: 1,
|
|
220
|
+
overflow: 'hidden',
|
|
221
|
+
paddingLeft: 1,
|
|
222
|
+
paddingRight: 1,
|
|
223
|
+
}),
|
|
224
|
+
)
|
|
225
|
+
columnBox.add(headerBox)
|
|
226
|
+
}
|
|
221
227
|
|
|
222
228
|
for (let row = 0; row < this._rows.length; row++) {
|
|
223
229
|
const cell = this._rows[row]?.[col] ?? ''
|
|
@@ -254,28 +260,30 @@ export class TableRenderable extends Renderable {
|
|
|
254
260
|
headerFg: StyleDefinition['fg'],
|
|
255
261
|
stripeBg: StyleDefinition['fg'],
|
|
256
262
|
): void {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
263
|
+
if (this._headers.length > 0) {
|
|
264
|
+
const headerRow = new BoxRenderable(this.ctx, {
|
|
265
|
+
id: `${this.id}-header-row`,
|
|
266
|
+
flexDirection: 'row',
|
|
267
|
+
backgroundColor: headerBg,
|
|
268
|
+
})
|
|
269
|
+
for (let col = 0; col < colCount; col++) {
|
|
270
|
+
const headerContent = this._headers[col] ?? ''
|
|
271
|
+
let headerStyledText = this.toStyledText(headerContent)
|
|
272
|
+
headerStyledText = this.styledHeaderChunks(headerStyledText, headingStyle, headerFg)
|
|
273
|
+
|
|
274
|
+
headerRow.add(
|
|
275
|
+
new TextRenderable(this.ctx, {
|
|
276
|
+
id: `${this.id}-header-${col}`,
|
|
277
|
+
content: headerStyledText,
|
|
278
|
+
flexGrow: 1,
|
|
279
|
+
flexBasis: 0,
|
|
280
|
+
paddingLeft: 1,
|
|
281
|
+
paddingRight: 1,
|
|
282
|
+
}),
|
|
283
|
+
)
|
|
284
|
+
}
|
|
285
|
+
this.add(headerRow)
|
|
277
286
|
}
|
|
278
|
-
this.add(headerRow)
|
|
279
287
|
|
|
280
288
|
for (let row = 0; row < this._rows.length; row++) {
|
|
281
289
|
const isOddRow = row % 2 === 1
|
|
@@ -322,7 +330,8 @@ export class TableRenderable extends Renderable {
|
|
|
322
330
|
const stripeBg = concealStyle?.bg
|
|
323
331
|
|
|
324
332
|
const columns = (this as any)._childrenInLayoutOrder as Renderable[]
|
|
325
|
-
const colCount = this._headers.length
|
|
333
|
+
const colCount = this._headers.length || this._rows[0]?.length || 0
|
|
334
|
+
const hasHeaders = this._headers.length > 0
|
|
326
335
|
|
|
327
336
|
for (let col = 0; col < colCount; col++) {
|
|
328
337
|
const columnBox = columns[col]
|
|
@@ -330,23 +339,26 @@ export class TableRenderable extends Renderable {
|
|
|
330
339
|
|
|
331
340
|
const columnChildren = (columnBox as any)._childrenInLayoutOrder as Renderable[]
|
|
332
341
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
headerBox
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
342
|
+
if (hasHeaders) {
|
|
343
|
+
// Update header
|
|
344
|
+
const headerBox = columnChildren[0]
|
|
345
|
+
if (headerBox instanceof BoxRenderable) {
|
|
346
|
+
headerBox.backgroundColor = headerBg ?? 'transparent'
|
|
347
|
+
const headerChildren = (headerBox as any)._childrenInLayoutOrder as Renderable[]
|
|
348
|
+
const headerText = headerChildren[0]
|
|
349
|
+
if (headerText instanceof TextRenderable) {
|
|
350
|
+
const headerContent = this._headers[col] ?? ''
|
|
351
|
+
let headerStyledText = this.toStyledText(headerContent)
|
|
352
|
+
headerStyledText = this.styledHeaderChunks(headerStyledText, headingStyle, headerFg)
|
|
353
|
+
headerText.content = headerStyledText
|
|
354
|
+
}
|
|
344
355
|
}
|
|
345
356
|
}
|
|
346
357
|
|
|
347
358
|
// Update data rows
|
|
359
|
+
const rowOffset = hasHeaders ? 1 : 0
|
|
348
360
|
for (let row = 0; row < this._rows.length; row++) {
|
|
349
|
-
const cellContainer = columnChildren[row +
|
|
361
|
+
const cellContainer = columnChildren[row + rowOffset]
|
|
350
362
|
if (cellContainer instanceof BoxRenderable) {
|
|
351
363
|
const isOddRow = row % 2 === 1
|
|
352
364
|
cellContainer.backgroundColor = isOddRow && stripeBg ? stripeBg : 'transparent'
|
|
@@ -370,25 +382,29 @@ export class TableRenderable extends Renderable {
|
|
|
370
382
|
const stripeBg = concealStyle?.bg
|
|
371
383
|
|
|
372
384
|
const allRows = (this as any)._childrenInLayoutOrder as Renderable[]
|
|
373
|
-
const colCount = this._headers.length
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if (
|
|
377
|
-
headerRow
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
const
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
385
|
+
const colCount = this._headers.length || this._rows[0]?.length || 0
|
|
386
|
+
const hasHeaders = this._headers.length > 0
|
|
387
|
+
|
|
388
|
+
if (hasHeaders) {
|
|
389
|
+
const headerRow = allRows[0]
|
|
390
|
+
if (headerRow instanceof BoxRenderable) {
|
|
391
|
+
headerRow.backgroundColor = headerBg ?? 'transparent'
|
|
392
|
+
const headerCells = (headerRow as any)._childrenInLayoutOrder as Renderable[]
|
|
393
|
+
for (let col = 0; col < colCount; col++) {
|
|
394
|
+
const headerText = headerCells[col]
|
|
395
|
+
if (headerText instanceof TextRenderable) {
|
|
396
|
+
const headerContent = this._headers[col] ?? ''
|
|
397
|
+
let headerStyledText = this.toStyledText(headerContent)
|
|
398
|
+
headerStyledText = this.styledHeaderChunks(headerStyledText, headingStyle, headerFg)
|
|
399
|
+
headerText.content = headerStyledText
|
|
400
|
+
}
|
|
386
401
|
}
|
|
387
402
|
}
|
|
388
403
|
}
|
|
389
404
|
|
|
405
|
+
const rowOffset = hasHeaders ? 1 : 0
|
|
390
406
|
for (let row = 0; row < this._rows.length; row++) {
|
|
391
|
-
const rowBox = allRows[row +
|
|
407
|
+
const rowBox = allRows[row + rowOffset]
|
|
392
408
|
if (!(rowBox instanceof BoxRenderable)) continue
|
|
393
409
|
|
|
394
410
|
const isOddRow = row % 2 === 1
|
|
@@ -461,8 +477,9 @@ type TableLayoutProps = Partial<
|
|
|
461
477
|
>
|
|
462
478
|
|
|
463
479
|
export interface TableProps extends TableLayoutProps {
|
|
464
|
-
/** Column header labels
|
|
465
|
-
headers
|
|
480
|
+
/** Column header labels. When omitted, no header row is rendered — useful for
|
|
481
|
+
* key-value tables where headers like "Field"/"Value" add no information. */
|
|
482
|
+
headers?: string[]
|
|
466
483
|
/** Row data – each inner array is one row of cell strings */
|
|
467
484
|
rows: string[][]
|
|
468
485
|
/** When true, cell text wraps instead of being truncated to one line. Default false. */
|
|
@@ -474,7 +491,7 @@ export interface TableProps extends TableLayoutProps {
|
|
|
474
491
|
}
|
|
475
492
|
|
|
476
493
|
export function Table(props: TableProps): any {
|
|
477
|
-
const { headers, rows, wrapText, width = '100%', height, ...layoutProps } = props
|
|
494
|
+
const { headers = [], rows, wrapText, width = '100%', height, ...layoutProps } = props
|
|
478
495
|
|
|
479
496
|
const themeName = useStore((state) => state.currentThemeName)
|
|
480
497
|
const syntaxStyle = React.useMemo(() => {
|
package/src/diagram-parser.tsx
CHANGED
|
@@ -128,9 +128,23 @@ export function diagramToDebugString(parsed: ParsedDiagramLine[]): string {
|
|
|
128
128
|
|
|
129
129
|
/**
|
|
130
130
|
* Convert ASCII diagram characters to Unicode box-drawing equivalents.
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
131
|
+
*
|
|
132
|
+
* ASCII glyphs have visible gaps in monospaced fonts because they don't
|
|
133
|
+
* fill the entire terminal cell:
|
|
134
|
+
* `|` — the pipe glyph is shorter than the cell height, so stacked
|
|
135
|
+
* pipes show a gap between every row.
|
|
136
|
+
* `-` — the hyphen glyph is narrower than the cell width and sits
|
|
137
|
+
* centered, so consecutive hyphens show gaps between each cell.
|
|
138
|
+
*
|
|
139
|
+
* Unicode box-drawing characters are designed to span the full cell,
|
|
140
|
+
* connecting seamlessly with adjacent cells:
|
|
141
|
+
* `│` (U+2502) — fills full cell height, no vertical gaps.
|
|
142
|
+
* `─` (U+2500) — fills full cell width, no horizontal gaps.
|
|
143
|
+
*
|
|
144
|
+
* Replacements:
|
|
145
|
+
* `|` -> `│` (every occurrence)
|
|
146
|
+
* `--` (2+) -> `──` (only runs of 2+ to avoid converting single
|
|
147
|
+
* hyphens in regular text like "e-mail")
|
|
134
148
|
*/
|
|
135
149
|
export function convertAsciiToUnicode(content: string): string {
|
|
136
150
|
return content
|
|
@@ -87,10 +87,10 @@ test('many columns (20) clips with overflow hidden', async () => {
|
|
|
87
87
|
|
|
88
88
|
> Search...
|
|
89
89
|
|
|
90
|
-
Weekly Traffic 3 channels across 6 d │
|
|
91
|
-
Revenue by Region EMEA / APAC / Amer │
|
|
92
|
-
Server Load CPU / Memory / IO │ ███
|
|
93
|
-
›Many Columns (20) Overflow test with │ ███ ███
|
|
90
|
+
Weekly Traffic 3 channels across 6 d │
|
|
91
|
+
Revenue by Region EMEA / APAC / Amer │ ███
|
|
92
|
+
Server Load CPU / Memory / IO │ ███ ███ ███ ███ ██
|
|
93
|
+
›Many Columns (20) Overflow test with │ ███ ███ ███ ███ ███ ███ ██
|
|
94
94
|
Many Series (8) Legend overflow test │ ███ ███ ███ ███ ███ ███ ███ ███ ██
|
|
95
95
|
Long Labels Labels wider than bar co │ ███ ███ ███ ███ ███ ███ ███ ███ ██
|
|
96
96
|
Week 1 vs Week 2 Two graphs in a Row │ ███ ███ ███ ███ ███ ███ ███ ███ ██
|
|
@@ -91,7 +91,7 @@ test('detail metadata showcase renders markdown and metadata together', async ()
|
|
|
91
91
|
|
|
92
92
|
Team: Platform
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
─────────────────────────────────────────────────────────────────────────────────────────────
|
|
95
95
|
|
|
96
96
|
Status: Active
|
|
97
97
|
|
|
@@ -101,7 +101,7 @@ test('detail metadata showcase renders markdown and metadata together', async ()
|
|
|
101
101
|
|
|
102
102
|
Risk: Medium
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
─────────────────────────────────────────────────────────────────────────────────────────────
|
|
105
105
|
|
|
106
106
|
Description: This is a comprehensive metadata showcase that demonstrates all the different
|
|
107
107
|
ways you can display information using the Detail.Metadata component.
|
|
@@ -112,7 +112,7 @@ test('detail metadata showcase renders markdown and metadata together', async ()
|
|
|
112
112
|
|
|
113
113
|
Reviewer: Bob Smith
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
─────────────────────────────────────────────────────────────────────────────────────────────
|
|
116
116
|
|
|
117
117
|
Repository: github.com/example
|
|
118
118
|
|
|
@@ -120,7 +120,7 @@ test('detail metadata showcase renders markdown and metadata together', async ()
|
|
|
120
120
|
|
|
121
121
|
PR Link: github.com/organization/repository/pull/12345
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
─────────────────────────────────────────────────────────────────────────────────────────────
|
|
124
124
|
|
|
125
125
|
Labels: documentation enhancement good first issue
|
|
126
126
|
|
|
@@ -134,7 +134,7 @@ test('detail metadata showcase renders markdown and metadata together', async ()
|
|
|
134
134
|
|
|
135
135
|
Due Date: 2024-02-01
|
|
136
136
|
|
|
137
|
-
|
|
137
|
+
─────────────────────────────────────────────────────────────────────────────────────────────
|
|
138
138
|
|
|
139
139
|
Metrics
|
|
140
140
|
|
|
@@ -147,6 +147,7 @@ test('detail metadata showcase renders markdown and metadata together', async ()
|
|
|
147
147
|
Watchers: @alice @bob @charlie
|
|
148
148
|
|
|
149
149
|
|
|
150
|
+
esc go back ^k actions powered by termcast.app
|
|
150
151
|
|
|
151
152
|
|
|
152
153
|
|
|
@@ -159,7 +160,6 @@ test('detail metadata showcase renders markdown and metadata together', async ()
|
|
|
159
160
|
|
|
160
161
|
|
|
161
162
|
|
|
162
|
-
esc go back ^k actions powered by termcast.app
|
|
163
163
|
|
|
164
164
|
"
|
|
165
165
|
`)
|
|
@@ -443,7 +443,7 @@ test('detail metadata renders tag lists with multiple items', async () => {
|
|
|
443
443
|
|
|
444
444
|
Team: Platform
|
|
445
445
|
|
|
446
|
-
|
|
446
|
+
─────────────────────────────────────────────────────────────────────────────────────────────
|
|
447
447
|
|
|
448
448
|
Status: Active
|
|
449
449
|
|
|
@@ -453,7 +453,7 @@ test('detail metadata renders tag lists with multiple items', async () => {
|
|
|
453
453
|
|
|
454
454
|
Risk: Medium
|
|
455
455
|
|
|
456
|
-
|
|
456
|
+
─────────────────────────────────────────────────────────────────────────────────────────────
|
|
457
457
|
|
|
458
458
|
Description: This is a comprehensive metadata showcase that demonstrates all the different
|
|
459
459
|
ways you can display information using the Detail.Metadata component.
|
|
@@ -464,7 +464,7 @@ test('detail metadata renders tag lists with multiple items', async () => {
|
|
|
464
464
|
|
|
465
465
|
Reviewer: Bob Smith
|
|
466
466
|
|
|
467
|
-
|
|
467
|
+
─────────────────────────────────────────────────────────────────────────────────────────────
|
|
468
468
|
|
|
469
469
|
Repository: github.com/example
|
|
470
470
|
|
|
@@ -472,7 +472,7 @@ test('detail metadata renders tag lists with multiple items', async () => {
|
|
|
472
472
|
|
|
473
473
|
PR Link: github.com/organization/repository/pull/12345
|
|
474
474
|
|
|
475
|
-
|
|
475
|
+
─────────────────────────────────────────────────────────────────────────────────────────────
|
|
476
476
|
|
|
477
477
|
Labels: documentation enhancement good first issue
|
|
478
478
|
|
|
@@ -486,7 +486,7 @@ test('detail metadata renders tag lists with multiple items', async () => {
|
|
|
486
486
|
|
|
487
487
|
Due Date: 2024-02-01
|
|
488
488
|
|
|
489
|
-
|
|
489
|
+
─────────────────────────────────────────────────────────────────────────────────────────────
|
|
490
490
|
|
|
491
491
|
Metrics
|
|
492
492
|
|
|
@@ -499,6 +499,7 @@ test('detail metadata renders tag lists with multiple items', async () => {
|
|
|
499
499
|
Watchers: @alice @bob @charlie
|
|
500
500
|
|
|
501
501
|
|
|
502
|
+
esc go back ^k actions powered by termcast.app
|
|
502
503
|
|
|
503
504
|
|
|
504
505
|
|
|
@@ -511,7 +512,6 @@ test('detail metadata renders tag lists with multiple items', async () => {
|
|
|
511
512
|
|
|
512
513
|
|
|
513
514
|
|
|
514
|
-
esc go back ^k actions powered by termcast.app
|
|
515
515
|
|
|
516
516
|
"
|
|
517
517
|
`)
|
|
@@ -3,6 +3,22 @@ import { launchTerminal, Session } from 'tuistory/src'
|
|
|
3
3
|
|
|
4
4
|
let session: Session
|
|
5
5
|
|
|
6
|
+
function escapeRegExp(value: string) {
|
|
7
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function focusedFieldRegex(title: string) {
|
|
11
|
+
return new RegExp(`◆\\s+${escapeRegExp(title)}`, 'm')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function waitForFocusedField(title: string) {
|
|
15
|
+
return session.text({
|
|
16
|
+
waitFor: (text) => {
|
|
17
|
+
return focusedFieldRegex(title).test(text)
|
|
18
|
+
},
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
6
22
|
beforeEach(async () => {
|
|
7
23
|
session = await launchTerminal({
|
|
8
24
|
command: 'bun',
|
|
@@ -204,8 +220,8 @@ test('form date picker selection with space and enter', async () => {
|
|
|
204
220
|
│
|
|
205
221
|
◇ Date of Birth
|
|
206
222
|
│
|
|
207
|
-
│ ←
|
|
208
|
-
│ ←
|
|
223
|
+
│ ← 2026 →
|
|
224
|
+
│ ← February →
|
|
209
225
|
|
|
210
226
|
|
|
211
227
|
ctrl ↵ submit tab navigate ^k actions
|
|
@@ -262,8 +278,8 @@ test('form date picker selection with space and enter', async () => {
|
|
|
262
278
|
│
|
|
263
279
|
◇ Date of Birth
|
|
264
280
|
│
|
|
265
|
-
│ ←
|
|
266
|
-
│ ←
|
|
281
|
+
│ ← 2026 →
|
|
282
|
+
│ ← February →
|
|
267
283
|
|
|
268
284
|
|
|
269
285
|
ctrl ↵ submit tab navigate ^k actions
|
|
@@ -321,8 +337,8 @@ test('form date picker selection with space and enter', async () => {
|
|
|
321
337
|
│
|
|
322
338
|
◇ Date of Birth
|
|
323
339
|
│
|
|
324
|
-
│ ←
|
|
325
|
-
│ ←
|
|
340
|
+
│ ← 2026 →
|
|
341
|
+
│ ← February →
|
|
326
342
|
|
|
327
343
|
|
|
328
344
|
ctrl ↵ submit tab navigate ^k actions
|
|
@@ -396,8 +412,8 @@ test('form dropdown navigation', async () => {
|
|
|
396
412
|
│
|
|
397
413
|
◇ Date of Birth
|
|
398
414
|
│
|
|
399
|
-
│ ←
|
|
400
|
-
│ ←
|
|
415
|
+
│ ← 2026 →
|
|
416
|
+
│ ← February →
|
|
401
417
|
|
|
402
418
|
|
|
403
419
|
ctrl ↵ submit tab navigate ^k actions
|
|
@@ -454,8 +470,8 @@ test('form dropdown navigation', async () => {
|
|
|
454
470
|
│
|
|
455
471
|
◇ Date of Birth
|
|
456
472
|
│
|
|
457
|
-
│ ←
|
|
458
|
-
│ ←
|
|
473
|
+
│ ← 2026 →
|
|
474
|
+
│ ← February →
|
|
459
475
|
|
|
460
476
|
|
|
461
477
|
ctrl ↵ submit tab navigate ^k actions
|
|
@@ -514,8 +530,8 @@ test('form dropdown navigation', async () => {
|
|
|
514
530
|
│
|
|
515
531
|
◇ Date of Birth
|
|
516
532
|
│
|
|
517
|
-
│ ←
|
|
518
|
-
│ ←
|
|
533
|
+
│ ← 2026 →
|
|
534
|
+
│ ← February →
|
|
519
535
|
|
|
520
536
|
|
|
521
537
|
ctrl ↵ submit tab navigate ^k actions
|
|
@@ -572,8 +588,8 @@ test('form dropdown navigation', async () => {
|
|
|
572
588
|
│
|
|
573
589
|
◇ Date of Birth
|
|
574
590
|
│
|
|
575
|
-
│ ←
|
|
576
|
-
│ ←
|
|
591
|
+
│ ← 2026 →
|
|
592
|
+
│ ← February →
|
|
577
593
|
|
|
578
594
|
|
|
579
595
|
ctrl ↵ submit tab navigate ^k actions
|
|
@@ -746,8 +762,8 @@ test('arrow down from checkbox to dropdown lands on first item', async () => {
|
|
|
746
762
|
│
|
|
747
763
|
◇ Date of Birth
|
|
748
764
|
│
|
|
749
|
-
│ ←
|
|
750
|
-
│ ←
|
|
765
|
+
│ ← 2026 →
|
|
766
|
+
│ ← February →
|
|
751
767
|
|
|
752
768
|
|
|
753
769
|
ctrl ↵ submit tab navigate ^k actions
|
|
@@ -917,3 +933,88 @@ test('textarea arrow keys move focus between adjacent form fields', async () =>
|
|
|
917
933
|
const afterUpSnapshot = await session.text()
|
|
918
934
|
expect(afterUpSnapshot).toMatch(/◆\s+Biography/)
|
|
919
935
|
}, 10000)
|
|
936
|
+
|
|
937
|
+
test('arrow up/down navigates all form inputs and respects widget edges', async () => {
|
|
938
|
+
await session.text({
|
|
939
|
+
waitFor: (text) => {
|
|
940
|
+
return /Form Component Demo/i.test(text)
|
|
941
|
+
},
|
|
942
|
+
})
|
|
943
|
+
|
|
944
|
+
await session.press('tab')
|
|
945
|
+
let text = await waitForFocusedField('Username')
|
|
946
|
+
expect(text).toMatch(/◆\s+Username/)
|
|
947
|
+
|
|
948
|
+
await session.press('down')
|
|
949
|
+
text = await waitForFocusedField('Password')
|
|
950
|
+
|
|
951
|
+
await session.press('down')
|
|
952
|
+
text = await waitForFocusedField('Biography')
|
|
953
|
+
|
|
954
|
+
await session.type('line 1')
|
|
955
|
+
await session.press('enter')
|
|
956
|
+
await session.type('line 2')
|
|
957
|
+
|
|
958
|
+
await session.press('up')
|
|
959
|
+
text = await waitForFocusedField('Biography')
|
|
960
|
+
|
|
961
|
+
await session.press('up')
|
|
962
|
+
text = await waitForFocusedField('Password')
|
|
963
|
+
|
|
964
|
+
await session.press('down')
|
|
965
|
+
text = await waitForFocusedField('Biography')
|
|
966
|
+
|
|
967
|
+
await session.press('down')
|
|
968
|
+
text = await waitForFocusedField('Biography')
|
|
969
|
+
|
|
970
|
+
await session.press('down')
|
|
971
|
+
text = await waitForFocusedField('Email Preferences')
|
|
972
|
+
|
|
973
|
+
await session.press('down')
|
|
974
|
+
text = await waitForFocusedField('Country')
|
|
975
|
+
expect(text).toMatch(/›.*United States/)
|
|
976
|
+
|
|
977
|
+
await session.press('up')
|
|
978
|
+
text = await waitForFocusedField('Email Preferences')
|
|
979
|
+
|
|
980
|
+
await session.press('down')
|
|
981
|
+
text = await waitForFocusedField('Country')
|
|
982
|
+
expect(text).toMatch(/›.*United States/)
|
|
983
|
+
|
|
984
|
+
await session.press('down')
|
|
985
|
+
await session.press('down')
|
|
986
|
+
await session.press('down')
|
|
987
|
+
await session.press('down')
|
|
988
|
+
await session.press('down')
|
|
989
|
+
|
|
990
|
+
text = await waitForFocusedField('Country')
|
|
991
|
+
expect(text).toMatch(/›.*Germany/)
|
|
992
|
+
|
|
993
|
+
await session.press('down')
|
|
994
|
+
text = await waitForFocusedField('Empty Dropdown')
|
|
995
|
+
|
|
996
|
+
await session.press('down')
|
|
997
|
+
text = await waitForFocusedField('Minimal Field')
|
|
998
|
+
|
|
999
|
+
await session.press('down')
|
|
1000
|
+
text = await waitForFocusedField('Date of Birth')
|
|
1001
|
+
|
|
1002
|
+
await session.press('down')
|
|
1003
|
+
text = await waitForFocusedField('Date of Birth')
|
|
1004
|
+
|
|
1005
|
+
let reachedFilePicker = false
|
|
1006
|
+
for (let i = 0; i < 8; i++) {
|
|
1007
|
+
await session.press('down')
|
|
1008
|
+
const nextText = await session.text()
|
|
1009
|
+
if (focusedFieldRegex('Upload Documents').test(nextText)) {
|
|
1010
|
+
reachedFilePicker = true
|
|
1011
|
+
break
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
expect(reachedFilePicker).toBe(true)
|
|
1016
|
+
|
|
1017
|
+
await session.press('up')
|
|
1018
|
+
text = await waitForFocusedField('Date of Birth')
|
|
1019
|
+
expect(text).toMatch(/◆\s+Date of Birth/)
|
|
1020
|
+
}, 20000)
|
|
@@ -35,7 +35,7 @@ test('initial render shows bar chart for Monthly Budget', async () => {
|
|
|
35
35
|
›Monthly Budget Spent / Remaining / Savings │ ┌Spent: 78.6%┐
|
|
36
36
|
Disk Usage System / Apps / Media / Free │
|
|
37
37
|
Investment Portfolio Stocks / Bonds / Cash / C │
|
|
38
|
-
CPU Time User / System / IO Wait / Idle │
|
|
38
|
+
CPU Time User / System / IO Wait / Idle │ ───────────────────────────────────────────
|
|
39
39
|
Revenue by Product 6 product lines │
|
|
40
40
|
A/B Test Split Control vs Variant (50/50) │ Total: $6,174
|
|
41
41
|
Storage Full 100% used │
|
|
@@ -46,7 +46,7 @@ test('initial render shows bar chart for Monthly Budget', async () => {
|
|
|
46
46
|
Stress Test (20 items) Many small equal segmen │
|
|
47
47
|
│
|
|
48
48
|
│
|
|
49
|
-
|
|
49
|
+
│
|
|
50
50
|
│
|
|
51
51
|
│
|
|
52
52
|
│
|
|
@@ -39,17 +39,17 @@ test('side detail shows two graphs in a row', async () => {
|
|
|
39
39
|
Mixed Variants Area left, Striped right │ Filled chart (right) shows memory steadily
|
|
40
40
|
Sparse Data (Zeros) Filled vs Striped with zer │ climbing.
|
|
41
41
|
│
|
|
42
|
-
│ 100│
|
|
43
|
-
│ │
|
|
44
|
-
│ 67│
|
|
45
|
-
│ │
|
|
46
|
-
│ │
|
|
47
|
-
│ 33
|
|
48
|
-
│
|
|
49
|
-
│ 0
|
|
50
|
-
│ 0h 6h
|
|
42
|
+
│ 100│ ⡀ 100│
|
|
43
|
+
│ │ ⣠ ⢠⣾⣷⡀ │ ▄▄▀▀
|
|
44
|
+
│ 67│ ⣰⣿⣧ ⢀⣿⣿⣿⣇ 67│ ▄▄▀▀▀▀▀▀▀
|
|
45
|
+
│ │ ⢠⣷⣿⣿⣿⣆ ⣾⣿⣿⣿⣿⡄ │ ▄▄▀▀▀▀▀▀▀▀▀▀▀▀
|
|
46
|
+
│ │ ⢀⣿⣿⣿⣿⣿⣿⣆⣸⣿⣿⣿⣿⣿⣿⣄ │▄▄▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
|
47
|
+
│ 33│⣀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧ 33│▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
|
48
|
+
│ │⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ │▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
|
49
|
+
│ 0│⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ 0│▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
|
50
|
+
│ 0h 6h 12h 18h24h 0h 6h 12h 18h24h
|
|
51
51
|
│
|
|
52
|
-
│
|
|
52
|
+
│ ────────────────────────────────────────────
|
|
53
53
|
│
|
|
54
54
|
│ CPU Peak: 90%
|
|
55
55
|
│
|