33 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44
55import { arch } from '@tauri-apps/plugin-os'
6- import { AlertTriangle , Check , Download , Loader2 , Terminal } from 'lucide-react'
6+ import { AlertTriangle , Check , Download , ExternalLink , Loader2 , Terminal } from 'lucide-react'
77import { useState , useTransition } from 'react'
88import { CopyCommandRow } from '@/components/settings/copy-command-row'
99import { Button } from '@/components/ui/button'
@@ -20,6 +20,7 @@ import { getPlatform, isTauri } from '@/lib/platform'
2020
2121/** Shell one-liner to build the CLI from source when no prebuilt binary applies. */
2222const manualBuildCommand = 'cd cli && bun install && bun run build && ./install.sh'
23+ const cliInstallGuideUrl = 'https://github.com/thunderbird/thunderbolt/blob/main/cli/README.md#install'
2324
2425type InstallState =
2526 | { status : 'idle' }
@@ -43,7 +44,8 @@ type ThunderboltCliInstallCardProps = {
4344 * the prebuilt binary into `~/.local/bin`, then renders the installed path (with
4445 * a PATH hint if the dir isn't on `PATH`) or a clear error. When a release has no
4546 * CLI assets, it surfaces the manual build fallback instead of failing silently.
46- * Renders nothing on unpublished OS/architecture pairs or outside Tauri.
47+ * Web builds link to the install guide; Tauri builds render nothing on unpublished
48+ * OS/architecture pairs.
4749 */
4850export const ThunderboltCliInstallCard = ( {
4951 install = installThunderboltCli ,
@@ -57,7 +59,7 @@ export const ThunderboltCliInstallCard = ({
5759 const isTauriEnv = tauri ?? isTauri ( )
5860 const runtimeArchitecture = architecture ?? ( isTauriEnv ? arch ( ) : 'unknown' )
5961
60- if ( ! canInstallThunderboltCli ( platform ?? getPlatform ( ) , runtimeArchitecture , isTauriEnv ) ) {
62+ if ( isTauriEnv && ! canInstallThunderboltCli ( platform ?? getPlatform ( ) , runtimeArchitecture , isTauriEnv ) ) {
6163 return null
6264 }
6365
@@ -81,50 +83,69 @@ export const ThunderboltCliInstallCard = ({
8183 < div className = "flex flex-col gap-1 min-w-0" >
8284 < CardTitle > Thunderbolt CLI</ CardTitle >
8385 < CardDescription >
84- Install the standalone < code className = "font-mono" > thunderbolt</ code > terminal agent to{ ' ' }
85- < code className = "font-mono" > ~/.local/bin</ code > .
86+ { isTauriEnv ? (
87+ < >
88+ Install the standalone < code className = "font-mono" > thunderbolt</ code > terminal agent to{ ' ' }
89+ < code className = "font-mono" > ~/.local/bin</ code > .
90+ </ >
91+ ) : (
92+ < >
93+ Install the standalone < code className = "font-mono" > thunderbolt</ code > terminal agent from your shell.
94+ </ >
95+ ) }
8696 </ CardDescription >
8797 </ div >
8898 </ div >
8999 </ CardHeader >
90100 < CardContent className = "flex flex-col gap-4" >
91- < Button variant = "secondary" className = "self-start" disabled = { isPending } onClick = { handleInstall } >
92- { isPending ? < Loader2 className = "animate-spin" /> : < Download /> }
93- { isPending ? 'Installing…' : 'Install CLI' }
94- </ Button >
101+ { isTauriEnv ? (
102+ < >
103+ < Button variant = "secondary" className = "self-start" disabled = { isPending } onClick = { handleInstall } >
104+ { isPending ? < Loader2 className = "animate-spin" /> : < Download /> }
105+ { isPending ? 'Installing…' : 'Install CLI' }
106+ </ Button >
95107
96- { state . status === 'success' && (
97- < div className = "flex flex-col gap-3" >
98- < p className = "flex items-center gap-2 text-[length:var(--font-size-sm)]" >
99- < Check className = "size-4 shrink-0 text-green-600" aria-hidden = "true" />
100- Installed to < code className = "font-mono" > { state . result . path } </ code >
101- </ p >
102- { ! state . result . onPath && state . result . pathHint && (
103- < div className = "flex flex-col gap-2" >
104- < p className = "text-[length:var(--font-size-xs)] text-muted-foreground" >
105- Add < code className = "font-mono" > ~/.local/bin</ code > to your PATH, then restart your shell:
108+ { state . status === 'success' && (
109+ < div className = "flex flex-col gap-3" >
110+ < p className = "flex items-center gap-2 text-[length:var(--font-size-sm)]" >
111+ < Check className = "size-4 shrink-0 text-green-600" aria-hidden = "true" />
112+ Installed to < code className = "font-mono" > { state . result . path } </ code >
106113 </ p >
107- < CopyCommandRow command = { state . result . pathHint } label = "Copy PATH command" />
114+ { ! state . result . onPath && state . result . pathHint && (
115+ < div className = "flex flex-col gap-2" >
116+ < p className = "text-[length:var(--font-size-xs)] text-muted-foreground" >
117+ Add < code className = "font-mono" > ~/.local/bin</ code > to your PATH, then restart your shell:
118+ </ p >
119+ < CopyCommandRow command = { state . result . pathHint } label = "Copy PATH command" />
120+ </ div >
121+ ) }
108122 </ div >
109123 ) }
110- </ div >
111- ) }
112124
113- { state . status === 'error' && (
114- < div className = "flex flex-col gap-3" >
115- < p className = "flex items-start gap-2 text-[length:var(--font-size-sm)] text-destructive" >
116- < AlertTriangle className = "size-4 mt-0.5 shrink-0" aria-hidden = "true" />
117- { state . message }
118- </ p >
119- { state . showManualBuild && (
120- < div className = "flex flex-col gap-2" >
121- < p className = "text-[length:var(--font-size-xs)] text-muted-foreground" >
122- Build it from source instead (requires Bun):
125+ { state . status === 'error' && (
126+ < div className = "flex flex-col gap-3" >
127+ < p className = "flex items-start gap-2 text-[length:var(--font-size-sm)] text-destructive" >
128+ < AlertTriangle className = "size-4 mt-0.5 shrink-0" aria-hidden = "true" />
129+ { state . message }
123130 </ p >
124- < CopyCommandRow command = { manualBuildCommand } label = "Copy build command" />
131+ { state . showManualBuild && (
132+ < div className = "flex flex-col gap-2" >
133+ < p className = "text-[length:var(--font-size-xs)] text-muted-foreground" >
134+ Build it from source instead (requires Bun):
135+ </ p >
136+ < CopyCommandRow command = { manualBuildCommand } label = "Copy build command" />
137+ </ div >
138+ ) }
125139 </ div >
126140 ) }
127- </ div >
141+ </ >
142+ ) : (
143+ < Button asChild variant = "secondary" className = "self-start" >
144+ < a href = { cliInstallGuideUrl } target = "_blank" rel = "noopener noreferrer" >
145+ < ExternalLink />
146+ View install guide
147+ </ a >
148+ </ Button >
128149 ) }
129150 </ CardContent >
130151 </ Card >
0 commit comments