Skip to content

Commit 7da405b

Browse files
committed
Release v0.0.36
## What's New ### Features - Desktop notifications toggle in settings - easily enable/disable system notifications - Chat export via context menu - download or copy to clipboard in Markdown, JSON, or Plain Text formats (supports both full workspace and individual sub-chat export) ### Improvements & Fixes - Improved notification throttling with priority system --- Thanks to @cyxzdev for the original implementation of desktop notifications and chat export (#47)
1 parent 9fc00f0 commit 7da405b

17 files changed

Lines changed: 811 additions & 36 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "21st-desktop",
3-
"version": "0.0.35",
3+
"version": "0.0.36",
44
"private": true,
55
"description": "1Code - UI for parallel work with AI agents",
66
"author": {

src/main/index.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,9 +562,13 @@ if (gotTheLock) {
562562
// Track update availability for menu
563563
let updateAvailable = false
564564
let availableVersion: string | null = null
565+
// Track devtools unlock state (hidden feature - 5 clicks on Beta tab)
566+
let devToolsUnlocked = false
565567

566568
// Function to build and set application menu
567569
const buildMenu = () => {
570+
// Show devtools menu item only in dev mode or when unlocked
571+
const showDevTools = !app.isPackaged || devToolsUnlocked
568572
const template: Electron.MenuItemConstructorOptions[] = [
569573
{
570574
label: app.name,
@@ -669,7 +673,8 @@ if (gotTheLock) {
669673
submenu: [
670674
{ role: "reload" },
671675
{ role: "forceReload" },
672-
{ role: "toggleDevTools" },
676+
// Only show DevTools in dev mode or when unlocked via hidden feature
677+
...(showDevTools ? [{ role: "toggleDevTools" as const }] : []),
673678
{ type: "separator" },
674679
{ role: "resetZoom" },
675680
{ role: "zoomIn" },
@@ -710,8 +715,19 @@ if (gotTheLock) {
710715
buildMenu()
711716
}
712717

718+
// Unlock devtools and rebuild menu (called from renderer via IPC)
719+
const unlockDevTools = () => {
720+
if (!devToolsUnlocked) {
721+
devToolsUnlocked = true
722+
console.log("[App] DevTools unlocked via hidden feature")
723+
buildMenu()
724+
}
725+
}
726+
713727
// Expose setUpdateAvailable globally for auto-updater
714728
;(global as any).__setUpdateAvailable = setUpdateAvailable
729+
// Expose unlockDevTools globally for IPC handler
730+
;(global as any).__unlockDevTools = unlockDevTools
715731

716732
// Build initial menu
717733
buildMenu()

src/main/lib/analytics.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
import { PostHog } from "posthog-node"
77
import { app } from "electron"
8+
import * as fs from "fs"
9+
import * as path from "path"
810

911
// PostHog configuration - hardcoded key for opensource users, env var override for internal builds
1012
// This enables analytics for all users including those building from source
@@ -15,6 +17,40 @@ let posthog: PostHog | null = null
1517
let currentUserId: string | null = null
1618
let userOptedOut = false // Synced from renderer
1719

20+
// track first launch using a marker file
21+
const FIRST_LAUNCH_MARKER = ".first_launch_tracked"
22+
23+
function getFirstLaunchMarkerPath(): string {
24+
try {
25+
return path.join(app.getPath("userData"), FIRST_LAUNCH_MARKER)
26+
} catch {
27+
// app not ready yet
28+
return ""
29+
}
30+
}
31+
32+
function isFirstLaunch(): boolean {
33+
const markerPath = getFirstLaunchMarkerPath()
34+
if (!markerPath) return false
35+
36+
try {
37+
return !fs.existsSync(markerPath)
38+
} catch {
39+
return false
40+
}
41+
}
42+
43+
function markFirstLaunchTracked(): void {
44+
const markerPath = getFirstLaunchMarkerPath()
45+
if (!markerPath) return
46+
47+
try {
48+
fs.writeFileSync(markerPath, new Date().toISOString())
49+
} catch {
50+
// ignore errors writing marker
51+
}
52+
}
53+
1854
// Cached user properties for analytics enrichment
1955
let cachedSubscriptionPlan: string | null = null
2056
let cachedConnectionMethod: string | null = null
@@ -183,9 +219,22 @@ export async function shutdown() {
183219
* Track app opened event
184220
*/
185221
export function trackAppOpened() {
222+
const firstLaunch = isFirstLaunch()
223+
186224
capture("desktop_opened", {
187-
first_launch: false, // TODO: track first launch
225+
first_launch: firstLaunch,
188226
})
227+
228+
if (firstLaunch) {
229+
// mark as tracked so subsequent opens don't count as first launch
230+
markFirstLaunchTracked()
231+
232+
// also fire a separate first_launch event for funnel analysis
233+
capture("first_launch", {
234+
app_version: app.getVersion(),
235+
platform: process.platform,
236+
})
237+
}
189238
}
190239

191240
/**

0 commit comments

Comments
 (0)