termcast 1.3.26 → 1.3.28

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.
Files changed (47) hide show
  1. package/dist/apis/cache.d.ts.map +1 -1
  2. package/dist/apis/cache.js +36 -9
  3. package/dist/apis/cache.js.map +1 -1
  4. package/dist/apis/environment.d.ts.map +1 -1
  5. package/dist/apis/environment.js +6 -1
  6. package/dist/apis/environment.js.map +1 -1
  7. package/dist/apis/localstorage.d.ts.map +1 -1
  8. package/dist/apis/localstorage.js +28 -5
  9. package/dist/apis/localstorage.js.map +1 -1
  10. package/dist/cli.js +1 -0
  11. package/dist/cli.js.map +1 -1
  12. package/dist/compile.d.ts +2 -2
  13. package/dist/compile.d.ts.map +1 -1
  14. package/dist/compile.js +26 -6
  15. package/dist/compile.js.map +1 -1
  16. package/dist/examples/internal/text-stacking.js +5 -1
  17. package/dist/examples/internal/text-stacking.js.map +1 -1
  18. package/dist/examples/nested-navigation.js +2 -9
  19. package/dist/examples/nested-navigation.js.map +1 -1
  20. package/dist/examples/simple-navigation.js +2 -9
  21. package/dist/examples/simple-navigation.js.map +1 -1
  22. package/dist/extensions/dev.d.ts +3 -3
  23. package/dist/extensions/dev.d.ts.map +1 -1
  24. package/dist/extensions/dev.js +24 -15
  25. package/dist/extensions/dev.js.map +1 -1
  26. package/dist/extensions/home.d.ts.map +1 -1
  27. package/dist/extensions/home.js +2 -6
  28. package/dist/extensions/home.js.map +1 -1
  29. package/dist/internal/navigation.d.ts.map +1 -1
  30. package/dist/internal/navigation.js +12 -5
  31. package/dist/internal/navigation.js.map +1 -1
  32. package/dist/utils.d.ts.map +1 -1
  33. package/dist/utils.js +5 -1
  34. package/dist/utils.js.map +1 -1
  35. package/package.json +5 -3
  36. package/src/apis/cache.tsx +44 -15
  37. package/src/apis/environment.tsx +7 -1
  38. package/src/apis/localstorage.tsx +33 -7
  39. package/src/cli.tsx +1 -0
  40. package/src/compile.tsx +38 -10
  41. package/src/examples/internal/text-stacking.tsx +5 -1
  42. package/src/examples/nested-navigation.tsx +2 -14
  43. package/src/examples/simple-navigation.tsx +2 -14
  44. package/src/extensions/dev.tsx +27 -19
  45. package/src/extensions/home.tsx +2 -11
  46. package/src/internal/navigation.tsx +16 -10
  47. package/src/utils.tsx +5 -1
package/src/compile.tsx CHANGED
@@ -15,14 +15,23 @@ const raycastAliasPlugin: BunPlugin = {
15
15
  build.onResolve({ filter: /^termcast/ }, (args) => ({
16
16
  path: require.resolve(args.path),
17
17
  }))
18
+ build.onResolve({ filter: /^react$/ }, () => ({
19
+ path: require.resolve('react'),
20
+ }))
21
+ build.onResolve({ filter: /^react\/jsx-runtime$/ }, () => ({
22
+ path: require.resolve('react/jsx-runtime'),
23
+ }))
24
+ build.onResolve({ filter: /^react\/jsx-dev-runtime$/ }, () => ({
25
+ path: require.resolve('react/jsx-dev-runtime'),
26
+ }))
18
27
  },
19
28
  }
20
29
 
21
30
  export function generateEntryCode({
22
- extensionPath,
31
+ packageJson,
23
32
  commands,
24
33
  }: {
25
- extensionPath: string
34
+ packageJson: import('./package-json').RaycastPackageJson
26
35
  commands: Array<{ name: string; bundledPath: string }>
27
36
  }): string {
28
37
  const commandImports = commands
@@ -52,7 +61,7 @@ ${commandsArray}
52
61
  const { startCompiledExtension } = await import('termcast/src/extensions/dev');
53
62
 
54
63
  await startCompiledExtension({
55
- extensionPath: ${JSON.stringify(extensionPath)},
64
+ packageJson: ${JSON.stringify(packageJson)},
56
65
  compiledCommands,
57
66
  skipArgv: 0,
58
67
  });
@@ -86,7 +95,12 @@ export function targetToString(target: CompileTarget): string {
86
95
  }
87
96
 
88
97
  export function targetToFileSuffix(target: CompileTarget): string {
89
- const os = target.os === 'win32' ? 'windows' : target.os === 'darwin' ? 'darwin' : 'linux'
98
+ const os =
99
+ target.os === 'win32'
100
+ ? 'windows'
101
+ : target.os === 'darwin'
102
+ ? 'darwin'
103
+ : 'linux'
90
104
  const ext = target.os === 'win32' ? '.exe' : ''
91
105
  const parts = [os, target.arch]
92
106
  if (target.abi) {
@@ -169,7 +183,7 @@ export async function compileExtension({
169
183
  fs.writeFileSync(path.join(bundleDir, '.gitignore'), '*\n')
170
184
 
171
185
  const entryCode = generateEntryCode({
172
- extensionPath: resolvedPath,
186
+ packageJson,
173
187
  commands: existingCommands.map((cmd) => ({
174
188
  name: cmd.name,
175
189
  bundledPath: cmd.filePath,
@@ -181,8 +195,12 @@ export async function compileExtension({
181
195
  fs.writeFileSync(tempEntryPath, entryCode)
182
196
 
183
197
  const bunTarget = target ? targetToString(target) : 'bun'
198
+ const distDir = path.join(resolvedPath, 'dist')
199
+ if (!fs.existsSync(distDir)) {
200
+ fs.mkdirSync(distDir, { recursive: true })
201
+ }
184
202
  const defaultOutfile =
185
- outfile || path.join(resolvedPath, packageJson.name || 'extension')
203
+ outfile || path.join(distDir, packageJson.name || 'extension')
186
204
 
187
205
  try {
188
206
  const result = await Bun.build({
@@ -194,7 +212,10 @@ export async function compileExtension({
194
212
  compile: {
195
213
  outfile: defaultOutfile,
196
214
  },
197
- define: { 'process.env.VERSION': JSON.stringify(version || '') },
215
+ define: {
216
+ 'process.env.VERSION': JSON.stringify(version || ''),
217
+ 'process.env.NODE_ENV': JSON.stringify('production'),
218
+ },
198
219
  plugins: [raycastAliasPlugin, swiftLoaderPlugin],
199
220
  throw: false,
200
221
  } as Parameters<typeof Bun.build>[0])
@@ -203,7 +224,9 @@ export async function compileExtension({
203
224
  const errorDetails = result.logs.map((log: any) => {
204
225
  const parts = [log.message || String(log)]
205
226
  if (log.position) {
206
- parts.push(` at ${log.position.file}:${log.position.line}:${log.position.column}`)
227
+ parts.push(
228
+ ` at ${log.position.file}:${log.position.line}:${log.position.column}`,
229
+ )
207
230
  }
208
231
  if (log.notes) {
209
232
  for (const note of log.notes) {
@@ -216,10 +239,15 @@ export async function compileExtension({
216
239
  return parts.join('\n')
217
240
  })
218
241
  logger.log('Compile errors:', JSON.stringify(result.logs, null, 2))
219
- throw new Error(`Compile failed: ${errorDetails.join('\n\n') || 'Unknown error'}`)
242
+ throw new Error(
243
+ `Compile failed: ${errorDetails.join('\n\n') || 'Unknown error'}`,
244
+ )
220
245
  }
221
246
 
222
- logger.log('Build outputs:', result.outputs?.map((o) => o.path))
247
+ logger.log(
248
+ 'Build outputs:',
249
+ result.outputs?.map((o) => o.path),
250
+ )
223
251
 
224
252
  if (!fs.existsSync(defaultOutfile)) {
225
253
  throw new Error(
@@ -88,5 +88,9 @@ function TextStackingExample() {
88
88
  )
89
89
  }
90
90
 
91
- const renderer = await createCliRenderer()
91
+ const renderer = await createCliRenderer({
92
+ onDestroy: () => {
93
+ process.exit(0)
94
+ },
95
+ })
92
96
  createRoot(renderer).render(<TextStackingExample />)
@@ -1,10 +1,7 @@
1
1
  import React from 'react'
2
- import { createRoot } from '@opentui/react'
3
- import { createCliRenderer } from '@opentui/core'
4
- import List from 'termcast'
2
+ import List, { renderWithProviders } from 'termcast'
5
3
  import { Action, ActionPanel } from 'termcast'
6
4
  import { useNavigation } from 'termcast/src/internal/navigation'
7
- import { TermcastProvider } from 'termcast/src/internal/providers'
8
5
 
9
6
  function ThirdLevel({ path }: { path: string[] }): any {
10
7
  const { pop } = useNavigation()
@@ -108,13 +105,4 @@ function FirstLevel(): any {
108
105
  )
109
106
  }
110
107
 
111
- function App(): any {
112
- return (
113
- <TermcastProvider>
114
- <FirstLevel />
115
- </TermcastProvider>
116
- )
117
- }
118
-
119
- const renderer = await createCliRenderer()
120
- createRoot(renderer).render(<App />)
108
+ await renderWithProviders(<FirstLevel />)
@@ -1,10 +1,7 @@
1
1
  import React from 'react'
2
- import { createRoot } from '@opentui/react'
3
- import { createCliRenderer } from '@opentui/core'
4
- import List from 'termcast'
2
+ import List, { renderWithProviders } from 'termcast'
5
3
  import { Action, ActionPanel } from 'termcast'
6
4
  import { useNavigation } from 'termcast/src/internal/navigation'
7
- import { TermcastProvider } from 'termcast/src/internal/providers'
8
5
 
9
6
  function GoBackAction(): any {
10
7
  const { pop } = useNavigation()
@@ -87,13 +84,4 @@ function MainView(): any {
87
84
  )
88
85
  }
89
86
 
90
- function App(): any {
91
- return (
92
- <TermcastProvider>
93
- <MainView />
94
- </TermcastProvider>
95
- )
96
- }
97
-
98
- const renderer = await createCliRenderer()
99
- createRoot(renderer).render(<App />)
87
+ await renderWithProviders(<MainView />)
@@ -9,7 +9,7 @@ import { useNavigation } from 'termcast/src/internal/navigation'
9
9
  import { TermcastProvider } from 'termcast/src/internal/providers'
10
10
  import { showToast, Toast } from 'termcast/src/apis/toast'
11
11
  import { Icon } from 'termcast'
12
- import { getCommandsWithFiles, CommandWithFile } from '../package-json'
12
+ import { getCommandsWithFiles, CommandWithFile, RaycastPackageJson } from '../package-json'
13
13
  import { buildExtensionCommands } from '../build'
14
14
  import {
15
15
  runCommand,
@@ -24,18 +24,15 @@ interface BundledCommand extends CommandWithFile {
24
24
  }
25
25
 
26
26
  function ExtensionCommandsList({
27
- extensionPath,
27
+ packageJson,
28
28
  commands,
29
29
  skipArgv,
30
30
  }: {
31
- extensionPath: string
31
+ packageJson: RaycastPackageJson
32
32
  commands: BundledCommand[]
33
33
  skipArgv?: number
34
34
  }): any {
35
35
  const { push } = useNavigation()
36
- const { packageJson } = getCommandsWithFiles({
37
- packageJsonPath: path.join(extensionPath, 'package.json'),
38
- })
39
36
 
40
37
  const visibleCommands = commands.filter((cmd) => cmd.mode !== 'menu-bar')
41
38
 
@@ -205,7 +202,7 @@ export async function startDevMode({
205
202
  extensionPath: resolvedPath,
206
203
  extensionPackageJson: packageJson,
207
204
  devElement: (
208
- <ExtensionCommandsList extensionPath={resolvedPath} commands={commands} skipArgv={skipArgv} />
205
+ <ExtensionCommandsList packageJson={packageJson} commands={commands} skipArgv={skipArgv} />
209
206
  ),
210
207
  devRebuildCount: 1,
211
208
  })
@@ -217,32 +214,39 @@ export async function startDevMode({
217
214
  return <TermcastProvider key={String(devRebuildCount)}>{devElement}</TermcastProvider>
218
215
  }
219
216
 
220
- const renderer = await createCliRenderer()
217
+ const renderer = await createCliRenderer({
218
+ onDestroy: () => {
219
+ process.exit(0)
220
+ },
221
+ })
221
222
  createRoot(renderer).render(<App />)
222
223
  }
223
224
 
224
225
  export async function startCompiledExtension({
225
- extensionPath,
226
+ packageJson,
226
227
  compiledCommands,
227
228
  skipArgv = 0,
228
229
  }: {
229
- extensionPath: string
230
+ packageJson: RaycastPackageJson
230
231
  compiledCommands: Array<{
231
232
  name: string
232
- bundledPath: string
233
233
  Component: (props: any) => any
234
234
  }>
235
235
  skipArgv?: number
236
236
  }): Promise<void> {
237
- const packageJsonPath = path.join(extensionPath, 'package.json')
238
- const { packageJson, commands: commandsMetadata } = getCommandsWithFiles({
239
- packageJsonPath,
240
- })
237
+ // Build command metadata from packageJson.commands
238
+ const commandsMetadata = (packageJson.commands || []).map((cmd) => ({
239
+ ...cmd,
240
+ filePath: '',
241
+ exists: true,
242
+ }))
241
243
 
242
244
  const commands: BundledCommand[] = compiledCommands.map((compiled) => {
243
245
  const metadata = commandsMetadata.find((cmd) => cmd.name === compiled.name)
244
246
  return {
245
247
  ...metadata!,
248
+ filePath: '',
249
+ exists: true,
246
250
  bundledPath: '',
247
251
  Component: compiled.Component,
248
252
  }
@@ -261,10 +265,10 @@ export async function startCompiledExtension({
261
265
 
262
266
  useStore.setState({
263
267
  ...useStore.getInitialState(),
264
- extensionPath,
268
+ extensionPath: '', // No filesystem path for compiled extensions
265
269
  extensionPackageJson: packageJson,
266
270
  devElement: (
267
- <ExtensionCommandsList extensionPath={extensionPath} commands={commands} skipArgv={skipArgv} />
271
+ <ExtensionCommandsList packageJson={packageJson} commands={commands} skipArgv={skipArgv} />
268
272
  ),
269
273
  devRebuildCount: 1,
270
274
  })
@@ -274,7 +278,11 @@ export async function startCompiledExtension({
274
278
  return <TermcastProvider>{devElement}</TermcastProvider>
275
279
  }
276
280
 
277
- const renderer = await createCliRenderer()
281
+ const renderer = await createCliRenderer({
282
+ onDestroy: () => {
283
+ process.exit(0)
284
+ },
285
+ })
278
286
  createRoot(renderer).render(<App />)
279
287
  }
280
288
 
@@ -301,7 +309,7 @@ export async function triggerRebuild({
301
309
  extensionPackageJson: packageJson,
302
310
  devElement: (
303
311
  <ExtensionCommandsList
304
- extensionPath={extensionPath}
312
+ packageJson={packageJson}
305
313
  commands={commands}
306
314
  />
307
315
  ),
@@ -1,12 +1,9 @@
1
1
  import fs from 'node:fs'
2
2
  import path from 'node:path'
3
3
  import React from 'react'
4
- import { createRoot } from '@opentui/react'
5
- import { createCliRenderer } from '@opentui/core'
6
- import { List, logger, useStore } from 'termcast'
4
+ import { List, logger, useStore, renderWithProviders } from 'termcast'
7
5
  import { Action, ActionPanel } from 'termcast'
8
6
  import { useNavigation } from 'termcast/src/internal/navigation'
9
- import { TermcastProvider } from 'termcast/src/internal/providers'
10
7
  import { showToast, Toast } from 'termcast/src/apis/toast'
11
8
  import { Icon } from 'termcast'
12
9
  import { getStoredExtensions } from '../utils'
@@ -185,13 +182,7 @@ function ExtensionsList({
185
182
 
186
183
  export async function runHomeCommand(): Promise<void> {
187
184
  logger.log(`preparing to render the home command component`)
188
-
189
- const renderer = await createCliRenderer()
190
- createRoot(renderer).render(
191
- <TermcastProvider>
192
- <Home />
193
- </TermcastProvider>,
194
- )
185
+ await renderWithProviders(<Home />)
195
186
  logger.log(`rendered home command component`)
196
187
  }
197
188
 
@@ -8,7 +8,7 @@ import React, {
8
8
  startTransition,
9
9
  useTransition,
10
10
  } from 'react'
11
- import { useKeyboard } from '@opentui/react'
11
+ import { useKeyboard, useRenderer } from '@opentui/react'
12
12
  import { CommonProps } from 'termcast/src/utils'
13
13
  import { useStore, type NavigationStackItem } from 'termcast/src/state'
14
14
  import { useIsInFocus } from 'termcast/src/internal/focus-context'
@@ -117,18 +117,24 @@ export function NavigationProvider(props: NavigationProviderProps): any {
117
117
  )
118
118
 
119
119
  const inFocus = useIsInFocus()
120
+ const renderer = useRenderer()
120
121
 
121
- // Handle ESC key to pop navigation
122
+ // Handle ESC key to pop navigation or exit
122
123
  useKeyboard((evt) => {
123
124
  if (!inFocus) return
124
- if (evt.name === 'escape' && stack.length > 1) {
125
- logger.log(
126
- 'popping navigation',
127
- stack.length - 1,
128
- 'stack:',
129
- stack.map((item) => (item.element as any).type.name),
130
- )
131
- pop()
125
+ if (evt.name === 'escape') {
126
+ if (stack.length > 1) {
127
+ logger.log(
128
+ 'popping navigation',
129
+ stack.length - 1,
130
+ 'stack:',
131
+ stack.map((item) => (item.element as any).type.name),
132
+ )
133
+ pop()
134
+ } else {
135
+ // At root with no dialogs - exit the CLI
136
+ renderer.destroy()
137
+ }
132
138
  }
133
139
  })
134
140
 
package/src/utils.tsx CHANGED
@@ -14,7 +14,11 @@ import {
14
14
  import { logger } from './logger'
15
15
 
16
16
  export async function renderWithProviders(element: ReactNode): Promise<void> {
17
- const renderer = await createCliRenderer()
17
+ const renderer = await createCliRenderer({
18
+ onDestroy: () => {
19
+ process.exit(0)
20
+ },
21
+ })
18
22
  createRoot(renderer).render(<TermcastProvider>{element}</TermcastProvider>)
19
23
  }
20
24