Files
hikari-desktop/src/lib/stores/achievements.ts
T
hikari 46339a040a perf: virtual windowing, markdown memoisation, and search debounce
- Terminal: virtual windowing renders max 150 lines, loads 50 older on
  scroll-up with scroll position compensation; auto-advances window
  forward during auto-scroll so old DOM nodes are unloaded continuously
- Markdown: two-stage derived rendering separates expensive parse step
  (marked + hljs + spoilers, runs on content change) from cheap search
  highlight step (runs on query change only)
- Achievements: fix double Object.keys() call in derived store
- Terminal: 150ms debounce on search query to reduce redundant updates
- Tests: add Markdown.test.ts for processSpoilers and highlightSearchMatches;
  extend Terminal.test.ts with virtual windowing helper coverage
2026-03-06 18:03:42 -08:00

1777 lines
42 KiB
TypeScript

import { writable, derived } from "svelte/store";
import { listen } from "@tauri-apps/api/event";
import { invoke } from "@tauri-apps/api/core";
import type { Achievement, AchievementUnlockedEvent, AchievementId } from "$lib/types/achievements";
import { playAchievementSound } from "$lib/sounds/achievement";
interface AchievementState {
achievements: Record<AchievementId, Achievement>;
totalUnlocked: number;
lastUnlocked: Achievement | null;
}
// Initial achievement definitions - must match Rust backend exactly!
const achievementDefinitions: Record<
AchievementId,
Omit<Achievement, "unlocked" | "unlockedAt">
> = {
// ============================================
// Token Milestones
// ============================================
FirstSteps: {
id: "FirstSteps",
name: "First Steps",
description: "Generated your first 1,000 tokens",
icon: "👶",
rarity: "common",
maxProgress: 1000,
},
GrowingStrong: {
id: "GrowingStrong",
name: "Growing Strong",
description: "Reached 10,000 tokens total",
icon: "🌱",
rarity: "common",
maxProgress: 10000,
},
BlossomingCoder: {
id: "BlossomingCoder",
name: "Blossoming Coder",
description: "Generated 100,000 tokens - you're really growing!",
icon: "🌸",
rarity: "rare",
maxProgress: 100000,
},
TokenMaster: {
id: "TokenMaster",
name: "Token Master",
description: "One million tokens! You're unstoppable!",
icon: "👑",
rarity: "epic",
maxProgress: 1000000,
},
TokenBillionaire: {
id: "TokenBillionaire",
name: "Token Billionaire",
description: "Generated 10 million tokens!",
icon: "💰",
rarity: "legendary",
maxProgress: 10000000,
},
TokenTreasure: {
id: "TokenTreasure",
name: "Token Treasure",
description: "Generated 50 million tokens - a true treasure trove!",
icon: "💎",
rarity: "legendary",
maxProgress: 50000000,
},
// ============================================
// Code Generation
// ============================================
HelloWorld: {
id: "HelloWorld",
name: "Hello, World!",
description: "Generated your first code block",
icon: "👋",
rarity: "common",
maxProgress: 1,
},
CodeWizard: {
id: "CodeWizard",
name: "Code Wizard",
description: "100 code blocks generated",
icon: "🧙‍♀️",
rarity: "rare",
maxProgress: 100,
},
ThousandBlocks: {
id: "ThousandBlocks",
name: "Thousand Blocks",
description: "1,000 code blocks! You're a code machine!",
icon: "🏗️",
rarity: "epic",
maxProgress: 1000,
},
CodeFactory: {
id: "CodeFactory",
name: "Code Factory",
description: "Generated 5,000 code blocks!",
icon: "🏭",
rarity: "epic",
maxProgress: 5000,
},
CodeEmpire: {
id: "CodeEmpire",
name: "Code Empire",
description: "Generated 10,000 code blocks - an empire of code!",
icon: "🏰",
rarity: "legendary",
maxProgress: 10000,
},
// ============================================
// File Operations
// ============================================
FileManipulator: {
id: "FileManipulator",
name: "File Manipulator",
description: "Edited 10 files",
icon: "📝",
rarity: "common",
maxProgress: 10,
},
FileArchitect: {
id: "FileArchitect",
name: "File Architect",
description: "Edited 100 files",
icon: "🏛️",
rarity: "rare",
maxProgress: 100,
},
FileEngineer: {
id: "FileEngineer",
name: "File Engineer",
description: "Edited 500 files",
icon: "⚙️",
rarity: "epic",
maxProgress: 500,
},
FileLegend: {
id: "FileLegend",
name: "File Legend",
description: "Edited 1,000 files - legendary file manipulation!",
icon: "🌟",
rarity: "legendary",
maxProgress: 1000,
},
// ============================================
// Conversation Milestones
// ============================================
ConversationStarter: {
id: "ConversationStarter",
name: "Conversation Starter",
description: "Exchanged 10 messages",
icon: "💬",
rarity: "common",
maxProgress: 10,
},
ChattyKathy: {
id: "ChattyKathy",
name: "Chatty Kathy",
description: "100 messages exchanged",
icon: "🗣️",
rarity: "common",
maxProgress: 100,
},
Conversationalist: {
id: "Conversationalist",
name: "Master Conversationalist",
description: "1,000 messages! We're really connecting!",
icon: "💖",
rarity: "rare",
maxProgress: 1000,
},
ChatMarathon: {
id: "ChatMarathon",
name: "Chat Marathon",
description: "5,000 messages exchanged!",
icon: "🏃‍♀️",
rarity: "epic",
maxProgress: 5000,
},
ChatLegend: {
id: "ChatLegend",
name: "Chat Legend",
description: "10,000 messages - a legendary conversationalist!",
icon: "👑",
rarity: "legendary",
maxProgress: 10000,
},
// ============================================
// Tool Usage
// ============================================
Toolsmith: {
id: "Toolsmith",
name: "Toolsmith",
description: "Used 5 different tools",
icon: "🔨",
rarity: "common",
maxProgress: 5,
},
ToolMaster: {
id: "ToolMaster",
name: "Tool Master",
description: "Used 10 different tools efficiently",
icon: "🛠️",
rarity: "rare",
maxProgress: 10,
},
// ============================================
// Time-Based Achievements
// ============================================
EarlyBird: {
id: "EarlyBird",
name: "Early Bird",
description: "Started a session between 5 AM and 7 AM",
icon: "🐦",
rarity: "common",
},
NightOwl: {
id: "NightOwl",
name: "Night Owl",
description: "Coding after midnight",
icon: "🦉",
rarity: "common",
},
AllNighter: {
id: "AllNighter",
name: "All Nighter",
description: "Worked through the night (2 AM - 5 AM)",
icon: "🌙",
rarity: "rare",
},
WeekendWarrior: {
id: "WeekendWarrior",
name: "Weekend Warrior",
description: "Coding on a weekend",
icon: "⚔️",
rarity: "common",
},
DedicatedDeveloper: {
id: "DedicatedDeveloper",
name: "Dedicated Developer",
description: "Coded for 30 days in a row",
icon: "🏆",
rarity: "legendary",
maxProgress: 30,
},
// ============================================
// Search and Exploration
// ============================================
Explorer: {
id: "Explorer",
name: "Explorer",
description: "Used search tools 50 times",
icon: "🔍",
rarity: "common",
maxProgress: 50,
},
MasterSearcher: {
id: "MasterSearcher",
name: "Master Searcher",
description: "Searched 500 times across files",
icon: "🕵️‍♀️",
rarity: "rare",
maxProgress: 500,
},
// ============================================
// Session Achievements
// ============================================
QuickSession: {
id: "QuickSession",
name: "Quick Session",
description: "Completed a productive session in under 5 minutes",
icon: "⚡",
rarity: "common",
},
FocusedWork: {
id: "FocusedWork",
name: "Focused Work",
description: "Worked for 30 minutes straight",
icon: "🎯",
rarity: "common",
},
DeepDive: {
id: "DeepDive",
name: "Deep Dive",
description: "Worked for 2 hours continuously",
icon: "🏊‍♀️",
rarity: "rare",
},
MarathonSession: {
id: "MarathonSession",
name: "Marathon Session",
description: "5+ hour coding session!",
icon: "🏃‍♀️",
rarity: "epic",
},
UltraMarathon: {
id: "UltraMarathon",
name: "Ultra Marathon",
description: "8 hour coding session!",
icon: "🦸‍♀️",
rarity: "epic",
},
CodingRetreat: {
id: "CodingRetreat",
name: "Coding Retreat",
description: "12 hour coding session - take care of yourself!",
icon: "🏕️",
rarity: "legendary",
},
// ============================================
// Special Achievements
// ============================================
FirstMessage: {
id: "FirstMessage",
name: "First Message",
description: "Sent your first message to Hikari",
icon: "✨",
rarity: "common",
maxProgress: 1,
},
FirstTool: {
id: "FirstTool",
name: "First Tool",
description: "Used your first tool",
icon: "🔧",
rarity: "common",
maxProgress: 1,
},
FirstCodeBlock: {
id: "FirstCodeBlock",
name: "First Code",
description: "Generated your first code block",
icon: "📦",
rarity: "common",
maxProgress: 1,
},
FirstFileEdit: {
id: "FirstFileEdit",
name: "First Edit",
description: "Made your first file edit",
icon: "✏️",
rarity: "common",
maxProgress: 1,
},
Polyglot: {
id: "Polyglot",
name: "Polyglot",
description: "Generated code in 5+ languages in one session",
icon: "🌍",
rarity: "rare",
maxProgress: 5,
},
SpeedCoder: {
id: "SpeedCoder",
name: "Speed Coder",
description: "Generated 10 code blocks in 10 minutes",
icon: "🚀",
rarity: "rare",
},
ClaudeConnoisseur: {
id: "ClaudeConnoisseur",
name: "Claude Connoisseur",
description: "Used all available Claude models",
icon: "🎨",
rarity: "epic",
maxProgress: 5,
},
MarathonCoder: {
id: "MarathonCoder",
name: "Marathon Coder",
description: "10,000 tokens in a single session",
icon: "🏃‍♂️",
rarity: "epic",
maxProgress: 10000,
},
// ============================================
// Relationship & Greetings
// ============================================
GoodMorning: {
id: "GoodMorning",
name: "Good Morning!",
description: "Greeted Hikari with a good morning",
icon: "🌅",
rarity: "common",
maxProgress: 1,
},
GoodNight: {
id: "GoodNight",
name: "Sweet Dreams",
description: "Said good night to Hikari",
icon: "🌃",
rarity: "common",
maxProgress: 1,
},
ThankYou: {
id: "ThankYou",
name: "Grateful Heart",
description: "Thanked Hikari for her help",
icon: "🙏",
rarity: "common",
maxProgress: 1,
},
LoveYou: {
id: "LoveYou",
name: "Heartfelt",
description: "Expressed love to Hikari",
icon: "💕",
rarity: "rare",
maxProgress: 1,
},
HelloHikari: {
id: "HelloHikari",
name: "Hello Hikari!",
description: "Greeted Hikari by name",
icon: "👋",
rarity: "common",
maxProgress: 1,
},
HowAreYou: {
id: "HowAreYou",
name: "Caring Friend",
description: "Asked how Hikari is doing",
icon: "💭",
rarity: "common",
maxProgress: 1,
},
MissedYou: {
id: "MissedYou",
name: "Missed You",
description: "Told Hikari you missed her",
icon: "🥺",
rarity: "rare",
maxProgress: 1,
},
BackAgain: {
id: "BackAgain",
name: "Back Again",
description: "Announced your return",
icon: "🔙",
rarity: "common",
maxProgress: 1,
},
// ============================================
// Personality & Fun
// ============================================
EmojiUser: {
id: "EmojiUser",
name: "Emoji User",
description: "Used an emoji in a message",
icon: "😄",
rarity: "common",
maxProgress: 1,
},
QuestionMaster: {
id: "QuestionMaster",
name: "Question Master",
description: "Asked 20 questions with ?",
icon: "❓",
rarity: "common",
maxProgress: 20,
},
CapsLock: {
id: "CapsLock",
name: "CAPS LOCK",
description: "SENT A MESSAGE IN ALL CAPS",
icon: "🔊",
rarity: "common",
maxProgress: 1,
},
PleaseAndThankYou: {
id: "PleaseAndThankYou",
name: "Polite Programmer",
description: "Uses please in messages",
icon: "🎩",
rarity: "common",
maxProgress: 1,
},
// ============================================
// Emotional
// ============================================
Frustrated: {
id: "Frustrated",
name: "Frustrated",
description: "Expressed frustration (we all have those days)",
icon: "😤",
rarity: "common",
maxProgress: 1,
},
Excited: {
id: "Excited",
name: "Excited",
description: "Expressed excitement!",
icon: "🎉",
rarity: "common",
maxProgress: 1,
},
Confused: {
id: "Confused",
name: "Confused",
description: "Expressed confusion",
icon: "😕",
rarity: "common",
maxProgress: 1,
},
Curious: {
id: "Curious",
name: "Curious Mind",
description: "Asked why or how something works",
icon: "🤔",
rarity: "common",
maxProgress: 1,
},
Impressed: {
id: "Impressed",
name: "Impressed",
description: "Was impressed by something",
icon: "😲",
rarity: "common",
maxProgress: 1,
},
// ============================================
// Git & Development
// ============================================
GitGuru: {
id: "GitGuru",
name: "Git Guru",
description: "Used git commands 10 times",
icon: "🌲",
rarity: "common",
maxProgress: 10,
},
TestWriter: {
id: "TestWriter",
name: "Test Writer",
description: "Created test files",
icon: "🧪",
rarity: "common",
maxProgress: 1,
},
Debugger: {
id: "Debugger",
name: "Debugger",
description: "Fixed bugs (messages with fix, bug, error)",
icon: "🐛",
rarity: "common",
maxProgress: 1,
},
CommitKing: {
id: "CommitKing",
name: "Commit King",
description: "Made 50 commits",
icon: "📝",
rarity: "rare",
maxProgress: 50,
},
CommitLegend: {
id: "CommitLegend",
name: "Commit Legend",
description: "Made 200 commits!",
icon: "🏅",
rarity: "epic",
maxProgress: 200,
},
BranchMaster: {
id: "BranchMaster",
name: "Branch Master",
description: "Created 10 branches",
icon: "🌿",
rarity: "rare",
maxProgress: 10,
},
MergeExpert: {
id: "MergeExpert",
name: "Merge Expert",
description: "Merged 20 PRs",
icon: "🔀",
rarity: "rare",
maxProgress: 20,
},
ConflictResolver: {
id: "ConflictResolver",
name: "Conflict Resolver",
description: "Resolved merge conflicts",
icon: "🤝",
rarity: "rare",
maxProgress: 1,
},
// ============================================
// Tool Mastery
// ============================================
BashMaster: {
id: "BashMaster",
name: "Bash Master",
description: "Used Bash tool 50 times",
icon: "💻",
rarity: "rare",
maxProgress: 50,
},
FileExplorer: {
id: "FileExplorer",
name: "File Explorer",
description: "Used Read tool 100 times",
icon: "📂",
rarity: "rare",
maxProgress: 100,
},
SearchExpert: {
id: "SearchExpert",
name: "Search Expert",
description: "Used Grep tool 50 times",
icon: "🔎",
rarity: "rare",
maxProgress: 50,
},
EditMaster: {
id: "EditMaster",
name: "Edit Master",
description: "Used Edit tool 100 times",
icon: "✂️",
rarity: "rare",
maxProgress: 100,
},
WriteMaster: {
id: "WriteMaster",
name: "Write Master",
description: "Used Write tool 50 times",
icon: "📝",
rarity: "rare",
maxProgress: 50,
},
GlobMaster: {
id: "GlobMaster",
name: "Glob Master",
description: "Used Glob tool 100 times",
icon: "🌐",
rarity: "rare",
maxProgress: 100,
},
TaskMaster: {
id: "TaskMaster",
name: "Task Master",
description: "Used Task tool 50 times",
icon: "📋",
rarity: "rare",
maxProgress: 50,
},
WebFetcher: {
id: "WebFetcher",
name: "Web Fetcher",
description: "Used WebFetch tool 20 times",
icon: "🌐",
rarity: "rare",
maxProgress: 20,
},
McpExplorer: {
id: "McpExplorer",
name: "MCP Explorer",
description: "Used MCP tools 50 times",
icon: "🔮",
rarity: "epic",
maxProgress: 50,
},
// ============================================
// Daily Streaks
// ============================================
WeekStreak: {
id: "WeekStreak",
name: "Week Streak",
description: "7 days in a row!",
icon: "📅",
rarity: "common",
maxProgress: 7,
},
TwoWeekStreak: {
id: "TwoWeekStreak",
name: "Two Week Streak",
description: "14 days in a row!",
icon: "🗓️",
rarity: "rare",
maxProgress: 14,
},
MonthStreak: {
id: "MonthStreak",
name: "Month Streak",
description: "30 days in a row!",
icon: "📆",
rarity: "epic",
maxProgress: 30,
},
QuarterStreak: {
id: "QuarterStreak",
name: "Quarter Streak",
description: "90 days in a row - incredible dedication!",
icon: "🎖️",
rarity: "legendary",
maxProgress: 90,
},
// ============================================
// Time Challenges
// ============================================
MorningPerson: {
id: "MorningPerson",
name: "Morning Person",
description: "10 sessions started before 9 AM",
icon: "🌄",
rarity: "common",
maxProgress: 10,
},
NightCoder: {
id: "NightCoder",
name: "Night Coder",
description: "10 sessions after 10 PM",
icon: "🌃",
rarity: "common",
maxProgress: 10,
},
LunchBreakCoder: {
id: "LunchBreakCoder",
name: "Lunch Break Coder",
description: "Session during 12-1 PM",
icon: "🍱",
rarity: "common",
maxProgress: 1,
},
CoffeeTime: {
id: "CoffeeTime",
name: "Coffee Time",
description: "Session during 3-4 PM",
icon: "☕",
rarity: "common",
maxProgress: 1,
},
// ============================================
// Day-Specific
// ============================================
MondayMotivation: {
id: "MondayMotivation",
name: "Monday Motivation",
description: "Coding on Monday",
icon: "💪",
rarity: "common",
maxProgress: 1,
},
FridayFinisher: {
id: "FridayFinisher",
name: "Friday Finisher",
description: "Coding on Friday",
icon: "🎊",
rarity: "common",
maxProgress: 1,
},
HumpDay: {
id: "HumpDay",
name: "Hump Day",
description: "Coding on Wednesday",
icon: "🐫",
rarity: "common",
maxProgress: 1,
},
// ============================================
// Seasonal/Special Times
// ============================================
NewYearCoder: {
id: "NewYearCoder",
name: "New Year Coder",
description: "Coding on January 1st",
icon: "🎆",
rarity: "rare",
maxProgress: 1,
},
ValentinesDev: {
id: "ValentinesDev",
name: "Valentine's Dev",
description: "Coding on February 14th",
icon: "💝",
rarity: "rare",
maxProgress: 1,
},
SpookyCode: {
id: "SpookyCode",
name: "Spooky Code",
description: "Coding on October 31st",
icon: "🎃",
rarity: "rare",
maxProgress: 1,
},
HolidayCoder: {
id: "HolidayCoder",
name: "Holiday Coder",
description: "Coding on December 25th",
icon: "🎄",
rarity: "rare",
maxProgress: 1,
},
LeapDayCoder: {
id: "LeapDayCoder",
name: "Leap Day Coder",
description: "Coding on February 29th - rare!",
icon: "🐸",
rarity: "epic",
maxProgress: 1,
},
// ============================================
// Message Content
// ============================================
LongMessage: {
id: "LongMessage",
name: "Long Message",
description: "Sent a message over 500 characters",
icon: "📜",
rarity: "common",
maxProgress: 1,
},
NovelWriter: {
id: "NovelWriter",
name: "Novel Writer",
description: "Sent a message over 2000 characters",
icon: "📖",
rarity: "rare",
maxProgress: 1,
},
ShortAndSweet: {
id: "ShortAndSweet",
name: "Short and Sweet",
description: "Completed a task with messages under 50 chars each",
icon: "🍬",
rarity: "rare",
maxProgress: 1,
},
CodeInMessage: {
id: "CodeInMessage",
name: "Code in Message",
description: "Included code block in your message",
icon: "💻",
rarity: "common",
maxProgress: 1,
},
MarkdownMaster: {
id: "MarkdownMaster",
name: "Markdown Master",
description: "Used markdown formatting in message",
icon: "📋",
rarity: "common",
maxProgress: 1,
},
// ============================================
// Programming Languages
// ============================================
RustDeveloper: {
id: "RustDeveloper",
name: "Rust Developer",
description: "Generated Rust code",
icon: "🦀",
rarity: "common",
maxProgress: 1,
},
PythonDeveloper: {
id: "PythonDeveloper",
name: "Python Developer",
description: "Generated Python code",
icon: "🐍",
rarity: "common",
maxProgress: 1,
},
JavaScriptDev: {
id: "JavaScriptDev",
name: "JavaScript Dev",
description: "Generated JavaScript code",
icon: "📜",
rarity: "common",
maxProgress: 1,
},
TypeScriptDev: {
id: "TypeScriptDev",
name: "TypeScript Dev",
description: "Generated TypeScript code",
icon: "💎",
rarity: "common",
maxProgress: 1,
},
GoDeveloper: {
id: "GoDeveloper",
name: "Go Developer",
description: "Generated Go code",
icon: "🐹",
rarity: "common",
maxProgress: 1,
},
CppDeveloper: {
id: "CppDeveloper",
name: "C++ Developer",
description: "Generated C++ code",
icon: "⚙️",
rarity: "common",
maxProgress: 1,
},
JavaDeveloper: {
id: "JavaDeveloper",
name: "Java Developer",
description: "Generated Java code",
icon: "☕",
rarity: "common",
maxProgress: 1,
},
HtmlCssDev: {
id: "HtmlCssDev",
name: "HTML/CSS Dev",
description: "Generated HTML/CSS code",
icon: "🎨",
rarity: "common",
maxProgress: 1,
},
SqlDeveloper: {
id: "SqlDeveloper",
name: "SQL Developer",
description: "Generated SQL code",
icon: "🗄️",
rarity: "common",
maxProgress: 1,
},
ShellScripter: {
id: "ShellScripter",
name: "Shell Scripter",
description: "Generated shell/bash scripts",
icon: "🐚",
rarity: "common",
maxProgress: 1,
},
FullStackDev: {
id: "FullStackDev",
name: "Full Stack Dev",
description: "Generated code in 10+ languages",
icon: "🌈",
rarity: "epic",
maxProgress: 10,
},
// ============================================
// Project Types
// ============================================
FrontendDev: {
id: "FrontendDev",
name: "Frontend Dev",
description: "Worked on frontend files",
icon: "🖼️",
rarity: "common",
maxProgress: 1,
},
BackendDev: {
id: "BackendDev",
name: "Backend Dev",
description: "Worked on backend files",
icon: "⚙️",
rarity: "common",
maxProgress: 1,
},
ConfigEditor: {
id: "ConfigEditor",
name: "Config Editor",
description: "Edited config files",
icon: "⚙️",
rarity: "common",
maxProgress: 1,
},
DocWriter: {
id: "DocWriter",
name: "Doc Writer",
description: "Edited documentation",
icon: "📚",
rarity: "common",
maxProgress: 1,
},
// ============================================
// Error Handling
// ============================================
ErrorHunter: {
id: "ErrorHunter",
name: "Error Hunter",
description: "Fixed 10 errors",
icon: "🎯",
rarity: "common",
maxProgress: 10,
},
ExceptionSlayer: {
id: "ExceptionSlayer",
name: "Exception Slayer",
description: "Fixed 50 errors",
icon: "⚔️",
rarity: "rare",
maxProgress: 50,
},
BugExterminator: {
id: "BugExterminator",
name: "Bug Exterminator",
description: "Fixed 100 bugs",
icon: "🪲",
rarity: "epic",
maxProgress: 100,
},
// ============================================
// Refactoring
// ============================================
CleanCoder: {
id: "CleanCoder",
name: "Clean Coder",
description: "Refactored code",
icon: "🧹",
rarity: "common",
maxProgress: 1,
},
Optimizer: {
id: "Optimizer",
name: "Optimizer",
description: "Optimized performance",
icon: "⚡",
rarity: "rare",
maxProgress: 1,
},
Simplifier: {
id: "Simplifier",
name: "Simplifier",
description: "Simplified complex code",
icon: "✨",
rarity: "rare",
maxProgress: 1,
},
// ============================================
// Testing
// ============================================
TestNovice: {
id: "TestNovice",
name: "Test Novice",
description: "Wrote 10 tests",
icon: "🧪",
rarity: "common",
maxProgress: 10,
},
TestEnthusiast: {
id: "TestEnthusiast",
name: "Test Enthusiast",
description: "Wrote 50 tests",
icon: "🔬",
rarity: "rare",
maxProgress: 50,
},
TestMaster: {
id: "TestMaster",
name: "Test Master",
description: "Wrote 100 tests",
icon: "🏆",
rarity: "epic",
maxProgress: 100,
},
CoverageKing: {
id: "CoverageKing",
name: "Coverage King",
description: "Achieved test coverage mentions",
icon: "👑",
rarity: "epic",
maxProgress: 1,
},
// ============================================
// Documentation
// ============================================
Documenter: {
id: "Documenter",
name: "Documenter",
description: "Wrote documentation",
icon: "📝",
rarity: "common",
maxProgress: 1,
},
CommentWriter: {
id: "CommentWriter",
name: "Comment Writer",
description: "Added comments to code",
icon: "💬",
rarity: "common",
maxProgress: 1,
},
ReadmeHero: {
id: "ReadmeHero",
name: "README Hero",
description: "Created/edited README files",
icon: "📖",
rarity: "common",
maxProgress: 1,
},
// ============================================
// API & Integration
// ============================================
ApiExplorer: {
id: "ApiExplorer",
name: "API Explorer",
description: "Worked with APIs",
icon: "🔌",
rarity: "common",
maxProgress: 1,
},
DatabaseDev: {
id: "DatabaseDev",
name: "Database Dev",
description: "Worked with databases",
icon: "🗄️",
rarity: "common",
maxProgress: 1,
},
CloudCoder: {
id: "CloudCoder",
name: "Cloud Coder",
description: "Worked with cloud services",
icon: "☁️",
rarity: "common",
maxProgress: 1,
},
// ============================================
// Special Milestones
// ============================================
CenturyClub: {
id: "CenturyClub",
name: "Century Club",
description: "100 sessions",
icon: "💯",
rarity: "rare",
maxProgress: 100,
},
ThousandSessions: {
id: "ThousandSessions",
name: "Thousand Sessions",
description: "1000 sessions!",
icon: "🎊",
rarity: "epic",
maxProgress: 1000,
},
Veteran: {
id: "Veteran",
name: "Veteran",
description: "Used Hikari for 30+ days total",
icon: "🎖️",
rarity: "rare",
maxProgress: 30,
},
OldTimer: {
id: "OldTimer",
name: "Old Timer",
description: "Used Hikari for 90+ days total",
icon: "🧓",
rarity: "epic",
maxProgress: 90,
},
Loyalist: {
id: "Loyalist",
name: "Loyalist",
description: "Used Hikari for 365+ days total",
icon: "🏆",
rarity: "legendary",
maxProgress: 365,
},
// ============================================
// Fun & Easter Eggs
// ============================================
Perfectionist: {
id: "Perfectionist",
name: "Perfectionist",
description: "Redo something 5 times",
icon: "✨",
rarity: "common",
maxProgress: 5,
},
Persistent: {
id: "Persistent",
name: "Persistent",
description: "Asked the same question 3 times",
icon: "🔄",
rarity: "common",
maxProgress: 3,
},
Patient: {
id: "Patient",
name: "Patient",
description: "Waited for a long response",
icon: "⏳",
rarity: "common",
maxProgress: 1,
},
Speedy: {
id: "Speedy",
name: "Speedy",
description: "Sent 10 messages in 1 minute",
icon: "💨",
rarity: "rare",
maxProgress: 10,
},
// ============================================
// UI Exploration
// ============================================
MultiTasker: {
id: "MultiTasker",
name: "Multi-Tasker",
description: "Used multiple conversations",
icon: "🪟",
rarity: "common",
maxProgress: 1,
},
Minimalist: {
id: "Minimalist",
name: "Minimalist",
description: "Used compact mode",
icon: "📦",
rarity: "common",
maxProgress: 1,
},
PrivacyFirst: {
id: "PrivacyFirst",
name: "Privacy First",
description: "Used streamer mode",
icon: "🔒",
rarity: "common",
maxProgress: 1,
},
ThemeChanger: {
id: "ThemeChanger",
name: "Theme Changer",
description: "Changed themes",
icon: "🎨",
rarity: "common",
maxProgress: 1,
},
SettingsTweaker: {
id: "SettingsTweaker",
name: "Settings Tweaker",
description: "Adjusted settings",
icon: "⚙️",
rarity: "common",
maxProgress: 1,
},
// ============================================
// Achievement Meta
// ============================================
AchievementHunter: {
id: "AchievementHunter",
name: "Achievement Hunter",
description: "Unlocked 25 achievements",
icon: "🏅",
rarity: "rare",
maxProgress: 25,
},
Completionist: {
id: "Completionist",
name: "Completionist",
description: "Unlocked 50 achievements",
icon: "🏆",
rarity: "epic",
maxProgress: 50,
},
MasterUnlocker: {
id: "MasterUnlocker",
name: "Master Unlocker",
description: "Unlocked 100 achievements",
icon: "🔓",
rarity: "epic",
maxProgress: 100,
},
PlatinumStatus: {
id: "PlatinumStatus",
name: "Platinum Status",
description: "Unlocked all achievements!",
icon: "💎",
rarity: "legendary",
},
// ============================================
// Clipboard & Snippets
// ============================================
ClipboardCollector: {
id: "ClipboardCollector",
name: "Clipboard Collector",
description: "Used clipboard history",
icon: "📋",
rarity: "common",
maxProgress: 1,
},
SnippetCreator: {
id: "SnippetCreator",
name: "Snippet Creator",
description: "Created a snippet",
icon: "✂️",
rarity: "common",
maxProgress: 1,
},
SnippetMaster: {
id: "SnippetMaster",
name: "Snippet Master",
description: "Created 20 snippets",
icon: "📚",
rarity: "rare",
maxProgress: 20,
},
// ============================================
// Other Features
// ============================================
QuickActionUser: {
id: "QuickActionUser",
name: "Quick Action User",
description: "Used quick actions",
icon: "⚡",
rarity: "common",
maxProgress: 1,
},
HistoryBuff: {
id: "HistoryBuff",
name: "History Buff",
description: "Browsed history",
icon: "📜",
rarity: "common",
maxProgress: 1,
},
Archivist: {
id: "Archivist",
name: "Archivist",
description: "Exported sessions",
icon: "🗃️",
rarity: "common",
maxProgress: 1,
},
SessionExporter: {
id: "SessionExporter",
name: "Session Exporter",
description: "Exported a session",
icon: "📤",
rarity: "common",
maxProgress: 1,
},
GitPanelUser: {
id: "GitPanelUser",
name: "Git Panel User",
description: "Used the Git panel",
icon: "🌲",
rarity: "common",
maxProgress: 1,
},
FeatureExplorer: {
id: "FeatureExplorer",
name: "Feature Explorer",
description: "Used 10+ features",
icon: "🗺️",
rarity: "rare",
maxProgress: 10,
},
};
// Initialize all achievements as locked
const initialAchievements: Record<AchievementId, Achievement> = {} as Record<
AchievementId,
Achievement
>;
for (const [id, def] of Object.entries(achievementDefinitions)) {
initialAchievements[id as AchievementId] = {
...def,
unlocked: false,
progress: 0,
};
}
// Create the main store
function createAchievementsStore() {
const { subscribe, update } = writable<AchievementState>({
achievements: initialAchievements,
totalUnlocked: 0,
lastUnlocked: null,
});
return {
subscribe,
unlockAchievement: (event: AchievementUnlockedEvent, playSound: boolean = true) => {
update((state) => {
const achievement = state.achievements[event.achievement.id];
if (achievement && !achievement.unlocked) {
achievement.unlocked = true;
achievement.unlockedAt = event.achievement.unlocked_at
? new Date(event.achievement.unlocked_at)
: new Date();
state.totalUnlocked++;
state.lastUnlocked = achievement;
// Play achievement sound only for new unlocks, not when loading saved ones
if (playSound) {
try {
playAchievementSound();
} catch (error) {
console.error("Failed to play achievement sound:", error);
}
}
}
return state;
});
},
updateProgress: (id: AchievementId, progress: number) => {
update((state) => {
const achievement = state.achievements[id];
if (achievement) {
achievement.progress = progress;
}
return state;
});
},
reset: () => {
update(() => ({
achievements: initialAchievements,
totalUnlocked: 0,
lastUnlocked: null,
}));
},
};
}
export const achievementsStore = createAchievementsStore();
// Derived stores for different views
export const unlockedAchievements = derived(achievementsStore, ($store) =>
Object.values($store.achievements).filter((a) => a.unlocked)
);
export const lockedAchievements = derived(achievementsStore, ($store) =>
Object.values($store.achievements).filter((a) => !a.unlocked)
);
export const achievementsByRarity = derived(achievementsStore, ($store) => {
const byRarity: Record<string, Achievement[]> = {
common: [],
rare: [],
epic: [],
legendary: [],
};
for (const achievement of Object.values($store.achievements)) {
byRarity[achievement.rarity].push(achievement);
}
return byRarity;
});
export const achievementProgress = derived(achievementsStore, ($store) => {
const total = Object.keys($store.achievements).length;
return {
unlocked: $store.totalUnlocked,
total,
percentage: Math.round(($store.totalUnlocked / total) * 100),
};
});
// Initialize achievement listener
export async function initAchievementsListener() {
// Listen for achievement unlocked events
await listen<AchievementUnlockedEvent>("achievement:unlocked", (event) => {
achievementsStore.unlockAchievement(event.payload);
});
// Load saved achievements from persistent storage
try {
const savedAchievements = await invoke<AchievementUnlockedEvent[]>("load_saved_achievements");
// Update the store with saved achievements (don't play sounds)
for (const event of savedAchievements) {
achievementsStore.unlockAchievement(event, false);
}
} catch (error) {
console.error("Failed to load saved achievements:", error);
}
}
// Export achievement categories for the display panel
export const achievementCategories = [
{
name: "Token Milestones",
description: "Track your token generation progress",
ids: [
"FirstSteps",
"GrowingStrong",
"BlossomingCoder",
"TokenMaster",
"TokenBillionaire",
"TokenTreasure",
] as AchievementId[],
},
{
name: "Code Generation",
description: "Achievements for generating code",
ids: [
"HelloWorld",
"CodeWizard",
"ThousandBlocks",
"CodeFactory",
"CodeEmpire",
] as AchievementId[],
},
{
name: "File Operations",
description: "Working with files and projects",
ids: ["FileManipulator", "FileArchitect", "FileEngineer", "FileLegend"] as AchievementId[],
},
{
name: "Conversations",
description: "Building our relationship through chat",
ids: [
"ConversationStarter",
"ChattyKathy",
"Conversationalist",
"ChatMarathon",
"ChatLegend",
] as AchievementId[],
},
{
name: "Tools & Skills",
description: "Mastering different tools",
ids: ["Toolsmith", "ToolMaster"] as AchievementId[],
},
{
name: "Time-Based",
description: "When you code matters too!",
ids: [
"EarlyBird",
"NightOwl",
"AllNighter",
"WeekendWarrior",
"DedicatedDeveloper",
] as AchievementId[],
},
{
name: "Search & Explore",
description: "Finding what you need",
ids: ["Explorer", "MasterSearcher"] as AchievementId[],
},
{
name: "Session Records",
description: "Your coding session achievements",
ids: [
"QuickSession",
"FocusedWork",
"DeepDive",
"MarathonSession",
"UltraMarathon",
"CodingRetreat",
"MarathonCoder",
] as AchievementId[],
},
{
name: "Relationship & Greetings",
description: "Our special moments together",
ids: [
"GoodMorning",
"GoodNight",
"ThankYou",
"LoveYou",
"HelloHikari",
"HowAreYou",
"MissedYou",
"BackAgain",
] as AchievementId[],
},
{
name: "Personality & Fun",
description: "Express yourself!",
ids: ["EmojiUser", "CapsLock", "QuestionMaster", "PleaseAndThankYou"] as AchievementId[],
},
{
name: "Emotional",
description: "All the feels",
ids: ["Frustrated", "Excited", "Confused", "Curious", "Impressed"] as AchievementId[],
},
{
name: "Git & Development",
description: "Version control mastery",
ids: [
"GitGuru",
"TestWriter",
"Debugger",
"CommitKing",
"CommitLegend",
"BranchMaster",
"MergeExpert",
"ConflictResolver",
] as AchievementId[],
},
{
name: "Tool Mastery",
description: "Master of all tools",
ids: [
"BashMaster",
"FileExplorer",
"SearchExpert",
"EditMaster",
"WriteMaster",
"GlobMaster",
"TaskMaster",
"WebFetcher",
"McpExplorer",
] as AchievementId[],
},
{
name: "Daily Streaks",
description: "Keep the momentum going!",
ids: ["WeekStreak", "TwoWeekStreak", "MonthStreak", "QuarterStreak"] as AchievementId[],
},
{
name: "Time Challenges",
description: "When you code",
ids: ["MorningPerson", "NightCoder", "LunchBreakCoder", "CoffeeTime"] as AchievementId[],
},
{
name: "Day-Specific",
description: "Special days for coding",
ids: ["MondayMotivation", "FridayFinisher", "HumpDay"] as AchievementId[],
},
{
name: "Seasonal",
description: "Holiday coding!",
ids: [
"NewYearCoder",
"ValentinesDev",
"SpookyCode",
"HolidayCoder",
"LeapDayCoder",
] as AchievementId[],
},
{
name: "Message Content",
description: "How you communicate",
ids: [
"LongMessage",
"NovelWriter",
"ShortAndSweet",
"CodeInMessage",
"MarkdownMaster",
] as AchievementId[],
},
{
name: "Programming Languages",
description: "Languages you've used",
ids: [
"RustDeveloper",
"PythonDeveloper",
"JavaScriptDev",
"TypeScriptDev",
"GoDeveloper",
"CppDeveloper",
"JavaDeveloper",
"HtmlCssDev",
"SqlDeveloper",
"ShellScripter",
"FullStackDev",
] as AchievementId[],
},
{
name: "Project Types",
description: "Types of work you do",
ids: ["FrontendDev", "BackendDev", "ConfigEditor", "DocWriter"] as AchievementId[],
},
{
name: "Error Handling",
description: "Bug squashing achievements",
ids: ["ErrorHunter", "ExceptionSlayer", "BugExterminator"] as AchievementId[],
},
{
name: "Refactoring",
description: "Code improvement",
ids: ["CleanCoder", "Optimizer", "Simplifier"] as AchievementId[],
},
{
name: "Testing",
description: "Test-driven achievements",
ids: ["TestNovice", "TestEnthusiast", "TestMaster", "CoverageKing"] as AchievementId[],
},
{
name: "Documentation",
description: "Document your work",
ids: ["Documenter", "CommentWriter", "ReadmeHero"] as AchievementId[],
},
{
name: "API & Integration",
description: "Working with services",
ids: ["ApiExplorer", "DatabaseDev", "CloudCoder"] as AchievementId[],
},
{
name: "Special Milestones",
description: "Long-term achievements",
ids: ["CenturyClub", "ThousandSessions", "Veteran", "OldTimer", "Loyalist"] as AchievementId[],
},
{
name: "Fun & Easter Eggs",
description: "Hidden gems",
ids: ["Perfectionist", "Persistent", "Patient", "Speedy"] as AchievementId[],
},
{
name: "UI Exploration",
description: "Explore Hikari's features",
ids: [
"MultiTasker",
"Minimalist",
"PrivacyFirst",
"ThemeChanger",
"SettingsTweaker",
] as AchievementId[],
},
{
name: "Achievement Meta",
description: "Achievements about achievements",
ids: [
"AchievementHunter",
"Completionist",
"MasterUnlocker",
"PlatinumStatus",
] as AchievementId[],
},
{
name: "Clipboard & Snippets",
description: "Clipboard and snippet features",
ids: ["ClipboardCollector", "SnippetCreator", "SnippetMaster"] as AchievementId[],
},
{
name: "Other Features",
description: "Miscellaneous features",
ids: [
"QuickActionUser",
"HistoryBuff",
"Archivist",
"SessionExporter",
"GitPanelUser",
"FeatureExplorer",
] as AchievementId[],
},
{
name: "Special",
description: "Unique accomplishments",
ids: [
"FirstMessage",
"FirstTool",
"FirstCodeBlock",
"FirstFileEdit",
"Polyglot",
"SpeedCoder",
"ClaudeConnoisseur",
] as AchievementId[],
},
];