release: 0.1.9 — balance progress bar + roll up of 0.1.6-0.1.8

Today's 0.1.6/0.1.7/0.1.8 all built but never shipped a manifest
(some were superseded mid-iteration; 0.1.8 had Win binary on blob
but Mac wasn't ready). 0.1.9 ships the full stack as one release:

- BalanceBar under the composer now has a real progress bar (was
  text only). Fill = remaining/(remaining+used); color shifts
  green → amber → red below 30%/10%.
- (from 0.1.8) AskUserQuestion early-return moved below all hooks
  so the render order is stable across input mutations.
- (from 0.1.8) chatStore content_delta throttle is now per-session
  (Map<sessionId, {pending, timer}>); no more cross-tab text bleed.
- (from 0.1.8) endpoints array trimmed to blob-only — SWA URL gone
  so a fallback failure no longer flashes a third-party domain.
- (from 0.1.7) new app icon — already on disk in icons/ + public/.
- (from 0.1.6) Manager desktop_download.go reads blob manifest so
  the /desktop-client page tracks releases without env wrangling.
- Build pipeline: `tauri build --bundles nsis` is the release path
  (skips MSI/WiX, ~2-3 min/build saved). sccache wired into
  ~/.cargo/config.toml; next build is the first with warm cache.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-14 13:18:12 +08:00
co-authored by Claude Opus 4.7
parent dea6d251a4
commit 47a74eaf03
7 changed files with 72 additions and 30 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "heicode-desktop",
"private": true,
"version": "0.1.8",
"version": "0.1.9",
"type": "module",
"scripts": {
"dev": "vite",
+1 -1
View File
@@ -1525,7 +1525,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "heicode-desktop"
version = "0.1.6"
version = "0.1.8"
dependencies = [
"anyhow",
"portable-pty",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "heicode-desktop"
version = "0.1.8"
version = "0.1.9"
edition = "2021"
[lib]
+2 -3
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://raw.githubusercontent.com/nicegui/nicegui/main/nicegui/static/tauri-schema-v2.json",
"productName": "HeiCode",
"version": "0.1.8",
"version": "0.1.9",
"identifier": "com.heicode.desktop",
"build": {
"frontendDist": "../dist",
@@ -33,8 +33,7 @@
"updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEI3Q0FBOUQ2MTgwRkM4NjQKUldSa3lBOFkxcW5LdDhLUFZnNUlEdmllY0ZudjFhb2VXTUpSWmpkUmM1cjdoNDFwRW1MZTN5Yi8K",
"endpoints": [
"https://heicodeblob.blob.core.windows.net/msi/updater/latest.json",
"https://ashy-dune-0e22d7b00.7.azurestaticapps.net/updater/latest.json"
"https://heicodeblob.blob.core.windows.net/msi/updater/latest.json"
],
"windows": {
"installMode": "passive"
@@ -11,14 +11,33 @@ const POLL_MS = 60_000
function formatUsd(quota: number): string {
const usd = quota / QUOTA_PER_USD
// Show 2 decimals up to $100, 4 decimals below to make pennies legible.
return `$${usd >= 100 ? usd.toFixed(2) : usd >= 1 ? usd.toFixed(2) : usd.toFixed(4)}`
}
/**
* Compact balance pill that lives right under the chat composer. Renders
* nothing until we have a positive answer — silent on logged-out / failed
* states so the layout doesn't flash an error strip.
* Bar fill semantics: `remaining / (remaining + used)` — how much of the
* lifetime-funded total is still in the wallet. 100% = brand-new account
* that hasn't spent anything; 0% = quota fully consumed.
*
* Color thresholds:
* ≥ 30% → green (healthy)
* ≥ 10% → amber (getting low)
* < 10% → red (top up soon)
*/
function healthOf(remainingFraction: number): { color: string; bg: string } {
if (remainingFraction >= 0.3) {
return { color: 'var(--color-success)', bg: 'rgba(34,197,94,0.16)' }
}
if (remainingFraction >= 0.1) {
return { color: 'var(--color-warning)', bg: 'rgba(245,158,11,0.18)' }
}
return { color: 'var(--color-error)', bg: 'rgba(239,68,68,0.18)' }
}
/**
* Compact balance pill with progress bar that lives right under the chat
* composer. Renders nothing until we have a positive answer — silent on
* logged-out / failed states so the layout doesn't flash an error strip.
*/
export function BalanceBar() {
const [balance, setBalance] = useState<HeicodeBalance | null>(null)
@@ -41,27 +60,49 @@ export function BalanceBar() {
if (!balance) return null
const total = balance.quota + balance.usedQuota
const remainingFraction = total > 0 ? balance.quota / total : 1
const fillPercent = Math.max(0, Math.min(1, remainingFraction)) * 100
const health = healthOf(remainingFraction)
return (
<div className="mx-auto mb-2 mt-1 flex w-fit max-w-full items-center gap-2 rounded-full border border-[var(--color-border)]/40 bg-[var(--color-surface-container-low)]/60 px-3 py-1 text-[11px] text-[var(--color-text-secondary)]">
<span className="material-symbols-outlined text-[14px] text-[var(--color-brand)]">
account_balance_wallet
</span>
<span className="font-medium text-[var(--color-text-primary)]">
{formatUsd(balance.quota)}
</span>
<span className="text-[var(--color-text-tertiary)]">{t('balance.remaining')}</span>
<span className="h-3 w-px bg-[var(--color-border)]/70" aria-hidden />
<span className="tabular-nums text-[var(--color-text-tertiary)]">
{t('balance.usedPrefix')}{formatUsd(balance.usedQuota)}
</span>
{typeof balance.requestCount === 'number' && balance.requestCount > 0 && (
<>
<span className="h-3 w-px bg-[var(--color-border)]/70" aria-hidden />
<span className="tabular-nums text-[var(--color-text-tertiary)]">
{balance.requestCount} {t('balance.requestsSuffix')}
</span>
</>
)}
<div
className="mx-auto mb-2 mt-1 w-fit min-w-[280px] max-w-full rounded-[14px] border border-[var(--color-border)]/40 bg-[var(--color-surface-container-low)]/70 px-3 py-1.5 backdrop-blur-sm"
title={t('balance.tooltip')}
>
<div className="flex items-center gap-2 text-[11px] text-[var(--color-text-secondary)]">
<span
className="material-symbols-outlined text-[14px]"
style={{ color: health.color }}
>
account_balance_wallet
</span>
<span className="font-semibold text-[var(--color-text-primary)] tabular-nums">
{formatUsd(balance.quota)}
</span>
<span className="text-[var(--color-text-tertiary)]">{t('balance.remaining')}</span>
<span className="ml-auto tabular-nums text-[var(--color-text-tertiary)]">
{t('balance.usedPrefix')}{formatUsd(balance.usedQuota)}
{typeof balance.requestCount === 'number' && balance.requestCount > 0 && (
<> · {balance.requestCount} {t('balance.requestsSuffix')}</>
)}
</span>
</div>
{/* Progress bar — fills with remaining / total. The full track is the
lifetime-funded amount; the filled portion is what's still in the
wallet. Color tracks the same green/amber/red health bands. */}
<div
className="mt-1.5 h-1.5 w-full overflow-hidden rounded-full"
style={{ backgroundColor: health.bg }}
>
<div
className="h-full rounded-full transition-[width] duration-500 ease-out"
style={{
width: `${fillPercent}%`,
backgroundColor: health.color,
}}
/>
</div>
</div>
)
}
+1
View File
@@ -41,6 +41,7 @@ export const en = {
'balance.remaining': 'left',
'balance.usedPrefix': 'used ',
'balance.requestsSuffix': 'reqs',
'balance.tooltip': 'Balance from Heicode platform · bar shows remaining / total',
'sidebar.collapse': 'Collapse sidebar',
'sidebar.expand': 'Expand sidebar',
'sidebar.logout': 'Log out',
+1
View File
@@ -43,6 +43,7 @@ export const zh: Record<TranslationKey, string> = {
'balance.remaining': '剩余',
'balance.usedPrefix': '已用 ',
'balance.requestsSuffix': '次',
'balance.tooltip': '余额来自 Heicode 平台 · 进度条为剩余 / 总额',
'sidebar.collapse': '折叠侧边栏',
'sidebar.expand': '展开侧边栏',
'sidebar.logout': '退出登录',