termcast 1.4.1 → 1.5.0

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 (140) hide show
  1. package/dist/build.d.ts.map +1 -1
  2. package/dist/build.js +8 -7
  3. package/dist/build.js.map +1 -1
  4. package/dist/cli.js +0 -40
  5. package/dist/cli.js.map +1 -1
  6. package/dist/components/bar-graph.d.ts +23 -8
  7. package/dist/components/bar-graph.d.ts.map +1 -1
  8. package/dist/components/bar-graph.js +84 -40
  9. package/dist/components/bar-graph.js.map +1 -1
  10. package/dist/components/dotted-line-graph.d.ts +86 -0
  11. package/dist/components/dotted-line-graph.d.ts.map +1 -0
  12. package/dist/components/dotted-line-graph.js +260 -0
  13. package/dist/components/dotted-line-graph.js.map +1 -0
  14. package/dist/components/extension-preferences.d.ts.map +1 -1
  15. package/dist/components/extension-preferences.js +1 -10
  16. package/dist/components/extension-preferences.js.map +1 -1
  17. package/dist/components/graph.d.ts.map +1 -1
  18. package/dist/components/graph.js +7 -1
  19. package/dist/components/graph.js.map +1 -1
  20. package/dist/components/histogram.d.ts +42 -0
  21. package/dist/components/histogram.d.ts.map +1 -0
  22. package/dist/components/histogram.js +115 -0
  23. package/dist/components/histogram.js.map +1 -0
  24. package/dist/components/horizontal-bar-graph.d.ts +47 -0
  25. package/dist/components/horizontal-bar-graph.d.ts.map +1 -0
  26. package/dist/components/horizontal-bar-graph.js +137 -0
  27. package/dist/components/horizontal-bar-graph.js.map +1 -0
  28. package/dist/components/list.d.ts +2 -0
  29. package/dist/components/list.d.ts.map +1 -1
  30. package/dist/components/list.js +10 -10
  31. package/dist/components/list.js.map +1 -1
  32. package/dist/examples/bar-graph-weekly.js +2 -2
  33. package/dist/examples/bar-graph-weekly.js.map +1 -1
  34. package/dist/examples/charts-showcase-barchart.d.ts +2 -0
  35. package/dist/examples/charts-showcase-barchart.d.ts.map +1 -0
  36. package/dist/examples/charts-showcase-barchart.js +10 -0
  37. package/dist/examples/charts-showcase-barchart.js.map +1 -0
  38. package/dist/examples/charts-showcase-bargraph.d.ts +2 -0
  39. package/dist/examples/charts-showcase-bargraph.d.ts.map +1 -0
  40. package/dist/examples/charts-showcase-bargraph.js +60 -0
  41. package/dist/examples/charts-showcase-bargraph.js.map +1 -0
  42. package/dist/examples/charts-showcase-candle.d.ts +2 -0
  43. package/dist/examples/charts-showcase-candle.d.ts.map +1 -0
  44. package/dist/examples/charts-showcase-candle.js +30 -0
  45. package/dist/examples/charts-showcase-candle.js.map +1 -0
  46. package/dist/examples/charts-showcase-graph.d.ts +2 -0
  47. package/dist/examples/charts-showcase-graph.d.ts.map +1 -0
  48. package/dist/examples/charts-showcase-graph.js +33 -0
  49. package/dist/examples/charts-showcase-graph.js.map +1 -0
  50. package/dist/examples/charts-showcase-heatmap.d.ts +2 -0
  51. package/dist/examples/charts-showcase-heatmap.d.ts.map +1 -0
  52. package/dist/examples/charts-showcase-heatmap.js +36 -0
  53. package/dist/examples/charts-showcase-heatmap.js.map +1 -0
  54. package/dist/examples/charts-showcase-mixed.d.ts +2 -0
  55. package/dist/examples/charts-showcase-mixed.d.ts.map +1 -0
  56. package/dist/examples/charts-showcase-mixed.js +30 -0
  57. package/dist/examples/charts-showcase-mixed.js.map +1 -0
  58. package/dist/examples/charts-showcase-progress.d.ts +2 -0
  59. package/dist/examples/charts-showcase-progress.d.ts.map +1 -0
  60. package/dist/examples/charts-showcase-progress.js +10 -0
  61. package/dist/examples/charts-showcase-progress.js.map +1 -0
  62. package/dist/examples/graph-multi-series.js +1 -1
  63. package/dist/examples/graph-multi-series.js.map +1 -1
  64. package/dist/examples/horizontal-bar-graph-weekly.d.ts +2 -0
  65. package/dist/examples/horizontal-bar-graph-weekly.d.ts.map +1 -0
  66. package/dist/examples/horizontal-bar-graph-weekly.js +67 -0
  67. package/dist/examples/horizontal-bar-graph-weekly.js.map +1 -0
  68. package/dist/examples/simple-dotted-line-graph.d.ts +2 -0
  69. package/dist/examples/simple-dotted-line-graph.d.ts.map +1 -0
  70. package/dist/examples/simple-dotted-line-graph.js +39 -0
  71. package/dist/examples/simple-dotted-line-graph.js.map +1 -0
  72. package/dist/examples/simple-histogram.d.ts +2 -0
  73. package/dist/examples/simple-histogram.d.ts.map +1 -0
  74. package/dist/examples/simple-histogram.js +47 -0
  75. package/dist/examples/simple-histogram.js.map +1 -0
  76. package/dist/index.d.ts +6 -0
  77. package/dist/index.d.ts.map +1 -1
  78. package/dist/index.js +6 -0
  79. package/dist/index.js.map +1 -1
  80. package/dist/platform/node/sqlite.d.ts +6 -5
  81. package/dist/platform/node/sqlite.d.ts.map +1 -1
  82. package/dist/platform/node/sqlite.js +30 -14
  83. package/dist/platform/node/sqlite.js.map +1 -1
  84. package/dist/theme.d.ts.map +1 -1
  85. package/dist/theme.js +11 -9
  86. package/dist/theme.js.map +1 -1
  87. package/dist/utils/run-command.d.ts.map +1 -1
  88. package/dist/utils/run-command.js +8 -19
  89. package/dist/utils/run-command.js.map +1 -1
  90. package/dist/utils.d.ts +1 -19
  91. package/dist/utils.d.ts.map +1 -1
  92. package/dist/utils.js +1 -100
  93. package/dist/utils.js.map +1 -1
  94. package/package.json +6 -8
  95. package/src/build.tsx +11 -10
  96. package/src/cli.tsx +3 -40
  97. package/src/compile.vitest.tsx +3 -3
  98. package/src/components/bar-graph.tsx +217 -111
  99. package/src/components/dotted-line-graph.tsx +407 -0
  100. package/src/components/extension-preferences.tsx +2 -12
  101. package/src/components/graph.tsx +5 -1
  102. package/src/components/histogram.tsx +228 -0
  103. package/src/components/horizontal-bar-graph.tsx +279 -0
  104. package/src/components/list.tsx +20 -15
  105. package/src/examples/action-shortcut.vitest.tsx +17 -17
  106. package/src/examples/bar-graph-weekly.tsx +2 -2
  107. package/src/examples/bar-graph-weekly.vitest.tsx +63 -62
  108. package/src/examples/charts-showcase-bargraph.tsx +103 -0
  109. package/src/examples/detail-metadata-showcase.vitest.tsx +12 -12
  110. package/src/examples/form-basic.vitest.tsx +11 -11
  111. package/src/examples/form-dropdown.vitest.tsx +11 -11
  112. package/src/examples/form-scroll.vitest.tsx +1 -1
  113. package/src/examples/form-tagpicker.vitest.tsx +11 -11
  114. package/src/examples/github.vitest.tsx +22 -22
  115. package/src/examples/graph-bar-chart.vitest.tsx +8 -8
  116. package/src/examples/graph-multi-series.tsx +1 -1
  117. package/src/examples/graph-row.vitest.tsx +14 -14
  118. package/src/examples/graph-styles.vitest.tsx +77 -77
  119. package/src/examples/horizontal-bar-graph-weekly.tsx +138 -0
  120. package/src/examples/horizontal-bar-graph-weekly.vitest.tsx +164 -0
  121. package/src/examples/list-detail-metadata.vitest.tsx +4 -4
  122. package/src/examples/list-with-detail.vitest.tsx +46 -46
  123. package/src/examples/simple-candle-chart.vitest.tsx +8 -8
  124. package/src/examples/simple-dotted-line-graph.tsx +53 -0
  125. package/src/examples/simple-dotted-line-graph.vitest.tsx +62 -0
  126. package/src/examples/simple-grid.vitest.tsx +4 -4
  127. package/src/examples/simple-heatmap.vitest.tsx +9 -9
  128. package/src/examples/simple-histogram.tsx +90 -0
  129. package/src/examples/simple-navigation.vitest.tsx +4 -4
  130. package/src/examples/swift-extension.vitest.tsx +3 -3
  131. package/src/extensions/dev.vitest.tsx +8 -8
  132. package/src/index.tsx +21 -0
  133. package/src/platform/node/sqlite.ts +29 -13
  134. package/src/theme.tsx +11 -10
  135. package/src/utils/run-command.tsx +10 -19
  136. package/src/utils.tsx +0 -163
  137. package/src/examples/store.tsx +0 -4
  138. package/src/examples/store.vitest.tsx +0 -78
  139. package/src/extensions/home.tsx +0 -227
  140. package/src/extensions/store.tsx +0 -375
@@ -1,227 +0,0 @@
1
- import fs from 'node:fs'
2
- import path from 'node:path'
3
- import React from 'react'
4
- import { List, logger, useStore, renderWithProviders } from 'termcast'
5
- import { Action, ActionPanel } from 'termcast'
6
- import { useNavigation } from 'termcast/src/internal/navigation'
7
- import { showToast, Toast } from 'termcast/src/apis/toast'
8
- import { Icon } from 'termcast'
9
- import { getStoredExtensions } from '../utils'
10
- import Store from './store'
11
- import { ExtensionPreferences } from '../components/extension-preferences'
12
- import { runCommand, clearCommandArguments } from '../utils/run-command'
13
- import '../globals'
14
-
15
- interface ExtensionCommand {
16
- extensionName: string
17
- extensionTitle: string
18
- extensionDir?: string
19
- command: any
20
- bundledPath?: string
21
- loadComponent?: () => Promise<(props: any) => any>
22
- packageJson?: any
23
- }
24
-
25
- // Built-in extensions available globally
26
- const builtinExtensions: ExtensionCommand[] = [
27
- {
28
- extensionName: 'termcast-store',
29
- extensionTitle: 'Termcast Store',
30
- command: {
31
- name: 'store',
32
- title: 'Store - Install Extensions',
33
- description: 'Browse and install extensions from the Raycast Store',
34
- mode: 'view',
35
- icon: 'Store',
36
- },
37
- loadComponent: async () => Store,
38
- },
39
- ]
40
-
41
- function ExtensionsList({
42
- allCommands,
43
- initialSearchQuery = '',
44
- }: {
45
- allCommands: ExtensionCommand[]
46
- initialSearchQuery?: string
47
- }): any {
48
- const { push, replace } = useNavigation()
49
- const [searchText, setSearchText] = React.useState(initialSearchQuery)
50
-
51
- const handleCommandSelect = async (item: ExtensionCommand) => {
52
- clearCommandArguments()
53
-
54
- try {
55
- await runCommand({
56
- command: item.command,
57
- extensionName: item.extensionName,
58
- packageJson: item.packageJson,
59
- bundledPath: item.bundledPath,
60
- loadComponent: item.loadComponent,
61
- push,
62
- replace,
63
- })
64
- } catch (error: any) {
65
- await showToast({
66
- style: Toast.Style.Failure,
67
- title: 'Failed to load command',
68
- message: error.message || String(error),
69
- })
70
- }
71
- }
72
-
73
- // Group commands by extension
74
- const groupedByExtension = allCommands.reduce(
75
- (acc, cmd) => {
76
- if (!acc[cmd.extensionName]) {
77
- acc[cmd.extensionName] = {
78
- title: cmd.extensionTitle,
79
- commands: [],
80
- }
81
- }
82
- acc[cmd.extensionName].commands.push(cmd)
83
- return acc
84
- },
85
- {} as Record<string, { title: string; commands: ExtensionCommand[] }>,
86
- )
87
-
88
- return (
89
- <List
90
- navigationTitle='Installed Extensions'
91
- searchBarPlaceholder='Search commands...'
92
- filtering={true}
93
- onSearchTextChange={setSearchText}
94
- searchText={searchText}
95
- >
96
- {Object.entries(groupedByExtension).map(
97
- ([extensionName, { title, commands }]) => (
98
- <List.Section key={extensionName} title={title}>
99
- {commands.map((item) => (
100
- <List.Item
101
- key={`${item.extensionName}-${item.command.name}`}
102
- id={`${item.extensionName}-${item.command.name}`}
103
- title={item.command.title}
104
- subtitle={item.command.description}
105
- icon={
106
- item.command.icon
107
- ? Icon[item.command.icon as keyof typeof Icon]
108
- : undefined
109
- }
110
- accessories={
111
- item.command.mode ? [{ text: item.command.mode }] : []
112
- }
113
- keywords={[
114
- ...(item.command.keywords || []),
115
- item.extensionName,
116
- ]}
117
- actions={
118
- <ActionPanel>
119
- <Action
120
- title='Run Command'
121
- onAction={() => {
122
- handleCommandSelect(item)
123
- }}
124
- />
125
- <Action
126
- title='Configure Extension'
127
- onAction={() => {
128
- push(
129
- <ExtensionPreferences
130
- extensionName={item.extensionName}
131
- onSubmit={() => {
132
- handleCommandSelect(item)
133
- }}
134
- />,
135
- )
136
- }}
137
- />
138
- <Action
139
- title='Configure Command'
140
- onAction={() => {
141
- push(
142
- <ExtensionPreferences
143
- extensionName={item.extensionName}
144
- commandName={item.command.name}
145
- onSubmit={() => {
146
- handleCommandSelect(item)
147
- }}
148
- />,
149
- )
150
- }}
151
- />
152
- {item.bundledPath && (
153
- <Action.CopyToClipboard
154
- content={item.bundledPath}
155
- title='Copy Bundle Path'
156
- />
157
- )}
158
- <Action.CopyToClipboard
159
- content={JSON.stringify(item.command, null, 2)}
160
- title='Copy Command Info'
161
- />
162
- </ActionPanel>
163
- }
164
- />
165
- ))}
166
- </List.Section>
167
- ),
168
- )}
169
-
170
- {allCommands.length === 0 && (
171
- <List.Section title='No Commands'>
172
- <List.Item
173
- title='No extensions installed'
174
- subtitle='Use "termcast build" to install an extension'
175
- />
176
- </List.Section>
177
- )}
178
- </List>
179
- )
180
- }
181
-
182
- export async function runHomeCommand(): Promise<void> {
183
- logger.log(`preparing to render the home command component`)
184
- await renderWithProviders(<Home />, { extensionName: 'termcast-home' })
185
- logger.log(`rendered home command component`)
186
- }
187
-
188
- export default function Home({
189
- initialSearchQuery = '',
190
- key,
191
- }: {
192
- initialSearchQuery?: string
193
- key?: React.Key
194
- }): any {
195
- const storedExtensions = getStoredExtensions()
196
-
197
- const allCommands: ExtensionCommand[] = []
198
-
199
- allCommands.push(...builtinExtensions)
200
-
201
- for (const extension of storedExtensions) {
202
- const packageJson = JSON.parse(
203
- fs.readFileSync(extension.packageJsonPath, 'utf-8'),
204
- )
205
- const extensionPath = path.dirname(extension.packageJsonPath)
206
-
207
- for (const command of extension.commands) {
208
- if (command.bundledPath) {
209
- allCommands.push({
210
- extensionName: extension.name,
211
- extensionTitle: packageJson.title || extension.name,
212
- extensionDir: extensionPath,
213
- command,
214
- bundledPath: command.bundledPath,
215
- packageJson,
216
- })
217
- }
218
- }
219
- }
220
-
221
- return (
222
- <ExtensionsList
223
- allCommands={allCommands}
224
- initialSearchQuery={initialSearchQuery}
225
- />
226
- )
227
- }
@@ -1,375 +0,0 @@
1
- import React, { useState } from 'react'
2
- import fs from 'node:fs'
3
- import path from 'node:path'
4
- import dedent from 'string-dedent'
5
- import { useQuery } from '@tanstack/react-query'
6
- import {
7
- List,
8
- Detail,
9
- Action,
10
- ActionPanel,
11
- showToast,
12
- Toast,
13
- Icon,
14
- } from 'termcast'
15
- import { useNavigation } from 'termcast/src/internal/navigation'
16
- import { ExtensionPreferences } from '../components/extension-preferences'
17
- import { searchStoreListings } from '../store-api/search'
18
- import { fetchExtension } from '../store-api/extension'
19
- import { downloadExtension } from '../store-api/download'
20
- import { getStoreDirectory } from '../utils'
21
- import { buildExtensionCommands } from '../build'
22
- import { logger } from '../logger'
23
- import { parsePackageJson } from '../package-json'
24
- import Home from './home'
25
- import {
26
- checkExtensionPreferences,
27
- type ExtensionPreferencesInfo,
28
- } from '../utils'
29
-
30
- interface StoreListing {
31
- id: string
32
- name: string
33
- author: {
34
- name: string
35
- handle: string
36
- }
37
- owner: {
38
- name: string
39
- handle: string
40
- }
41
- title: string
42
- description: string
43
- download_count: number
44
- updated_at: number
45
- categories: string[]
46
- commands: Array<{
47
- title: string
48
- description: string
49
- mode: string
50
- }>
51
- }
52
-
53
- function StoreSearch(): any {
54
- const [searchQuery, setSearchQuery] = useState('')
55
- const { push } = useNavigation()
56
-
57
- const {
58
- isLoading,
59
- isPending,
60
- isFetching,
61
- data: extensions,
62
- error,
63
- } = useQuery({
64
- queryKey: ['store-search', searchQuery],
65
- queryFn: async () => {
66
- // If no query, use 'raycast' to get default popular extensions
67
- const query = searchQuery.trim() || 'raycast'
68
- logger.log(`Store searching for: "${query}"`)
69
- const response = await searchStoreListings({ query })
70
- logger.log(`Store search returned ${response.data?.length || 0} results`)
71
- return response.data
72
- },
73
- placeholderData: (previousData) => previousData, // Keep previous data while fetching
74
- })
75
-
76
- // Show error toast if query fails
77
- React.useEffect(() => {
78
- if (error) {
79
- showToast({
80
- style: Toast.Style.Failure,
81
- title: 'Search failed',
82
- message: (error as Error).message,
83
- })
84
- }
85
- }, [error])
86
-
87
- return (
88
- <List
89
- navigationTitle='Store - Install Extensions'
90
- searchBarPlaceholder='Search extensions...'
91
- filtering={false}
92
- onSearchTextChange={setSearchQuery}
93
- isLoading={isPending || isFetching}
94
- >
95
- {extensions?.map((ext) => (
96
- <List.Item
97
- key={ext.id}
98
- id={ext.id}
99
- title={ext.title}
100
- subtitle={ext.description}
101
- accessories={[]}
102
- keywords={ext.categories}
103
- actions={
104
- <ActionPanel>
105
- <Action
106
- title='View Details'
107
- onAction={() => {
108
- const Component = () => <ExtensionDetails extension={ext} />
109
- Component.displayName = `store-extension-${ext.name}`
110
- push(<Component />)
111
- }}
112
- />
113
- <Action.OpenInBrowser
114
- url={`https://raycast.com/${ext.owner.handle}/${ext.name}`}
115
- title='Open in Raycast Store'
116
- />
117
- </ActionPanel>
118
- }
119
- />
120
- ))}
121
- </List>
122
- )
123
- }
124
-
125
- function ExtensionDetails({ extension }: { extension: StoreListing }): any {
126
- const [isInstalling, setIsInstalling] = useState(false)
127
- const [isInstalled, setIsInstalled] = useState(false)
128
- const [extensionInfo, setExtensionInfo] = useState<ExtensionPreferencesInfo>({
129
- hasPreferences: false,
130
- hasRequiredPreferences: false,
131
- })
132
- const { pop, push } = useNavigation()
133
-
134
- // Check if extension is already installed and get preferences info
135
- React.useEffect(() => {
136
- const storeDir = getStoreDirectory()
137
- const extensionDir = path.join(storeDir, extension.name)
138
- const installed = fs.existsSync(extensionDir)
139
- setIsInstalled(installed)
140
-
141
- // Check preferences info if installed
142
- if (installed) {
143
- const prefsInfo = checkExtensionPreferences(extension.name)
144
- setExtensionInfo(prefsInfo)
145
- }
146
- }, [extension.name])
147
-
148
- const formatDate = (timestamp: number) => {
149
- return new Date(timestamp * 1000).toLocaleDateString('en-US', {
150
- year: 'numeric',
151
- month: 'long',
152
- day: 'numeric',
153
- })
154
- }
155
-
156
- const handleInstall = async () => {
157
- setIsInstalling(true)
158
- try {
159
- await showToast({
160
- style: Toast.Style.Animated,
161
- title: 'Downloading extension...',
162
- })
163
-
164
- const files = await downloadExtension({
165
- author: extension.owner.handle,
166
- extension: extension.name,
167
- })
168
-
169
- const storeDir = getStoreDirectory()
170
- const extensionDir = path.join(storeDir, extension.name)
171
-
172
- if (fs.existsSync(extensionDir)) {
173
- fs.rmSync(extensionDir, { recursive: true, force: true })
174
- }
175
-
176
- fs.mkdirSync(extensionDir, { recursive: true })
177
-
178
- // Write all files to the extension directory
179
- for (const file of files) {
180
- const filePath = path.join(extensionDir, file.filename)
181
- const fileDir = path.dirname(filePath)
182
-
183
- if (!fs.existsSync(fileDir)) {
184
- fs.mkdirSync(fileDir, { recursive: true })
185
- }
186
-
187
- fs.writeFileSync(filePath, file.buffer)
188
- }
189
-
190
- await showToast({
191
- style: Toast.Style.Animated,
192
- title: 'Building extension...',
193
- })
194
-
195
- // Build the extension commands to create bundles
196
- try {
197
- const buildResult = await buildExtensionCommands({
198
- extensionPath: extensionDir,
199
- format: 'cjs',
200
- target: 'node',
201
- })
202
- logger.log(
203
- `Built ${buildResult.commands.length} commands for ${extension.name}`,
204
- )
205
- } catch (buildError: any) {
206
- // Log build error but don't fail installation
207
- logger.error(
208
- `Failed to build extension commands: ${buildError.message}`,
209
- )
210
- await showToast({
211
- style: Toast.Style.Animated,
212
- title: 'Warning',
213
- message: buildError?.message,
214
- })
215
- return
216
- }
217
-
218
- await showToast({
219
- style: Toast.Style.Success,
220
- title: 'Extension installed',
221
- message: `${extension.title} has been installed successfully`,
222
- })
223
-
224
- logger.log(`Extension '${extension.name}' installed to ${extensionDir}`)
225
- setIsInstalled(true)
226
-
227
- // Update extension info after installation
228
- const prefsInfo = checkExtensionPreferences(extension.name)
229
- setExtensionInfo(prefsInfo)
230
-
231
- // Ask if user wants to configure preferences
232
- await showToast({
233
- style: Toast.Style.Success,
234
- title: 'Configure preferences?',
235
- message: 'Would you like to configure extension preferences now?',
236
- primaryAction: {
237
- title: 'Configure',
238
- onAction: () => {
239
- const Component = () => (
240
- <ExtensionPreferences extensionName={extension.name} />
241
- )
242
- Component.displayName = `store-preferences-${extension.name}`
243
- push(<Component />)
244
- },
245
- },
246
- })
247
- } catch (error: any) {
248
- await showToast({
249
- style: Toast.Style.Failure,
250
- title: 'Installation failed',
251
- message: error.message,
252
- })
253
- } finally {
254
- setIsInstalling(false)
255
- }
256
- }
257
-
258
- const markdownContent = dedent`
259
- # ${extension.title}
260
-
261
- ${extension.description}
262
-
263
- ## Commands
264
-
265
- ${extension.commands
266
- .map(
267
- (cmd) => dedent`
268
- ### ${cmd.title}
269
- ${cmd.description || 'No description available'}
270
- **Mode:** ${cmd.mode}
271
- `,
272
- )
273
- .join('\n\n')}
274
-
275
- ## Information
276
-
277
- - **Author:** ${extension.author.name} (@${extension.author.handle})
278
- - **Downloads:** ${extension.download_count.toLocaleString()}
279
- - **Last Updated:** ${formatDate(extension.updated_at)}
280
- - **Categories:** ${extension.categories.join(', ')}
281
- `
282
-
283
- return (
284
- <Detail
285
- navigationTitle={extension.title}
286
- markdown={markdownContent}
287
- metadata={
288
- <Detail.Metadata>
289
- <Detail.Metadata.Label title='Author' text={extension.author.name} />
290
- <Detail.Metadata.Label
291
- title='Downloads'
292
- text={extension.download_count.toLocaleString()}
293
- />
294
- <Detail.Metadata.Label
295
- title='Commands'
296
- text={extension.commands.length.toString()}
297
- />
298
- <Detail.Metadata.Separator />
299
- <Detail.Metadata.Label
300
- title='Updated'
301
- text={formatDate(extension.updated_at)}
302
- />
303
- <Detail.Metadata.TagList title='Categories'>
304
- {extension.categories.map((cat, index) => (
305
- <Detail.Metadata.TagList.Item text={cat} />
306
- ))}
307
- </Detail.Metadata.TagList>
308
- </Detail.Metadata>
309
- }
310
- actions={
311
- <ActionPanel>
312
- {!isInstalled ? (
313
- <Action
314
- title={isInstalling ? 'Installing...' : 'Install Extension'}
315
- onAction={handleInstall}
316
- />
317
- ) : (
318
- <>
319
- {extensionInfo.hasRequiredPreferences && (
320
- <Action
321
- title='Configure Extension'
322
- onAction={() => {
323
- const Component = () => (
324
- <ExtensionPreferences extensionName={extension.name} />
325
- )
326
- Component.displayName = `store-preferences-${extension.name}`
327
- push(<Component />)
328
- }}
329
- />
330
- )}
331
- <Action
332
- title='Use Extension'
333
- onAction={() => {
334
- const Component = () => (
335
- <Home
336
- key={extension.name}
337
- initialSearchQuery={extension.name}
338
- />
339
- )
340
- Component.displayName = `store-home-${extension.name}`
341
- push(<Component />)
342
- }}
343
- />
344
- {extensionInfo.hasPreferences &&
345
- !extensionInfo.hasRequiredPreferences && (
346
- <Action
347
- title='Configure Extension'
348
- onAction={() => {
349
- const Component = () => (
350
- <ExtensionPreferences extensionName={extension.name} />
351
- )
352
- Component.displayName = `store-preferences-${extension.name}`
353
- push(<Component />)
354
- }}
355
- />
356
- )}
357
- <Action title='Reinstall Extension' onAction={handleInstall} />
358
- </>
359
- )}
360
- <Action.OpenInBrowser
361
- url={`https://raycast.com/${extension.owner.handle}/${extension.name}`}
362
- title='View in Raycast Store'
363
- />
364
- </ActionPanel>
365
- }
366
- />
367
- )
368
- }
369
- ExtensionDetails.displayName = 'ExtensionDetails'
370
-
371
- StoreSearch.displayName = 'StoreSearch'
372
- export default function Store(): any {
373
- return <StoreSearch />
374
- }
375
- Store.displayName = 'Store'