termcast 1.3.24 → 1.3.25
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/toast.d.ts +5 -0
- package/dist/apis/toast.d.ts.map +1 -1
- package/dist/apis/toast.js +7 -43
- package/dist/apis/toast.js.map +1 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +1 -0
- package/dist/build.js.map +1 -1
- package/dist/cli.js +20 -10
- package/dist/cli.js.map +1 -1
- package/dist/compile.d.ts +2 -1
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +9 -7
- package/dist/compile.js.map +1 -1
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +5 -3
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/list.d.ts.map +1 -1
- package/dist/components/list.js +16 -9
- package/dist/components/list.js.map +1 -1
- package/dist/examples/toast-variations.d.ts +2 -0
- package/dist/examples/toast-variations.d.ts.map +1 -0
- package/dist/examples/toast-variations.js +122 -0
- package/dist/examples/toast-variations.js.map +1 -0
- package/dist/release.d.ts +1 -1
- package/dist/release.d.ts.map +1 -1
- package/dist/release.js +29 -33
- package/dist/release.js.map +1 -1
- package/dist/utils/run-command.d.ts +3 -2
- package/dist/utils/run-command.d.ts.map +1 -1
- package/dist/utils/run-command.js +9 -3
- package/dist/utils/run-command.js.map +1 -1
- package/package.json +1 -3
- package/src/apis/toast.tsx +37 -62
- package/src/build.tsx +1 -0
- package/src/cli.tsx +21 -11
- package/src/compile.tsx +10 -6
- package/src/components/form/index.tsx +20 -11
- package/src/components/list.tsx +54 -31
- package/src/examples/form-basic.vitest.tsx +8 -8
- package/src/examples/list-with-detail.vitest.tsx +8 -8
- package/src/examples/list-with-sections.vitest.tsx +9 -9
- package/src/examples/list-with-toast.vitest.tsx +16 -16
- package/src/examples/simple-file-picker.vitest.tsx +12 -8
- package/src/examples/simple-grid.vitest.tsx +2 -2
- package/src/examples/simple-navigation.vitest.tsx +16 -16
- package/src/examples/swift-extension.vitest.tsx +2 -2
- package/src/examples/toast-variations.tsx +150 -0
- package/src/examples/toast-variations.vitest.tsx +370 -0
- package/src/extensions/dev.vitest.tsx +12 -10
- package/src/release.tsx +32 -38
- package/src/utils/run-command.tsx +10 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "termcast",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.25",
|
|
4
4
|
"description": "Raycast for the terminal",
|
|
5
5
|
"repository": "https://github.com/remorses/termcast",
|
|
6
6
|
"scripts": {
|
|
@@ -37,13 +37,11 @@
|
|
|
37
37
|
"@tanstack/react-query-persist-client": "^5.85.5",
|
|
38
38
|
"cac": "^6.7.14",
|
|
39
39
|
"change-case": "^5.4.4",
|
|
40
|
-
"cheerio": "^1.1.2",
|
|
41
40
|
"colord": "^2.9.3",
|
|
42
41
|
"google-auth-library": "^10.3.0",
|
|
43
42
|
"jszip": "^3.10.1",
|
|
44
43
|
"nanoid": "^5.1.5",
|
|
45
44
|
"node-diff3": "^3.2.0",
|
|
46
|
-
"node-pty": "^1.0.0",
|
|
47
45
|
"react": "^19.2.0",
|
|
48
46
|
"react-dom": "^19.2.0",
|
|
49
47
|
"react-hook-form": "^7.62.0",
|
package/src/apis/toast.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react'
|
|
2
2
|
import { Theme } from 'termcast/src/theme'
|
|
3
3
|
import { TextAttributes } from '@opentui/core'
|
|
4
|
-
import { logger } from 'termcast/src/logger'
|
|
5
4
|
import { useStore } from 'termcast/src/state'
|
|
6
5
|
import { useKeyboard, useTerminalDimensions } from '@opentui/react'
|
|
7
6
|
import { useIsInFocus } from 'termcast/src/internal/focus-context'
|
|
@@ -121,14 +120,13 @@ export class Toast {
|
|
|
121
120
|
}
|
|
122
121
|
}
|
|
123
122
|
|
|
124
|
-
interface ToastContentProps {
|
|
123
|
+
export interface ToastContentProps {
|
|
125
124
|
toast: Toast
|
|
126
125
|
onHide: () => void
|
|
127
126
|
}
|
|
128
127
|
|
|
129
|
-
function ToastContent({ toast, onHide }: ToastContentProps): any {
|
|
128
|
+
export function ToastContent({ toast, onHide }: ToastContentProps): any {
|
|
130
129
|
const [, forceUpdate] = useState(0)
|
|
131
|
-
const dimensions = useTerminalDimensions()
|
|
132
130
|
const inFocus = useIsInFocus()
|
|
133
131
|
|
|
134
132
|
useEffect(() => {
|
|
@@ -203,78 +201,55 @@ function ToastContent({ toast, onHide }: ToastContentProps): any {
|
|
|
203
201
|
const icon =
|
|
204
202
|
toast.style === Toast.Style.Animated ? getIcon()[animationFrame] : getIcon()
|
|
205
203
|
|
|
206
|
-
const wrapText = (text: string, maxWidth: number): string[] => {
|
|
207
|
-
if (!text) return []
|
|
208
|
-
|
|
209
|
-
const words = text.split(' ')
|
|
210
|
-
const lines: string[] = []
|
|
211
|
-
let currentLine = ''
|
|
212
|
-
|
|
213
|
-
for (const word of words) {
|
|
214
|
-
const testLine = currentLine ? `${currentLine} ${word}` : word
|
|
215
|
-
if (testLine.length <= maxWidth) {
|
|
216
|
-
currentLine = testLine
|
|
217
|
-
} else {
|
|
218
|
-
if (currentLine) {
|
|
219
|
-
lines.push(currentLine)
|
|
220
|
-
}
|
|
221
|
-
currentLine = word
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (currentLine) {
|
|
226
|
-
lines.push(currentLine)
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return lines.slice(0, 3)
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const iconLength = 2
|
|
233
|
-
const titleLength = toast.title.length
|
|
234
|
-
const actionsLength = (() => {
|
|
235
|
-
let length = 0
|
|
236
|
-
if (toast.primaryAction) {
|
|
237
|
-
length += toast.primaryAction.title.length + 4
|
|
238
|
-
}
|
|
239
|
-
if (toast.secondaryAction) {
|
|
240
|
-
length += toast.secondaryAction.title.length + 4
|
|
241
|
-
}
|
|
242
|
-
return length
|
|
243
|
-
})()
|
|
244
|
-
|
|
245
|
-
const availableWidth =
|
|
246
|
-
dimensions.width - iconLength - titleLength - actionsLength - 8
|
|
247
|
-
const messageLines = toast.message
|
|
248
|
-
? wrapText(toast.message, Math.max(20, availableWidth))
|
|
249
|
-
: []
|
|
250
|
-
|
|
251
204
|
return (
|
|
252
205
|
<box
|
|
253
206
|
borderColor={Theme.border}
|
|
207
|
+
backgroundColor={Theme.background}
|
|
254
208
|
paddingLeft={1}
|
|
255
209
|
paddingRight={1}
|
|
256
210
|
flexDirection='column'
|
|
257
211
|
>
|
|
258
212
|
<box flexDirection='row' alignItems='center'>
|
|
259
|
-
<text fg={getIconColor()}>
|
|
260
|
-
|
|
213
|
+
<text flexShrink={0} fg={getIconColor()}>
|
|
214
|
+
{icon}{' '}
|
|
215
|
+
</text>
|
|
216
|
+
<text flexShrink={0} fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
261
217
|
{toast.title}
|
|
262
218
|
</text>
|
|
263
|
-
{messageLines.length > 0 && (
|
|
264
|
-
<text fg={Theme.textMuted}> - {messageLines[0]}</text>
|
|
265
|
-
)}
|
|
266
219
|
{toast.primaryAction && (
|
|
267
|
-
<
|
|
220
|
+
<box
|
|
221
|
+
flexShrink={0}
|
|
222
|
+
onMouseDown={() => {
|
|
223
|
+
toast.primaryAction?.onAction(toast)
|
|
224
|
+
}}
|
|
225
|
+
>
|
|
226
|
+
<text fg={Theme.primary}>
|
|
227
|
+
{' '}
|
|
228
|
+
[{toast.primaryAction.title} ↵]
|
|
229
|
+
</text>
|
|
230
|
+
</box>
|
|
268
231
|
)}
|
|
269
232
|
{toast.secondaryAction && (
|
|
270
|
-
<
|
|
233
|
+
<box
|
|
234
|
+
flexShrink={0}
|
|
235
|
+
onMouseDown={() => {
|
|
236
|
+
toast.secondaryAction?.onAction(toast)
|
|
237
|
+
}}
|
|
238
|
+
>
|
|
239
|
+
<text fg={Theme.textMuted}>
|
|
240
|
+
{' '}
|
|
241
|
+
[{toast.secondaryAction.title} ⇥]
|
|
242
|
+
</text>
|
|
243
|
+
</box>
|
|
271
244
|
)}
|
|
272
245
|
</box>
|
|
273
|
-
{
|
|
274
|
-
<box
|
|
275
|
-
<text fg={Theme.textMuted}>
|
|
246
|
+
{toast.message && (
|
|
247
|
+
<box paddingLeft={2}>
|
|
248
|
+
<text flexShrink={0} fg={Theme.textMuted}>
|
|
249
|
+
{toast.message}
|
|
250
|
+
</text>
|
|
276
251
|
</box>
|
|
277
|
-
)
|
|
252
|
+
)}
|
|
278
253
|
</box>
|
|
279
254
|
)
|
|
280
255
|
}
|
|
@@ -291,9 +266,9 @@ export function ToastOverlay(): any {
|
|
|
291
266
|
<box
|
|
292
267
|
position='absolute'
|
|
293
268
|
left={0}
|
|
294
|
-
|
|
269
|
+
bottom={0}
|
|
295
270
|
width={dimensions.width}
|
|
296
|
-
|
|
271
|
+
maxHeight={10}
|
|
297
272
|
justifyContent='flex-end'
|
|
298
273
|
alignItems='center'
|
|
299
274
|
>
|
package/src/build.tsx
CHANGED
|
@@ -204,6 +204,7 @@ export async function buildExtensionCommands({
|
|
|
204
204
|
if (!fs.existsSync(bundleDir)) {
|
|
205
205
|
fs.mkdirSync(bundleDir, { recursive: true })
|
|
206
206
|
}
|
|
207
|
+
fs.writeFileSync(path.join(bundleDir, '.gitignore'), '*\n')
|
|
207
208
|
|
|
208
209
|
const commandsData = getCommandsWithFiles({
|
|
209
210
|
packageJsonPath: path.join(resolvedPath, 'package.json'),
|
package/src/cli.tsx
CHANGED
|
@@ -222,7 +222,7 @@ cli
|
|
|
222
222
|
single: options.single,
|
|
223
223
|
})
|
|
224
224
|
|
|
225
|
-
console.log(`\nRelease complete:
|
|
225
|
+
console.log(`\nRelease complete: ${result.tag}`)
|
|
226
226
|
console.log(`Uploaded ${result.uploadedFiles.length} binaries`)
|
|
227
227
|
} catch (error: any) {
|
|
228
228
|
console.error('Release failed:', error.message)
|
|
@@ -409,14 +409,17 @@ cli
|
|
|
409
409
|
cli
|
|
410
410
|
.command('download <extensionName>', 'Download extension from Raycast extensions repo')
|
|
411
411
|
.option('-o, --output <path>', 'Output directory', { default: '.' })
|
|
412
|
-
.
|
|
412
|
+
.option('--no-dir', 'Put files directly in output directory instead of creating extension subdirectory')
|
|
413
|
+
.action(async (extensionName: string, options: { output: string; dir: boolean }) => {
|
|
413
414
|
try {
|
|
414
415
|
const destPath = path.resolve(options.output)
|
|
415
|
-
|
|
416
|
+
// When --no-dir is passed, dir is false; put files directly in destPath
|
|
417
|
+
const extensionDir = options.dir ? path.join(destPath, extensionName) : destPath
|
|
418
|
+
const tempCloneDir = path.join(destPath, `.tmp-${extensionName}-${Date.now()}`)
|
|
416
419
|
|
|
417
420
|
console.log(`Downloading extension '${extensionName}' from raycast/extensions...`)
|
|
418
421
|
|
|
419
|
-
if (fs.existsSync(extensionDir)) {
|
|
422
|
+
if (options.dir && fs.existsSync(extensionDir)) {
|
|
420
423
|
console.log(`Removing existing directory: ${extensionDir}`)
|
|
421
424
|
fs.rmSync(extensionDir, { recursive: true, force: true })
|
|
422
425
|
}
|
|
@@ -424,7 +427,8 @@ cli
|
|
|
424
427
|
fs.mkdirSync(destPath, { recursive: true })
|
|
425
428
|
|
|
426
429
|
const repoUrl = 'https://github.com/raycast/extensions.git'
|
|
427
|
-
const
|
|
430
|
+
const cloneDirName = path.basename(tempCloneDir)
|
|
431
|
+
const cloneCmd = `git clone -n --depth=1 --filter=tree:0 "${repoUrl}" "${cloneDirName}"`
|
|
428
432
|
console.log(`Running: ${cloneCmd}`)
|
|
429
433
|
try {
|
|
430
434
|
execSync(cloneCmd, {
|
|
@@ -440,11 +444,12 @@ cli
|
|
|
440
444
|
console.log(`Running: ${sparseCmd}`)
|
|
441
445
|
try {
|
|
442
446
|
execSync(sparseCmd, {
|
|
443
|
-
cwd:
|
|
447
|
+
cwd: tempCloneDir,
|
|
444
448
|
stdio: 'inherit',
|
|
445
449
|
})
|
|
446
450
|
} catch (error) {
|
|
447
451
|
console.error(`Failed to set sparse-checkout`)
|
|
452
|
+
fs.rmSync(tempCloneDir, { recursive: true, force: true })
|
|
448
453
|
process.exit(1)
|
|
449
454
|
}
|
|
450
455
|
|
|
@@ -452,22 +457,27 @@ cli
|
|
|
452
457
|
console.log(`Running: ${checkoutCmd}`)
|
|
453
458
|
try {
|
|
454
459
|
execSync(checkoutCmd, {
|
|
455
|
-
cwd:
|
|
460
|
+
cwd: tempCloneDir,
|
|
456
461
|
stdio: 'inherit',
|
|
457
462
|
})
|
|
458
463
|
} catch (error) {
|
|
459
464
|
console.error(`Failed to checkout files`)
|
|
465
|
+
fs.rmSync(tempCloneDir, { recursive: true, force: true })
|
|
460
466
|
process.exit(1)
|
|
461
467
|
}
|
|
462
468
|
|
|
463
|
-
const extensionPath = path.join(
|
|
469
|
+
const extensionPath = path.join(tempCloneDir, 'extensions', extensionName)
|
|
464
470
|
|
|
465
471
|
if (!fs.existsSync(extensionPath)) {
|
|
466
472
|
console.error(`Extension '${extensionName}' not found in raycast/extensions repo`)
|
|
467
|
-
fs.rmSync(
|
|
473
|
+
fs.rmSync(tempCloneDir, { recursive: true, force: true })
|
|
468
474
|
process.exit(1)
|
|
469
475
|
}
|
|
470
476
|
|
|
477
|
+
// Move files to final destination
|
|
478
|
+
if (options.dir) {
|
|
479
|
+
fs.mkdirSync(extensionDir, { recursive: true })
|
|
480
|
+
}
|
|
471
481
|
const filesToMove = fs.readdirSync(extensionPath)
|
|
472
482
|
for (const file of filesToMove) {
|
|
473
483
|
const src = path.join(extensionPath, file)
|
|
@@ -475,8 +485,8 @@ cli
|
|
|
475
485
|
fs.renameSync(src, dest)
|
|
476
486
|
}
|
|
477
487
|
|
|
478
|
-
|
|
479
|
-
fs.rmSync(
|
|
488
|
+
// Clean up temp clone directory
|
|
489
|
+
fs.rmSync(tempCloneDir, { recursive: true, force: true })
|
|
480
490
|
|
|
481
491
|
console.log(`\nInstalling dependencies...`)
|
|
482
492
|
execSync('npm install', {
|
package/src/compile.tsx
CHANGED
|
@@ -105,15 +105,15 @@ export function getArchiveExtension(target: CompileTarget): string {
|
|
|
105
105
|
export const ALL_TARGETS: CompileTarget[] = [
|
|
106
106
|
{ os: 'linux', arch: 'arm64' },
|
|
107
107
|
{ os: 'linux', arch: 'x64' },
|
|
108
|
-
{ os: 'linux', arch: 'x64', avx2: false },
|
|
109
|
-
{ os: 'linux', arch: 'arm64', abi: 'musl' },
|
|
110
|
-
{ os: 'linux', arch: 'x64', abi: 'musl' },
|
|
111
|
-
{ os: 'linux', arch: 'x64', abi: 'musl', avx2: false },
|
|
108
|
+
// { os: 'linux', arch: 'x64', avx2: false },
|
|
109
|
+
// { os: 'linux', arch: 'arm64', abi: 'musl' },
|
|
110
|
+
// { os: 'linux', arch: 'x64', abi: 'musl' },
|
|
111
|
+
// { os: 'linux', arch: 'x64', abi: 'musl', avx2: false },
|
|
112
112
|
{ os: 'darwin', arch: 'arm64' },
|
|
113
113
|
{ os: 'darwin', arch: 'x64' },
|
|
114
|
-
{ os: 'darwin', arch: 'x64', avx2: false },
|
|
114
|
+
// { os: 'darwin', arch: 'x64', avx2: false },
|
|
115
115
|
{ os: 'win32', arch: 'x64' },
|
|
116
|
-
{ os: 'win32', arch: 'x64', avx2: false },
|
|
116
|
+
// { os: 'win32', arch: 'x64', avx2: false },
|
|
117
117
|
]
|
|
118
118
|
|
|
119
119
|
export function getCurrentTarget(): CompileTarget {
|
|
@@ -127,6 +127,7 @@ export interface CompileOptions {
|
|
|
127
127
|
outfile?: string
|
|
128
128
|
minify?: boolean
|
|
129
129
|
target?: CompileTarget
|
|
130
|
+
version?: string
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
export interface CompileResult {
|
|
@@ -139,6 +140,7 @@ export async function compileExtension({
|
|
|
139
140
|
outfile,
|
|
140
141
|
minify = false,
|
|
141
142
|
target,
|
|
143
|
+
version,
|
|
142
144
|
}: CompileOptions): Promise<CompileResult> {
|
|
143
145
|
const resolvedPath = path.resolve(extensionPath)
|
|
144
146
|
|
|
@@ -164,6 +166,7 @@ export async function compileExtension({
|
|
|
164
166
|
if (!fs.existsSync(bundleDir)) {
|
|
165
167
|
fs.mkdirSync(bundleDir, { recursive: true })
|
|
166
168
|
}
|
|
169
|
+
fs.writeFileSync(path.join(bundleDir, '.gitignore'), '*\n')
|
|
167
170
|
|
|
168
171
|
const entryCode = generateEntryCode({
|
|
169
172
|
extensionPath: resolvedPath,
|
|
@@ -191,6 +194,7 @@ export async function compileExtension({
|
|
|
191
194
|
compile: {
|
|
192
195
|
outfile: defaultOutfile,
|
|
193
196
|
},
|
|
197
|
+
define: { 'process.env.VERSION': JSON.stringify(version || '') },
|
|
194
198
|
plugins: [raycastAliasPlugin, swiftLoaderPlugin],
|
|
195
199
|
throw: false,
|
|
196
200
|
} as Parameters<typeof Bun.build>[0])
|
|
@@ -102,17 +102,10 @@ export const useFormSubmit = () => {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
function FormFooter(): any {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
paddingLeft: 1,
|
|
110
|
-
paddingRight: 1,
|
|
111
|
-
paddingTop: 1,
|
|
112
|
-
marginTop: 1,
|
|
113
|
-
flexDirection: 'row',
|
|
114
|
-
}}
|
|
115
|
-
>
|
|
105
|
+
const hasToast = useStore((s) => s.toast !== null)
|
|
106
|
+
|
|
107
|
+
const content = hasToast ? null : (
|
|
108
|
+
<>
|
|
116
109
|
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
117
110
|
ctrl ↵
|
|
118
111
|
</text>
|
|
@@ -125,6 +118,22 @@ function FormFooter(): any {
|
|
|
125
118
|
{' '}^k
|
|
126
119
|
</text>
|
|
127
120
|
<text fg={Theme.textMuted}> actions</text>
|
|
121
|
+
</>
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<box
|
|
126
|
+
border={false}
|
|
127
|
+
height={1}
|
|
128
|
+
style={{
|
|
129
|
+
paddingLeft: 1,
|
|
130
|
+
paddingRight: 1,
|
|
131
|
+
paddingTop: 1,
|
|
132
|
+
marginTop: 1,
|
|
133
|
+
flexDirection: 'row',
|
|
134
|
+
}}
|
|
135
|
+
>
|
|
136
|
+
{content}
|
|
128
137
|
</box>
|
|
129
138
|
)
|
|
130
139
|
}
|
package/src/components/list.tsx
CHANGED
|
@@ -70,19 +70,10 @@ interface ActionsInterface {
|
|
|
70
70
|
|
|
71
71
|
function ListFooter(): any {
|
|
72
72
|
const firstActionTitle = useStore((s) => s.firstActionTitle)
|
|
73
|
+
const hasToast = useStore((s) => s.toast !== null)
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
border={false}
|
|
77
|
-
style={{
|
|
78
|
-
paddingLeft: 1,
|
|
79
|
-
flexShrink: 0,
|
|
80
|
-
paddingRight: 1,
|
|
81
|
-
paddingTop: 1,
|
|
82
|
-
marginTop: 1,
|
|
83
|
-
flexDirection: 'row',
|
|
84
|
-
}}
|
|
85
|
-
>
|
|
75
|
+
const content = hasToast ? null : (
|
|
76
|
+
<>
|
|
86
77
|
{firstActionTitle && (
|
|
87
78
|
<>
|
|
88
79
|
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
@@ -99,6 +90,23 @@ function ListFooter(): any {
|
|
|
99
90
|
{' '}^k
|
|
100
91
|
</text>
|
|
101
92
|
<text fg={Theme.textMuted}> actions</text>
|
|
93
|
+
</>
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
return (
|
|
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}
|
|
102
110
|
</box>
|
|
103
111
|
)
|
|
104
112
|
}
|
|
@@ -515,30 +523,45 @@ function ListDropdownDialog(props: ListDropdownDialogProps): any {
|
|
|
515
523
|
)}
|
|
516
524
|
</box>
|
|
517
525
|
|
|
518
|
-
<
|
|
519
|
-
border={false}
|
|
520
|
-
style={{
|
|
521
|
-
paddingRight: 2,
|
|
522
|
-
paddingLeft: 3,
|
|
523
|
-
paddingBottom: 1,
|
|
524
|
-
paddingTop: 1,
|
|
525
|
-
flexDirection: 'row',
|
|
526
|
-
}}
|
|
527
|
-
>
|
|
528
|
-
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
529
|
-
↵
|
|
530
|
-
</text>
|
|
531
|
-
<text fg={Theme.textMuted}> select</text>
|
|
532
|
-
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
533
|
-
{' '}↑↓
|
|
534
|
-
</text>
|
|
535
|
-
<text fg={Theme.textMuted}> navigate</text>
|
|
536
|
-
</box>
|
|
526
|
+
<DropdownFooter />
|
|
537
527
|
</box>
|
|
538
528
|
</DropdownDescendantsProvider>
|
|
539
529
|
)
|
|
540
530
|
}
|
|
541
531
|
|
|
532
|
+
function DropdownFooter(): any {
|
|
533
|
+
const hasToast = useStore((s) => s.toast !== null)
|
|
534
|
+
|
|
535
|
+
const content = hasToast ? null : (
|
|
536
|
+
<>
|
|
537
|
+
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
538
|
+
↵
|
|
539
|
+
</text>
|
|
540
|
+
<text fg={Theme.textMuted}> select</text>
|
|
541
|
+
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
542
|
+
{' '}↑↓
|
|
543
|
+
</text>
|
|
544
|
+
<text fg={Theme.textMuted}> navigate</text>
|
|
545
|
+
</>
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
return (
|
|
549
|
+
<box
|
|
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
|
+
>
|
|
560
|
+
{content}
|
|
561
|
+
</box>
|
|
562
|
+
)
|
|
563
|
+
}
|
|
564
|
+
|
|
542
565
|
// Render a single list item row
|
|
543
566
|
function ListItemRow(props: {
|
|
544
567
|
title: string
|
|
@@ -113,7 +113,7 @@ test('password field always shows asterisks and submits real value', async () =>
|
|
|
113
113
|
│ Required field
|
|
114
114
|
│
|
|
115
115
|
◆ Password
|
|
116
|
-
│
|
|
116
|
+
│ **********
|
|
117
117
|
│ Must be at least 8 characters
|
|
118
118
|
│
|
|
119
119
|
◇ Biography
|
|
@@ -170,7 +170,7 @@ test('password field always shows asterisks and submits real value', async () =>
|
|
|
170
170
|
│ Required field
|
|
171
171
|
│
|
|
172
172
|
◇ Password
|
|
173
|
-
│
|
|
173
|
+
│ **********
|
|
174
174
|
│ Must be at least 8 characters
|
|
175
175
|
│
|
|
176
176
|
◆ Biography
|
|
@@ -232,7 +232,7 @@ test('password field always shows asterisks and submits real value', async () =>
|
|
|
232
232
|
│ Required field
|
|
233
233
|
│
|
|
234
234
|
◇ Password
|
|
235
|
-
│
|
|
235
|
+
│ **********
|
|
236
236
|
│ Must be at least 8 characters
|
|
237
237
|
│┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
238
238
|
◆┃
|
|
@@ -290,7 +290,7 @@ test('password field always shows asterisks and submits real value', async () =>
|
|
|
290
290
|
│ Required field
|
|
291
291
|
│
|
|
292
292
|
◇ Password
|
|
293
|
-
│
|
|
293
|
+
│ **********
|
|
294
294
|
│ Must be at least 8 characters
|
|
295
295
|
│
|
|
296
296
|
◆ Biography
|
|
@@ -324,10 +324,10 @@ test('password field always shows asterisks and submits real value', async () =>
|
|
|
324
324
|
│
|
|
325
325
|
│ Mo Tu We Th Fr Sa Su
|
|
326
326
|
│ 1 2 3 4 5 6 7
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
327
|
+
┌────────────────────────────────────────────────┐
|
|
328
|
+
│ ✓ Form Submitted │
|
|
329
|
+
│ All form data has been captured successfully │
|
|
330
|
+
└────────────────────────────────────────────────┘"
|
|
331
331
|
`)
|
|
332
332
|
|
|
333
333
|
// The toast "Form Submitted" in afterEnterSnapshot proves the form was submitted
|
|
@@ -47,18 +47,18 @@ test('list with detail view display and navigation', async () => {
|
|
|
47
47
|
│
|
|
48
48
|
│ Characteristics
|
|
49
49
|
│ - Height: 0.7m
|
|
50
|
-
│ -
|
|
50
|
+
│ - Weight: 6.9kg
|
|
51
51
|
│
|
|
52
|
-
│
|
|
53
|
-
│
|
|
54
|
-
│
|
|
52
|
+
│ Abilities
|
|
53
|
+
│ - Chlorophyll
|
|
54
|
+
│ - Overgrow
|
|
55
|
+
│ ─────────────────────────────────
|
|
55
56
|
│
|
|
56
|
-
│
|
|
57
|
-
│ Weight:
|
|
58
|
-
│ 6.9kg
|
|
57
|
+
│ Types:
|
|
59
58
|
│
|
|
59
|
+
│ Grass:
|
|
60
60
|
↵toggle detail ↑↓ navigate ^kactions │ ─────────────────
|
|
61
|
-
│
|
|
61
|
+
│ ▼"
|
|
62
62
|
`)
|
|
63
63
|
|
|
64
64
|
await session.press('down')
|
|
@@ -480,19 +480,19 @@ test('list actions panel with ctrl+k', async () => {
|
|
|
480
480
|
Search items...
|
|
481
481
|
|
|
482
482
|
Fruits ▲
|
|
483
|
-
›Apple Red and sweet Fresh [Popular]
|
|
484
|
-
Banana Yellow and nutritious Ripe
|
|
485
|
-
Orange Citrus and juicy Fresh
|
|
486
|
-
Grape Sweet clusters [Seasonal]
|
|
483
|
+
›Apple Red and sweet Fresh [Popular] █
|
|
484
|
+
Banana Yellow and nutritious Ripe █
|
|
485
|
+
Orange Citrus and juicy Fresh █
|
|
486
|
+
Grape Sweet clusters [Seasonal] █
|
|
487
487
|
Mango Tropical delight Imported
|
|
488
488
|
Pineapple Sweet and tangy
|
|
489
489
|
Strawberry Red and sweet [Popular]
|
|
490
|
-
▼
|
|
491
|
-
|
|
492
490
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
491
|
+
Vegetables ▼
|
|
492
|
+
┌─────────────────────────────────────┐
|
|
493
|
+
│ ✓ Added to Cart │
|
|
494
|
+
│ Apple has been added to your cart │
|
|
495
|
+
└─────────────────────────────────────┘"
|
|
496
496
|
`)
|
|
497
497
|
}, 10000)
|
|
498
498
|
|
|
@@ -42,10 +42,10 @@ test('list navigation works while toast is shown', async () => {
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
┌──────────────┐
|
|
46
|
+
│ ✓ Selected │
|
|
47
|
+
│ First Item │
|
|
48
|
+
└──────────────┘"
|
|
49
49
|
`)
|
|
50
50
|
|
|
51
51
|
await session.press('down')
|
|
@@ -70,10 +70,10 @@ test('list navigation works while toast is shown', async () => {
|
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
┌───────────────┐
|
|
74
|
+
│ ✓ Selected │
|
|
75
|
+
│ Second Item │
|
|
76
|
+
└───────────────┘"
|
|
77
77
|
`)
|
|
78
78
|
|
|
79
79
|
await session.press('down')
|
|
@@ -98,10 +98,10 @@ test('list navigation works while toast is shown', async () => {
|
|
|
98
98
|
|
|
99
99
|
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
101
|
+
┌──────────────┐
|
|
102
|
+
│ ✓ Selected │
|
|
103
|
+
│ Third Item │
|
|
104
|
+
└──────────────┘"
|
|
105
105
|
`)
|
|
106
106
|
|
|
107
107
|
await session.press('up')
|
|
@@ -126,9 +126,9 @@ test('list navigation works while toast is shown', async () => {
|
|
|
126
126
|
|
|
127
127
|
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
┌───────────────┐
|
|
130
|
+
│ ✓ Selected │
|
|
131
|
+
│ Second Item │
|
|
132
|
+
└───────────────┘"
|
|
133
133
|
`)
|
|
134
134
|
}, 10000)
|