termcast 1.3.46 → 1.3.48
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/cache.d.ts.map +1 -1
- package/dist/apis/cache.js +1 -2
- package/dist/apis/cache.js.map +1 -1
- package/dist/apis/localstorage.d.ts.map +1 -1
- package/dist/apis/localstorage.js +1 -2
- package/dist/apis/localstorage.js.map +1 -1
- package/dist/apis/sqlite.d.ts +7 -0
- package/dist/apis/sqlite.d.ts.map +1 -0
- package/dist/apis/sqlite.js +13 -0
- package/dist/apis/sqlite.js.map +1 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +7 -1
- package/dist/build.js.map +1 -1
- package/dist/compile.js +1 -1
- package/dist/compile.js.map +1 -1
- package/dist/components/actions.d.ts +18 -0
- package/dist/components/actions.d.ts.map +1 -1
- package/dist/components/actions.js +70 -5
- package/dist/components/actions.js.map +1 -1
- package/dist/components/detail.d.ts.map +1 -1
- package/dist/components/detail.js +3 -1
- package/dist/components/detail.js.map +1 -1
- package/dist/components/dropdown.d.ts.map +1 -1
- package/dist/components/dropdown.js +40 -11
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/form/dropdown.d.ts.map +1 -1
- package/dist/components/form/dropdown.js +26 -10
- package/dist/components/form/dropdown.js.map +1 -1
- package/dist/components/list.d.ts +7 -0
- package/dist/components/list.d.ts.map +1 -1
- package/dist/components/list.js +82 -15
- package/dist/components/list.js.map +1 -1
- package/dist/components/metadata.d.ts.map +1 -1
- package/dist/components/metadata.js +2 -1
- package/dist/components/metadata.js.map +1 -1
- package/dist/components/spinner.d.ts +6 -0
- package/dist/components/spinner.d.ts.map +1 -0
- package/dist/components/spinner.js +12 -0
- package/dist/components/spinner.js.map +1 -0
- package/dist/examples/action-shortcut.d.ts +2 -0
- package/dist/examples/action-shortcut.d.ts.map +1 -0
- package/dist/examples/action-shortcut.js +20 -0
- package/dist/examples/action-shortcut.js.map +1 -0
- package/dist/examples/internal/scrollbox-with-descendants.js +19 -8
- package/dist/examples/internal/scrollbox-with-descendants.js.map +1 -1
- package/dist/examples/list-spacing-default.d.ts +5 -0
- package/dist/examples/list-spacing-default.d.ts.map +1 -0
- package/dist/examples/list-spacing-default.js +10 -0
- package/dist/examples/list-spacing-default.js.map +1 -0
- package/dist/examples/list-spacing-mode.d.ts +10 -0
- package/dist/examples/list-spacing-mode.d.ts.map +1 -0
- package/dist/examples/list-spacing-mode.js +26 -0
- package/dist/examples/list-spacing-mode.js.map +1 -0
- package/dist/examples/list-spacing-relaxed.d.ts +5 -0
- package/dist/examples/list-spacing-relaxed.d.ts.map +1 -0
- package/dist/examples/list-spacing-relaxed.js +10 -0
- package/dist/examples/list-spacing-relaxed.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +2 -1
- package/dist/logger.js.map +1 -1
- package/dist/state.d.ts +9 -0
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +2 -0
- package/dist/state.js.map +1 -1
- package/dist/swift-runtime.d.ts.map +1 -1
- package/dist/swift-runtime.js +20 -5
- package/dist/swift-runtime.js.map +1 -1
- package/dist/utils.js +1 -1
- package/dist/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/apis/cache.test.ts +1 -1
- package/src/apis/cache.tsx +1 -2
- package/src/apis/localstorage.tsx +1 -2
- package/src/apis/sqlite.ts +14 -0
- package/src/build.tsx +7 -1
- package/src/compile.tsx +1 -1
- package/src/components/actions.tsx +77 -5
- package/src/components/detail.tsx +3 -1
- package/src/components/dropdown.tsx +49 -11
- package/src/components/form/dropdown.tsx +32 -10
- package/src/components/list.tsx +157 -18
- package/src/components/metadata.tsx +3 -2
- package/src/components/spinner.tsx +25 -0
- package/src/examples/action-shortcut.tsx +53 -0
- package/src/examples/action-shortcut.vitest.tsx +178 -0
- package/src/examples/actions-context.vitest.tsx +4 -4
- package/src/examples/detail-metadata-showcase.vitest.tsx +32 -18
- package/src/examples/form-dropdown.vitest.tsx +8 -8
- package/src/examples/form-tagpicker.vitest.tsx +4 -4
- package/src/examples/github.vitest.tsx +16 -9
- package/src/examples/internal/scrollbox-with-descendants.tsx +27 -8
- package/src/examples/list-detail-metadata.vitest.tsx +3 -1
- package/src/examples/list-loading-empty-view.vitest.tsx +5 -5
- package/src/examples/list-scrollbox.vitest.tsx +5 -5
- package/src/examples/list-spacing-default.tsx +38 -0
- package/src/examples/list-spacing-mode.tsx +137 -0
- package/src/examples/list-spacing-mode.vitest.tsx +158 -0
- package/src/examples/list-spacing-relaxed.tsx +38 -0
- package/src/examples/list-with-detail.vitest.tsx +63 -28
- package/src/examples/list-with-sections.vitest.tsx +14 -14
- package/src/examples/simple-detail-markdown.vitest.tsx +3 -4
- package/src/examples/simple-file-picker.vitest.tsx +1 -1
- package/src/examples/simple-grid.vitest.tsx +42 -35
- package/src/examples/simple-navigation.vitest.tsx +14 -14
- package/src/extensions/dev.vitest.tsx +9 -3
- package/src/index.tsx +2 -0
- package/src/logger.tsx +2 -1
- package/src/state.tsx +13 -0
- package/src/swift-runtime.tsx +19 -5
- package/src/utils.test.tsx +1 -1
- package/src/utils.tsx +1 -1
|
@@ -33,7 +33,8 @@ test('detail metadata showcase renders markdown and metadata together', async ()
|
|
|
33
33
|
text.includes('Project Update') &&
|
|
34
34
|
text.includes('Basic Information') &&
|
|
35
35
|
text.includes('Alice Johnson') &&
|
|
36
|
-
text.includes('Watchers')
|
|
36
|
+
text.includes('Watchers') &&
|
|
37
|
+
text.includes('powered by termcast')
|
|
37
38
|
)
|
|
38
39
|
},
|
|
39
40
|
timeout: 20000,
|
|
@@ -59,6 +60,7 @@ test('detail metadata showcase renders markdown and metadata together', async ()
|
|
|
59
60
|
- Migrated 85% of users to the new platform
|
|
60
61
|
- Reduced API response time by 40%
|
|
61
62
|
|
|
63
|
+
|
|
62
64
|
Technical Details
|
|
63
65
|
|
|
64
66
|
The refactoring effort focused on three main areas:
|
|
@@ -67,6 +69,7 @@ test('detail metadata showcase renders markdown and metadata together', async ()
|
|
|
67
69
|
2. Caching layer - Added Redis for session management
|
|
68
70
|
3. Code cleanup - Removed deprecated endpoints
|
|
69
71
|
|
|
72
|
+
|
|
70
73
|
Next Steps
|
|
71
74
|
|
|
72
75
|
We will continue with Phase 2 in the upcoming sprint. The team should prioritize:
|
|
@@ -75,6 +78,7 @@ test('detail metadata showcase renders markdown and metadata together', async ()
|
|
|
75
78
|
- Implementing the new dashboard
|
|
76
79
|
- Writing integration tests
|
|
77
80
|
|
|
81
|
+
|
|
78
82
|
---
|
|
79
83
|
|
|
80
84
|
Last updated: January 20, 2024
|
|
@@ -157,9 +161,6 @@ test('detail metadata showcase renders markdown and metadata together', async ()
|
|
|
157
161
|
|
|
158
162
|
|
|
159
163
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
164
|
"
|
|
164
165
|
`)
|
|
165
166
|
|
|
@@ -202,7 +203,11 @@ test('detail metadata showcase renders markdown and metadata together', async ()
|
|
|
202
203
|
|
|
203
204
|
test('detail metadata renders long values in column layout', async () => {
|
|
204
205
|
const snapshot = await session.text({
|
|
205
|
-
waitFor: (text) =>
|
|
206
|
+
waitFor: (text) =>
|
|
207
|
+
text.includes('Description:') &&
|
|
208
|
+
text.includes('comprehensive') &&
|
|
209
|
+
text.includes('PR Link:') &&
|
|
210
|
+
text.includes('powered by termcast'),
|
|
206
211
|
timeout: 15000,
|
|
207
212
|
})
|
|
208
213
|
|
|
@@ -226,6 +231,7 @@ test('detail metadata renders long values in column layout', async () => {
|
|
|
226
231
|
- Migrated 85% of users to the new platform
|
|
227
232
|
- Reduced API response time by 40%
|
|
228
233
|
|
|
234
|
+
|
|
229
235
|
Technical Details
|
|
230
236
|
|
|
231
237
|
The refactoring effort focused on three main areas:
|
|
@@ -234,6 +240,7 @@ test('detail metadata renders long values in column layout', async () => {
|
|
|
234
240
|
2. Caching layer - Added Redis for session management
|
|
235
241
|
3. Code cleanup - Removed deprecated endpoints
|
|
236
242
|
|
|
243
|
+
|
|
237
244
|
Next Steps
|
|
238
245
|
|
|
239
246
|
We will continue with Phase 2 in the upcoming sprint. The team should prioritize:
|
|
@@ -242,6 +249,7 @@ test('detail metadata renders long values in column layout', async () => {
|
|
|
242
249
|
- Implementing the new dashboard
|
|
243
250
|
- Writing integration tests
|
|
244
251
|
|
|
252
|
+
|
|
245
253
|
---
|
|
246
254
|
|
|
247
255
|
Last updated: January 20, 2024
|
|
@@ -324,9 +332,6 @@ test('detail metadata renders long values in column layout', async () => {
|
|
|
324
332
|
|
|
325
333
|
|
|
326
334
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
335
|
"
|
|
331
336
|
`)
|
|
332
337
|
|
|
@@ -344,8 +349,17 @@ test('detail metadata renders long values in column layout', async () => {
|
|
|
344
349
|
}, 30000)
|
|
345
350
|
|
|
346
351
|
test('detail metadata renders links', async () => {
|
|
352
|
+
// The showcase is long; scroll so the link section is visible.
|
|
353
|
+
await session.scrollDown(40)
|
|
354
|
+
|
|
347
355
|
const snapshot = await session.text({
|
|
348
|
-
waitFor: (text) =>
|
|
356
|
+
waitFor: (text) =>
|
|
357
|
+
text.includes('Repository:') &&
|
|
358
|
+
text.includes('github.com/example') &&
|
|
359
|
+
text.includes('Docs:') &&
|
|
360
|
+
text.includes('docs.example.com') &&
|
|
361
|
+
text.includes('PR Link:') &&
|
|
362
|
+
text.includes('github.com/organization/repository/pull/12345'),
|
|
349
363
|
timeout: 15000,
|
|
350
364
|
})
|
|
351
365
|
|
|
@@ -398,6 +412,7 @@ test('detail metadata renders tag lists with multiple items', async () => {
|
|
|
398
412
|
- Migrated 85% of users to the new platform
|
|
399
413
|
- Reduced API response time by 40%
|
|
400
414
|
|
|
415
|
+
|
|
401
416
|
Technical Details
|
|
402
417
|
|
|
403
418
|
The refactoring effort focused on three main areas:
|
|
@@ -406,6 +421,7 @@ test('detail metadata renders tag lists with multiple items', async () => {
|
|
|
406
421
|
2. Caching layer - Added Redis for session management
|
|
407
422
|
3. Code cleanup - Removed deprecated endpoints
|
|
408
423
|
|
|
424
|
+
|
|
409
425
|
Next Steps
|
|
410
426
|
|
|
411
427
|
We will continue with Phase 2 in the upcoming sprint. The team should prioritize:
|
|
@@ -414,6 +430,7 @@ test('detail metadata renders tag lists with multiple items', async () => {
|
|
|
414
430
|
- Implementing the new dashboard
|
|
415
431
|
- Writing integration tests
|
|
416
432
|
|
|
433
|
+
|
|
417
434
|
---
|
|
418
435
|
|
|
419
436
|
Last updated: January 20, 2024
|
|
@@ -426,7 +443,7 @@ test('detail metadata renders tag lists with multiple items', async () => {
|
|
|
426
443
|
|
|
427
444
|
Team: Platform
|
|
428
445
|
|
|
429
|
-
|
|
446
|
+
────────────────────────────────────────────────────────────────────────────────────────────
|
|
430
447
|
|
|
431
448
|
Status: Active
|
|
432
449
|
|
|
@@ -436,7 +453,7 @@ test('detail metadata renders tag lists with multiple items', async () => {
|
|
|
436
453
|
|
|
437
454
|
Risk: Medium
|
|
438
455
|
|
|
439
|
-
|
|
456
|
+
────────────────────────────────────────────────────────────────────────────────────────────
|
|
440
457
|
|
|
441
458
|
Description: This is a comprehensive metadata showcase that demonstrates all the different
|
|
442
459
|
ways you can display information using the Detail.Metadata component.
|
|
@@ -447,7 +464,7 @@ test('detail metadata renders tag lists with multiple items', async () => {
|
|
|
447
464
|
|
|
448
465
|
Reviewer: Bob Smith
|
|
449
466
|
|
|
450
|
-
|
|
467
|
+
────────────────────────────────────────────────────────────────────────────────────────────
|
|
451
468
|
|
|
452
469
|
Repository: github.com/example
|
|
453
470
|
|
|
@@ -455,7 +472,7 @@ test('detail metadata renders tag lists with multiple items', async () => {
|
|
|
455
472
|
|
|
456
473
|
PR Link: github.com/organization/repository/pull/12345
|
|
457
474
|
|
|
458
|
-
|
|
475
|
+
────────────────────────────────────────────────────────────────────────────────────────────
|
|
459
476
|
|
|
460
477
|
Labels: documentation enhancement good first issue
|
|
461
478
|
|
|
@@ -469,7 +486,7 @@ test('detail metadata renders tag lists with multiple items', async () => {
|
|
|
469
486
|
|
|
470
487
|
Due Date: 2024-02-01
|
|
471
488
|
|
|
472
|
-
|
|
489
|
+
────────────────────────────────────────────────────────────────────────────────────────────
|
|
473
490
|
|
|
474
491
|
Metrics
|
|
475
492
|
|
|
@@ -483,10 +500,6 @@ test('detail metadata renders tag lists with multiple items', async () => {
|
|
|
483
500
|
|
|
484
501
|
|
|
485
502
|
|
|
486
|
-
esc go back powered by termcast
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
503
|
|
|
491
504
|
|
|
492
505
|
|
|
@@ -498,6 +511,7 @@ test('detail metadata renders tag lists with multiple items', async () => {
|
|
|
498
511
|
|
|
499
512
|
|
|
500
513
|
|
|
514
|
+
esc go back powered by termcast
|
|
501
515
|
|
|
502
516
|
"
|
|
503
517
|
`)
|
|
@@ -351,11 +351,11 @@ test('form dropdown keyboard navigation', async () => {
|
|
|
351
351
|
◆ Programming Languages █
|
|
352
352
|
│ TypeScript, Rust █
|
|
353
353
|
│ █
|
|
354
|
-
│ ○ React █
|
|
355
|
-
│ ○ Vue █
|
|
356
354
|
│› ○ Svelte █
|
|
357
355
|
│ Backend █
|
|
358
356
|
│ ○ Node.js █
|
|
357
|
+
│ ○ Python █
|
|
358
|
+
│ ○ Go █
|
|
359
359
|
│ █
|
|
360
360
|
│ Choose your preferred programming languages █
|
|
361
361
|
│ █
|
|
@@ -465,11 +465,11 @@ test('form dropdown keyboard navigation', async () => {
|
|
|
465
465
|
◆ Programming Languages █
|
|
466
466
|
│ TypeScript, Rust █
|
|
467
467
|
│ █
|
|
468
|
-
│ ○ React █
|
|
469
|
-
│ ○ Vue █
|
|
470
468
|
│› ○ Svelte █
|
|
471
469
|
│ Backend █
|
|
472
470
|
│ ○ Node.js █
|
|
471
|
+
│ ○ Python █
|
|
472
|
+
│ ○ Go █
|
|
473
473
|
│ █
|
|
474
474
|
│ Choose your preferred programming languages █
|
|
475
475
|
│ █
|
|
@@ -522,11 +522,11 @@ test('form dropdown keyboard navigation', async () => {
|
|
|
522
522
|
◆ Programming Languages █
|
|
523
523
|
│ TypeScript, Rust █
|
|
524
524
|
│ █
|
|
525
|
-
│ ○ React █
|
|
526
|
-
│ ○ Vue █
|
|
527
525
|
│› ○ Svelte █
|
|
528
526
|
│ Backend █
|
|
529
527
|
│ ○ Node.js █
|
|
528
|
+
│ ○ Python █
|
|
529
|
+
│ ○ Go █
|
|
530
530
|
│ █
|
|
531
531
|
│ Choose your preferred programming languages █
|
|
532
532
|
│ █
|
|
@@ -715,11 +715,11 @@ test('selecting second-to-last visible item should not scroll', async () => {
|
|
|
715
715
|
◆ Programming Languages █
|
|
716
716
|
│ TypeScript, Rust █
|
|
717
717
|
│ █
|
|
718
|
+
│ Frontend █
|
|
718
719
|
│ ● TypeScript █
|
|
719
720
|
│ ○ JavaScript █
|
|
720
721
|
│› ○ React █
|
|
721
722
|
│ ○ Vue █
|
|
722
|
-
│ ○ Svelte █
|
|
723
723
|
│ █
|
|
724
724
|
│ Choose your preferred programming languages █
|
|
725
725
|
│ █
|
|
@@ -772,11 +772,11 @@ test('selecting second-to-last visible item should not scroll', async () => {
|
|
|
772
772
|
◆ Programming Languages █
|
|
773
773
|
│ TypeScript, Rust, React █
|
|
774
774
|
│ █
|
|
775
|
+
│ Frontend █
|
|
775
776
|
│ ● TypeScript █
|
|
776
777
|
│ ○ JavaScript █
|
|
777
778
|
│› ● React █
|
|
778
779
|
│ ○ Vue █
|
|
779
|
-
│ ○ Svelte █
|
|
780
780
|
│ █
|
|
781
781
|
│ Choose your preferred programming languages █
|
|
782
782
|
│ █
|
|
@@ -395,11 +395,11 @@ test('form tagpicker keyboard navigation', async () => {
|
|
|
395
395
|
◆ Favorite Sport
|
|
396
396
|
│ Choose your favorite sport...
|
|
397
397
|
│
|
|
398
|
+
│ ○ Basketball
|
|
399
|
+
│ ○ Football
|
|
398
400
|
│ ○ Tennis
|
|
399
401
|
│ ○ Baseball
|
|
400
402
|
│› ○ Golf
|
|
401
|
-
│ ○ Swimming
|
|
402
|
-
│ ○ Cycling
|
|
403
403
|
│
|
|
404
404
|
│ Select your favorite sport from the list
|
|
405
405
|
│
|
|
@@ -509,11 +509,11 @@ test('form tagpicker keyboard navigation', async () => {
|
|
|
509
509
|
◆ Favorite Sport
|
|
510
510
|
│ Choose your favorite sport...
|
|
511
511
|
│
|
|
512
|
-
│ ○ Tennis
|
|
513
512
|
│ ○ Baseball
|
|
514
513
|
│› ○ Golf
|
|
515
514
|
│ ○ Swimming
|
|
516
515
|
│ ○ Cycling
|
|
516
|
+
│ ○ Running
|
|
517
517
|
│
|
|
518
518
|
│ Select your favorite sport from the list
|
|
519
519
|
│
|
|
@@ -566,11 +566,11 @@ test('form tagpicker keyboard navigation', async () => {
|
|
|
566
566
|
◆ Favorite Sport
|
|
567
567
|
│ Choose your favorite sport...
|
|
568
568
|
│
|
|
569
|
-
│ ○ Tennis
|
|
570
569
|
│ ○ Baseball
|
|
571
570
|
│› ○ Golf
|
|
572
571
|
│ ○ Swimming
|
|
573
572
|
│ ○ Cycling
|
|
573
|
+
│ ○ Running
|
|
574
574
|
│
|
|
575
575
|
│ Select your favorite sport from the list
|
|
576
576
|
│
|
|
@@ -61,6 +61,13 @@ test.skipIf(!extensionExists)('github extension shows command list on launch', a
|
|
|
61
61
|
timeout: 30000,
|
|
62
62
|
})
|
|
63
63
|
|
|
64
|
+
// Wait for the full command list to render.
|
|
65
|
+
// The list can paint the first item before all descendants are registered.
|
|
66
|
+
await session.text({
|
|
67
|
+
waitFor: (text) => text.includes('My Pull Requests') && text.includes('Search Repositories'),
|
|
68
|
+
timeout: 30000,
|
|
69
|
+
})
|
|
70
|
+
|
|
64
71
|
expect(initialView).toContain('My Pull Requests')
|
|
65
72
|
expect(initialView).toContain('Search Repositories')
|
|
66
73
|
expect(initialView).toMatchInlineSnapshot(`
|
|
@@ -101,7 +108,7 @@ test.skipIf(!extensionExists)('github extension shows command list on launch', a
|
|
|
101
108
|
test.skipIf(!extensionExists)('github extension can navigate commands', async () => {
|
|
102
109
|
// Wait for command list
|
|
103
110
|
await session.text({
|
|
104
|
-
waitFor: (text) =>
|
|
111
|
+
waitFor: (text) => text.includes('My Pull Requests') && text.includes('Search Repositories'),
|
|
105
112
|
timeout: 30000,
|
|
106
113
|
})
|
|
107
114
|
|
|
@@ -172,26 +179,26 @@ test.skipIf(!extensionExists)('github extension can open actions panel', async (
|
|
|
172
179
|
|
|
173
180
|
> Search commands...
|
|
174
181
|
|
|
175
|
-
Commands
|
|
176
|
-
›My Pull Requests List pull requests you created, participated in, or view
|
|
177
182
|
╭──────────────────────────────────────────────────────────────────────────╮
|
|
178
183
|
│ │
|
|
179
184
|
│ Actions esc │
|
|
180
185
|
│ │
|
|
181
186
|
│ > Search actions... │
|
|
182
187
|
│ │
|
|
183
|
-
│ ›Run Command
|
|
184
|
-
│ Copy File Path
|
|
185
|
-
│ Copy Command Info
|
|
186
|
-
│
|
|
188
|
+
│ ›Run Command │
|
|
189
|
+
│ Copy File Path │
|
|
190
|
+
│ Copy Command Info │
|
|
191
|
+
│ │
|
|
187
192
|
│ Settings │
|
|
188
|
-
│ Configure GitHub...
|
|
193
|
+
│ Configure GitHub... ⌃⇧, │
|
|
189
194
|
│ Change Theme... │
|
|
195
|
+
│ See Console Logs │
|
|
196
|
+
│ │
|
|
197
|
+
│ │
|
|
190
198
|
│ │
|
|
191
199
|
│ ↵ select ↑↓ navigate │
|
|
192
200
|
│ │
|
|
193
201
|
╰──────────────────────────────────────────────────────────────────────────╯
|
|
194
|
-
↵ run command ↑↓ navigate ^k actions powered by termcast
|
|
195
202
|
|
|
196
203
|
|
|
197
204
|
|
|
@@ -16,20 +16,39 @@ function ScrollboxWithDescendants() {
|
|
|
16
16
|
const [selectedIndex, setSelectedIndex] = React.useState(0)
|
|
17
17
|
const scrollBoxRef = React.useRef<any>(null)
|
|
18
18
|
|
|
19
|
-
const
|
|
19
|
+
const scrollToItemIfNeeded = ({
|
|
20
|
+
item,
|
|
21
|
+
direction,
|
|
22
|
+
}: {
|
|
23
|
+
item: { props?: ItemDescendant }
|
|
24
|
+
direction: -1 | 1
|
|
25
|
+
}) => {
|
|
20
26
|
const scrollBox = scrollBoxRef.current
|
|
21
27
|
const elementRef = item.props?.elementRef
|
|
22
28
|
if (!scrollBox || !elementRef) return
|
|
23
29
|
|
|
24
30
|
const contentY = scrollBox.content?.y || 0
|
|
31
|
+
const scrollTop = scrollBox.scrollTop || 0
|
|
25
32
|
const viewportHeight = scrollBox.viewport?.height || 10
|
|
26
33
|
|
|
27
|
-
// Calculate item position relative to content
|
|
28
34
|
const itemTop = elementRef.y - contentY
|
|
35
|
+
const itemHeight = elementRef.height || 1
|
|
36
|
+
const itemBottom = itemTop + itemHeight
|
|
29
37
|
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
38
|
+
const viewportTop = scrollTop
|
|
39
|
+
const viewportBottom = scrollTop + viewportHeight
|
|
40
|
+
|
|
41
|
+
if (direction === 1) {
|
|
42
|
+
if (itemBottom > viewportBottom) {
|
|
43
|
+
scrollBox.scrollTo(Math.max(0, itemTop))
|
|
44
|
+
}
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (itemTop < viewportTop) {
|
|
49
|
+
const targetScrollTop = itemBottom - viewportHeight
|
|
50
|
+
scrollBox.scrollTo(Math.max(0, targetScrollTop))
|
|
51
|
+
}
|
|
33
52
|
}
|
|
34
53
|
|
|
35
54
|
const move = (direction: -1 | 1) => {
|
|
@@ -40,15 +59,15 @@ function ScrollboxWithDescendants() {
|
|
|
40
59
|
if (items.length === 0) return
|
|
41
60
|
|
|
42
61
|
let nextIndex = selectedIndex + direction
|
|
43
|
-
if (nextIndex < 0)
|
|
44
|
-
if (nextIndex >= items.length)
|
|
62
|
+
if (nextIndex < 0) return
|
|
63
|
+
if (nextIndex >= items.length) return
|
|
45
64
|
|
|
46
65
|
const nextItem = items[nextIndex]
|
|
47
66
|
if (nextItem) {
|
|
48
67
|
flushSync(() => {
|
|
49
68
|
setSelectedIndex(nextIndex)
|
|
50
69
|
})
|
|
51
|
-
|
|
70
|
+
scrollToItemIfNeeded({ item: nextItem, direction })
|
|
52
71
|
}
|
|
53
72
|
}
|
|
54
73
|
|
|
@@ -23,7 +23,9 @@ test('list detail metadata label renders short values in row layout (key: value)
|
|
|
23
23
|
text.includes('Metadata Test') &&
|
|
24
24
|
text.includes('Name') &&
|
|
25
25
|
text.includes('John Doe') &&
|
|
26
|
-
text.includes('Email')
|
|
26
|
+
text.includes('Email') &&
|
|
27
|
+
text.includes('Website') &&
|
|
28
|
+
text.includes('↑↓ navigate')
|
|
27
29
|
)
|
|
28
30
|
},
|
|
29
31
|
})
|
|
@@ -25,19 +25,19 @@ test('empty view shows spinner when list is loading', async () => {
|
|
|
25
25
|
},
|
|
26
26
|
})
|
|
27
27
|
|
|
28
|
-
// Spinner animates. Normalize for stable snapshots.
|
|
29
|
-
const normalized = text.replace(/[
|
|
28
|
+
// Spinner animates (pulsing dots: ' ' · •). Normalize for stable snapshots.
|
|
29
|
+
const normalized = text.replace(/[·•]/g, '•')
|
|
30
30
|
expect(normalized).toMatchInlineSnapshot(`
|
|
31
31
|
"
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
Loading Empty View ───────────────────────────────────
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
• Search...
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
• loading...
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
|
|
@@ -45,5 +45,5 @@ test('empty view shows spinner when list is loading', async () => {
|
|
|
45
45
|
|
|
46
46
|
↑↓ navigate ^k actions"
|
|
47
47
|
`)
|
|
48
|
-
expect(text).toMatch(/[
|
|
48
|
+
expect(text).toMatch(/[·• ]\s*loading\.\.\./)
|
|
49
49
|
}, 10000)
|
|
@@ -69,12 +69,12 @@ test('list scrollbox auto-scrolls when navigating down', async () => {
|
|
|
69
69
|
|
|
70
70
|
> Search items...
|
|
71
71
|
|
|
72
|
+
○ Item 1 Description for item 1
|
|
73
|
+
★ Item 2 Description for item 2
|
|
72
74
|
◆ Item 3 Description for item 3
|
|
73
75
|
↯ Item 4 Description for item 4
|
|
74
76
|
▷ Item 5 Description for item 5
|
|
75
|
-
›▦ Item 6 Description for item 6
|
|
76
|
-
◴ Item 7 Description for item 7
|
|
77
|
-
▯ Item 8 Description for item 8"
|
|
77
|
+
›▦ Item 6 Description for item 6"
|
|
78
78
|
`)
|
|
79
79
|
|
|
80
80
|
await session.press('down')
|
|
@@ -90,12 +90,12 @@ test('list scrollbox auto-scrolls when navigating down', async () => {
|
|
|
90
90
|
|
|
91
91
|
> Search items...
|
|
92
92
|
|
|
93
|
-
▦ Item 6 Description for item 6
|
|
94
93
|
◴ Item 7 Description for item 7
|
|
95
94
|
▯ Item 8 Description for item 8
|
|
96
95
|
›▤ Item 9 Description for item 9
|
|
97
96
|
Item 10 Description for item 10
|
|
98
|
-
○ Item 11 Description for item 11
|
|
97
|
+
○ Item 11 Description for item 11
|
|
98
|
+
★ Item 12 Description for item 12"
|
|
99
99
|
`)
|
|
100
100
|
|
|
101
101
|
await session.press('up')
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test file for List with spacingMode="default" (single-line items)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { List, Icon, Color, renderWithProviders } from 'termcast'
|
|
6
|
+
|
|
7
|
+
function ListSpacingDefault() {
|
|
8
|
+
return (
|
|
9
|
+
<List navigationTitle="Default Mode" spacingMode="default">
|
|
10
|
+
<List.Section title="With Icons">
|
|
11
|
+
<List.Item
|
|
12
|
+
icon={Icon.Document}
|
|
13
|
+
title="Report"
|
|
14
|
+
subtitle="Q4 financial summary"
|
|
15
|
+
accessories={[{ tag: { value: 'Draft', color: Color.Yellow } }]}
|
|
16
|
+
/>
|
|
17
|
+
<List.Item
|
|
18
|
+
icon={Icon.Code}
|
|
19
|
+
title="API Docs"
|
|
20
|
+
subtitle="REST endpoints guide"
|
|
21
|
+
accessories={[{ text: 'v2.1' }]}
|
|
22
|
+
/>
|
|
23
|
+
</List.Section>
|
|
24
|
+
<List.Section title="Without Icons">
|
|
25
|
+
<List.Item
|
|
26
|
+
title="Meeting Notes"
|
|
27
|
+
subtitle="Weekly standup points"
|
|
28
|
+
accessories={[{ tag: 'Important' }]}
|
|
29
|
+
/>
|
|
30
|
+
</List.Section>
|
|
31
|
+
<List.Section title="No Subtitle">
|
|
32
|
+
<List.Item icon={Icon.Star} title="Favorites" />
|
|
33
|
+
</List.Section>
|
|
34
|
+
</List>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
await renderWithProviders(<ListSpacingDefault />)
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example demonstrating List spacingMode prop.
|
|
3
|
+
*
|
|
4
|
+
* - 'default': Single-line items with title and subtitle on same row
|
|
5
|
+
* - 'relaxed': Two-line items with title on first row, subtitle below aligned with title start
|
|
6
|
+
*
|
|
7
|
+
* Use Action to toggle between modes and see the difference.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { useState } from 'react'
|
|
11
|
+
import {
|
|
12
|
+
List,
|
|
13
|
+
ActionPanel,
|
|
14
|
+
Action,
|
|
15
|
+
Icon,
|
|
16
|
+
Color,
|
|
17
|
+
renderWithProviders,
|
|
18
|
+
type ListSpacingMode,
|
|
19
|
+
} from 'termcast'
|
|
20
|
+
|
|
21
|
+
function ListSpacingModeExample() {
|
|
22
|
+
const [spacingMode, setSpacingMode] = useState<ListSpacingMode>('relaxed')
|
|
23
|
+
|
|
24
|
+
const toggleMode = () => {
|
|
25
|
+
setSpacingMode((prev) => (prev === 'default' ? 'relaxed' : 'default'))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<List
|
|
30
|
+
navigationTitle={`Spacing Mode: ${spacingMode}`}
|
|
31
|
+
spacingMode={spacingMode}
|
|
32
|
+
>
|
|
33
|
+
<List.Section title="With Icons" subtitle="Items have icon, title, subtitle">
|
|
34
|
+
<List.Item
|
|
35
|
+
icon={Icon.Document}
|
|
36
|
+
title="Quarterly Report"
|
|
37
|
+
subtitle="Q4 2024 financial summary and projections"
|
|
38
|
+
accessories={[{ tag: { value: 'Draft', color: Color.Yellow } }]}
|
|
39
|
+
actions={
|
|
40
|
+
<ActionPanel>
|
|
41
|
+
<Action title="Toggle Spacing Mode" onAction={toggleMode} />
|
|
42
|
+
</ActionPanel>
|
|
43
|
+
}
|
|
44
|
+
/>
|
|
45
|
+
<List.Item
|
|
46
|
+
icon={Icon.Code}
|
|
47
|
+
title="API Documentation"
|
|
48
|
+
subtitle="REST endpoints and authentication guide"
|
|
49
|
+
accessories={[
|
|
50
|
+
{ text: 'v2.1' },
|
|
51
|
+
{ date: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000) },
|
|
52
|
+
]}
|
|
53
|
+
actions={
|
|
54
|
+
<ActionPanel>
|
|
55
|
+
<Action title="Toggle Spacing Mode" onAction={toggleMode} />
|
|
56
|
+
</ActionPanel>
|
|
57
|
+
}
|
|
58
|
+
/>
|
|
59
|
+
<List.Item
|
|
60
|
+
icon={Icon.Gear}
|
|
61
|
+
title="Configuration"
|
|
62
|
+
subtitle="System settings and preferences"
|
|
63
|
+
accessories={[{ tag: { value: 'Active', color: Color.Green } }]}
|
|
64
|
+
actions={
|
|
65
|
+
<ActionPanel>
|
|
66
|
+
<Action title="Toggle Spacing Mode" onAction={toggleMode} />
|
|
67
|
+
</ActionPanel>
|
|
68
|
+
}
|
|
69
|
+
/>
|
|
70
|
+
</List.Section>
|
|
71
|
+
|
|
72
|
+
<List.Section title="Without Icons" subtitle="Plain text items">
|
|
73
|
+
<List.Item
|
|
74
|
+
title="Meeting Notes"
|
|
75
|
+
subtitle="Weekly standup discussion points"
|
|
76
|
+
accessories={[{ date: new Date(Date.now() - 60 * 60 * 1000) }]}
|
|
77
|
+
actions={
|
|
78
|
+
<ActionPanel>
|
|
79
|
+
<Action title="Toggle Spacing Mode" onAction={toggleMode} />
|
|
80
|
+
</ActionPanel>
|
|
81
|
+
}
|
|
82
|
+
/>
|
|
83
|
+
<List.Item
|
|
84
|
+
title="Project Timeline"
|
|
85
|
+
subtitle="Milestones and deadlines for Q1"
|
|
86
|
+
accessories={[{ text: { value: 'Important', color: Color.Red } }]}
|
|
87
|
+
actions={
|
|
88
|
+
<ActionPanel>
|
|
89
|
+
<Action title="Toggle Spacing Mode" onAction={toggleMode} />
|
|
90
|
+
</ActionPanel>
|
|
91
|
+
}
|
|
92
|
+
/>
|
|
93
|
+
</List.Section>
|
|
94
|
+
|
|
95
|
+
<List.Section title="No Subtitle" subtitle="Title only items">
|
|
96
|
+
<List.Item
|
|
97
|
+
icon={Icon.Star}
|
|
98
|
+
title="Favorites"
|
|
99
|
+
accessories={[{ tag: '12 items' }]}
|
|
100
|
+
actions={
|
|
101
|
+
<ActionPanel>
|
|
102
|
+
<Action title="Toggle Spacing Mode" onAction={toggleMode} />
|
|
103
|
+
</ActionPanel>
|
|
104
|
+
}
|
|
105
|
+
/>
|
|
106
|
+
<List.Item
|
|
107
|
+
title="Recent"
|
|
108
|
+
accessories={[{ date: new Date() }]}
|
|
109
|
+
actions={
|
|
110
|
+
<ActionPanel>
|
|
111
|
+
<Action title="Toggle Spacing Mode" onAction={toggleMode} />
|
|
112
|
+
</ActionPanel>
|
|
113
|
+
}
|
|
114
|
+
/>
|
|
115
|
+
</List.Section>
|
|
116
|
+
|
|
117
|
+
<List.Section title="Long Content" subtitle="Testing overflow behavior">
|
|
118
|
+
<List.Item
|
|
119
|
+
icon={Icon.Text}
|
|
120
|
+
title="Very Long Title That Might Need Truncation"
|
|
121
|
+
subtitle="This is a particularly verbose subtitle that describes the item in great detail"
|
|
122
|
+
accessories={[
|
|
123
|
+
{ tag: { value: 'Beta', color: Color.Blue } },
|
|
124
|
+
{ text: 'Updated' },
|
|
125
|
+
]}
|
|
126
|
+
actions={
|
|
127
|
+
<ActionPanel>
|
|
128
|
+
<Action title="Toggle Spacing Mode" onAction={toggleMode} />
|
|
129
|
+
</ActionPanel>
|
|
130
|
+
}
|
|
131
|
+
/>
|
|
132
|
+
</List.Section>
|
|
133
|
+
</List>
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
await renderWithProviders(<ListSpacingModeExample />)
|