Skip to content

Commit feb73da

Browse files
committed
Dashboard v0.2.2: app menu with Check for Updates, tray cleanup, updater endpoint fix, promote candidates, tray icon fix, arm64 naming
1 parent 0a77695 commit feb73da

File tree

6 files changed

+164
-52
lines changed

6 files changed

+164
-52
lines changed

.github/workflows/dashboard-release.yml

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,19 @@ jobs:
2222
- host: macos-latest
2323
target: aarch64-apple-darwin
2424
label: macOS ARM64
25+
arch: arm64
2526
- host: macos-latest
2627
target: x86_64-apple-darwin
2728
label: macOS Intel
29+
arch: x64
2830
- host: ubuntu-22.04
2931
target: x86_64-unknown-linux-gnu
3032
label: Linux x64
33+
arch: x64
3134
- host: windows-latest
3235
target: x86_64-pc-windows-msvc
3336
label: Windows x64
37+
arch: x64
3438

3539
runs-on: ${{ matrix.settings.host }}
3640
steps:
@@ -104,7 +108,7 @@ jobs:
104108
tagName: ${{ github.ref_name }}
105109
releaseName: "Dashboard ${{ github.ref_name }}"
106110
releaseDraft: true
107-
assetNamePattern: magic-context-dashboard-[platform]-[arch][ext]
111+
assetNamePattern: magic-context-dashboard-[platform]-${{ matrix.settings.arch }}[ext]
108112
env:
109113
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
110114
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
@@ -116,3 +120,34 @@ jobs:
116120
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
117121
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
118122
APPLE_API_KEY_PATH: ${{ runner.temp }}/apple-api-key.p8
123+
124+
# Deploy latest.json to gh-pages for the updater endpoint
125+
deploy-updater:
126+
name: Deploy updater manifest
127+
needs: build
128+
runs-on: ubuntu-latest
129+
steps:
130+
- uses: actions/checkout@v4
131+
132+
- name: Wait for release assets
133+
run: sleep 30
134+
135+
- name: Download latest.json from release
136+
env:
137+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
138+
run: |
139+
TAG="${{ github.ref_name }}"
140+
# Try draft release first, then published
141+
gh release download "$TAG" --pattern "latest.json" --output latest.json --clobber 2>/dev/null || \
142+
gh release download "$TAG" --pattern "latest.json" --output latest.json --clobber
143+
cat latest.json
144+
145+
- name: Deploy to gh-pages
146+
uses: peaceiris/actions-gh-pages@v4
147+
with:
148+
github_token: ${{ secrets.GITHUB_TOKEN }}
149+
publish_dir: .
150+
publish_branch: gh-pages
151+
keep_files: false
152+
force_orphan: true
153+
include_files: latest.json

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
<p align="center">
99
<a href="https://www.npmjs.com/package/@cortexkit/opencode-magic-context"><img src="https://img.shields.io/npm/v/@cortexkit/opencode-magic-context?color=blue&style=flat-square" alt="npm"></a>
10-
<a href="https://www.npmjs.com/package/@cortexkit/opencode-magic-context"><img src="https://img.shields.io/npm/dm/@cortexkit/opencode-magic-context?color=brightgreen&style=flat-square&label=downloads" alt="downloads"></a>
1110
<a href="https://github.com/cortexkit/opencode-magic-context/stargazers"><img src="https://img.shields.io/github/stars/cortexkit/opencode-magic-context?style=flat-square&color=yellow" alt="stars"></a>
1211
<a href="https://github.com/cortexkit/opencode-magic-context/commits"><img src="https://img.shields.io/github/last-commit/cortexkit/opencode-magic-context?style=flat-square&color=green" alt="last commit"></a>
1312
<a href="https://github.com/cortexkit/opencode-magic-context/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="MIT License"></a>

packages/dashboard/src-tauri/src/main.rs

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
22

3-
use magic_context_dashboard_lib::{commands, db, AppState};
3+
use magic_context_dashboard_lib::{commands, AppState};
44
use tauri::{
5-
menu::MenuBuilder,
5+
menu::{MenuBuilder, MenuItemBuilder, SubmenuBuilder},
66
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
7-
Manager,
7+
Emitter, Manager,
88
};
99

1010
const OPEN_DASHBOARD_MENU_ID: &str = "open_dashboard";
11-
const TRIGGER_DREAMER_MENU_ID: &str = "trigger_dreamer";
11+
const CHECK_UPDATES_MENU_ID: &str = "check_updates";
1212
const QUIT_MENU_ID: &str = "quit";
1313

1414
fn show_main_window(app: &tauri::AppHandle) {
@@ -19,24 +19,6 @@ fn show_main_window(app: &tauri::AppHandle) {
1919
}
2020
}
2121

22-
fn trigger_dreamer(app: &tauri::AppHandle) -> Result<(), String> {
23-
let state = app.state::<AppState>();
24-
let path = state.get_db_path()?;
25-
let conn = db::open_readwrite(&path).map_err(|e| e.to_string())?;
26-
// Resolve a real project identity from the memories table instead of using "."
27-
// Include both active and permanent memories — a project with only permanent memories is still valid
28-
let project_path: String = conn
29-
.query_row(
30-
"SELECT project_path FROM memories WHERE status IN ('active', 'permanent') GROUP BY project_path ORDER BY MAX(updated_at) DESC LIMIT 1",
31-
[],
32-
|row| row.get(0),
33-
)
34-
.map_err(|_| "No project found — write a memory first to enable dreamer from tray".to_string())?;
35-
db::enqueue_dream(&conn, &project_path, "Manual trigger from dashboard tray")
36-
.map(|_| ())
37-
.map_err(|e| e.to_string())
38-
}
39-
4022
fn main() {
4123
tauri::Builder::default()
4224
// shell plugin removed — no shell:default capability needed
@@ -90,10 +72,45 @@ fn main() {
9072
commands::get_db_health,
9173
])
9274
.setup(|app| {
75+
// ── macOS app menu bar ──
76+
let app_handle_for_menu = app.app_handle().clone();
77+
let check_updates_item = MenuItemBuilder::with_id("app_check_updates", "Check for Updates...")
78+
.build(app)?;
79+
let app_submenu = SubmenuBuilder::new(app, "Magic Context")
80+
.about(None)
81+
.item(&check_updates_item)
82+
.separator()
83+
.hide()
84+
.hide_others()
85+
.show_all()
86+
.separator()
87+
.quit()
88+
.build()?;
89+
let edit_submenu = SubmenuBuilder::new(app, "Edit")
90+
.undo()
91+
.redo()
92+
.separator()
93+
.cut()
94+
.copy()
95+
.paste()
96+
.select_all()
97+
.build()?;
98+
let app_menu = MenuBuilder::new(app)
99+
.item(&app_submenu)
100+
.item(&edit_submenu)
101+
.build()?;
102+
app.set_menu(app_menu)?;
103+
app.on_menu_event(move |_app, event| {
104+
if event.id().as_ref() == "app_check_updates" {
105+
let _ = app_handle_for_menu.emit("check-for-updates", ());
106+
}
107+
});
108+
109+
// ── System tray ──
93110
let tray_app_handle = app.app_handle().clone();
94111
let tray_menu = MenuBuilder::new(app)
95112
.text(OPEN_DASHBOARD_MENU_ID, "Open Dashboard")
96-
.text(TRIGGER_DREAMER_MENU_ID, "Trigger Dreamer")
113+
.text(CHECK_UPDATES_MENU_ID, "Check for Updates...")
97114
.separator()
98115
.text(QUIT_MENU_ID, "Quit")
99116
.build()?;
@@ -103,10 +120,9 @@ fn main() {
103120
.show_menu_on_left_click(false)
104121
.on_menu_event(|app, event| match event.id().as_ref() {
105122
OPEN_DASHBOARD_MENU_ID => show_main_window(app),
106-
TRIGGER_DREAMER_MENU_ID => {
107-
if let Err(error) = trigger_dreamer(app) {
108-
eprintln!("failed to trigger dreamer from tray: {error}");
109-
}
123+
CHECK_UPDATES_MENU_ID => {
124+
show_main_window(app);
125+
let _ = app.emit("check-for-updates", ());
110126
}
111127
QUIT_MENU_ID => app.exit(0),
112128
_ => {}

packages/dashboard/src-tauri/tauri.conf.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://raw.githubusercontent.com/nicehash/tauri/dev/packages/tauri-utils/schema.json",
33
"productName": "Magic Context Dashboard",
4-
"version": "0.1.0",
4+
"version": "0.2.2",
55
"identifier": "com.cortexkit.magic-context-dashboard",
66
"build": {
77
"beforeDevCommand": "bun run dev:frontend",
@@ -47,7 +47,7 @@
4747
"updater": {
4848
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDY5MUNBMDIxNjUxMEM2MkYKUldRdnhoQmxJYUFjYVU0SlNNbzRWMGJ3MndKOENIVDR0N0N6b0Jyd1l6TUNFTlpZTzV3RXlRYkEK",
4949
"endpoints": [
50-
"https://github.com/cortexkit/opencode-magic-context/releases/latest/download/latest.json"
50+
"https://cortexkit.github.io/opencode-magic-context/latest.json"
5151
]
5252
}
5353
}

packages/dashboard/src/App.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { createSignal, createResource, Show, onMount, onCleanup, ErrorBoundary } from "solid-js";
22
import type { NavSection, DbHealth } from "./lib/types";
33
import { getDbHealth, getAvailableModels } from "./lib/api";
4-
import { checkForUpdate, installAndRelaunch } from "./lib/updater";
4+
import { checkForUpdate, installAndRelaunch, runUpdater } from "./lib/updater";
5+
import { listen } from "@tauri-apps/api/event";
56
import Sidebar from "./components/Layout/Sidebar";
67
import StatusBar from "./components/Layout/StatusBar";
78
import MemoryBrowser from "./components/MemoryBrowser/MemoryBrowser";
@@ -53,6 +54,15 @@ export default function App() {
5354
});
5455
onCleanup(() => { if (updateInterval) clearInterval(updateInterval); });
5556

57+
// Listen for "Check for Updates" tray menu event
58+
let unlistenUpdate: (() => void) | undefined;
59+
onMount(() => {
60+
listen("check-for-updates", () => {
61+
runUpdater({ alertOnFail: true });
62+
}).then((unlisten) => { unlistenUpdate = unlisten; });
63+
});
64+
onCleanup(() => { unlistenUpdate?.(); });
65+
5666
const handleInstall = async () => {
5767
setUpdateInstalling(true);
5868
await installAndRelaunch();
Lines changed: 72 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,87 @@
11
import { check, type Update } from "@tauri-apps/plugin-updater";
22
import { relaunch } from "@tauri-apps/plugin-process";
3+
import { ask, message } from "@tauri-apps/plugin-dialog";
34

4-
let pendingUpdate: Update | null = null;
5+
let cachedUpdate: Update | null = null;
56

67
/**
7-
* Check for updates, download if available.
8-
* Returns the new version string or null.
8+
* Check if an update is available. Returns the version string if found, null otherwise.
9+
* Used by the background polling in App.tsx for the toast notification.
910
*/
1011
export async function checkForUpdate(): Promise<string | null> {
11-
try {
12-
const update = await check();
13-
if (!update) return null;
14-
15-
// Download in background
16-
await update.download();
17-
pendingUpdate = update;
18-
return update.version;
19-
} catch {
12+
try {
13+
const update = await check();
14+
if (update) {
15+
cachedUpdate = update;
16+
return update.version;
17+
}
18+
} catch {
19+
// Silent failure for background checks
20+
}
2021
return null;
21-
}
2222
}
2323

2424
/**
25-
* Install a previously downloaded update and relaunch.
25+
* Download and install the cached update, then relaunch.
26+
* Called when user clicks "Install & Restart" in the toast.
2627
*/
2728
export async function installAndRelaunch(): Promise<void> {
28-
if (!pendingUpdate) return;
29-
try {
30-
await pendingUpdate.install();
29+
if (!cachedUpdate) return;
30+
try {
31+
await cachedUpdate.download();
32+
await cachedUpdate.install();
33+
await relaunch();
34+
} catch {
35+
// If install fails, user stays on current version
36+
}
37+
}
38+
39+
/**
40+
* Run a full interactive update check with dialogs.
41+
* Called from "Check for Updates..." tray menu item.
42+
* Following OpenCode's pattern: check → download → ask → install → relaunch.
43+
*/
44+
export async function runUpdater({ alertOnFail }: { alertOnFail: boolean }) {
45+
let update;
46+
try {
47+
update = await check();
48+
} catch {
49+
if (alertOnFail) {
50+
await message("Failed to check for updates", { title: "Update Check Failed" });
51+
}
52+
return;
53+
}
54+
55+
if (!update) {
56+
if (alertOnFail) {
57+
await message("You are already using the latest version of Magic Context Dashboard", {
58+
title: "No Update Available",
59+
});
60+
}
61+
return;
62+
}
63+
64+
try {
65+
await update.download();
66+
} catch {
67+
if (alertOnFail) {
68+
await message("Failed to download update", { title: "Update Failed" });
69+
}
70+
return;
71+
}
72+
73+
const shouldUpdate = await ask(
74+
`Magic Context Dashboard ${update.version} has been downloaded. Would you like to install and restart?`,
75+
{ title: "Update Downloaded" },
76+
);
77+
if (!shouldUpdate) return;
78+
79+
try {
80+
await update.install();
81+
} catch {
82+
await message("Failed to install update", { title: "Update Failed" });
83+
return;
84+
}
85+
3186
await relaunch();
32-
} catch (e) {
33-
console.error("Failed to install update:", e);
34-
}
3587
}

0 commit comments

Comments
 (0)