termcast 1.4.0 → 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.
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +8 -7
- package/dist/build.js.map +1 -1
- package/dist/cli.js +0 -40
- package/dist/cli.js.map +1 -1
- package/dist/components/bar-graph.d.ts +23 -8
- package/dist/components/bar-graph.d.ts.map +1 -1
- package/dist/components/bar-graph.js +84 -40
- package/dist/components/bar-graph.js.map +1 -1
- package/dist/components/dotted-line-graph.d.ts +86 -0
- package/dist/components/dotted-line-graph.d.ts.map +1 -0
- package/dist/components/dotted-line-graph.js +260 -0
- package/dist/components/dotted-line-graph.js.map +1 -0
- package/dist/components/extension-preferences.d.ts.map +1 -1
- package/dist/components/extension-preferences.js +1 -10
- package/dist/components/extension-preferences.js.map +1 -1
- package/dist/components/graph.d.ts.map +1 -1
- package/dist/components/graph.js +7 -1
- package/dist/components/graph.js.map +1 -1
- package/dist/components/histogram.d.ts +42 -0
- package/dist/components/histogram.d.ts.map +1 -0
- package/dist/components/histogram.js +115 -0
- package/dist/components/histogram.js.map +1 -0
- package/dist/components/horizontal-bar-graph.d.ts +47 -0
- package/dist/components/horizontal-bar-graph.d.ts.map +1 -0
- package/dist/components/horizontal-bar-graph.js +137 -0
- package/dist/components/horizontal-bar-graph.js.map +1 -0
- package/dist/components/list.d.ts +2 -0
- package/dist/components/list.d.ts.map +1 -1
- package/dist/components/list.js +10 -10
- package/dist/components/list.js.map +1 -1
- package/dist/examples/bar-graph-weekly.js +2 -2
- package/dist/examples/bar-graph-weekly.js.map +1 -1
- package/dist/examples/charts-showcase-barchart.d.ts +2 -0
- package/dist/examples/charts-showcase-barchart.d.ts.map +1 -0
- package/dist/examples/charts-showcase-barchart.js +10 -0
- package/dist/examples/charts-showcase-barchart.js.map +1 -0
- package/dist/examples/charts-showcase-bargraph.d.ts +2 -0
- package/dist/examples/charts-showcase-bargraph.d.ts.map +1 -0
- package/dist/examples/charts-showcase-bargraph.js +60 -0
- package/dist/examples/charts-showcase-bargraph.js.map +1 -0
- package/dist/examples/charts-showcase-candle.d.ts +2 -0
- package/dist/examples/charts-showcase-candle.d.ts.map +1 -0
- package/dist/examples/charts-showcase-candle.js +30 -0
- package/dist/examples/charts-showcase-candle.js.map +1 -0
- package/dist/examples/charts-showcase-graph.d.ts +2 -0
- package/dist/examples/charts-showcase-graph.d.ts.map +1 -0
- package/dist/examples/charts-showcase-graph.js +33 -0
- package/dist/examples/charts-showcase-graph.js.map +1 -0
- package/dist/examples/charts-showcase-heatmap.d.ts +2 -0
- package/dist/examples/charts-showcase-heatmap.d.ts.map +1 -0
- package/dist/examples/charts-showcase-heatmap.js +36 -0
- package/dist/examples/charts-showcase-heatmap.js.map +1 -0
- package/dist/examples/charts-showcase-mixed.d.ts +2 -0
- package/dist/examples/charts-showcase-mixed.d.ts.map +1 -0
- package/dist/examples/charts-showcase-mixed.js +30 -0
- package/dist/examples/charts-showcase-mixed.js.map +1 -0
- package/dist/examples/charts-showcase-progress.d.ts +2 -0
- package/dist/examples/charts-showcase-progress.d.ts.map +1 -0
- package/dist/examples/charts-showcase-progress.js +10 -0
- package/dist/examples/charts-showcase-progress.js.map +1 -0
- package/dist/examples/graph-multi-series.js +1 -1
- package/dist/examples/graph-multi-series.js.map +1 -1
- package/dist/examples/horizontal-bar-graph-weekly.d.ts +2 -0
- package/dist/examples/horizontal-bar-graph-weekly.d.ts.map +1 -0
- package/dist/examples/horizontal-bar-graph-weekly.js +67 -0
- package/dist/examples/horizontal-bar-graph-weekly.js.map +1 -0
- package/dist/examples/simple-dotted-line-graph.d.ts +2 -0
- package/dist/examples/simple-dotted-line-graph.d.ts.map +1 -0
- package/dist/examples/simple-dotted-line-graph.js +39 -0
- package/dist/examples/simple-dotted-line-graph.js.map +1 -0
- package/dist/examples/simple-histogram.d.ts +2 -0
- package/dist/examples/simple-histogram.d.ts.map +1 -0
- package/dist/examples/simple-histogram.js +47 -0
- package/dist/examples/simple-histogram.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +15 -6
- package/dist/logger.js.map +1 -1
- package/dist/platform/node/sqlite.d.ts +6 -5
- package/dist/platform/node/sqlite.d.ts.map +1 -1
- package/dist/platform/node/sqlite.js +30 -14
- package/dist/platform/node/sqlite.js.map +1 -1
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +11 -9
- package/dist/theme.js.map +1 -1
- package/dist/utils/run-command.d.ts.map +1 -1
- package/dist/utils/run-command.js +8 -19
- package/dist/utils/run-command.js.map +1 -1
- package/dist/utils.d.ts +1 -19
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +1 -100
- package/dist/utils.js.map +1 -1
- package/package.json +14 -16
- package/src/build.tsx +11 -10
- package/src/cli.tsx +3 -40
- package/src/compile.vitest.tsx +3 -3
- package/src/components/bar-graph.tsx +217 -111
- package/src/components/dotted-line-graph.tsx +407 -0
- package/src/components/extension-preferences.tsx +2 -12
- package/src/components/graph.tsx +5 -1
- package/src/components/histogram.tsx +228 -0
- package/src/components/horizontal-bar-graph.tsx +279 -0
- package/src/components/list.tsx +20 -15
- package/src/examples/action-shortcut.vitest.tsx +17 -17
- package/src/examples/bar-graph-weekly.tsx +2 -2
- package/src/examples/bar-graph-weekly.vitest.tsx +63 -62
- package/src/examples/charts-showcase-bargraph.tsx +103 -0
- package/src/examples/detail-metadata-showcase.vitest.tsx +13 -18
- package/src/examples/form-basic.vitest.tsx +35 -35
- package/src/examples/form-dropdown.vitest.tsx +11 -11
- package/src/examples/form-scroll.vitest.tsx +1 -1
- package/src/examples/form-tagpicker.vitest.tsx +11 -11
- package/src/examples/github.vitest.tsx +22 -22
- package/src/examples/graph-bar-chart.vitest.tsx +8 -8
- package/src/examples/graph-multi-series.tsx +1 -1
- package/src/examples/graph-row.vitest.tsx +14 -14
- package/src/examples/graph-styles.vitest.tsx +77 -77
- package/src/examples/horizontal-bar-graph-weekly.tsx +138 -0
- package/src/examples/horizontal-bar-graph-weekly.vitest.tsx +164 -0
- package/src/examples/list-detail-metadata.vitest.tsx +4 -4
- package/src/examples/list-with-detail.vitest.tsx +46 -46
- package/src/examples/simple-candle-chart.vitest.tsx +8 -8
- package/src/examples/simple-dotted-line-graph.tsx +53 -0
- package/src/examples/simple-dotted-line-graph.vitest.tsx +62 -0
- package/src/examples/simple-grid.vitest.tsx +4 -4
- package/src/examples/simple-histogram.tsx +90 -0
- package/src/examples/simple-navigation.vitest.tsx +4 -4
- package/src/examples/swift-extension.vitest.tsx +3 -3
- package/src/examples/toast-variations.vitest.tsx +5 -5
- package/src/extensions/dev.vitest.tsx +8 -8
- package/src/index.tsx +21 -0
- package/src/logger.tsx +16 -6
- package/src/platform/node/sqlite.ts +29 -13
- package/src/theme.tsx +11 -10
- package/src/utils/run-command.tsx +10 -19
- package/src/utils.tsx +0 -163
- package/src/examples/store.tsx +0 -4
- package/src/examples/store.vitest.tsx +0 -78
- package/src/extensions/home.tsx +0 -227
- package/src/extensions/store.tsx +0 -375
package/src/extensions/home.tsx
DELETED
|
@@ -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
|
-
}
|
package/src/extensions/store.tsx
DELETED
|
@@ -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'
|