termcast 1.3.30 → 1.3.32
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 +4 -39
- package/dist/apis/cache.js.map +1 -1
- package/dist/apis/hud.d.ts.map +1 -1
- package/dist/apis/hud.js +13 -31
- package/dist/apis/hud.js.map +1 -1
- package/dist/apis/localstorage.d.ts.map +1 -1
- package/dist/apis/localstorage.js +3 -27
- package/dist/apis/localstorage.js.map +1 -1
- package/dist/apis/toast.d.ts +16 -43
- package/dist/apis/toast.d.ts.map +1 -1
- package/dist/apis/toast.js +78 -177
- package/dist/apis/toast.js.map +1 -1
- package/dist/build.d.ts +3 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +52 -2
- package/dist/build.js.map +1 -1
- package/dist/cli.d.ts +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +206 -25
- package/dist/cli.js.map +1 -1
- package/dist/colors.d.ts.map +1 -1
- package/dist/colors.js +1 -0
- package/dist/colors.js.map +1 -1
- package/dist/compile.d.ts +0 -1
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +18 -23
- package/dist/compile.js.map +1 -1
- package/dist/components/actions.d.ts.map +1 -1
- package/dist/components/actions.js +30 -15
- package/dist/components/actions.js.map +1 -1
- package/dist/components/animation-tick.d.ts +12 -0
- package/dist/components/animation-tick.d.ts.map +1 -0
- package/dist/components/animation-tick.js +63 -0
- package/dist/components/animation-tick.js.map +1 -0
- package/dist/components/detail.d.ts.map +1 -1
- package/dist/components/detail.js +10 -13
- package/dist/components/detail.js.map +1 -1
- package/dist/components/dropdown.d.ts +1 -0
- package/dist/components/dropdown.d.ts.map +1 -1
- package/dist/components/dropdown.js +27 -26
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/extension-preferences.d.ts.map +1 -1
- package/dist/components/extension-preferences.js +15 -10
- package/dist/components/extension-preferences.js.map +1 -1
- package/dist/components/footer.d.ts +13 -0
- package/dist/components/footer.d.ts.map +1 -0
- package/dist/components/footer.js +106 -0
- package/dist/components/footer.js.map +1 -0
- package/dist/components/form/file-autocomplete.d.ts +19 -4
- package/dist/components/form/file-autocomplete.d.ts.map +1 -1
- package/dist/components/form/file-autocomplete.js +56 -55
- package/dist/components/form/file-autocomplete.js.map +1 -1
- package/dist/components/form/file-picker.d.ts.map +1 -1
- package/dist/components/form/file-picker.js +26 -15
- package/dist/components/form/file-picker.js.map +1 -1
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +17 -15
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/form/with-left-border.d.ts.map +1 -1
- package/dist/components/form/with-left-border.js +4 -12
- package/dist/components/form/with-left-border.js.map +1 -1
- package/dist/components/list.d.ts.map +1 -1
- package/dist/components/list.js +126 -86
- package/dist/components/list.js.map +1 -1
- package/dist/components/loading-bar.d.ts.map +1 -1
- package/dist/components/loading-bar.js +5 -22
- package/dist/components/loading-bar.js.map +1 -1
- package/dist/components/loading-text.d.ts.map +1 -1
- package/dist/components/loading-text.js +3 -22
- package/dist/components/loading-text.js.map +1 -1
- package/dist/components/theme-picker.d.ts +2 -0
- package/dist/components/theme-picker.d.ts.map +1 -0
- package/dist/components/theme-picker.js +37 -0
- package/dist/components/theme-picker.js.map +1 -0
- package/dist/descendants.d.ts +6 -0
- package/dist/descendants.d.ts.map +1 -1
- package/dist/descendants.js +74 -8
- package/dist/descendants.js.map +1 -1
- package/dist/examples/internal/descendants-rerender.d.ts +14 -0
- package/dist/examples/internal/descendants-rerender.d.ts.map +1 -0
- package/dist/examples/internal/descendants-rerender.js +145 -0
- package/dist/examples/internal/descendants-rerender.js.map +1 -0
- package/dist/examples/internal/simple-dialog.js +4 -1
- package/dist/examples/internal/simple-dialog.js.map +1 -1
- package/dist/examples/internal/simple-scrollbox.js +1 -1
- package/dist/examples/internal/simple-scrollbox.js.map +1 -1
- package/dist/examples/list-with-dropdown.js +1 -1
- package/dist/examples/list-with-dropdown.js.map +1 -1
- package/dist/examples/miscellaneous.js +1 -1
- package/dist/examples/miscellaneous.js.map +1 -1
- package/dist/examples/toast-action.d.ts +2 -0
- package/dist/examples/toast-action.d.ts.map +1 -0
- package/dist/examples/toast-action.js +76 -0
- package/dist/examples/toast-action.js.map +1 -0
- package/dist/examples/toast-variations.js +38 -36
- package/dist/examples/toast-variations.js.map +1 -1
- package/dist/extensions/dev.d.ts +1 -1
- package/dist/extensions/dev.d.ts.map +1 -1
- package/dist/extensions/dev.js +62 -30
- package/dist/extensions/dev.js.map +1 -1
- package/dist/extensions/home.d.ts.map +1 -1
- package/dist/extensions/home.js +4 -3
- package/dist/extensions/home.js.map +1 -1
- package/dist/extensions/react-refresh-init.d.ts +5 -0
- package/dist/extensions/react-refresh-init.d.ts.map +1 -0
- package/dist/extensions/react-refresh-init.js +52 -0
- package/dist/extensions/react-refresh-init.js.map +1 -0
- package/dist/internal/date-picker-widget.js +1 -1
- package/dist/internal/date-picker-widget.js.map +1 -1
- package/dist/internal/dialog.d.ts +8 -3
- package/dist/internal/dialog.d.ts.map +1 -1
- package/dist/internal/dialog.js +37 -53
- package/dist/internal/dialog.js.map +1 -1
- package/dist/internal/navigation.d.ts +1 -0
- package/dist/internal/navigation.d.ts.map +1 -1
- package/dist/internal/navigation.js +25 -1
- package/dist/internal/navigation.js.map +1 -1
- package/dist/internal/providers.d.ts.map +1 -1
- package/dist/internal/providers.js +9 -197
- package/dist/internal/providers.js.map +1 -1
- package/dist/internal/scrollbox.d.ts.map +1 -1
- package/dist/internal/scrollbox.js +1 -0
- package/dist/internal/scrollbox.js.map +1 -1
- package/dist/release.d.ts +1 -0
- package/dist/release.d.ts.map +1 -1
- package/dist/release.js +16 -9
- package/dist/release.js.map +1 -1
- package/dist/state.d.ts +27 -1
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +6 -0
- package/dist/state.js.map +1 -1
- package/dist/theme.d.ts +6 -19
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +76 -45
- package/dist/theme.js.map +1 -1
- package/dist/themes/aura.json +69 -0
- package/dist/themes/ayu.json +80 -0
- package/dist/themes/catppuccin-frappe.json +233 -0
- package/dist/themes/catppuccin-macchiato.json +233 -0
- package/dist/themes/catppuccin.json +112 -0
- package/dist/themes/cobalt2.json +228 -0
- package/dist/themes/cursor.json +249 -0
- package/dist/themes/dracula.json +219 -0
- package/dist/themes/everforest.json +241 -0
- package/dist/themes/flexoki.json +237 -0
- package/dist/themes/github-light.json +56 -0
- package/dist/themes/github.json +241 -0
- package/dist/themes/gruvbox.json +95 -0
- package/dist/themes/kanagawa.json +77 -0
- package/dist/themes/lucent-orng.json +227 -0
- package/dist/themes/material.json +235 -0
- package/dist/themes/matrix.json +77 -0
- package/dist/themes/mercury.json +245 -0
- package/dist/themes/monokai.json +221 -0
- package/dist/themes/nightowl.json +221 -0
- package/dist/themes/nord.json +223 -0
- package/dist/themes/one-dark.json +84 -0
- package/dist/themes/opencode-light.json +62 -0
- package/dist/themes/opencode.json +245 -0
- package/dist/themes/orng.json +245 -0
- package/dist/themes/palenight.json +222 -0
- package/dist/themes/rosepine.json +234 -0
- package/dist/themes/solarized.json +223 -0
- package/dist/themes/synthwave84.json +226 -0
- package/dist/themes/termcast.json +226 -0
- package/dist/themes/tokyonight.json +243 -0
- package/dist/themes/vercel.json +255 -0
- package/dist/themes/vesper.json +218 -0
- package/dist/themes/zenburn.json +223 -0
- package/dist/themes.d.ts +57 -0
- package/dist/themes.d.ts.map +1 -0
- package/dist/themes.js +181 -0
- package/dist/themes.js.map +1 -0
- package/dist/utils/run-command.d.ts +2 -1
- package/dist/utils/run-command.d.ts.map +1 -1
- package/dist/utils/run-command.js +20 -10
- package/dist/utils/run-command.js.map +1 -1
- package/dist/utils.d.ts +2 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +90 -17
- package/dist/utils.js.map +1 -1
- package/dist/watcher.d.ts +3 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +16 -0
- package/dist/watcher.js.map +1 -0
- package/package.json +16 -10
- package/src/apis/cache.tsx +5 -44
- package/src/apis/hud.tsx +17 -62
- package/src/apis/localstorage.tsx +3 -32
- package/src/apis/toast.tsx +91 -275
- package/src/build.test.tsx +10 -0
- package/src/build.tsx +61 -1
- package/src/cli.tsx +365 -103
- package/src/colors.tsx +1 -0
- package/src/compile.tsx +21 -29
- package/src/compile.vitest.tsx +300 -0
- package/src/components/actions.tsx +64 -45
- package/src/components/animation-tick.tsx +85 -0
- package/src/components/detail.tsx +31 -35
- package/src/components/dropdown.tsx +32 -21
- package/src/components/extension-preferences.tsx +14 -10
- package/src/components/footer.tsx +241 -0
- package/src/components/form/file-autocomplete.tsx +80 -60
- package/src/components/form/file-picker.tsx +37 -25
- package/src/components/form/index.tsx +45 -41
- package/src/components/form/with-left-border.tsx +4 -14
- package/src/components/list.tsx +181 -121
- package/src/components/loading-bar.tsx +5 -25
- package/src/components/loading-text.tsx +4 -23
- package/src/components/theme-picker.tsx +57 -0
- package/src/descendants.tsx +98 -9
- package/src/examples/actions-dialog-layout.vitest.tsx +112 -0
- package/src/examples/file-autocomplete.vitest.tsx +131 -122
- package/src/examples/form-basic.vitest.tsx +463 -644
- package/src/examples/form-dropdown.vitest.tsx +553 -571
- package/src/examples/form-scroll.vitest.tsx +112 -102
- package/src/examples/form-tagpicker.vitest.tsx +364 -338
- package/src/examples/internal/descendants-rerender.tsx +273 -0
- package/src/examples/internal/descendants-rerender.vitest.tsx +194 -0
- package/src/examples/internal/simple-dialog.tsx +4 -4
- package/src/examples/internal/simple-scrollbox.tsx +2 -2
- package/src/examples/internal/simple-scrollbox.vitest.tsx +43 -31
- package/src/examples/list-detail-metadata.vitest.tsx +34 -30
- package/src/examples/list-dropdown-default.vitest.tsx +84 -72
- package/src/examples/list-empty-view.vitest.tsx +93 -0
- package/src/examples/list-fetch-data.vitest.tsx +36 -30
- package/src/examples/list-scrollbox.vitest.tsx +59 -39
- package/src/examples/list-with-detail.vitest.tsx +339 -314
- package/src/examples/list-with-dropdown.tsx +1 -0
- package/src/examples/list-with-dropdown.vitest.tsx +176 -150
- package/src/examples/list-with-sections.vitest.tsx +289 -270
- package/src/examples/list-with-toast.vitest.tsx +44 -44
- package/src/examples/miscellaneous.tsx +10 -0
- package/src/examples/simple-file-picker.vitest.tsx +90 -86
- package/src/examples/simple-grid.vitest.tsx +275 -249
- package/src/examples/simple-navigation.vitest.tsx +192 -168
- package/src/examples/store.vitest.tsx +6 -4
- package/src/examples/swift-extension.vitest.tsx +31 -19
- package/src/examples/synonyms.vitest.tsx +93 -83
- package/src/examples/toast-action.tsx +160 -0
- package/src/examples/toast-action.vitest.tsx +404 -0
- package/src/examples/toast-variations.tsx +58 -57
- package/src/examples/toast-variations.vitest.tsx +186 -166
- package/src/extensions/dev.tsx +74 -33
- package/src/extensions/dev.vitest.tsx +162 -69
- package/src/extensions/home.tsx +5 -6
- package/src/extensions/react-refresh-init.tsx +59 -0
- package/src/internal/date-picker-widget.tsx +1 -1
- package/src/internal/dialog.tsx +59 -83
- package/src/internal/navigation.tsx +37 -4
- package/src/internal/providers.tsx +27 -315
- package/src/internal/scrollbox.tsx +1 -0
- package/src/release.tsx +16 -10
- package/src/state.tsx +36 -3
- package/src/theme.tsx +82 -51
- package/src/themes/aura.json +69 -0
- package/src/themes/ayu.json +80 -0
- package/src/themes/catppuccin-frappe.json +233 -0
- package/src/themes/catppuccin-macchiato.json +233 -0
- package/src/themes/catppuccin.json +112 -0
- package/src/themes/cobalt2.json +228 -0
- package/src/themes/cursor.json +249 -0
- package/src/themes/dracula.json +219 -0
- package/src/themes/everforest.json +241 -0
- package/src/themes/flexoki.json +237 -0
- package/src/themes/github-light.json +56 -0
- package/src/themes/github.json +241 -0
- package/src/themes/gruvbox.json +95 -0
- package/src/themes/kanagawa.json +77 -0
- package/src/themes/lucent-orng.json +227 -0
- package/src/themes/material.json +235 -0
- package/src/themes/matrix.json +77 -0
- package/src/themes/mercury.json +252 -0
- package/src/themes/monokai.json +221 -0
- package/src/themes/nightowl.json +221 -0
- package/src/themes/nord.json +223 -0
- package/src/themes/one-dark.json +84 -0
- package/src/themes/opencode-light.json +62 -0
- package/src/themes/opencode.json +245 -0
- package/src/themes/orng.json +245 -0
- package/src/themes/palenight.json +222 -0
- package/src/themes/rosepine.json +234 -0
- package/src/themes/solarized.json +223 -0
- package/src/themes/synthwave84.json +226 -0
- package/src/themes/termcast.json +227 -0
- package/src/themes/tokyonight.json +243 -0
- package/src/themes/vercel.json +255 -0
- package/src/themes/vesper.json +218 -0
- package/src/themes/zenburn.json +223 -0
- package/src/themes.ts +291 -0
- package/src/utils/run-command.tsx +23 -12
- package/src/utils.tsx +115 -18
- package/src/watcher.tsx +19 -0
package/src/components/list.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
TextAttributes,
|
|
5
5
|
TextareaRenderable,
|
|
6
6
|
} from '@opentui/core'
|
|
7
|
-
import { useKeyboard } from '@opentui/react'
|
|
7
|
+
import { useKeyboard, flushSync } from '@opentui/react'
|
|
8
8
|
import React, {
|
|
9
9
|
ReactElement,
|
|
10
10
|
ReactNode,
|
|
@@ -17,6 +17,7 @@ import React, {
|
|
|
17
17
|
useState
|
|
18
18
|
} from 'react'
|
|
19
19
|
import { LoadingBar } from 'termcast/src/components/loading-bar'
|
|
20
|
+
import { Footer } from 'termcast/src/components/footer'
|
|
20
21
|
import { createDescendants } from 'termcast/src/descendants'
|
|
21
22
|
import { useStore } from 'termcast/src/state'
|
|
22
23
|
import { useDialog } from 'termcast/src/internal/dialog'
|
|
@@ -27,6 +28,7 @@ import { ScrollBox } from 'termcast/src/internal/scrollbox'
|
|
|
27
28
|
|
|
28
29
|
import { Color, resolveColor } from 'termcast/src/colors'
|
|
29
30
|
import { getIconEmoji } from 'termcast/src/components/icon'
|
|
31
|
+
import { ActionPanel } from 'termcast/src/components/actions'
|
|
30
32
|
import { Theme, markdownSyntaxStyle } from 'termcast/src/theme'
|
|
31
33
|
import { CommonProps } from 'termcast/src/utils'
|
|
32
34
|
|
|
@@ -71,44 +73,35 @@ interface ActionsInterface {
|
|
|
71
73
|
function ListFooter(): any {
|
|
72
74
|
const firstActionTitle = useStore((s) => s.firstActionTitle)
|
|
73
75
|
const hasToast = useStore((s) => s.toast !== null)
|
|
76
|
+
const listContext = useContext(ListContext)
|
|
77
|
+
const isShowingDetail = listContext?.isShowingDetail ?? false
|
|
74
78
|
|
|
75
79
|
const content = hasToast ? null : (
|
|
76
|
-
|
|
80
|
+
<box style={{ flexDirection: 'row', gap: 3 }}>
|
|
77
81
|
{firstActionTitle && (
|
|
78
|
-
|
|
79
|
-
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
82
|
+
<box style={{ flexDirection: 'row', gap: 1 }}>
|
|
83
|
+
<text flexShrink={0} fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
80
84
|
↵
|
|
81
85
|
</text>
|
|
82
|
-
<text fg={Theme.textMuted}>
|
|
83
|
-
|
|
86
|
+
<text flexShrink={0} fg={Theme.textMuted}>{firstActionTitle.toLowerCase()}</text>
|
|
87
|
+
</box>
|
|
84
88
|
)}
|
|
85
|
-
<
|
|
86
|
-
{
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
<box
|
|
98
|
-
border={false}
|
|
99
|
-
height={1}
|
|
100
|
-
style={{
|
|
101
|
-
paddingLeft: 1,
|
|
102
|
-
flexShrink: 0,
|
|
103
|
-
paddingRight: 1,
|
|
104
|
-
paddingTop: 1,
|
|
105
|
-
marginTop: 1,
|
|
106
|
-
flexDirection: 'row',
|
|
107
|
-
}}
|
|
108
|
-
>
|
|
109
|
-
{content}
|
|
89
|
+
<box style={{ flexDirection: 'row', gap: 1 }}>
|
|
90
|
+
<text flexShrink={0} fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
91
|
+
↑↓
|
|
92
|
+
</text>
|
|
93
|
+
<text flexShrink={0} fg={Theme.textMuted}>navigate</text>
|
|
94
|
+
</box>
|
|
95
|
+
<box style={{ flexDirection: 'row', gap: 1 }}>
|
|
96
|
+
<text flexShrink={0} fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
97
|
+
^k
|
|
98
|
+
</text>
|
|
99
|
+
<text flexShrink={0} fg={Theme.textMuted}>actions</text>
|
|
100
|
+
</box>
|
|
110
101
|
</box>
|
|
111
102
|
)
|
|
103
|
+
|
|
104
|
+
return <Footer hidePoweredBy={isShowingDetail}>{content}</Footer>
|
|
112
105
|
}
|
|
113
106
|
|
|
114
107
|
interface NavigationChildInterface {
|
|
@@ -350,6 +343,8 @@ const {
|
|
|
350
343
|
DescendantsProvider: ListDescendantsProvider,
|
|
351
344
|
useDescendants: useListDescendants,
|
|
352
345
|
useDescendant: useListItemDescendant,
|
|
346
|
+
useDescendantsRerender: useListDescendantsRerender,
|
|
347
|
+
useDescendantsMap: useListDescendantsMap,
|
|
353
348
|
} = createDescendants<ListItemDescendant>()
|
|
354
349
|
|
|
355
350
|
// Create descendants for Dropdown items
|
|
@@ -395,8 +390,10 @@ function ListDropdownDialog(props: ListDropdownDialogProps): any {
|
|
|
395
390
|
|
|
396
391
|
// Wrapper function that updates search text
|
|
397
392
|
const setSearchText = (value: string) => {
|
|
398
|
-
|
|
399
|
-
|
|
393
|
+
// Using flushSync to force descendants to update visibility before querying
|
|
394
|
+
flushSync(() => {
|
|
395
|
+
setSearchTextRaw(value)
|
|
396
|
+
})
|
|
400
397
|
const items = Object.values(descendantsContext.map.current)
|
|
401
398
|
.filter((item) => item.index !== -1 && item.props?.visible !== false)
|
|
402
399
|
.sort((a, b) => a.index - b.index)
|
|
@@ -472,13 +469,15 @@ function ListDropdownDialog(props: ListDropdownDialogProps): any {
|
|
|
472
469
|
justifyContent: 'space-between',
|
|
473
470
|
}}
|
|
474
471
|
>
|
|
475
|
-
<text
|
|
476
|
-
<text fg={Theme.textMuted}>esc</text>
|
|
472
|
+
<text flexShrink={0} fg={Theme.textMuted}>{props.tooltip}</text>
|
|
473
|
+
<text flexShrink={0} fg={Theme.textMuted}>esc</text>
|
|
477
474
|
</box>
|
|
478
|
-
<box style={{ paddingTop: 1, paddingBottom: 1 }}>
|
|
475
|
+
<box style={{ paddingTop: 1, paddingBottom: 1, flexDirection: 'row' }}>
|
|
476
|
+
<text flexShrink={0} fg={Theme.textMuted}>> </text>
|
|
479
477
|
<textarea
|
|
480
478
|
ref={inputRef}
|
|
481
479
|
height={1}
|
|
480
|
+
flexGrow={1}
|
|
482
481
|
wrapMode='none'
|
|
483
482
|
keyBindings={[
|
|
484
483
|
{ name: 'return', action: 'submit' },
|
|
@@ -518,7 +517,7 @@ function ListDropdownDialog(props: ListDropdownDialogProps): any {
|
|
|
518
517
|
</box>
|
|
519
518
|
{props.isLoading && (
|
|
520
519
|
<box style={{ paddingLeft: 1 }}>
|
|
521
|
-
<text fg={Theme.textMuted}>Loading...</text>
|
|
520
|
+
<text flexShrink={0} fg={Theme.textMuted}>Loading...</text>
|
|
522
521
|
</box>
|
|
523
522
|
)}
|
|
524
523
|
</box>
|
|
@@ -533,32 +532,26 @@ function DropdownFooter(): any {
|
|
|
533
532
|
const hasToast = useStore((s) => s.toast !== null)
|
|
534
533
|
|
|
535
534
|
const content = hasToast ? null : (
|
|
536
|
-
|
|
537
|
-
<
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
535
|
+
<box style={{ flexDirection: 'row', gap: 3 }}>
|
|
536
|
+
<box style={{ flexDirection: 'row', gap: 1 }}>
|
|
537
|
+
<text flexShrink={0} fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
538
|
+
↵
|
|
539
|
+
</text>
|
|
540
|
+
<text flexShrink={0} fg={Theme.textMuted}>select</text>
|
|
541
|
+
</box>
|
|
542
|
+
<box style={{ flexDirection: 'row', gap: 1 }}>
|
|
543
|
+
<text flexShrink={0} fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
544
|
+
↑↓
|
|
545
|
+
</text>
|
|
546
|
+
<text flexShrink={0} fg={Theme.textMuted}>navigate</text>
|
|
547
|
+
</box>
|
|
548
|
+
</box>
|
|
546
549
|
)
|
|
547
550
|
|
|
548
551
|
return (
|
|
549
|
-
<
|
|
550
|
-
border={false}
|
|
551
|
-
height={1}
|
|
552
|
-
style={{
|
|
553
|
-
paddingRight: 2,
|
|
554
|
-
paddingLeft: 3,
|
|
555
|
-
paddingBottom: 1,
|
|
556
|
-
paddingTop: 1,
|
|
557
|
-
flexDirection: 'row',
|
|
558
|
-
}}
|
|
559
|
-
>
|
|
552
|
+
<Footer paddingLeft={3} paddingRight={2} paddingBottom={1} marginTop={0}>
|
|
560
553
|
{content}
|
|
561
|
-
</
|
|
554
|
+
</Footer>
|
|
562
555
|
)
|
|
563
556
|
}
|
|
564
557
|
|
|
@@ -592,6 +585,7 @@ function ListItemRow(props: {
|
|
|
592
585
|
accessoryElements.push(
|
|
593
586
|
<text
|
|
594
587
|
key={`text-${textValue}`}
|
|
588
|
+
flexShrink={0}
|
|
595
589
|
fg={active ? Theme.background : resolveColor(textColor) || Theme.info}
|
|
596
590
|
wrapMode="none"
|
|
597
591
|
>
|
|
@@ -611,6 +605,7 @@ function ListItemRow(props: {
|
|
|
611
605
|
accessoryElements.push(
|
|
612
606
|
<text
|
|
613
607
|
key={`tag-${tagValue}`}
|
|
608
|
+
flexShrink={0}
|
|
614
609
|
fg={active ? Theme.background : resolveColor(tagColor) || Theme.warning}
|
|
615
610
|
wrapMode="none"
|
|
616
611
|
>
|
|
@@ -633,6 +628,7 @@ function ListItemRow(props: {
|
|
|
633
628
|
accessoryElements.push(
|
|
634
629
|
<text
|
|
635
630
|
key={`date-${dateValue.getTime()}`}
|
|
631
|
+
flexShrink={0}
|
|
636
632
|
fg={active ? Theme.background : resolveColor(dateColor) || Theme.success}
|
|
637
633
|
wrapMode="none"
|
|
638
634
|
>
|
|
@@ -670,9 +666,10 @@ function ListItemRow(props: {
|
|
|
670
666
|
>
|
|
671
667
|
<box style={{ flexDirection: 'row', flexGrow: 1, flexShrink: 1, overflow: 'hidden', gap: 1 }}>
|
|
672
668
|
<box style={{ flexDirection: 'row', flexShrink: 0 }}>
|
|
673
|
-
<text fg={active ? Theme.background : Theme.text} attributes={active ? TextAttributes.BOLD : undefined} selectable={false} wrapMode="none">{active ? '›' : ' '}</text>
|
|
674
|
-
{icon && <text fg={active ? Theme.background : iconColor || Theme.text} selectable={false} wrapMode="none">{getIconEmoji(icon)} </text>}
|
|
669
|
+
<text flexShrink={0} fg={active ? Theme.background : Theme.text} attributes={active ? TextAttributes.BOLD : undefined} selectable={false} wrapMode="none">{active ? '›' : ' '}</text>
|
|
670
|
+
{icon && <text flexShrink={0} fg={active ? Theme.background : iconColor || Theme.text} selectable={false} wrapMode="none">{getIconEmoji(icon)} </text>}
|
|
675
671
|
<text
|
|
672
|
+
flexShrink={0}
|
|
676
673
|
fg={active ? Theme.background : Theme.text}
|
|
677
674
|
attributes={active ? TextAttributes.BOLD : undefined}
|
|
678
675
|
selectable={false}
|
|
@@ -683,6 +680,7 @@ function ListItemRow(props: {
|
|
|
683
680
|
</box>
|
|
684
681
|
{subtitle && (
|
|
685
682
|
<text
|
|
683
|
+
flexShrink={0}
|
|
686
684
|
fg={active ? Theme.background : Theme.textMuted}
|
|
687
685
|
selectable={false}
|
|
688
686
|
wrapMode="none"
|
|
@@ -695,7 +693,7 @@ function ListItemRow(props: {
|
|
|
695
693
|
<box style={{ flexDirection: 'row', flexShrink: 0 }}>
|
|
696
694
|
{accessoryElements.map((elem, i) => (
|
|
697
695
|
<box key={i} style={{ flexDirection: 'row' }}>
|
|
698
|
-
{i > 0 && <text> </text>}
|
|
696
|
+
{i > 0 && <text flexShrink={0}> </text>}
|
|
699
697
|
{elem}
|
|
700
698
|
</box>
|
|
701
699
|
))}
|
|
@@ -777,9 +775,10 @@ export const List: ListType = (props) => {
|
|
|
777
775
|
|
|
778
776
|
// Wrapper function that updates search text
|
|
779
777
|
const setInternalSearchText = (value: string) => {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
778
|
+
// Using flushSync to force descendants to update visibility before querying
|
|
779
|
+
flushSync(() => {
|
|
780
|
+
setInternalSearchTextRaw(value)
|
|
781
|
+
})
|
|
783
782
|
const items = Object.values(descendantsContext.map.current)
|
|
784
783
|
.filter((item) => item.index !== -1 && item.props?.visible !== false)
|
|
785
784
|
.sort((a, b) => a.index - b.index)
|
|
@@ -919,11 +918,15 @@ export const List: ListType = (props) => {
|
|
|
919
918
|
if (evt.name === 'k' && evt.ctrl) {
|
|
920
919
|
// Show current item's actions if available
|
|
921
920
|
if (currentItem?.props?.actions) {
|
|
922
|
-
dialog.
|
|
921
|
+
dialog.pushActions(currentItem.props.actions)
|
|
923
922
|
}
|
|
924
923
|
// Otherwise show List's own actions
|
|
925
924
|
else if (props.actions) {
|
|
926
|
-
dialog.
|
|
925
|
+
dialog.pushActions(props.actions)
|
|
926
|
+
}
|
|
927
|
+
// Otherwise show empty ActionPanel (still has Settings section with Configure Extension, etc.)
|
|
928
|
+
else {
|
|
929
|
+
dialog.pushActions(<ActionPanel />)
|
|
927
930
|
}
|
|
928
931
|
return
|
|
929
932
|
}
|
|
@@ -936,7 +939,7 @@ export const List: ListType = (props) => {
|
|
|
936
939
|
|
|
937
940
|
if (currentItem.props.actions) {
|
|
938
941
|
useStore.setState({ shouldAutoExecuteFirstAction: true })
|
|
939
|
-
dialog.
|
|
942
|
+
dialog.pushActions(currentItem.props.actions)
|
|
940
943
|
}
|
|
941
944
|
}
|
|
942
945
|
})
|
|
@@ -995,13 +998,15 @@ export const List: ListType = (props) => {
|
|
|
995
998
|
<box
|
|
996
999
|
style={{
|
|
997
1000
|
flexGrow: 1,
|
|
998
|
-
flexDirection: '
|
|
1001
|
+
flexDirection: 'row',
|
|
999
1002
|
flexShrink: 1,
|
|
1000
1003
|
}}
|
|
1001
1004
|
>
|
|
1005
|
+
<text flexShrink={0} fg={Theme.textMuted}>> </text>
|
|
1002
1006
|
<textarea
|
|
1003
1007
|
ref={inputRef}
|
|
1004
1008
|
height={1}
|
|
1009
|
+
flexGrow={1}
|
|
1005
1010
|
wrapMode='none'
|
|
1006
1011
|
keyBindings={[
|
|
1007
1012
|
{ name: 'return', action: 'submit' },
|
|
@@ -1026,7 +1031,7 @@ export const List: ListType = (props) => {
|
|
|
1026
1031
|
{/* Main content area with optional detail view */}
|
|
1027
1032
|
<box style={{ flexDirection: 'row', flexGrow: 1, flexShrink: 1 }}>
|
|
1028
1033
|
{/* List content - render children which will register themselves */}
|
|
1029
|
-
<box style={{ width: isShowingDetail ? '50%' : '100%', flexGrow:
|
|
1034
|
+
<box style={{ width: isShowingDetail ? '50%' : '100%', flexGrow: 1, flexShrink: 1, flexDirection: 'column' }}>
|
|
1030
1035
|
{/* Scrollable list items */}
|
|
1031
1036
|
<ScrollBox
|
|
1032
1037
|
ref={scrollBoxRef}
|
|
@@ -1080,6 +1085,47 @@ export const List: ListType = (props) => {
|
|
|
1080
1085
|
)
|
|
1081
1086
|
}
|
|
1082
1087
|
|
|
1088
|
+
|
|
1089
|
+
function DefaultEmptyView(): any {
|
|
1090
|
+
// Subscribe to re-render when items are added/removed
|
|
1091
|
+
void useListDescendantsRerender()
|
|
1092
|
+
// Get live map ref for reading in useLayoutEffect
|
|
1093
|
+
const map = useListDescendantsMap()
|
|
1094
|
+
const [hasVisibleItems, setHasVisibleItems] = useState(true)
|
|
1095
|
+
|
|
1096
|
+
// We must check visibility in useLayoutEffect because:
|
|
1097
|
+
// 1. map.current is cleared by reset() during render, so it's empty if read during render
|
|
1098
|
+
// 2. committedMap is stale - it's a snapshot from the previous render cycle and doesn't
|
|
1099
|
+
// reflect prop changes like 'visible' (only tracks which items exist, not their props)
|
|
1100
|
+
// 3. Items register in their own useLayoutEffect, so map.current is only populated after
|
|
1101
|
+
// all items' layout effects have run
|
|
1102
|
+
useLayoutEffect(() => {
|
|
1103
|
+
const items = Object.values(map.current)
|
|
1104
|
+
.filter((item) => item.index !== -1 && item.props?.visible !== false)
|
|
1105
|
+
setHasVisibleItems(items.length > 0)
|
|
1106
|
+
})
|
|
1107
|
+
|
|
1108
|
+
if (hasVisibleItems) return null
|
|
1109
|
+
|
|
1110
|
+
return (
|
|
1111
|
+
<box
|
|
1112
|
+
style={{
|
|
1113
|
+
flexDirection: 'column',
|
|
1114
|
+
alignItems: 'center',
|
|
1115
|
+
justifyContent: 'center',
|
|
1116
|
+
paddingTop: 2,
|
|
1117
|
+
paddingBottom: 2,
|
|
1118
|
+
paddingLeft: 2,
|
|
1119
|
+
paddingRight: 2,
|
|
1120
|
+
}}
|
|
1121
|
+
>
|
|
1122
|
+
<text flexShrink={0} fg={Theme.textMuted}>
|
|
1123
|
+
No items found
|
|
1124
|
+
</text>
|
|
1125
|
+
</box>
|
|
1126
|
+
)
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1083
1129
|
// Component to render list items and sections
|
|
1084
1130
|
function ListItemsRenderer(props: { children?: ReactNode }): any {
|
|
1085
1131
|
const { children } = props
|
|
@@ -1090,6 +1136,7 @@ function ListItemsRenderer(props: { children?: ReactNode }): any {
|
|
|
1090
1136
|
return (
|
|
1091
1137
|
<ListSectionContext.Provider value={{ searchText }}>
|
|
1092
1138
|
{children}
|
|
1139
|
+
<DefaultEmptyView />
|
|
1093
1140
|
</ListSectionContext.Provider>
|
|
1094
1141
|
)
|
|
1095
1142
|
}
|
|
@@ -1164,8 +1211,8 @@ const ListItem: ListItemType = (props) => {
|
|
|
1164
1211
|
const handleMouseDown = () => {
|
|
1165
1212
|
if (listContext && index !== -1) {
|
|
1166
1213
|
// If clicking on already selected item, show actions (like pressing Enter)
|
|
1167
|
-
if (isActive
|
|
1168
|
-
dialog.
|
|
1214
|
+
if (isActive) {
|
|
1215
|
+
dialog.pushActions(props.actions || <ActionPanel />)
|
|
1169
1216
|
} else if (listContext.setSelectedIndex) {
|
|
1170
1217
|
// Otherwise just select the item
|
|
1171
1218
|
listContext.setSelectedIndex(index)
|
|
@@ -1221,13 +1268,13 @@ const ListItemDetail: ListItemDetailType = (props) => {
|
|
|
1221
1268
|
<box style={{ flexDirection: 'column', flexGrow: 1 }}>
|
|
1222
1269
|
{isLoading && (
|
|
1223
1270
|
<box style={{ paddingBottom: 1 }}>
|
|
1224
|
-
<text fg={Theme.textMuted}>Loading...</text>
|
|
1271
|
+
<text flexShrink={0} fg={Theme.textMuted}>Loading...</text>
|
|
1225
1272
|
</box>
|
|
1226
1273
|
)}
|
|
1227
1274
|
|
|
1228
1275
|
<ScrollBox
|
|
1229
1276
|
focused={false}
|
|
1230
|
-
flexGrow={1}
|
|
1277
|
+
// flexGrow={1}
|
|
1231
1278
|
flexShrink={1}
|
|
1232
1279
|
style={{
|
|
1233
1280
|
rootOptions: {
|
|
@@ -1271,8 +1318,8 @@ const ListItemDetailMetadata = (props: MetadataProps) => {
|
|
|
1271
1318
|
const ListItemDetailMetadataLabel = (props: { title: string; text?: string; icon?: Image.ImageLike }) => {
|
|
1272
1319
|
return (
|
|
1273
1320
|
<box style={{ flexDirection: 'column', paddingBottom: 0.5 }}>
|
|
1274
|
-
<text fg={Theme.textMuted}>{props.title}:</text>
|
|
1275
|
-
{props.text && <text fg={Theme.text}>{props.text}</text>}
|
|
1321
|
+
<text flexShrink={0} fg={Theme.textMuted}>{props.title}:</text>
|
|
1322
|
+
{props.text && <text flexShrink={0} fg={Theme.text}>{props.text}</text>}
|
|
1276
1323
|
</box>
|
|
1277
1324
|
)
|
|
1278
1325
|
}
|
|
@@ -1280,7 +1327,7 @@ const ListItemDetailMetadataLabel = (props: { title: string; text?: string; icon
|
|
|
1280
1327
|
const ListItemDetailMetadataSeparator = () => {
|
|
1281
1328
|
return (
|
|
1282
1329
|
<box style={{ paddingBottom: 0.5 }}>
|
|
1283
|
-
<text fg={Theme.border}>─────────────────</text>
|
|
1330
|
+
<text flexShrink={0} fg={Theme.border}>─────────────────</text>
|
|
1284
1331
|
</box>
|
|
1285
1332
|
)
|
|
1286
1333
|
}
|
|
@@ -1288,8 +1335,8 @@ const ListItemDetailMetadataSeparator = () => {
|
|
|
1288
1335
|
const ListItemDetailMetadataLink = (props: { title: string; target: string; text: string }) => {
|
|
1289
1336
|
return (
|
|
1290
1337
|
<box style={{ flexDirection: 'column', paddingBottom: 0.5 }}>
|
|
1291
|
-
<text fg={Theme.textMuted}>{props.title}:</text>
|
|
1292
|
-
<text fg={Theme.
|
|
1338
|
+
<text flexShrink={0} fg={Theme.textMuted}>{props.title}:</text>
|
|
1339
|
+
<text flexShrink={0} fg={Theme.markdownLink}>{props.text}</text>
|
|
1293
1340
|
</box>
|
|
1294
1341
|
)
|
|
1295
1342
|
}
|
|
@@ -1297,7 +1344,7 @@ const ListItemDetailMetadataLink = (props: { title: string; target: string; text
|
|
|
1297
1344
|
const ListItemDetailMetadataTagList = (props: { title: string; children: ReactNode }) => {
|
|
1298
1345
|
return (
|
|
1299
1346
|
<box style={{ flexDirection: 'column', paddingBottom: 0.5 }}>
|
|
1300
|
-
<text fg={Theme.textMuted}>{props.title}:</text>
|
|
1347
|
+
<text flexShrink={0} fg={Theme.textMuted}>{props.title}:</text>
|
|
1301
1348
|
<box style={{ flexDirection: 'row', paddingLeft: 1 }}>
|
|
1302
1349
|
{props.children}
|
|
1303
1350
|
</box>
|
|
@@ -1308,7 +1355,7 @@ const ListItemDetailMetadataTagList = (props: { title: string; children: ReactNo
|
|
|
1308
1355
|
const ListItemDetailMetadataTagListItem = (props: { text?: string; color?: Color.ColorLike; icon?: Image.ImageLike; onAction?: () => void }) => {
|
|
1309
1356
|
return (
|
|
1310
1357
|
<box style={{ paddingRight: 1 }}>
|
|
1311
|
-
<text fg={resolveColor(props.color) || Theme.accent}>[{props.text}]</text>
|
|
1358
|
+
<text flexShrink={0} fg={resolveColor(props.color) || Theme.accent}>[{props.text}]</text>
|
|
1312
1359
|
</box>
|
|
1313
1360
|
)
|
|
1314
1361
|
}
|
|
@@ -1322,6 +1369,14 @@ ListItemDetailMetadataTagList.Item = ListItemDetailMetadataTagListItem as any
|
|
|
1322
1369
|
|
|
1323
1370
|
ListItem.Detail = ListItemDetail
|
|
1324
1371
|
|
|
1372
|
+
/**
|
|
1373
|
+
* A dropdown menu shown in the right-hand-side of the search bar.
|
|
1374
|
+
* Open it with Ctrl+P or by clicking on it.
|
|
1375
|
+
*
|
|
1376
|
+
* Note: There is no built-in "All" or reset option. If you want users to be
|
|
1377
|
+
* able to reset the filter, add a `List.Dropdown.Item` with title="All" and
|
|
1378
|
+
* value="" (or your preferred reset value) at the top of your dropdown items.
|
|
1379
|
+
*/
|
|
1325
1380
|
const ListDropdown: ListDropdownType = (props) => {
|
|
1326
1381
|
const listContext = useContext(ListContext)
|
|
1327
1382
|
const [isHovered, setIsHovered] = useState(false)
|
|
@@ -1391,37 +1446,39 @@ const ListDropdown: ListDropdownType = (props) => {
|
|
|
1391
1446
|
useEffect(() => {
|
|
1392
1447
|
if (isDropdownOpen && !dialog.stack.length) {
|
|
1393
1448
|
// Pass the children to the dialog to render them there
|
|
1394
|
-
dialog.push(
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
const
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1449
|
+
dialog.push({
|
|
1450
|
+
element: (
|
|
1451
|
+
<ListDropdownDialog
|
|
1452
|
+
{...props}
|
|
1453
|
+
value={dropdownState.value}
|
|
1454
|
+
onChange={(newValue) => {
|
|
1455
|
+
// Find the title for this value
|
|
1456
|
+
let title = newValue
|
|
1457
|
+
for (const item of Object.values(descendantsContext.map.current)) {
|
|
1458
|
+
const itemProps = item.props as DropdownItemDescendant
|
|
1459
|
+
if (itemProps.value === newValue) {
|
|
1460
|
+
title = itemProps.title
|
|
1461
|
+
break
|
|
1462
|
+
}
|
|
1406
1463
|
}
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
'
|
|
1424
|
-
)
|
|
1464
|
+
setDropdownState({ value: newValue, title })
|
|
1465
|
+
setIsDropdownOpen(false)
|
|
1466
|
+
dialog.clear()
|
|
1467
|
+
if (props.onChange) {
|
|
1468
|
+
props.onChange(newValue)
|
|
1469
|
+
}
|
|
1470
|
+
// TODO: Handle storeValue to persist the value
|
|
1471
|
+
}}
|
|
1472
|
+
onCancel={() => {
|
|
1473
|
+
setIsDropdownOpen(false)
|
|
1474
|
+
dialog.clear()
|
|
1475
|
+
}}
|
|
1476
|
+
>
|
|
1477
|
+
{props.children}
|
|
1478
|
+
</ListDropdownDialog>
|
|
1479
|
+
),
|
|
1480
|
+
position: 'center',
|
|
1481
|
+
})
|
|
1425
1482
|
}
|
|
1426
1483
|
}, [isDropdownOpen, props.children])
|
|
1427
1484
|
|
|
@@ -1455,12 +1512,14 @@ const ListDropdown: ListDropdownType = (props) => {
|
|
|
1455
1512
|
>
|
|
1456
1513
|
{/*<text >^p </text>*/}
|
|
1457
1514
|
<text
|
|
1515
|
+
flexShrink={0}
|
|
1458
1516
|
fg={isHovered ? Theme.text : Theme.textMuted}
|
|
1459
1517
|
selectable={false}
|
|
1460
1518
|
>
|
|
1461
1519
|
{displayValue}
|
|
1462
1520
|
</text>
|
|
1463
1521
|
<text
|
|
1522
|
+
flexShrink={0}
|
|
1464
1523
|
fg={isHovered ? Theme.text : Theme.textMuted}
|
|
1465
1524
|
selectable={false}
|
|
1466
1525
|
>
|
|
@@ -1552,11 +1611,12 @@ ListDropdown.Item = (props) => {
|
|
|
1552
1611
|
>
|
|
1553
1612
|
<box style={{ flexDirection: 'row' }}>
|
|
1554
1613
|
{isActive && (
|
|
1555
|
-
<text fg={Theme.background} selectable={false}>
|
|
1614
|
+
<text flexShrink={0} fg={Theme.background} selectable={false}>
|
|
1556
1615
|
›{''}
|
|
1557
1616
|
</text>
|
|
1558
1617
|
)}
|
|
1559
1618
|
<text
|
|
1619
|
+
flexShrink={0}
|
|
1560
1620
|
fg={
|
|
1561
1621
|
isActive
|
|
1562
1622
|
? Theme.background
|
|
@@ -1605,7 +1665,7 @@ ListDropdown.Section = (props) => {
|
|
|
1605
1665
|
{/* Render section title if we're in the dialog and not searching */}
|
|
1606
1666
|
{showTitle && (
|
|
1607
1667
|
<box style={{ paddingTop: 1, paddingLeft: 1 }}>
|
|
1608
|
-
<text fg={Theme.accent} attributes={TextAttributes.BOLD}>
|
|
1668
|
+
<text flexShrink={0} fg={Theme.accent} attributes={TextAttributes.BOLD}>
|
|
1609
1669
|
{props.title}
|
|
1610
1670
|
</text>
|
|
1611
1671
|
</box>
|
|
@@ -1659,7 +1719,7 @@ const ListSection = (props: SectionProps) => {
|
|
|
1659
1719
|
paddingLeft: 1,
|
|
1660
1720
|
}}
|
|
1661
1721
|
>
|
|
1662
|
-
<text fg={Theme.accent} attributes={TextAttributes.BOLD}>
|
|
1722
|
+
<text flexShrink={0} fg={Theme.accent} attributes={TextAttributes.BOLD}>
|
|
1663
1723
|
{props.title}
|
|
1664
1724
|
</text>
|
|
1665
1725
|
</box>
|
|
@@ -1679,16 +1739,16 @@ List.EmptyView = (props: EmptyViewProps) => {
|
|
|
1679
1739
|
useKeyboard((evt) => {
|
|
1680
1740
|
if (!inFocus) return
|
|
1681
1741
|
|
|
1682
|
-
// Handle Ctrl+K to show actions
|
|
1683
|
-
if (evt.name === 'k' && evt.ctrl
|
|
1684
|
-
dialog.
|
|
1742
|
+
// Handle Ctrl+K to show actions (always show panel, even without actions)
|
|
1743
|
+
if (evt.name === 'k' && evt.ctrl) {
|
|
1744
|
+
dialog.pushActions(props.actions || <ActionPanel />)
|
|
1685
1745
|
return
|
|
1686
1746
|
}
|
|
1687
1747
|
|
|
1688
1748
|
// Handle Enter to execute first action
|
|
1689
1749
|
if (evt.name === 'return' && props.actions) {
|
|
1690
1750
|
useStore.setState({ shouldAutoExecuteFirstAction: true })
|
|
1691
|
-
dialog.
|
|
1751
|
+
dialog.pushActions(props.actions)
|
|
1692
1752
|
}
|
|
1693
1753
|
})
|
|
1694
1754
|
|
|
@@ -1724,17 +1784,17 @@ List.EmptyView = (props: EmptyViewProps) => {
|
|
|
1724
1784
|
}}
|
|
1725
1785
|
>
|
|
1726
1786
|
{iconEmoji && (
|
|
1727
|
-
<text fg={Theme.textMuted} style={{ marginBottom: 1 }}>
|
|
1787
|
+
<text flexShrink={0} fg={Theme.textMuted} style={{ marginBottom: 1 }}>
|
|
1728
1788
|
{iconEmoji}
|
|
1729
1789
|
</text>
|
|
1730
1790
|
)}
|
|
1731
1791
|
{props.title && (
|
|
1732
|
-
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
1792
|
+
<text flexShrink={0} fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
1733
1793
|
{props.title?.replace(/\bRaycast\b/g, 'Termcast').replace(/\braycast\b/g, 'termcast') || ''}
|
|
1734
1794
|
</text>
|
|
1735
1795
|
)}
|
|
1736
1796
|
{props.description && (
|
|
1737
|
-
<text fg={Theme.textMuted} wrapMode='word'>
|
|
1797
|
+
<text flexShrink={0} fg={Theme.textMuted} wrapMode='word'>
|
|
1738
1798
|
{props.description?.replace(/\bRaycast\b/g, 'Termcast').replace(/\braycast\b/g, 'termcast') || ''}
|
|
1739
1799
|
</text>
|
|
1740
1800
|
)}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import React, { useState,
|
|
1
|
+
import React, { useState, useRef, useLayoutEffect } from 'react'
|
|
2
2
|
import { BoxRenderable } from '@opentui/core'
|
|
3
|
-
import {} from '@opentui/react'
|
|
4
3
|
import { Theme } from 'termcast/src/theme'
|
|
5
|
-
import {
|
|
4
|
+
import { useAnimationTick, TICK_DIVISORS } from 'termcast/src/components/animation-tick'
|
|
6
5
|
|
|
7
6
|
interface LoadingBarProps {
|
|
8
7
|
title: string
|
|
@@ -12,12 +11,11 @@ interface LoadingBarProps {
|
|
|
12
11
|
|
|
13
12
|
export function LoadingBar(props: LoadingBarProps): any {
|
|
14
13
|
let { title, isLoading = false, barLength: propBarLength } = props
|
|
15
|
-
const [position, setPosition] = useState(0)
|
|
16
14
|
const [calculatedBarLength, setCalculatedBarLength] = useState(
|
|
17
15
|
propBarLength || 0,
|
|
18
16
|
)
|
|
19
|
-
const intervalRef = useRef<NodeJS.Timeout | null>(null)
|
|
20
17
|
const containerRef = useRef<BoxRenderable>(null)
|
|
18
|
+
const tick = useAnimationTick(isLoading ? TICK_DIVISORS.LOADING_BAR : 0)
|
|
21
19
|
|
|
22
20
|
// Calculate bar length based on container width
|
|
23
21
|
useLayoutEffect(() => {
|
|
@@ -73,26 +71,8 @@ export function LoadingBar(props: LoadingBarProps): any {
|
|
|
73
71
|
]
|
|
74
72
|
|
|
75
73
|
const waveWidth = waveColors.length
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (isLoading) {
|
|
79
|
-
intervalRef.current = setInterval(() => {
|
|
80
|
-
setPosition((prev) => (prev + 1) % (characters.length + waveWidth))
|
|
81
|
-
}, 10)
|
|
82
|
-
} else {
|
|
83
|
-
if (intervalRef.current) {
|
|
84
|
-
clearInterval(intervalRef.current)
|
|
85
|
-
intervalRef.current = null
|
|
86
|
-
}
|
|
87
|
-
setPosition(0)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return () => {
|
|
91
|
-
if (intervalRef.current) {
|
|
92
|
-
clearInterval(intervalRef.current)
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}, [isLoading, characters.length, waveWidth])
|
|
74
|
+
const totalAnimLength = characters.length + waveWidth
|
|
75
|
+
const position = isLoading ? tick % totalAnimLength : 0
|
|
96
76
|
|
|
97
77
|
// Calculate color for each character
|
|
98
78
|
const getCharacterColor = (index: number): string => {
|