Skip to content

Commit 17818ea

Browse files
authored
feat: desktop app with auto-update, telemetry, release pipeline, and site showcase (#80)
## Related Issue No issue — the desktop app work was planned directly on this branch; the problem is explained below. ## Problem Pythinker had no desktop distribution. The Electron shell on this branch needed the remaining production pieces: a 1:1 port of the reference desktop design (sidebar, collapse choreography, typography), a way to ship and update installed apps, operational telemetry, and a public landing spot on the site. Separately, resolving questions/approvals wrote events to the session journal that the schema could not read back, which bricked session loading with error 50001. ## What changed - **Protocol fix:** register the seven question/approval lifecycle events in the event schema and drop the unchecked casts at the publish sites; journals containing them now validate (regression-tested against real journal shapes). - **Desktop web UI:** reference sidebar (workspaces header, search/filter, inset session rows, settings footer), 90px collapsed rail hosting the macOS traffic lights, phased collapse/expand animation, empty-state halo, and the reference typography (system UI stack + SF Mono-led code stack) gated to `data-desktop-platform` so browser builds are unaffected. - **Auto-update:** electron-updater with a GitHub Releases feed, on by default, persisted setting, sandboxed preload bridge, and a Settings "Desktop app" section with the toggle, manual check, and restart-to-update. - **Telemetry:** desktop lifecycle and update-lifecycle events through the existing telemetry package; crash handlers and quit-time flush; no content, paths, or titles in properties. - **Release pipeline:** `desktop-release.yml` publishes DMG + ZIP + `latest-mac.yml` on `desktop-v*` tags (the updater feed); signing/notarization secrets optional. - **Site:** "Pythinker Desktop" showcase section with a webm loop, reduced-motion fallback, and a download CTA. - Workspace hygiene: desktop dev-dependency versions aligned with the root pins; flake `pnpmDeps` hash refreshed and validated with a clean `nix build .#pythinker-code`. Verified by: package builds/typechecks, desktop vitest (47), web tests (261), protocol (508) and server (502) suites, site build, YAML parse of the workflow, and the pre-push full gate. ## Checklist - [x] I have read the [CONTRIBUTING](https://github.com/PyModel/pythinker-code/blob/main/CONTRIBUTING.md) document. - [x] I have linked a related issue, or explained the problem above. - [x] I have added tests that prove my feature works. - [x] Ran `gen-changesets` skill, or this PR needs no changeset. - [ ] Ran `gen-docs` skill, or this PR needs no doc update. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a desktop app with macOS packaging, auto-updates, splash screen, system tray controls, and resilient session lifecycle management. * Added desktop update controls for automatic checks, manual checks, and restart-to-install. * Added sidebar search, workspace controls, collapsible navigation, refreshed colors, typography, and animated mascot states. * Added desktop app showcase content and download guidance to the website. * Added support for Node.js 20 and newer. * Added question, approval, and prompt lifecycle events. * **Bug Fixes** * Improved handling of invalid sessions and journal events. * Prevented duplicated streamed messages and restored paragraph breaks. * Clarified update notices with warning styling. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 26f3d18 commit 17818ea

97 files changed

Lines changed: 5682 additions & 218 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/desktop-design-port.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": minor
3+
---
4+
5+
Match the desktop app's sidebar, collapse animation, empty-state visuals, and typography to the desktop design.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": minor
3+
---
4+
5+
Add a Desktop app section to web settings with automatic updates on by default, a manual update check, and a restart-to-update action.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
Fix sessions failing to load with an invalid event journal error after questions or approvals were resolved.

.changeset/node-20-support.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
Run on Node 20 and newer by only re-executing for FFI support on Node 26.4+.

.changeset/sdk-event-union.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code-sdk": major
3+
---
4+
5+
Add question, approval, and prompt lifecycle events to the SDK session event types.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
Skip invalid sessions during listing instead of failing the whole list.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
Highlight the update notice in the terminal status bar with the warning color.

.changeset/web-brand-refresh.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": minor
3+
---
4+
5+
Refresh the web UI accent color and show the animated mascot on workflow cards, the activity spinner, and the empty state.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
Fix duplicated streamed transcript copies and lost paragraph breaks in the web UI.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
name: Desktop Release
2+
3+
on:
4+
push:
5+
tags: ['desktop-v*']
6+
workflow_dispatch: {}
7+
8+
permissions:
9+
contents: write
10+
11+
concurrency:
12+
group: desktop-release-${{ github.ref }}
13+
cancel-in-progress: false
14+
15+
jobs:
16+
mac:
17+
runs-on: macos-15
18+
steps:
19+
- name: Checkout
20+
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # pinned from v4
21+
with:
22+
fetch-depth: 0
23+
persist-credentials: true
24+
25+
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # pinned from v6
26+
27+
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # pinned from v6
28+
with:
29+
node-version-file: .nvmrc
30+
cache: pnpm
31+
32+
- run: pnpm install --frozen-lockfile
33+
34+
- name: Stamp desktop version for tag builds
35+
if: startsWith(github.ref, 'refs/tags/desktop-v')
36+
env:
37+
TAG_NAME: ${{ github.ref_name }}
38+
run: |
39+
export DESKTOP_VERSION="${TAG_NAME#desktop-v}"
40+
node -e 'const fs = require("node:fs"); const path = "apps/desktop/package.json"; const packageJson = JSON.parse(fs.readFileSync(path, "utf8")); packageJson.version = process.env.DESKTOP_VERSION; fs.writeFileSync(path, `${JSON.stringify(packageJson, null, 2)}\n`);'
41+
42+
- name: Build workspace
43+
run: pnpm --workspace-root run build
44+
45+
- name: Stage desktop runtime
46+
working-directory: apps/desktop
47+
run: node --import tsx scripts/stage-runtime.ts
48+
49+
# On a desktop-v* tag, --publish always creates or updates the draft-or-release
50+
# for that tag; contents: write makes GITHUB_TOKEN sufficient.
51+
# Without Developer ID signing secrets, electron-builder publishes an
52+
# ad-hoc/self-signed app. macOS auto-update will not accept unsigned updates,
53+
# but this still proves packaging and the feed shape.
54+
- name: Package and publish desktop release
55+
working-directory: apps/desktop
56+
run: pnpm exec electron-builder --mac dmg zip --publish always
57+
env:
58+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
59+
CSC_LINK: ${{ secrets.MAC_CSC_LINK }}
60+
CSC_KEY_PASSWORD: ${{ secrets.MAC_CSC_KEY_PASSWORD }}
61+
APPLE_ID: ${{ secrets.APPLE_ID }}
62+
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
63+
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
64+
65+
- name: Upload macOS artifacts for manual runs
66+
if: github.event_name == 'workflow_dispatch'
67+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # pinned from v7
68+
with:
69+
name: desktop-macos
70+
path: |
71+
apps/desktop/dist/*.dmg
72+
apps/desktop/dist/*.zip
73+
apps/desktop/dist/latest-mac.yml
74+
if-no-files-found: error

0 commit comments

Comments
 (0)