termcast 1.3.24 → 1.3.26
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.d.ts +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +21 -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 +2 -3
- package/src/apis/toast.tsx +37 -62
- package/src/build.tsx +1 -0
- package/src/cli.tsx +23 -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.26",
|
|
4
4
|
"description": "Raycast for the terminal",
|
|
5
5
|
"repository": "https://github.com/remorses/termcast",
|
|
6
6
|
"scripts": {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"test": "bun test && bun e2e",
|
|
14
14
|
"e2e": "vitest -u"
|
|
15
15
|
},
|
|
16
|
+
"bin": "./dist/cli.js",
|
|
16
17
|
"exports": {
|
|
17
18
|
".": {
|
|
18
19
|
"bun": "./src/index.tsx",
|
|
@@ -37,13 +38,11 @@
|
|
|
37
38
|
"@tanstack/react-query-persist-client": "^5.85.5",
|
|
38
39
|
"cac": "^6.7.14",
|
|
39
40
|
"change-case": "^5.4.4",
|
|
40
|
-
"cheerio": "^1.1.2",
|
|
41
41
|
"colord": "^2.9.3",
|
|
42
42
|
"google-auth-library": "^10.3.0",
|
|
43
43
|
"jszip": "^3.10.1",
|
|
44
44
|
"nanoid": "^5.1.5",
|
|
45
45
|
"node-diff3": "^3.2.0",
|
|
46
|
-
"node-pty": "^1.0.0",
|
|
47
46
|
"react": "^19.2.0",
|
|
48
47
|
"react-dom": "^19.2.0",
|
|
49
48
|
"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
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
1
3
|
import fs from 'node:fs'
|
|
2
4
|
import path from 'node:path'
|
|
3
5
|
import { execSync, spawn } from 'node:child_process'
|
|
@@ -222,7 +224,7 @@ cli
|
|
|
222
224
|
single: options.single,
|
|
223
225
|
})
|
|
224
226
|
|
|
225
|
-
console.log(`\nRelease complete:
|
|
227
|
+
console.log(`\nRelease complete: ${result.tag}`)
|
|
226
228
|
console.log(`Uploaded ${result.uploadedFiles.length} binaries`)
|
|
227
229
|
} catch (error: any) {
|
|
228
230
|
console.error('Release failed:', error.message)
|
|
@@ -409,14 +411,17 @@ cli
|
|
|
409
411
|
cli
|
|
410
412
|
.command('download <extensionName>', 'Download extension from Raycast extensions repo')
|
|
411
413
|
.option('-o, --output <path>', 'Output directory', { default: '.' })
|
|
412
|
-
.
|
|
414
|
+
.option('--no-dir', 'Put files directly in output directory instead of creating extension subdirectory')
|
|
415
|
+
.action(async (extensionName: string, options: { output: string; dir: boolean }) => {
|
|
413
416
|
try {
|
|
414
417
|
const destPath = path.resolve(options.output)
|
|
415
|
-
|
|
418
|
+
// When --no-dir is passed, dir is false; put files directly in destPath
|
|
419
|
+
const extensionDir = options.dir ? path.join(destPath, extensionName) : destPath
|
|
420
|
+
const tempCloneDir = path.join(destPath, `.tmp-${extensionName}-${Date.now()}`)
|
|
416
421
|
|
|
417
422
|
console.log(`Downloading extension '${extensionName}' from raycast/extensions...`)
|
|
418
423
|
|
|
419
|
-
if (fs.existsSync(extensionDir)) {
|
|
424
|
+
if (options.dir && fs.existsSync(extensionDir)) {
|
|
420
425
|
console.log(`Removing existing directory: ${extensionDir}`)
|
|
421
426
|
fs.rmSync(extensionDir, { recursive: true, force: true })
|
|
422
427
|
}
|
|
@@ -424,7 +429,8 @@ cli
|
|
|
424
429
|
fs.mkdirSync(destPath, { recursive: true })
|
|
425
430
|
|
|
426
431
|
const repoUrl = 'https://github.com/raycast/extensions.git'
|
|
427
|
-
const
|
|
432
|
+
const cloneDirName = path.basename(tempCloneDir)
|
|
433
|
+
const cloneCmd = `git clone -n --depth=1 --filter=tree:0 "${repoUrl}" "${cloneDirName}"`
|
|
428
434
|
console.log(`Running: ${cloneCmd}`)
|
|
429
435
|
try {
|
|
430
436
|
execSync(cloneCmd, {
|
|
@@ -440,11 +446,12 @@ cli
|
|
|
440
446
|
console.log(`Running: ${sparseCmd}`)
|
|
441
447
|
try {
|
|
442
448
|
execSync(sparseCmd, {
|
|
443
|
-
cwd:
|
|
449
|
+
cwd: tempCloneDir,
|
|
444
450
|
stdio: 'inherit',
|
|
445
451
|
})
|
|
446
452
|
} catch (error) {
|
|
447
453
|
console.error(`Failed to set sparse-checkout`)
|
|
454
|
+
fs.rmSync(tempCloneDir, { recursive: true, force: true })
|
|
448
455
|
process.exit(1)
|
|
449
456
|
}
|
|
450
457
|
|
|
@@ -452,22 +459,27 @@ cli
|
|
|
452
459
|
console.log(`Running: ${checkoutCmd}`)
|
|
453
460
|
try {
|
|
454
461
|
execSync(checkoutCmd, {
|
|
455
|
-
cwd:
|
|
462
|
+
cwd: tempCloneDir,
|
|
456
463
|
stdio: 'inherit',
|
|
457
464
|
})
|
|
458
465
|
} catch (error) {
|
|
459
466
|
console.error(`Failed to checkout files`)
|
|
467
|
+
fs.rmSync(tempCloneDir, { recursive: true, force: true })
|
|
460
468
|
process.exit(1)
|
|
461
469
|
}
|
|
462
470
|
|
|
463
|
-
const extensionPath = path.join(
|
|
471
|
+
const extensionPath = path.join(tempCloneDir, 'extensions', extensionName)
|
|
464
472
|
|
|
465
473
|
if (!fs.existsSync(extensionPath)) {
|
|
466
474
|
console.error(`Extension '${extensionName}' not found in raycast/extensions repo`)
|
|
467
|
-
fs.rmSync(
|
|
475
|
+
fs.rmSync(tempCloneDir, { recursive: true, force: true })
|
|
468
476
|
process.exit(1)
|
|
469
477
|
}
|
|
470
478
|
|
|
479
|
+
// Move files to final destination
|
|
480
|
+
if (options.dir) {
|
|
481
|
+
fs.mkdirSync(extensionDir, { recursive: true })
|
|
482
|
+
}
|
|
471
483
|
const filesToMove = fs.readdirSync(extensionPath)
|
|
472
484
|
for (const file of filesToMove) {
|
|
473
485
|
const src = path.join(extensionPath, file)
|
|
@@ -475,8 +487,8 @@ cli
|
|
|
475
487
|
fs.renameSync(src, dest)
|
|
476
488
|
}
|
|
477
489
|
|
|
478
|
-
|
|
479
|
-
fs.rmSync(
|
|
490
|
+
// Clean up temp clone directory
|
|
491
|
+
fs.rmSync(tempCloneDir, { recursive: true, force: true })
|
|
480
492
|
|
|
481
493
|
console.log(`\nInstalling dependencies...`)
|
|
482
494
|
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)
|