release: 0.1.10 — balance widget actually shows + avatar ring

Balance pill was rendering nothing for admin/root accounts because
mcp-server's §4 returns HEICODE_USER_NOT_FOUND for users that never
came through from-agnet onboarding. Fall back to Heicode NewAPI's
own /api/user/self when that happens; reshape into the same envelope
so the UI is path-agnostic.

New balanceStore (zustand) — single polling loop, BalanceBar +
avatar ring share it. AppShell starts it once auth bootstraps.
BalanceBar now shows a "loading…" placeholder on first fetch so
the widget is visible from frame one.

SidebarUserCard avatar wears an SVG ring whose arc length tracks
remaining/(remaining+used) and color hits the same green→amber→red
thresholds as the bar.

Manager: /desktop-client drops the manifest-notes wall of text, the
old HEICODE_DESKTOP_FILE_* subtitle goes away, and a Mac fallback
entry is always spliced in when the live manifest is Win-only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-14 16:44:13 +08:00
co-authored by Claude Opus 4.7
parent 47a74eaf03
commit ac1aa87312
12 changed files with 260 additions and 54 deletions
+29 -2
View File
@@ -124,6 +124,19 @@ func filenameFromURL(rawURL string) string {
return rawURL
}
// Fallback URLs for platforms the live manifest doesn't currently carry.
// We ship Win and Mac on different cadences (Mac builds depend on physical
// hardware access). When a release goes out Win-only, the manifest only
// names windows-x86_64 — but the Manager download page should still show
// the latest Mac binary that ever shipped to blob, so Mac users aren't
// stranded.
//
// Update these constants whenever a NEW Mac binary is uploaded to blob.
const (
fallbackMacArmVersion = "0.1.5"
fallbackMacArmURL = "https://heicodeblob.blob.core.windows.net/msi/desktop/0.1.5/HeiCode.app.tar.gz"
)
// GetDesktopDownloads returns the latest released desktop client metadata
// (per platform) for authenticated users.
func GetDesktopDownloads(c *gin.Context) {
@@ -136,7 +149,7 @@ func GetDesktopDownloads(c *gin.Context) {
return
}
items := make([]desktopDownloadItem, 0, len(manifest.Platforms))
items := make([]desktopDownloadItem, 0, 3)
add := func(id, label, key string) {
p, ok := manifest.Platforms[key]
if !ok || strings.TrimSpace(p.URL) == "" {
@@ -153,12 +166,26 @@ func GetDesktopDownloads(c *gin.Context) {
add("macos_arm64", "macOS (Apple silicon)", "darwin-aarch64")
add("macos_x64", "macOS (Intel)", "darwin-x86_64")
// If the live manifest doesn't carry a Mac arm64 entry (Win-only
// release), splice in the last known Mac binary so the download
// page never goes blank on Mac users.
if _, hasMac := manifest.Platforms["darwin-aarch64"]; !hasMac {
items = append(items, desktopDownloadItem{
ID: "macos_arm64",
Label: "macOS (Apple silicon, " + fallbackMacArmVersion + ")",
Filename: filenameFromURL(fallbackMacArmURL),
DownloadURL: fallbackMacArmURL,
})
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
// `Notes` deliberately dropped here — the download page is for the
// download links + version number only; users don't want a wall of
// release-note text below it.
"data": desktopDownloadsPayload{
Version: manifest.Version,
Notes: manifest.Notes,
Items: items,
},
})
@@ -41,7 +41,7 @@ export function DesktopClientDownloadPage() {
</SectionPageLayout.Title>
<SectionPageLayout.Description>
{t(
'Installers are served only after you sign in. Place files on the server and set HEICODE_DESKTOP_FILE_* environment variables.',
'Sign in required to access the installers.',
)}
</SectionPageLayout.Description>
<SectionPageLayout.Content>
@@ -58,11 +58,6 @@ export function DesktopClientDownloadPage() {
<p className='text-muted-foreground text-sm'>
{t('Version: {{version}}', { version: payload.version })}
</p>
{payload.notes ? (
<p className='text-muted-foreground whitespace-pre-wrap text-sm'>
{payload.notes}
</p>
) : null}
<ul className='flex flex-col gap-3'>
{payload.items.map((item) => (
<li