generated from nhcarrigan/template
feat: stuffy feature bundle (#159)
## Summary This PR bundles a collection of new features and quality-of-life improvements identified during a Claude CLI 2.1.50 audit. - **Tab status indicator** — Tab stays yellow until the greeting is responded to, then turns green. Fixed disconnect not resetting to grey. Closes #157 - **Auth status display** — New "Account" section in settings sidebar showing login status, email, org, API key source, and Hikari override indicator. Includes login/logout buttons. Closes #153 - **CLI version badge** — New "Supported" badge showing the highest audited CLI version, colour-coded green/amber/red based on installed vs supported version. Closes #154 (bump to 2.1.50) - **Rate limit events** — `rate_limit_event` messages from the stream are now parsed and shown as amber `[rate-limit]` lines in the terminal instead of being silently dropped. Closes #155 - **"Prompt is too long" handling** — Detects this error in assistant messages and shows a ⚡ Compact Conversation button to send `/compact` directly. Closes #158 - **`last_assistant_message` in Agent Monitor** — Extracts the agent's final output from the `ToolResult` content block in the JSON stream and displays it as a snippet on completed agent cards. Closes #156 - **`--worktree` flag** — New "Worktree isolation" toggle in session settings passes `--worktree` to Claude Code. Hook events (`WorktreeCreate`/`WorktreeRemove`) are displayed as green `[worktree]` lines. Closes #152, Closes #150 - **ConfigChange hook events** — `[ConfigChange Hook]` stderr events are now displayed as cyan `[config]` lines instead of errors. Closes #151 - **`CLAUDE_CODE_DISABLE_1M_CONTEXT` toggle** — New "Disable 1M context" setting in session configuration injects this env var into the Claude process. Closes #154 ## Test plan - [ ] Tab status indicator: start a new session and verify the tab stays yellow until Claude responds to the greeting, then turns green - [ ] Auth status: open settings and verify the Account section shows correct login info - [ ] CLI version badge: verify the "Supported 2.1.50" badge shows green when CLI matches - [ ] Rate limit events: unit tests cover parsing; amber `[rate-limit]` lines display correctly - [ ] Compact button: unit tests cover detection; button renders correctly in terminal - [ ] Agent Monitor: use the Task tool and verify completed agent cards show a message snippet - [ ] Worktree: enable toggle, start session, verify `--worktree` flag appears in process args - [ ] ConfigChange: hook events display as `[config]` lines rather than errors - [ ] Disable 1M context: enable toggle, start session, verify `CLAUDE_CODE_DISABLE_1M_CONTEXT=1` in `/proc/<pid>/environ` ✨ This PR was created with help from Hikari~ 🌸 Reviewed-on: #159 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
This commit was merged in pull request #159.
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
} from "$lib/stores/config";
|
||||
import { claudeStore } from "$lib/stores/claude";
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import CostSummary from "./CostSummary.svelte";
|
||||
|
||||
let config: HikariConfig = $state({
|
||||
@@ -52,10 +53,26 @@
|
||||
budget_warning_threshold: 0.8,
|
||||
discord_rpc_enabled: true,
|
||||
show_thinking_blocks: true,
|
||||
use_worktree: false,
|
||||
disable_1m_context: false,
|
||||
});
|
||||
|
||||
let showCustomThemeEditor = $state(false);
|
||||
|
||||
interface AuthStatus {
|
||||
is_logged_in: boolean;
|
||||
email: string | null;
|
||||
org_name: string | null;
|
||||
api_key_source: string | null;
|
||||
api_provider: string | null;
|
||||
subscription_type: string | null;
|
||||
}
|
||||
|
||||
let authStatus: AuthStatus | null = $state(null);
|
||||
let authLoading = $state(false);
|
||||
let authActionLoading = $state(false);
|
||||
let authError: string | null = $state(null);
|
||||
|
||||
let isOpen = $state(false);
|
||||
let isSaving = $state(false);
|
||||
let saveError: string | null = $state(null);
|
||||
@@ -69,6 +86,9 @@
|
||||
|
||||
configStore.isSidebarOpen.subscribe((open) => {
|
||||
isOpen = open;
|
||||
if (open && authStatus === null) {
|
||||
void refreshAuthStatus();
|
||||
}
|
||||
});
|
||||
|
||||
configStore.saveError.subscribe((error) => {
|
||||
@@ -111,6 +131,44 @@
|
||||
"Task",
|
||||
];
|
||||
|
||||
async function refreshAuthStatus() {
|
||||
authLoading = true;
|
||||
authError = null;
|
||||
try {
|
||||
authStatus = await invoke<AuthStatus>("get_auth_status");
|
||||
} catch (e) {
|
||||
authError = String(e);
|
||||
} finally {
|
||||
authLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAuthLogin() {
|
||||
authActionLoading = true;
|
||||
authError = null;
|
||||
try {
|
||||
await invoke<string>("auth_login");
|
||||
await refreshAuthStatus();
|
||||
} catch (e) {
|
||||
authError = String(e);
|
||||
} finally {
|
||||
authActionLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAuthLogout() {
|
||||
authActionLoading = true;
|
||||
authError = null;
|
||||
try {
|
||||
await invoke<string>("auth_logout");
|
||||
await refreshAuthStatus();
|
||||
} catch (e) {
|
||||
authError = String(e);
|
||||
} finally {
|
||||
authActionLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
isSaving = true;
|
||||
saveError = null;
|
||||
@@ -228,6 +286,101 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Account Section -->
|
||||
<section class="mb-6">
|
||||
<h3 class="text-sm font-medium text-[var(--accent-primary)] uppercase tracking-wider mb-3">
|
||||
Account
|
||||
</h3>
|
||||
|
||||
{#if authLoading}
|
||||
<div class="text-sm text-[var(--text-secondary)] py-2">Checking auth status...</div>
|
||||
{:else if authStatus}
|
||||
<div class="flex items-center gap-2 mb-3">
|
||||
<span
|
||||
class="inline-block w-2.5 h-2.5 rounded-full flex-shrink-0 {authStatus.is_logged_in
|
||||
? 'bg-green-500'
|
||||
: 'bg-red-500'}"
|
||||
></span>
|
||||
<span class="text-sm font-medium text-[var(--text-primary)]">
|
||||
{authStatus.is_logged_in ? "Logged in" : "Not logged in"}
|
||||
</span>
|
||||
</div>
|
||||
{#if authStatus.email || authStatus.org_name || authStatus.api_key_source || config.api_key}
|
||||
<dl class="text-xs space-y-1 mb-3">
|
||||
{#if authStatus.email}
|
||||
<div class="flex gap-2">
|
||||
<dt class="text-[var(--text-tertiary)] w-20 flex-shrink-0">Email</dt>
|
||||
<dd class="text-[var(--text-primary)] break-all">{authStatus.email}</dd>
|
||||
</div>
|
||||
{/if}
|
||||
{#if authStatus.org_name}
|
||||
<div class="flex gap-2">
|
||||
<dt class="text-[var(--text-tertiary)] w-20 flex-shrink-0">Org</dt>
|
||||
<dd class="text-[var(--text-primary)]">{authStatus.org_name}</dd>
|
||||
</div>
|
||||
{/if}
|
||||
{#if authStatus.api_key_source}
|
||||
<div class="flex gap-2">
|
||||
<dt class="text-[var(--text-tertiary)] w-20 flex-shrink-0">API key</dt>
|
||||
<dd class="text-[var(--text-primary)]">{authStatus.api_key_source}</dd>
|
||||
</div>
|
||||
{/if}
|
||||
{#if authStatus.subscription_type}
|
||||
<div class="flex gap-2">
|
||||
<dt class="text-[var(--text-tertiary)] w-20 flex-shrink-0">Plan</dt>
|
||||
<dd class="text-[var(--text-primary)]">{authStatus.subscription_type}</dd>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex gap-2">
|
||||
<dt class="text-[var(--text-tertiary)] w-20 flex-shrink-0">Override</dt>
|
||||
<dd class="text-[var(--text-primary)]">
|
||||
{#if config.api_key}
|
||||
{config.streamer_mode ? "Custom key set 🔒" : "Custom key set"}
|
||||
{:else}
|
||||
None
|
||||
{/if}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="text-sm text-[var(--text-secondary)] py-2">Auth status unavailable</div>
|
||||
{/if}
|
||||
|
||||
{#if authError}
|
||||
<div class="mb-3 p-2 bg-red-500/20 border border-red-500/50 rounded text-red-400 text-xs">
|
||||
{authError}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
onclick={refreshAuthStatus}
|
||||
disabled={authLoading || authActionLoading}
|
||||
class="px-3 py-1.5 text-sm bg-[var(--bg-primary)] border border-[var(--border-color)] rounded-lg text-[var(--text-secondary)] hover:border-[var(--accent-primary)] hover:text-[var(--text-primary)] transition-colors disabled:opacity-50"
|
||||
>
|
||||
Refresh
|
||||
</button>
|
||||
{#if authStatus && !authStatus.is_logged_in}
|
||||
<button
|
||||
onclick={handleAuthLogin}
|
||||
disabled={authActionLoading}
|
||||
class="btn-trans-gradient px-3 py-1.5 text-sm rounded-lg disabled:opacity-50"
|
||||
>
|
||||
{authActionLoading ? "Logging in..." : "Login"}
|
||||
</button>
|
||||
{:else if authStatus && authStatus.is_logged_in}
|
||||
<button
|
||||
onclick={handleAuthLogout}
|
||||
disabled={authActionLoading}
|
||||
class="px-3 py-1.5 text-sm bg-red-500/20 border border-red-500/50 rounded-lg text-red-400 hover:bg-red-500/30 transition-colors disabled:opacity-50"
|
||||
>
|
||||
{authActionLoading ? "Logging out..." : "Logout"}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Agent Settings Section -->
|
||||
<section class="mb-6">
|
||||
<h3 class="text-sm font-medium text-[var(--accent-primary)] uppercase tracking-wider mb-3">
|
||||
@@ -322,6 +475,37 @@
|
||||
class="w-full px-3 py-2 bg-[var(--bg-primary)] border border-[var(--border-color)] rounded-lg text-[var(--text-primary)] focus:outline-none focus:border-[var(--accent-primary)] resize-none"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Worktree Isolation -->
|
||||
<div class="mb-4">
|
||||
<label class="flex items-center gap-3 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={config.use_worktree}
|
||||
class="w-4 h-4 rounded border-[var(--border-color)] bg-[var(--bg-primary)] text-[var(--accent-primary)] focus:ring-[var(--accent-primary)]"
|
||||
/>
|
||||
<span class="text-sm text-[var(--text-primary)]">Worktree isolation</span>
|
||||
</label>
|
||||
<p class="text-xs text-[var(--text-tertiary)] mt-1 ml-7">
|
||||
Launch sessions with <code class="font-mono">--worktree</code> for isolated git worktree environments
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Disable 1M Context Window -->
|
||||
<div class="mb-4">
|
||||
<label class="flex items-center gap-3 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={config.disable_1m_context}
|
||||
class="w-4 h-4 rounded border-[var(--border-color)] bg-[var(--bg-primary)] text-[var(--accent-primary)] focus:ring-[var(--accent-primary)]"
|
||||
/>
|
||||
<span class="text-sm text-[var(--text-primary)]">Disable 1M context window</span>
|
||||
</label>
|
||||
<p class="text-xs text-[var(--text-tertiary)] mt-1 ml-7">
|
||||
Sets <code class="font-mono">CLAUDE_CODE_DISABLE_1M_CONTEXT=1</code> to opt out of the extended
|
||||
context window
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Greeting Section -->
|
||||
|
||||
Reference in New Issue
Block a user