Skip to content

Commit 60a8c93

Browse files
committed
Release v0.0.38
## What's New - Desktop app release v0.0.38 ## Downloads - **macOS ARM64 (Apple Silicon)**: Download the `-arm64.dmg` file - **macOS Intel**: Download the `.dmg` file (without arm64) Auto-updates are enabled. Existing users will be notified automatically.
1 parent 54c6273 commit 60a8c93

File tree

10 files changed

+768
-19
lines changed

10 files changed

+768
-19
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "21st-desktop",
3-
"version": "0.0.37",
3+
"version": "0.0.38",
44
"private": true,
55
"description": "1Code - UI for parallel work with AI agents",
66
"author": {
@@ -79,6 +79,7 @@
7979
"gray-matter": "^4.0.3",
8080
"jotai": "^2.11.1",
8181
"lucide-react": "^0.468.0",
82+
"mermaid": "^11.12.2",
8283
"motion": "^11.15.0",
8384
"next-themes": "^0.4.4",
8485
"node-pty": "^1.1.0",
@@ -90,6 +91,7 @@
9091
"react-hotkeys-hook": "^4.6.1",
9192
"react-icons": "^5.5.0",
9293
"react-syntax-highlighter": "^16.1.0",
94+
"react-zoom-pan-pinch": "^3.7.0",
9395
"remark-breaks": "^4.0.0",
9496
"remark-gfm": "^4.0.1",
9597
"shiki": "^1.24.4",

src/main/lib/auto-updater.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,29 @@ export async function initAutoUpdater(getWindow: () => BrowserWindow | null) {
139139
*/
140140
function registerIpcHandlers() {
141141
// Check for updates
142-
ipcMain.handle("update:check", async () => {
142+
ipcMain.handle("update:check", async (_event, force?: boolean) => {
143143
if (!app.isPackaged) {
144144
log.info("[AutoUpdater] Skipping update check in dev mode")
145145
return null
146146
}
147147
try {
148+
// If force is true, add cache-busting timestamp to URL
149+
if (force) {
150+
const cacheBuster = `?t=${Date.now()}`
151+
autoUpdater.setFeedURL({
152+
provider: "generic",
153+
url: `${CDN_BASE}${cacheBuster}`,
154+
})
155+
log.info("[AutoUpdater] Force check with cache-busting:", `${CDN_BASE}${cacheBuster}`)
156+
}
148157
const result = await autoUpdater.checkForUpdates()
158+
// Reset feed URL back to normal after force check
159+
if (force) {
160+
autoUpdater.setFeedURL({
161+
provider: "generic",
162+
url: CDN_BASE,
163+
})
164+
}
149165
return result?.updateInfo || null
150166
} catch (error) {
151167
log.error("[AutoUpdater] Check failed:", error)

src/preload/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export interface DesktopApi {
2525
getVersion: () => Promise<string>
2626

2727
// Auto-update
28-
checkForUpdates: () => Promise<UpdateInfo | null>
28+
checkForUpdates: (force?: boolean) => Promise<UpdateInfo | null>
2929
downloadUpdate: () => Promise<boolean>
3030
installUpdate: () => void
3131
onUpdateChecking: (callback: () => void) => () => void

src/preload/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ contextBridge.exposeInMainWorld("desktopApi", {
2525
isPackaged: () => ipcRenderer.invoke("app:isPackaged"),
2626

2727
// Auto-update methods
28-
checkForUpdates: () => ipcRenderer.invoke("update:check"),
28+
checkForUpdates: (force?: boolean) => ipcRenderer.invoke("update:check", force),
2929
downloadUpdate: () => ipcRenderer.invoke("update:download"),
3030
installUpdate: () => ipcRenderer.invoke("update:install"),
3131

src/renderer/components/chat-markdown-renderer.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import remarkGfm from "remark-gfm"
66
import { Copy, Check } from "lucide-react"
77
import { useCodeTheme } from "../lib/hooks/use-code-theme"
88
import { highlightCode } from "../lib/themes/shiki-theme-loader"
9+
import { MermaidBlock } from "./mermaid-block"
910

1011
// Function to strip emojis from text (only common emojis, preserving markdown symbols)
1112
export function stripEmojis(text: string): string {
@@ -246,8 +247,8 @@ const sizeStyles: Record<
246247
}
247248

248249
// Custom code component that uses our theme system
249-
function createCodeComponent(codeTheme: string, size: MarkdownSize, styles: typeof sizeStyles.md) {
250-
return function CodeComponent({ className, children, ...props }: any) {
250+
function createCodeComponent(codeTheme: string, size: MarkdownSize, styles: typeof sizeStyles.md, isStreaming: boolean = false) {
251+
return function CodeComponent({ className, children, node, ...props }: any) {
251252
const match = /language-(\w+)/.exec(className || "")
252253
const language = match ? match[1] : undefined
253254
const codeContent = String(children)
@@ -257,6 +258,13 @@ function createCodeComponent(codeTheme: string, size: MarkdownSize, styles: type
257258
const isCodeBlock = language || (codeContent.includes("\n") && codeContent.length > 100)
258259

259260
if (isCodeBlock) {
261+
// Route mermaid blocks to MermaidBlock component
262+
if (language === "mermaid") {
263+
// Pass isStreaming to MermaidBlock
264+
// When streaming, MermaidBlock shows a placeholder instead of trying to render
265+
return <MermaidBlock code={codeContent.replace(/\n$/, "")} size={size} isStreaming={isStreaming} />
266+
}
267+
260268
return (
261269
<CodeBlock
262270
language={language}
@@ -403,9 +411,9 @@ export const ChatMarkdownRenderer = memo(function ChatMarkdownRenderer({
403411
</td>
404412
),
405413
pre: ({ children }: any) => <>{children}</>,
406-
code: createCodeComponent(codeTheme, size, styles),
414+
code: createCodeComponent(codeTheme, size, styles, isStreaming),
407415
}),
408-
[styles, codeTheme, size],
416+
[styles, codeTheme, size, isStreaming],
409417
)
410418

411419
return (

src/renderer/components/dialogs/agents-settings-dialog.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ export function AgentsSettingsDialog({
536536
key={tab.id}
537537
tab={tab}
538538
isActive={activeTab === tab.id}
539-
onClick={() => setActiveTab(tab.id)}
539+
onClick={() => handleTabClick(tab.id)}
540540
/>
541541
))}
542542
</div>
@@ -551,7 +551,7 @@ export function AgentsSettingsDialog({
551551
key={tab.id}
552552
tab={tab}
553553
isActive={activeTab === tab.id}
554-
onClick={() => setActiveTab(tab.id)}
554+
onClick={() => handleTabClick(tab.id)}
555555
/>
556556
))}
557557
</div>
@@ -568,7 +568,7 @@ export function AgentsSettingsDialog({
568568
key={tab.id}
569569
tab={tab}
570570
isActive={activeTab === tab.id}
571-
onClick={() => setActiveTab(tab.id)}
571+
onClick={() => handleTabClick(tab.id)}
572572
/>
573573
))}
574574
</div>

src/renderer/components/dialogs/settings-tabs/agents-beta-tab.tsx

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import {
1616
SelectValue,
1717
} from "../../ui/select"
1818
import { ExternalLinkIcon } from "../../ui/icons"
19-
import { Copy, Check } from "lucide-react"
19+
import { Copy, Check, RefreshCw } from "lucide-react"
20+
import { Button } from "../../ui/button"
2021
import { cn } from "../../../lib/utils"
2122

2223
// Hook to detect narrow screen
@@ -46,6 +47,40 @@ export function AgentsBetaTab() {
4647
const [autoOffline, setAutoOffline] = useAtom(autoOfflineModeAtom)
4748
const [selectedOllamaModel, setSelectedOllamaModel] = useAtom(selectedOllamaModelAtom)
4849
const [copied, setCopied] = useState(false)
50+
const [updateStatus, setUpdateStatus] = useState<"idle" | "checking" | "available" | "not-available" | "error">("idle")
51+
const [updateVersion, setUpdateVersion] = useState<string | null>(null)
52+
const [currentVersion, setCurrentVersion] = useState<string | null>(null)
53+
54+
// Get current version on mount
55+
useEffect(() => {
56+
window.desktopApi?.getVersion().then(setCurrentVersion)
57+
}, [])
58+
59+
// Check for updates with force flag to bypass cache
60+
const handleCheckForUpdates = async () => {
61+
// Check if we're in dev mode
62+
const isPackaged = await window.desktopApi?.isPackaged?.()
63+
if (!isPackaged) {
64+
setUpdateStatus("error")
65+
console.log("Update check skipped in dev mode")
66+
return
67+
}
68+
69+
setUpdateStatus("checking")
70+
setUpdateVersion(null)
71+
try {
72+
const result = await window.desktopApi?.checkForUpdates(true)
73+
if (result) {
74+
setUpdateStatus("available")
75+
setUpdateVersion(result.version)
76+
} else {
77+
setUpdateStatus("not-available")
78+
}
79+
} catch (error) {
80+
console.error("Failed to check for updates:", error)
81+
setUpdateStatus("error")
82+
}
83+
}
4984

5085
// Get Ollama status
5186
const { data: ollamaStatus } = trpc.ollama.getStatus.useQuery(undefined, {
@@ -247,6 +282,44 @@ export function AgentsBetaTab() {
247282
</div>
248283
</div>
249284
)}
285+
286+
{/* Updates Section */}
287+
<div className="space-y-2">
288+
<div className="pb-2">
289+
<h4 className="text-sm font-medium text-foreground">Updates</h4>
290+
<p className="text-xs text-muted-foreground mt-1">
291+
Check for new versions manually (bypasses CDN cache)
292+
</p>
293+
</div>
294+
295+
<div className="bg-background rounded-lg border border-border overflow-hidden">
296+
<div className="p-4">
297+
<div className="flex items-center justify-between">
298+
<div className="flex flex-col space-y-1">
299+
<span className="text-sm font-medium text-foreground">
300+
{currentVersion ? `Current: v${currentVersion}` : "Version"}
301+
</span>
302+
<span className="text-xs text-muted-foreground">
303+
{updateStatus === "checking" && "Checking for updates..."}
304+
{updateStatus === "available" && `Update available: v${updateVersion}`}
305+
{updateStatus === "not-available" && "You're on the latest version"}
306+
{updateStatus === "error" && "Failed to check (dev mode?)"}
307+
{updateStatus === "idle" && "Click to check for updates"}
308+
</span>
309+
</div>
310+
<Button
311+
variant="outline"
312+
size="sm"
313+
onClick={handleCheckForUpdates}
314+
disabled={updateStatus === "checking"}
315+
>
316+
<RefreshCw className={cn("h-4 w-4 mr-2", updateStatus === "checking" && "animate-spin")} />
317+
{updateStatus === "checking" ? "Checking..." : "Check Now"}
318+
</Button>
319+
</div>
320+
</div>
321+
</div>
322+
</div>
250323
</div>
251324
)
252325
}

0 commit comments

Comments
 (0)