Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 0 additions & 29 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,6 @@
All notable changes to Thunderbolt are documented in this file.
Generated by [git-cliff](https://git-cliff.org).

## [0.2.0] - 2026-07-13

### Features
Comment thread
ital0 marked this conversation as resolved.
- Portable coding agent across cli, web, mobile, and p2p (#1032) (cca70cf)
- Frameless desktop window + menu-bar icon tray (#1067) (e318e94)
- Support editing MCP servers in settings (#1008) (a26735b)
- Require successful connection test before saving a model (#1006) (f558119)
- Quote-reply — quote a passage from a response into the composer (#1054) (b3c9253)

### Fixes
- Keep sidebar branding on web and mobile (#1069) (de70d95)
- Add accessible names to icon-only buttons and unlabeled controls (#1057) (f4b2fcb)

### Performance
- Cut per-token render and save overhead in the chat streaming pipeline (#1053) (c037a72)
- Lazy-load posthog-js so analytics leaves the entry chunk (#1058) (7f008f9)
- Lazy-load KaTeX and drop the duplicate markdown parser (#1059) (0852182)
- Stop leaking desktop-only tauri plugins into the web entry (#1060) (a5667ab)

### Refactor
- Make PowerSync JWT kid env-driven and drop orphan sync rule (#1068) (f2e1bc9)

### Documentation
- Update sync-rule workflow for self-hosted PowerSync (#1071) (605808c)

### Other
- Drop 5s test timeout in pr-metrics (#1072) (185431e)
- Replace demo-nightly with nightly image rebuild + scope preview cleanup (#1063) (5d83346)

## [0.1.106] - 2026-07-07

### Features
Expand Down
2 changes: 1 addition & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@thunderbolt/cli",
"version": "0.2.0",
"version": "0.1.106",
"private": true,
"type": "module",
"license": "MPL-2.0",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "thunderbolt",
"private": true,
"version": "0.2.0",
"version": "0.1.106",
Comment thread
ital0 marked this conversation as resolved.
"description": "Thunderbolt",
"type": "module",
"license": "MPL-2.0",
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "thunderbolt"
version = "0.2.0"
version = "0.1.106"
description = "Privacy-respecting AI assistant."
authors = ["Chris Roth <chris@cjroth.com>"]
license = "MPL-2.0"
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/gen/android/app/tauri.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
tauri.android.versionName=0.2.0
tauri.android.versionName=0.1.106
tauri.android.versionCode=1018
7 changes: 2 additions & 5 deletions src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Thunderbolt",
"version": "0.2.0",
"version": "0.1.106",
"identifier": "net.thunderbird.thunderbolt",
"build": {
"beforeDevCommand": "bun run dev",
Expand All @@ -20,10 +20,7 @@
"visible": false,
"titleBarStyle": "Overlay",
"hiddenTitle": true,
"trafficLightPosition": {
"x": 16,
"y": 26
}
"trafficLightPosition": { "x": 16, "y": 26 }
}
],
"security": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,21 @@ describe('ThunderboltCliInstallCard', () => {
expect(screen.getByRole('button', { name: /install cli/i })).toBeInTheDocument()
})

it('renders nothing on web, mobile or Windows', () => {
const { container: web } = renderCard({ tauri: false })
expect(web).toBeEmptyDOMElement()
cleanup()
it('renders the install guide on web instead of the one-click install action', () => {
renderCard({ tauri: false })

const guideLink = screen.getByRole('link', { name: /view install guide/i })
expect(guideLink).toHaveAttribute(
'href',
'https://github.com/thunderbird/thunderbolt/blob/main/cli/README.md#install',
)
expect(guideLink).toHaveAttribute('target', '_blank')
expect(guideLink).toHaveAttribute('rel', 'noopener noreferrer')
expect(screen.getByText(/install the standalone/i)).toHaveTextContent('from your shell')
expect(screen.queryByRole('button', { name: /install cli/i })).not.toBeInTheDocument()
})

it('renders nothing on unsupported Tauri platforms', () => {
const { container: windows } = renderCard({ platform: 'windows' })
expect(windows).toBeEmptyDOMElement()
cleanup()
Expand Down
89 changes: 55 additions & 34 deletions src/components/settings/agents/thunderbolt-cli-install-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { arch } from '@tauri-apps/plugin-os'
import { AlertTriangle, Check, Download, Loader2, Terminal } from 'lucide-react'
import { AlertTriangle, Check, Download, ExternalLink, Loader2, Terminal } from 'lucide-react'
import { useState, useTransition } from 'react'
import { CopyCommandRow } from '@/components/settings/copy-command-row'
import { Button } from '@/components/ui/button'
Expand All @@ -20,6 +20,7 @@ import { getPlatform, isTauri } from '@/lib/platform'

/** Shell one-liner to build the CLI from source when no prebuilt binary applies. */
const manualBuildCommand = 'cd cli && bun install && bun run build && ./install.sh'
const cliInstallGuideUrl = 'https://github.com/thunderbird/thunderbolt/blob/main/cli/README.md#install'

type InstallState =
| { status: 'idle' }
Expand All @@ -43,7 +44,8 @@ type ThunderboltCliInstallCardProps = {
* the prebuilt binary into `~/.local/bin`, then renders the installed path (with
* a PATH hint if the dir isn't on `PATH`) or a clear error. When a release has no
* CLI assets, it surfaces the manual build fallback instead of failing silently.
* Renders nothing on unpublished OS/architecture pairs or outside Tauri.
* Web builds link to the install guide; Tauri builds render nothing on unpublished
* OS/architecture pairs.
*/
export const ThunderboltCliInstallCard = ({
install = installThunderboltCli,
Expand All @@ -57,7 +59,7 @@ export const ThunderboltCliInstallCard = ({
const isTauriEnv = tauri ?? isTauri()
const runtimeArchitecture = architecture ?? (isTauriEnv ? arch() : 'unknown')

if (!canInstallThunderboltCli(platform ?? getPlatform(), runtimeArchitecture, isTauriEnv)) {
if (isTauriEnv && !canInstallThunderboltCli(platform ?? getPlatform(), runtimeArchitecture, isTauriEnv)) {
return null
}

Expand All @@ -81,50 +83,69 @@ export const ThunderboltCliInstallCard = ({
<div className="flex flex-col gap-1 min-w-0">
<CardTitle>Thunderbolt CLI</CardTitle>
<CardDescription>
Install the standalone <code className="font-mono">thunderbolt</code> terminal agent to{' '}
<code className="font-mono">~/.local/bin</code>.
{isTauriEnv ? (
<>
Install the standalone <code className="font-mono">thunderbolt</code> terminal agent to{' '}
<code className="font-mono">~/.local/bin</code>.
</>
) : (
<>
Install the standalone <code className="font-mono">thunderbolt</code> terminal agent from your shell.
</>
)}
</CardDescription>
</div>
</div>
</CardHeader>
<CardContent className="flex flex-col gap-4">
<Button variant="secondary" className="self-start" disabled={isPending} onClick={handleInstall}>
{isPending ? <Loader2 className="animate-spin" /> : <Download />}
{isPending ? 'Installing…' : 'Install CLI'}
</Button>
{isTauriEnv ? (
<>
<Button variant="secondary" className="self-start" disabled={isPending} onClick={handleInstall}>
{isPending ? <Loader2 className="animate-spin" /> : <Download />}
{isPending ? 'Installing…' : 'Install CLI'}
</Button>

{state.status === 'success' && (
<div className="flex flex-col gap-3">
<p className="flex items-center gap-2 text-[length:var(--font-size-sm)]">
<Check className="size-4 shrink-0 text-green-600" aria-hidden="true" />
Installed to <code className="font-mono">{state.result.path}</code>
</p>
{!state.result.onPath && state.result.pathHint && (
<div className="flex flex-col gap-2">
<p className="text-[length:var(--font-size-xs)] text-muted-foreground">
Add <code className="font-mono">~/.local/bin</code> to your PATH, then restart your shell:
{state.status === 'success' && (
<div className="flex flex-col gap-3">
<p className="flex items-center gap-2 text-[length:var(--font-size-sm)]">
<Check className="size-4 shrink-0 text-green-600" aria-hidden="true" />
Installed to <code className="font-mono">{state.result.path}</code>
</p>
<CopyCommandRow command={state.result.pathHint} label="Copy PATH command" />
{!state.result.onPath && state.result.pathHint && (
<div className="flex flex-col gap-2">
<p className="text-[length:var(--font-size-xs)] text-muted-foreground">
Add <code className="font-mono">~/.local/bin</code> to your PATH, then restart your shell:
</p>
<CopyCommandRow command={state.result.pathHint} label="Copy PATH command" />
</div>
)}
</div>
)}
</div>
)}

{state.status === 'error' && (
<div className="flex flex-col gap-3">
<p className="flex items-start gap-2 text-[length:var(--font-size-sm)] text-destructive">
<AlertTriangle className="size-4 mt-0.5 shrink-0" aria-hidden="true" />
{state.message}
</p>
{state.showManualBuild && (
<div className="flex flex-col gap-2">
<p className="text-[length:var(--font-size-xs)] text-muted-foreground">
Build it from source instead (requires Bun):
{state.status === 'error' && (
<div className="flex flex-col gap-3">
<p className="flex items-start gap-2 text-[length:var(--font-size-sm)] text-destructive">
<AlertTriangle className="size-4 mt-0.5 shrink-0" aria-hidden="true" />
{state.message}
</p>
<CopyCommandRow command={manualBuildCommand} label="Copy build command" />
{state.showManualBuild && (
<div className="flex flex-col gap-2">
<p className="text-[length:var(--font-size-xs)] text-muted-foreground">
Build it from source instead (requires Bun):
</p>
<CopyCommandRow command={manualBuildCommand} label="Copy build command" />
</div>
)}
</div>
)}
</div>
</>
) : (
<Button asChild variant="secondary" className="self-start">
<a href={cliInstallGuideUrl} target="_blank" rel="noopener noreferrer">
<ExternalLink />
View install guide
</a>
</Button>
)}
</CardContent>
</Card>
Expand Down
Loading