Polish for shipping: robust auto-detect, empty state, real icons, end-user README
- cli_usage::default_command now enumerates WSL distros and probes each for claude before falling back; no more hardcoded -d Ubuntu. - New autodetect_claude_command Tauri command + IPC binding so the UI knows whether claude is reachable. - App.svelte: clear 'Claude Code not found' empty state with install link. - Real icons: scripts/make-icon.py generates a 1024x1024 source.png; runtime produces 32/128/256 PNGs and a multi-resolution .ico. README in icons/ explains how to regen. - README rewritten for friends: install / requirements / troubleshooting on top; build-from-source moved to bottom.
This commit is contained in:
parent
0a960dae2d
commit
9be856d37c
14 changed files with 321 additions and 128 deletions
|
|
@ -7,6 +7,8 @@
|
|||
getCliUsage,
|
||||
onCliUsageUpdated,
|
||||
refreshCliUsage,
|
||||
autodetectClaudeCommand,
|
||||
getSettings,
|
||||
} from "../ipc";
|
||||
import TitleBar from "./TitleBar.svelte";
|
||||
import BlockRing from "./BlockRing.svelte";
|
||||
|
|
@ -18,6 +20,8 @@
|
|||
let cliUsage = $state<CliUsage | null>(null);
|
||||
let cliRefreshing = $state(false);
|
||||
let showSettings = $state(false);
|
||||
/** True when Claude Code can't be found anywhere on this machine. */
|
||||
let claudeMissing = $state(false);
|
||||
|
||||
let unlisten1: (() => void) | null = null;
|
||||
let unlisten2: (() => void) | null = null;
|
||||
|
|
@ -40,9 +44,18 @@
|
|||
console.error("listen failed", e);
|
||||
}
|
||||
|
||||
// If we have nothing yet, fire a one-shot refresh so the widget is
|
||||
// useful right away rather than waiting for the 5-min loop.
|
||||
if (!cliUsage) {
|
||||
// Probe whether claude is reachable at all.
|
||||
try {
|
||||
const settings = await getSettings();
|
||||
const hasOverride = !!(settings.claude_command && settings.claude_command.trim());
|
||||
const auto = await autodetectClaudeCommand();
|
||||
claudeMissing = !hasOverride && !auto;
|
||||
} catch (e) {
|
||||
console.warn("autodetect probe failed", e);
|
||||
}
|
||||
|
||||
// Trigger an initial refresh if we have nothing AND claude is reachable.
|
||||
if (!cliUsage && !claudeMissing) {
|
||||
void triggerRefresh();
|
||||
}
|
||||
});
|
||||
|
|
@ -71,19 +84,38 @@
|
|||
refreshing={cliRefreshing}
|
||||
/>
|
||||
|
||||
<BlockRing bar={cliUsage?.session ?? null} />
|
||||
{#if claudeMissing}
|
||||
<div class="empty-state">
|
||||
<div class="title">Claude Code not found</div>
|
||||
<p>
|
||||
This widget reads your subscription usage by running
|
||||
<code>claude /usage</code>. Install Claude Code first, then sign in.
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://docs.claude.com/en/docs/claude-code" target="_blank" rel="noopener">
|
||||
Install Claude Code →
|
||||
</a>
|
||||
</p>
|
||||
<p class="hint">
|
||||
Already installed? Open Settings and set <em>claude command</em>
|
||||
(e.g. <code>wsl.exe -d Ubuntu bash -lc claude</code>).
|
||||
</p>
|
||||
</div>
|
||||
{:else}
|
||||
<BlockRing bar={cliUsage?.session ?? null} />
|
||||
|
||||
<ModelStack
|
||||
breakdown={snap?.block?.by_family ?? snap?.weekly.by_family ?? { opus: 0, sonnet: 0, haiku: 0, other: 0 }}
|
||||
/>
|
||||
<ModelStack
|
||||
breakdown={snap?.block?.by_family ?? snap?.weekly.by_family ?? { opus: 0, sonnet: 0, haiku: 0, other: 0 }}
|
||||
/>
|
||||
|
||||
<WeeklyBar
|
||||
weekAll={cliUsage?.week_all ?? null}
|
||||
weekSonnet={cliUsage?.week_sonnet ?? null}
|
||||
/>
|
||||
<WeeklyBar
|
||||
weekAll={cliUsage?.week_all ?? null}
|
||||
weekSonnet={cliUsage?.week_sonnet ?? null}
|
||||
/>
|
||||
|
||||
{#if cliRefreshing && !cliUsage}
|
||||
<div class="loading">Reading /usage…</div>
|
||||
{#if cliRefreshing && !cliUsage}
|
||||
<div class="loading">Reading /usage…</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if showSettings}
|
||||
|
|
@ -98,4 +130,30 @@
|
|||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
.empty-state {
|
||||
flex: 1 1 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: 12px var(--pad);
|
||||
gap: 6px;
|
||||
color: var(--fg);
|
||||
font-size: 12px;
|
||||
}
|
||||
.empty-state .title {
|
||||
color: var(--fg);
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.empty-state p { margin: 0; line-height: 1.4; }
|
||||
.empty-state .hint { color: var(--fg-dim); font-size: 11px; }
|
||||
.empty-state a { color: var(--accent); text-decoration: none; }
|
||||
.empty-state a:hover { text-decoration: underline; }
|
||||
.empty-state code {
|
||||
background: var(--bg-card);
|
||||
padding: 1px 4px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ export const quitApp = (): Promise<void> => invoke("quit_app");
|
|||
export const detectPlanTier = (): Promise<TierInfo> => invoke("detect_plan_tier");
|
||||
export const getCliUsage = (): Promise<CliUsage | null> => invoke("get_cli_usage");
|
||||
export const refreshCliUsage = (): Promise<CliUsage> => invoke("refresh_cli_usage");
|
||||
export const autodetectClaudeCommand = (): Promise<string[] | null> =>
|
||||
invoke("autodetect_claude_command");
|
||||
|
||||
export const onUsageUpdated = (
|
||||
cb: (snap: UsageSnapshot) => void,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue