feat: productivity suite — task loop, workflow, theming, docs & more #197

Merged
naomi merged 16 commits from feat/productivity into main 2026-03-07 03:08:33 -08:00
16 changed files with 1360 additions and 81 deletions
Showing only changes of commit 105f87cf64 - Show all commits
+132 -81
View File
@@ -1,54 +1,69 @@
<script lang="ts">
import { HELP_PAGES, nextPage, prevPage, isFirstPage, isLastPage } from "./docs/helpPages";
import DocsGettingStarted from "./docs/DocsGettingStarted.svelte";
import DocsKeyboardShortcuts from "./docs/DocsKeyboardShortcuts.svelte";
import DocsChatInput from "./docs/DocsChatInput.svelte";
import DocsFileEditor from "./docs/DocsFileEditor.svelte";
import DocsGitPanel from "./docs/DocsGitPanel.svelte";
import DocsThemeCustomisation from "./docs/DocsThemeCustomisation.svelte";
import DocsModelConfig from "./docs/DocsModelConfig.svelte";
import DocsSessionManagement from "./docs/DocsSessionManagement.svelte";
import DocsTaskLoop from "./docs/DocsTaskLoop.svelte";
import DocsPanelsTools from "./docs/DocsPanelsTools.svelte";
import DocsTroubleshooting from "./docs/DocsTroubleshooting.svelte";
import DocsChangelog from "./docs/DocsChangelog.svelte";
interface Props {
onClose: () => void;
}
const { onClose }: Props = $props();
const sections = [
{
title: "Getting Started",
items: [
"Enter your Claude API key in Settings (gear icon)",
"Set your working directory and click Connect",
"Start chatting with Hikari - your AI assistant!",
],
},
{
title: "Key Features",
items: [
"🗂️ File Management: Hikari can read, write, and edit files in your project",
"💻 Terminal Access: Execute commands and run scripts",
"🔍 Code Search: Find files and search through code",
"🌐 Web Access: Fetch information from the web",
"📊 MCP Servers: Connect to external tools via Model Context Protocol",
"📁 Multi-tab Support: Work on multiple conversations simultaneously",
],
},
{
title: "Available Commands",
items: [
"Type naturally - Hikari understands context!",
"Ask to read, create, or modify files",
"Request code explanations or debugging help",
"Have Hikari run tests or build commands",
"Search for specific functions or patterns",
],
},
{
title: "Tips & Tricks",
items: [
"💡 Use the stats panel to track your usage",
"🎯 Be specific about file paths and requirements",
"🔒 Grant tool permissions as needed for security",
"📌 Pin important conversations for quick access",
"🎨 Customize your theme and preferences in Settings",
"⌨️ Check the keyboard icon for available shortcuts",
],
},
const PAGE_COMPONENTS = [
DocsGettingStarted,
DocsKeyboardShortcuts,
DocsChatInput,
DocsFileEditor,
DocsGitPanel,
DocsThemeCustomisation,
DocsModelConfig,
DocsSessionManagement,
DocsTaskLoop,
DocsPanelsTools,
DocsTroubleshooting,
DocsChangelog,
];
let currentPageIndex = $state(0);
const currentComponent = $derived(PAGE_COMPONENTS[currentPageIndex]);
const atFirst = $derived(isFirstPage(currentPageIndex));
const atLast = $derived(isLastPage(currentPageIndex, HELP_PAGES.length));
function handleKeydown(event: KeyboardEvent): void {
const target = event.target as HTMLElement;
const isInputFocused = target.tagName === "INPUT" || target.tagName === "TEXTAREA";
if (event.key === "Escape") {
onClose();
return;
}
if (isInputFocused) return;
if (event.key === "ArrowRight" || event.key === "ArrowDown") {
event.preventDefault();
currentPageIndex = nextPage(currentPageIndex, HELP_PAGES.length);
} else if (event.key === "ArrowLeft" || event.key === "ArrowUp") {
event.preventDefault();
currentPageIndex = prevPage(currentPageIndex);
}
}
</script>
<svelte:window onkeydown={handleKeydown} />
<!-- Backdrop -->
<div
class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4"
onclick={onClose}
@@ -56,17 +71,21 @@
tabindex="0"
onkeydown={(e) => e.key === "Escape" && onClose()}
>
<!-- Dialog -->
<div
class="bg-[var(--bg-primary)] border border-[var(--border-color)] rounded-lg shadow-xl max-w-2xl w-full max-h-[80vh] overflow-hidden flex flex-col"
class="bg-[var(--bg-primary)] border border-[var(--border-color)] rounded-lg shadow-xl w-full max-w-3xl h-[80vh] flex flex-col"
onclick={(e) => e.stopPropagation()}
onkeydown={(e) => e.stopPropagation()}
role="dialog"
aria-labelledby="help-title"
tabindex="-1"
>
<div class="flex items-center justify-between p-6 pb-4 border-b border-[var(--border-color)]">
<h2 id="help-title" class="text-xl font-semibold text-[var(--text-primary)]">
How to Use Hikari Desktop
<!-- Header -->
<div
class="flex items-center justify-between px-6 py-4 border-b border-[var(--border-color)] shrink-0"
>
<h2 id="help-title" class="text-lg font-semibold text-[var(--text-primary)]">
Help & Documentation
</h2>
<button
onclick={onClose}
@@ -84,32 +103,77 @@
</button>
</div>
<div class="overflow-y-auto flex-1 p-6 space-y-6">
{#each sections as section (section.title)}
<div>
<h3 class="font-medium text-[var(--text-primary)] mb-3">{section.title}</h3>
<ul class="space-y-2 text-sm text-[var(--text-secondary)]">
{#each section.items as item (item)}
<li class="flex items-start">
<span class="text-[var(--accent-primary)] mr-2 mt-0.5"></span>
<span>{item}</span>
</li>
{/each}
</ul>
</div>
{/each}
<!-- Body: sidebar + content -->
<div class="flex flex-1 overflow-hidden">
<!-- Sidebar TOC -->
<nav
class="w-44 shrink-0 border-r border-[var(--border-color)] overflow-y-auto py-2"
aria-label="Documentation pages"
>
{#each HELP_PAGES as page, i (page.id)}
<button
onclick={() => (currentPageIndex = i)}
class="w-full text-left px-4 py-2 text-sm transition-colors {i === currentPageIndex
? 'bg-[var(--bg-secondary)] text-[var(--accent-primary)] font-medium'
: 'text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-secondary)]'}"
aria-current={i === currentPageIndex ? "page" : undefined}
>
{page.title}
</button>
{/each}
</nav>
<div class="pt-4 border-t border-[var(--border-color)]">
<p class="text-sm text-[var(--text-tertiary)]">
<strong>Need more help?</strong> Join our Discord community for support and updates!
</p>
<!-- Page content -->
<div class="flex-1 overflow-y-auto p-6">
<svelte:component this={currentComponent} />
</div>
</div>
<!-- Footer: prev / page indicator / next -->
<div
class="flex items-center justify-between px-6 py-3 border-t border-[var(--border-color)] shrink-0"
>
<button
onclick={() => (currentPageIndex = prevPage(currentPageIndex))}
disabled={atFirst}
class="flex items-center gap-1 px-3 py-1.5 text-sm rounded transition-colors
{atFirst
? 'text-[var(--text-tertiary)] cursor-not-allowed'
: 'text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-secondary)]'}"
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 19l-7-7 7-7"
/>
</svg>
Previous
</button>
<span class="text-xs text-[var(--text-tertiary)]">
Page {currentPageIndex + 1} of {HELP_PAGES.length}
</span>
<button
onclick={() => (currentPageIndex = nextPage(currentPageIndex, HELP_PAGES.length))}
disabled={atLast}
class="flex items-center gap-1 px-3 py-1.5 text-sm rounded transition-colors
{atLast
? 'text-[var(--text-tertiary)] cursor-not-allowed'
: 'text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-secondary)]'}"
>
Next
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</button>
</div>
</div>
</div>
<style>
/* Ensure the panel appears above other content */
[role="dialog"] {
animation: slideIn 0.2s ease-out;
}
@@ -125,26 +189,13 @@
}
}
/* Custom scrollbar styling */
.overflow-y-auto {
nav {
scrollbar-width: thin;
scrollbar-color: var(--border-color) transparent;
}
.overflow-y-auto::-webkit-scrollbar {
width: 8px;
}
.overflow-y-auto::-webkit-scrollbar-track {
background: transparent;
}
.overflow-y-auto::-webkit-scrollbar-thumb {
background-color: var(--border-color);
border-radius: 4px;
}
.overflow-y-auto::-webkit-scrollbar-thumb:hover {
background-color: var(--accent-primary);
.overflow-y-auto {
scrollbar-width: thin;
scrollbar-color: var(--border-color) transparent;
}
</style>
+13
View File
@@ -92,8 +92,21 @@
function handleInjectContext(content: string): void {
injectTextStore.set(content);
}
function handleGlobalHelpShortcut(event: KeyboardEvent): void {
const target = event.target as HTMLElement;
const isInputFocused = target.tagName === "INPUT" || target.tagName === "TEXTAREA";
if (isInputFocused) return;
if (event.key === "?") {
event.preventDefault();
showHelp = !showHelp;
}
}
</script>
<svelte:window onkeydown={handleGlobalHelpShortcut} />
<div class="relative">
<button
onclick={() => (showMenu = !showMenu)}
@@ -0,0 +1,48 @@
<h3 class="text-lg font-semibold text-[var(--text-primary)] mb-4">Changelog</h3>
<div class="space-y-4 text-sm text-[var(--text-secondary)]">
<p>
The full version history for Hikari Desktop is available in the <strong>Changelog</strong>
panel — open it from the menu to browse release notes for every version, fetched directly from the
releases page.
</p>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Recent Highlights</h4>
<ul class="space-y-2">
<li>
<strong>Guided Workflow</strong> — four-phase project orchestration (Discuss → Plan → Execute
→ Verify) with Quick Mode and cross-panel navigation
</li>
<li>
<strong>Wave-based Task Loop</strong> — parallel task execution with dependency tracking, concurrency
control, and auto-commit support
</li>
<li>
<strong>PRD Creator</strong> — AI-assisted task list generation from plain-English project descriptions
</li>
<li>
<strong>Community Themes</strong> — Dracula, Catppuccin, Nord, Solarized, Gruvbox, and Rosé Pine
presets
</li>
<li>
<strong>Project Context Panel</strong> — persistent PROJECT.md / REQUIREMENTS.md / ROADMAP.md
/ STATE.md context engineering
</li>
<li>
<strong>Codebase Mapper</strong> — auto-generated CODEBASE.md architectural summaries
</li>
<li>
<strong>Embedded Docs</strong> — this help panel with full documentation and keyboard navigation
</li>
</ul>
</div>
<div class="pt-2 border-t border-[var(--border-color)]">
<p>
To see the complete changelog with all versions and patch notes, open <strong
>Changelog</strong
> from the main menu.
</p>
</div>
</div>
@@ -0,0 +1,105 @@
<h3 class="text-lg font-semibold text-[var(--text-primary)] mb-4">Chat & Input</h3>
<div class="space-y-4 text-sm text-[var(--text-secondary)]">
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Sending Messages</h4>
<ul class="space-y-1">
<li>• Press <kbd class="kbd">Enter</kbd> to send your message</li>
<li>• Press <kbd class="kbd">Shift+Enter</kbd> to insert a newline</li>
<li>• Paste images directly into the input — Hikari can see and discuss them</li>
<li>• Paste code or multi-line text — Hikari will handle it as context</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Quick Actions</h4>
<p>
Click the <strong>⚡ lightning bolt</strong> icon in the input toolbar to open a panel of predefined
prompt shortcuts. These let you trigger common tasks (explain code, write tests, refactor, and more)
with a single click instead of typing.
</p>
<p class="mt-2">
Quick actions send a pre-written prompt to Hikari immediately — no extra typing needed.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Snippet Library</h4>
<p>
Click the <strong>snippet icon</strong> in the input toolbar to open your personal snippet library
— a collection of reusable text blocks you can insert into the input with one click.
</p>
<ul class="space-y-1 mt-2">
<li>• Snippets are organised by category</li>
<li>• Click a snippet to insert it at the cursor position</li>
<li>• Default snippets are provided; add your own to build a personal library</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Clipboard History</h4>
<p>
Click the <strong>clipboard icon</strong> in the input toolbar to browse your clipboard history
— a list of text you've previously copied or pasted during the session.
</p>
<ul class="space-y-1 mt-2">
<li>• Longer text blocks and code snippets are captured automatically</li>
<li>• Click an entry to insert it into the current input</li>
<li>• Filter by language for code-specific entries</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Drafts</h4>
<p>
Click the <strong>draft icon</strong> in the input toolbar to open the Drafts panel — a place to
save and retrieve partially written messages.
</p>
<ul class="space-y-1 mt-2">
<li>• Save the current input as a draft for later</li>
<li>• Click a saved draft to restore it to the input</li>
<li>• Drafts persist across sessions so you can pick up where you left off</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Compact Mode</h4>
<p>
Compact mode shrinks the app to a small <strong>280×400 px</strong> floating window — useful for
keeping Hikari visible whilst working in another application.
</p>
<ul class="space-y-1 mt-2">
<li>
• Toggle via <strong>Compact Mode</strong> in the menu or
<kbd class="kbd">Ctrl+Shift+M</kbd>
</li>
<li>• Press the same shortcut again (or use the menu) to return to normal size</li>
<li>• Your previous window size is restored automatically when you exit compact mode</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Discord Rich Presence</h4>
<p>
Hikari Desktop can share your activity in Discord — showing the model you're using and session
duration as your Rich Presence status.
</p>
<p class="mt-2">
Toggle this in Settings (<kbd class="kbd">Ctrl+,</kbd>) under the Discord section. Disable it
if you'd rather keep your coding sessions private.
</p>
</div>
</div>
<style>
kbd.kbd {
display: inline-block;
padding: 0.1rem 0.35rem;
font-size: 0.75rem;
font-family: monospace;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 3px;
color: var(--text-primary);
}
</style>
@@ -0,0 +1,79 @@
<h3 class="text-lg font-semibold text-[var(--text-primary)] mb-4">File Editor</h3>
<div class="space-y-4 text-sm text-[var(--text-secondary)]">
<p>
The built-in file editor lets you read and edit project files directly in Hikari Desktop,
alongside your conversation. Open it via <strong>File Editor</strong> in the menu or press
<kbd class="kbd">Ctrl+E</kbd> (requires an active connection).
</p>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">File Browser</h4>
<p>
The file browser panel (toggle with <kbd class="kbd">Ctrl+B</kbd>) shows your working
directory tree. From it you can:
</p>
<ul class="space-y-1 mt-2">
<li>• Click a file to open it in a new editor tab</li>
<li>• Right-click for context menu options (rename, delete)</li>
<li>• Press <kbd class="kbd">Ctrl+N</kbd> to create a new file</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Editor Tabs</h4>
<p>Each open file gets its own tab. You can have multiple files open at once.</p>
<ul class="space-y-1 mt-2">
<li>• Click a tab to switch to that file</li>
<li>• Click <strong>×</strong> on a tab to close it (<kbd class="kbd">Ctrl+W</kbd>)</li>
<li>• Unsaved changes are indicated with a dot on the tab</li>
<li>• Press <kbd class="kbd">Ctrl+S</kbd> to save the current file</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Syntax Highlighting</h4>
<p>
The editor automatically applies syntax highlighting for a wide range of languages, including:
</p>
<p class="mt-1">
JavaScript · TypeScript · Python · Rust · Go · Java · C++ · HTML · CSS · JSON · YAML ·
Markdown · SQL · Shell · PHP · Ruby · Swift · R · TOML · Dockerfile · PowerShell
</p>
<p class="mt-2">
The editor theme follows your app theme — dark themes use dark editor backgrounds.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Editor Features</h4>
<ul class="space-y-1">
<li>• Line numbers, bracket matching, and code folding</li>
<li>• Search and replace (<kbd class="kbd">Ctrl+F</kbd> in most editors)</li>
<li>• Right-click context menu for common operations</li>
<li>• Full CodeMirror keybindings</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Working with Hikari</h4>
<p>
Hikari can read and edit files directly through her tool access — you don't need the file
editor open for her to work on files. The editor is most useful for reviewing changes she's
made, manually editing files alongside the conversation, or browsing the project structure.
</p>
</div>
</div>
<style>
kbd.kbd {
display: inline-block;
padding: 0.1rem 0.35rem;
font-size: 0.75rem;
font-family: monospace;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 3px;
color: var(--text-primary);
}
</style>
@@ -0,0 +1,61 @@
<h3 class="text-lg font-semibold text-[var(--text-primary)] mb-4">Getting Started</h3>
<div class="space-y-4 text-sm text-[var(--text-secondary)]">
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">1. Enter your API key</h4>
<p>
Open Settings (<kbd class="kbd">Ctrl+,</kbd>) and paste your Anthropic API key. Keys are
stored locally on your device and never sent anywhere except the Anthropic API.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">2. Set a working directory</h4>
<p>
Click the folder icon in the connection bar and choose the project directory you want Hikari
to work in. This gives her context for your files and project structure.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">3. Connect</h4>
<p>
Click <strong>Connect</strong> to start a Claude Code session. The status indicator will turn green
when you're connected.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">4. Start chatting</h4>
<p>
Type a message and press <kbd class="kbd">Enter</kbd> (or <kbd class="kbd">Ctrl+Enter</kbd>)
to send. Hikari can read, write, and edit files; run terminal commands; search code; fetch web
content; and connect to external tools via MCP.
</p>
</div>
<div class="pt-2 border-t border-[var(--border-color)]">
<h4 class="font-medium text-[var(--text-primary)] mb-2">Key Features</h4>
<ul class="space-y-1.5">
<li>🗂️ <strong>File Management</strong> — read, write, and edit files in your project</li>
<li>💻 <strong>Terminal Access</strong> — execute commands and run scripts</li>
<li>🔍 <strong>Code Search</strong> — find files and search through code</li>
<li>🌐 <strong>Web Access</strong> — fetch information from the web</li>
<li>📊 <strong>MCP Servers</strong> — connect external tools via Model Context Protocol</li>
<li>📁 <strong>Multi-tab Support</strong> — work on multiple conversations simultaneously</li>
</ul>
</div>
</div>
<style>
kbd.kbd {
display: inline-block;
padding: 0.1rem 0.35rem;
font-size: 0.75rem;
font-family: monospace;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 3px;
color: var(--text-primary);
}
</style>
@@ -0,0 +1,61 @@
<h3 class="text-lg font-semibold text-[var(--text-primary)] mb-4">Git & Version Control</h3>
<div class="space-y-4 text-sm text-[var(--text-secondary)]">
<p>
The Git panel gives you a visual interface for git operations directly inside Hikari Desktop.
Open it via <strong>Git Panel</strong> in the menu.
</p>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Changes Tab</h4>
<p>Shows all modified, staged, and untracked files in your working directory.</p>
<ul class="space-y-1 mt-2">
<li>• Click a file to view its diff</li>
<li>• Stage individual files or all changes at once</li>
<li>• Unstage files you don't want in the next commit</li>
<li>• Discard changes to revert a file to its last committed state</li>
<li>• Enter a commit message and click <strong>Commit</strong> to create a commit</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Branches Tab</h4>
<p>Manage your local and remote branches.</p>
<ul class="space-y-1 mt-2">
<li>• View all local and remote branches</li>
<li>• Click a branch to check it out</li>
<li>• Create new branches from the current HEAD</li>
<li>• See how many commits your branch is ahead/behind its remote</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">History Tab</h4>
<p>Browse your commit history with author names, dates, and commit messages.</p>
<ul class="space-y-1 mt-2">
<li>• Scrollable log of recent commits</li>
<li>• Abbreviated commit hashes for easy reference</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Sync Actions</h4>
<p>
Use the quick action buttons to fetch, pull, and push changes to your remote — all without
leaving Hikari Desktop.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Working with Hikari</h4>
<p>
Hikari can also run git commands for you through the terminal — just ask her to commit, push,
create a branch, or check the status. The Git panel is useful for reviewing changes visually
and for quick operations you want to do yourself.
</p>
<p class="mt-2">
The <strong>Task Loop auto-commit</strong> feature can automatically commit after each completed
task — configure it via the ⚙ icon in the Task Loop panel.
</p>
</div>
</div>
@@ -0,0 +1,130 @@
<h3 class="text-lg font-semibold text-[var(--text-primary)] mb-4">Keyboard Shortcuts</h3>
<div class="space-y-4 text-sm">
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">General</h4>
<table class="w-full">
<tbody class="divide-y divide-[var(--border-color)]">
<tr>
<td class="py-1.5 pr-4"><kbd class="kbd">?</kbd></td>
<td class="py-1.5 text-[var(--text-secondary)]">Open this help panel</td>
</tr>
<tr>
<td class="py-1.5 pr-4"><kbd class="kbd">Escape</kbd></td>
<td class="py-1.5 text-[var(--text-secondary)]">Close the active panel</td>
</tr>
<tr>
<td class="py-1.5 pr-4"><kbd class="kbd">Ctrl+,</kbd></td>
<td class="py-1.5 text-[var(--text-secondary)]">Open settings</td>
</tr>
<tr>
<td class="py-1.5 pr-4"><kbd class="kbd">Ctrl+L</kbd></td>
<td class="py-1.5 text-[var(--text-secondary)]">Clear the terminal output</td>
</tr>
<tr>
<td class="py-1.5 pr-4"><kbd class="kbd">Ctrl+C</kbd></td>
<td class="py-1.5 text-[var(--text-secondary)]"
>Interrupt Claude (when no text is selected)</td
>
</tr>
</tbody>
</table>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Font Size</h4>
<table class="w-full">
<tbody class="divide-y divide-[var(--border-color)]">
<tr>
<td class="py-1.5 pr-4"><kbd class="kbd">Ctrl++</kbd> / <kbd class="kbd">Ctrl+=</kbd></td>
<td class="py-1.5 text-[var(--text-secondary)]">Increase font size</td>
</tr>
<tr>
<td class="py-1.5 pr-4"><kbd class="kbd">Ctrl+-</kbd></td>
<td class="py-1.5 text-[var(--text-secondary)]">Decrease font size</td>
</tr>
<tr>
<td class="py-1.5 pr-4"><kbd class="kbd">Ctrl+0</kbd></td>
<td class="py-1.5 text-[var(--text-secondary)]">Reset font size to default</td>
</tr>
</tbody>
</table>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Display Modes</h4>
<table class="w-full">
<tbody class="divide-y divide-[var(--border-color)]">
<tr>
<td class="py-1.5 pr-4"><kbd class="kbd">Ctrl+Shift+S</kbd></td>
<td class="py-1.5 text-[var(--text-secondary)]">Toggle streamer mode</td>
</tr>
<tr>
<td class="py-1.5 pr-4"><kbd class="kbd">Ctrl+Shift+M</kbd></td>
<td class="py-1.5 text-[var(--text-secondary)]">Toggle compact mode</td>
</tr>
<tr>
<td class="py-1.5 pr-4"><kbd class="kbd">Ctrl+`</kbd></td>
<td class="py-1.5 text-[var(--text-secondary)]">Toggle debug console</td>
</tr>
</tbody>
</table>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Code Editor</h4>
<table class="w-full">
<tbody class="divide-y divide-[var(--border-color)]">
<tr>
<td class="py-1.5 pr-4"><kbd class="kbd">Ctrl+E</kbd></td>
<td class="py-1.5 text-[var(--text-secondary)]">Toggle editor panel (when connected)</td>
</tr>
<tr>
<td class="py-1.5 pr-4"><kbd class="kbd">Ctrl+B</kbd></td>
<td class="py-1.5 text-[var(--text-secondary)]">Toggle file browser (in editor)</td>
</tr>
<tr>
<td class="py-1.5 pr-4"><kbd class="kbd">Ctrl+S</kbd></td>
<td class="py-1.5 text-[var(--text-secondary)]">Save current file (in editor)</td>
</tr>
<tr>
<td class="py-1.5 pr-4"><kbd class="kbd">Ctrl+W</kbd></td>
<td class="py-1.5 text-[var(--text-secondary)]">Close current tab (in editor)</td>
</tr>
<tr>
<td class="py-1.5 pr-4"><kbd class="kbd">Ctrl+N</kbd></td>
<td class="py-1.5 text-[var(--text-secondary)]">New file (in editor)</td>
</tr>
</tbody>
</table>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Help Panel Navigation</h4>
<table class="w-full">
<tbody class="divide-y divide-[var(--border-color)]">
<tr>
<td class="py-1.5 pr-4"><kbd class="kbd"></kbd> / <kbd class="kbd"></kbd></td>
<td class="py-1.5 text-[var(--text-secondary)]">Previous page</td>
</tr>
<tr>
<td class="py-1.5 pr-4"><kbd class="kbd"></kbd> / <kbd class="kbd"></kbd></td>
<td class="py-1.5 text-[var(--text-secondary)]">Next page</td>
</tr>
</tbody>
</table>
</div>
</div>
<style>
kbd.kbd {
display: inline-block;
padding: 0.1rem 0.35rem;
font-size: 0.75rem;
font-family: monospace;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 3px;
color: var(--text-primary);
}
</style>
@@ -0,0 +1,72 @@
<h3 class="text-lg font-semibold text-[var(--text-primary)] mb-4">Model & API Configuration</h3>
<div class="space-y-4 text-sm text-[var(--text-secondary)]">
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">API Key</h4>
<p>
Enter your Anthropic API key in Settings (<kbd class="kbd">Ctrl+,</kbd>). The key is stored
locally on your device and used only to authenticate requests to the Anthropic API.
</p>
<p class="mt-2">
Get your API key at <strong>console.anthropic.com</strong>.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Model Selection</h4>
<p class="mb-2">Choose from available Claude models:</p>
<ul class="space-y-1.5">
<li>
<strong>claude-opus-4-6</strong> — most capable, highest quality; best for complex tasks
</li>
<li>
<strong>claude-sonnet-4-6</strong> — balanced speed and quality
<span class="text-[var(--accent-primary)]">(recommended)</span>
</li>
<li>
<strong>claude-haiku-4-5</strong> — fastest and most cost-efficient; good for quick tasks
</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Custom Instructions</h4>
<p>
Add persistent instructions in Settings that are prepended to every conversation. Use this to
set coding preferences, project conventions, or communication style.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">MCP Servers</h4>
<p>
Model Context Protocol (MCP) servers extend Hikari's capabilities with external tools and data
sources — databases, APIs, version control systems, and more.
</p>
<p class="mt-2">
Open <strong>MCP Servers</strong> from the menu to add and manage server configurations. Each server
is defined with a command and optional arguments.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Permission Mode</h4>
<p>
Controls how Hikari asks for tool use permissions. Choose between asking every time,
auto-approving trusted tools, or running in a more restricted mode.
</p>
</div>
</div>
<style>
kbd.kbd {
display: inline-block;
padding: 0.1rem 0.35rem;
font-size: 0.75rem;
font-family: monospace;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 3px;
color: var(--text-primary);
}
</style>
@@ -0,0 +1,128 @@
<h3 class="text-lg font-semibold text-[var(--text-primary)] mb-4">Panels & Tools</h3>
<div class="space-y-4 text-sm text-[var(--text-secondary)]">
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Agent Monitor</h4>
<p>
Displays a live dashboard of all Claude Code agents running during a session — useful when
using the Task Loop or any feature that spawns subagents.
</p>
<ul class="space-y-1 mt-2">
<li>• Hierarchical tree view showing parent agents and their subagents</li>
<li>
• Status indicators: <span class="text-blue-400">● running</span>,
<span class="text-green-400">● completed</span>,
<span class="text-red-400">● errored</span>
</li>
<li>• Live duration timers for running agents</li>
<li>• Kill all / clear finished buttons</li>
<li>• A badge on the menu icon shows the count of active agents</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">To-Do List</h4>
<p>
Shows Hikari's internal todo list in real time — the same tasks she tracks using the
<code class="code">TodoWrite</code> tool during complex multi-step work.
</p>
<ul class="space-y-1 mt-2">
<li>• See pending, in-progress, and completed tasks at a glance</li>
<li>• Progress bar shows overall completion percentage</li>
<li>• Updates live as Hikari works through her plan</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Profile</h4>
<p>Your personal profile within Hikari Desktop, with lifetime stats and sharing.</p>
<ul class="space-y-1 mt-2">
<li>• Edit your display name and bio</li>
<li>• Upload a profile avatar</li>
<li>
• View lifetime stats: messages sent, tokens used, code blocks generated, files
created/edited, total spend
</li>
<li>• Track achievement progress</li>
<li>• Generate a shareable profile card image</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Achievements</h4>
<p>
Hikari Desktop includes a fun achievement system that unlocks as you use the app — milestones
like your first message, first code block, staying up late, and more.
</p>
<p class="mt-2">
Open <strong>Achievements</strong> from the menu to see your progress. Newly unlocked achievements
show a badge count on the menu item.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Plugins</h4>
<p>
Plugins extend Hikari's capabilities with community-built additions. Open
<strong>Plugins</strong> from the menu to manage them.
</p>
<ul class="space-y-1 mt-2">
<li>• Install plugins from named sources or custom marketplace URLs</li>
<li>• Enable or disable individual plugins without uninstalling them</li>
<li>• Update plugins to their latest versions</li>
<li>• Add custom plugin marketplace sources (GitHub-hosted)</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Debug Console</h4>
<p>
A developer-facing log console that captures frontend events, errors, and debug output. Open
it via <strong>Debug Console</strong> in the menu or press
<kbd class="kbd">Ctrl+`</kbd>.
</p>
<p class="mt-2">
Useful when troubleshooting unexpected behaviour or reporting issues. Filter by log level
(info, warn, error, debug).
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Workspace Trust</h4>
<p>
When you connect to a working directory, Hikari scans it for potentially powerful
configurations and may display a trust prompt before proceeding. This includes:
</p>
<ul class="space-y-1 mt-2">
<li><strong>Hooks</strong> — shell commands that run automatically during sessions</li>
<li><strong>MCP servers</strong> — local processes with system-level access</li>
<li><strong>Custom slash commands</strong> — instructions that execute at invocation</li>
</ul>
<p class="mt-2">
Review each item carefully. Click <strong>Trust &amp; Connect</strong> to proceed or
<strong>Cancel</strong> to abort. Trusted workspaces remember your decision for future sessions.
</p>
</div>
</div>
<style>
kbd.kbd {
display: inline-block;
padding: 0.1rem 0.35rem;
font-size: 0.75rem;
font-family: monospace;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 3px;
color: var(--text-primary);
}
code.code {
font-family: monospace;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 3px;
padding: 0.05rem 0.3rem;
color: var(--text-primary);
}
</style>
@@ -0,0 +1,80 @@
<h3 class="text-lg font-semibold text-[var(--text-primary)] mb-4">Session Management</h3>
<div class="space-y-4 text-sm text-[var(--text-secondary)]">
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Multiple Conversations</h4>
<p>
Use tabs at the top of the chat area to manage multiple simultaneous sessions with Hikari.
</p>
<ul class="space-y-1 mt-2">
<li>• Click <strong>+</strong> to open a new conversation tab</li>
<li>• Click a tab to switch between active conversations</li>
<li>• Click <strong>×</strong> on a tab to close that conversation</li>
</ul>
<p class="mt-2">
Each tab runs its own independent Claude Code session with separate context and history.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Session History</h4>
<p>
Open <strong>Session History</strong> from the menu to browse and restore past conversations. Sessions
are saved automatically and indexed by date.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Context Compaction</h4>
<p>
As conversations grow long, use <strong>Compact Conversation</strong> from the menu to summarise
the history and free up context window space — without losing important information.
</p>
<p class="mt-2">
<strong>Start Fresh with Context</strong> creates a brand-new session but carries over a summary
of the previous conversation.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Tool Permissions</h4>
<p>
When Hikari needs to use a tool (file access, terminal, web fetch, etc.) she may ask for your
approval first. You can:
</p>
<ul class="space-y-1 mt-2">
<li><strong>Allow once</strong> — approve this single use</li>
<li><strong>Allow for session</strong> — approve all uses of this tool this session</li>
<li><strong>Deny</strong> — block the action</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Stats Panel</h4>
<p>
Open <strong>Stats</strong> from the menu to see real-time usage data: token counts, estimated cost,
context window usage, and per-session totals.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Streamer Mode</h4>
<p>
Toggle streamer mode (<kbd class="kbd">Ctrl+Shift+S</kbd>) to redact your API key and other
sensitive information from the display — useful for streaming or screen sharing.
</p>
</div>
</div>
<style>
kbd.kbd {
display: inline-block;
padding: 0.1rem 0.35rem;
font-size: 0.75rem;
font-family: monospace;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 3px;
color: var(--text-primary);
}
</style>
+102
View File
@@ -0,0 +1,102 @@
<h3 class="text-lg font-semibold text-[var(--text-primary)] mb-4">Task Loop & Automation</h3>
<div class="space-y-4 text-sm text-[var(--text-secondary)]">
<p>
Hikari Desktop includes a full project automation suite — open any of these tools from the menu.
</p>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Guided Workflow</h4>
<p>A four-phase project workflow that orchestrates the other automation tools:</p>
<ol class="space-y-1.5 mt-2 list-none">
<li>
<strong>1. Discuss</strong> — describe your project; Hikari writes a
<code class="code">CONTEXT.md</code> file capturing requirements and goals
</li>
<li>
<strong>2. Plan</strong> — use the PRD Creator to break the project into tasks
</li>
<li>
<strong>3. Execute</strong> — run the Task Loop to complete all tasks automatically
</li>
<li>
<strong>4. Verify</strong> — check acceptance criteria; Hikari writes
<code class="code">VERIFY.md</code>
</li>
</ol>
<p class="mt-2">
Enable <strong>Quick Mode</strong> to skip Claude interactions in steps 1 and 4.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">PRD Creator</h4>
<p>
Describe your project in plain English and Hikari will generate a structured
<code class="code">hikari-tasks.json</code> task list. Tasks include titles, prompts, priorities,
and dependency relationships.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Task Loop</h4>
<p>
Executes tasks from <code class="code">hikari-tasks.json</code> automatically. Features include:
</p>
<ul class="space-y-1 mt-2">
<li>
<strong>Wave-based parallelism</strong> — independent tasks run concurrently; dependent tasks
wait for their prerequisites
</li>
<li>
<strong>Concurrency control</strong> — adjust the <kbd class="kbd">[] N [+]</kbd> control to
limit how many tasks run in parallel
</li>
<li><strong>Blocked detection</strong> — tasks whose dependencies failed are marked ⊘</li>
<li>
<strong>Auto-commit</strong> — optionally commit changes after each task completes, with a configurable
prefix and optional SUMMARY.md
</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Project Context Panel</h4>
<p>
Manage persistent context files that are injected into every conversation:
<code class="code">PROJECT.md</code>, <code class="code">REQUIREMENTS.md</code>,
<code class="code">ROADMAP.md</code>, and <code class="code">STATE.md</code>. These help
Hikari maintain consistent project understanding across sessions.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Codebase Mapper</h4>
<p>
Generates a <code class="code">CODEBASE.md</code> architectural summary of your project — directory
structure, key files, and their roles. Useful for onboarding Hikari to large codebases quickly.
</p>
</div>
</div>
<style>
kbd.kbd {
display: inline-block;
padding: 0.1rem 0.35rem;
font-size: 0.75rem;
font-family: monospace;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 3px;
color: var(--text-primary);
}
code.code {
font-family: monospace;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 3px;
padding: 0.05rem 0.3rem;
color: var(--text-primary);
}
</style>
@@ -0,0 +1,85 @@
<h3 class="text-lg font-semibold text-[var(--text-primary)] mb-4">Theme Customisation</h3>
<div class="space-y-4 text-sm text-[var(--text-secondary)]">
<p>
Open <strong>Settings</strong> (<kbd class="kbd">Ctrl+,</kbd>) and scroll to the Appearance
section to customise your theme.
</p>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Built-in Themes</h4>
<ul class="space-y-1">
<li><strong>Default Dark</strong> — the classic dark Hikari look</li>
<li><strong>Default Light</strong> — a bright, clean light theme</li>
<li><strong>High Contrast</strong> — maximum contrast for accessibility</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Community Presets</h4>
<ul class="space-y-1 columns-2">
<li>• Dracula</li>
<li>• Catppuccin Mocha</li>
<li>• Catppuccin Latte</li>
<li>• Nord</li>
<li>• Solarized Dark</li>
<li>• Solarized Light</li>
<li>• Gruvbox Dark</li>
<li>• Gruvbox Light</li>
<li>• Rosé Pine</li>
<li>• Rosé Pine Dawn</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Custom Colours</h4>
<p>
Select <strong>Custom</strong> from the theme dropdown to set individual colours for each UI element:
</p>
<ul class="space-y-1 mt-2">
<li>• Text: primary, secondary, and tertiary levels</li>
<li>• Backgrounds: primary, secondary, and header</li>
<li>• Border colour</li>
<li>• Accent and pink highlight colours</li>
<li>• Trans flag stripe colours</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Custom Fonts</h4>
<p>
Upload a <code class="code">.ttf</code> or <code class="code">.otf</code> font file to apply a custom
UI font across the entire app.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Background Image</h4>
<p>
Set a custom background image that renders behind the chat area. Adjust opacity to keep it
subtle.
</p>
</div>
</div>
<style>
kbd.kbd {
display: inline-block;
padding: 0.1rem 0.35rem;
font-size: 0.75rem;
font-family: monospace;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 3px;
color: var(--text-primary);
}
code.code {
font-family: monospace;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 3px;
padding: 0.05rem 0.3rem;
color: var(--text-primary);
}
</style>
@@ -0,0 +1,125 @@
<h3 class="text-lg font-semibold text-[var(--text-primary)] mb-4">Troubleshooting</h3>
<div class="space-y-4 text-sm text-[var(--text-secondary)]">
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Can't Connect</h4>
<ul class="space-y-1">
<li>
• Check that your API key is entered correctly in Settings (<kbd class="kbd">Ctrl+,</kbd>)
</li>
<li>
• Ensure you have an active internet connection — Hikari needs to reach the Anthropic API
</li>
<li>• Try setting an explicit working directory rather than leaving it blank</li>
<li>• Check the Debug Console (<kbd class="kbd">Ctrl+`</kbd>) for error details</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">
Hikari Seems Stuck or Stopped Responding
</h4>
<ul class="space-y-1">
<li>
• Press <kbd class="kbd">Ctrl+C</kbd> (when no text is selected) to interrupt the current process
</li>
<li>• If that doesn't work, disconnect and reconnect from the connection bar</li>
<li>
• Check the Stats panel for context window usage — if it's near 100%, use
<strong>Compact Conversation</strong> to free up space
</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">API Errors or Rate Limits</h4>
<ul class="space-y-1">
<li>• Verify your API key is valid at <strong>console.anthropic.com</strong></li>
<li>• Check that your account has sufficient API credits</li>
<li>
• If you hit rate limits frequently, consider switching to a faster/cheaper model in
Settings
</li>
<li>• The Debug Console will show the specific error returned by the API</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Tool Permissions Blocking Work</h4>
<ul class="space-y-1">
<li>
• When prompted, you can choose <strong>Allow for session</strong> to avoid repeated prompts
</li>
<li>• Adjust the permission mode in Settings to auto-approve trusted tools</li>
<li>
• If a tool call was denied by mistake, simply ask Hikari to try again — she'll prompt you
once more
</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">
Missing Features or Greyed-Out Buttons
</h4>
<ul class="space-y-1">
<li>• Some features require an active connection — connect to a working directory first</li>
<li>
• The File Editor (<kbd class="kbd">Ctrl+E</kbd>) is only available when connected
</li>
<li>• The Agent Monitor shows activity only during Task Loop or multi-agent sessions</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Workspace Trust Prompt</h4>
<p>
If you see a trust prompt when connecting, it means Hikari detected hooks, MCP servers, or
slash commands in your working directory. Review them carefully before clicking
<strong>Trust &amp; Connect</strong>. See the <strong>Panels &amp; Tools</strong> page for more
details.
</p>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Context Window Full</h4>
<p>Watch the context bar in the Stats panel. When it approaches 100%:</p>
<ul class="space-y-1 mt-2">
<li>
• Use <strong>Compact Conversation</strong> from the menu to summarise and compress the history
</li>
<li>
• Use <strong>Start Fresh with Context</strong> to begin a new session that carries over a summary
</li>
</ul>
</div>
<div>
<h4 class="font-medium text-[var(--text-primary)] mb-2">Reporting Issues</h4>
<p>
If you encounter a bug or unexpected behaviour, please report it on our
<strong>GitHub issues page</strong>. Include:
</p>
<ul class="space-y-1 mt-2">
<li>• What you were doing when the issue occurred</li>
<li>• Any error messages from the Debug Console</li>
<li>• The app version (shown in the About panel)</li>
</ul>
<p class="mt-2">
You can also join our <strong>Discord community</strong> (link in the menu) for real-time support.
</p>
</div>
</div>
<style>
kbd.kbd {
display: inline-block;
padding: 0.1rem 0.35rem;
font-size: 0.75rem;
font-family: monospace;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 3px;
color: var(--text-primary);
}
</style>
+99
View File
@@ -0,0 +1,99 @@
import { describe, it, expect } from "vitest";
import { HELP_PAGES, nextPage, prevPage, clampPage, isFirstPage, isLastPage } from "./helpPages";
describe("HELP_PAGES", () => {
it("contains 12 pages", () => {
expect(HELP_PAGES).toHaveLength(12);
});
it("has unique ids", () => {
const ids = HELP_PAGES.map((p) => p.id);
expect(new Set(ids).size).toBe(ids.length);
});
it("has non-empty titles", () => {
for (const page of HELP_PAGES) {
expect(page.title.length).toBeGreaterThan(0);
}
});
});
describe("nextPage", () => {
it("advances to the next page", () => {
expect(nextPage(0, 7)).toBe(1);
expect(nextPage(3, 7)).toBe(4);
});
it("does not go past the last page", () => {
expect(nextPage(6, 7)).toBe(6);
});
it("clamps when already at the last page", () => {
expect(nextPage(10, 7)).toBe(6);
});
});
describe("prevPage", () => {
it("goes back to the previous page", () => {
expect(prevPage(3)).toBe(2);
expect(prevPage(1)).toBe(0);
});
it("does not go before the first page", () => {
expect(prevPage(0)).toBe(0);
});
it("clamps when already at the first page", () => {
expect(prevPage(-1)).toBe(0);
});
});
describe("clampPage", () => {
it("returns the page unchanged when in range", () => {
expect(clampPage(3, 7)).toBe(3);
expect(clampPage(0, 7)).toBe(0);
expect(clampPage(6, 7)).toBe(6);
});
it("clamps negative indices to 0", () => {
expect(clampPage(-1, 7)).toBe(0);
});
it("clamps over-range indices to the last page", () => {
expect(clampPage(10, 7)).toBe(6);
});
it("returns 0 when totalPages is 0", () => {
expect(clampPage(3, 0)).toBe(0);
});
it("returns 0 when totalPages is negative", () => {
expect(clampPage(3, -1)).toBe(0);
});
});
describe("isFirstPage", () => {
it("returns true for index 0", () => {
expect(isFirstPage(0)).toBe(true);
});
it("returns false for index greater than 0", () => {
expect(isFirstPage(1)).toBe(false);
expect(isFirstPage(6)).toBe(false);
});
});
describe("isLastPage", () => {
it("returns true for the last index", () => {
expect(isLastPage(6, 7)).toBe(true);
});
it("returns false for indices before the last", () => {
expect(isLastPage(5, 7)).toBe(false);
expect(isLastPage(0, 7)).toBe(false);
});
it("returns true when index exceeds total", () => {
expect(isLastPage(10, 7)).toBe(true);
});
});
+40
View File
@@ -0,0 +1,40 @@
export type HelpPageDef = {
id: string;
title: string;
};
export const HELP_PAGES: HelpPageDef[] = [
{ id: "getting-started", title: "Getting Started" },
{ id: "keyboard-shortcuts", title: "Keyboard Shortcuts" },
{ id: "chat-input", title: "Chat & Input" },
{ id: "file-editor", title: "File Editor" },
{ id: "git-panel", title: "Git & Version Control" },
{ id: "theme-customisation", title: "Theme Customisation" },
{ id: "model-config", title: "Model & API Configuration" },
{ id: "session-management", title: "Session Management" },
{ id: "task-loop", title: "Task Loop & Automation" },
{ id: "panels-tools", title: "Panels & Tools" },
{ id: "troubleshooting", title: "Troubleshooting" },
{ id: "changelog", title: "Changelog" },
];
export function nextPage(currentIndex: number, totalPages: number): number {
return Math.min(currentIndex + 1, totalPages - 1);
}
export function prevPage(currentIndex: number): number {
return Math.max(currentIndex - 1, 0);
}
export function clampPage(pageIndex: number, totalPages: number): number {
if (totalPages <= 0) return 0;
return Math.max(0, Math.min(pageIndex, totalPages - 1));
}
export function isFirstPage(currentIndex: number): boolean {
return currentIndex === 0;
}
export function isLastPage(currentIndex: number, totalPages: number): boolean {
return currentIndex >= totalPages - 1;
}