feat(companion): rename Tabby companion to Sagi, replace cat avatar with mascot

Full rename across files, identifiers, CSS classes, and user-facing strings
(en/vi locales, ARCHITECTURE.md). Replaces the hand-drawn animated cat
avatar with the pig-superhero mascot artwork, split into parts to keep
per-eye cursor tracking and blink animation working; whole-mascot mood
transforms (breathe/bob/shake/tilt) and new zzz/bang/sparkle overlays
carry the rest of the mood expression since the traced art has no shared
palette to key off of.
This commit is contained in:
2026-08-19 11:07:59 +07:00
parent dce54c8a3a
commit 4cc39f5069
25 changed files with 1390 additions and 1041 deletions
+19 -19
View File
@@ -23,7 +23,7 @@
* ## Internal dependencies
* - `../lib/api`
* - `../lib/eventBus`
* - `../components/Tabby/prefs`
* - `../components/Sagi/prefs`
* - `../lib/format`
* - `../lib/push`
* - `../components/Tip`
@@ -98,7 +98,7 @@ import {
Settings as SettingsIcon,
FolderOpen,
Info,
Cat,
PiggyBank,
History,
ChevronLeft,
ChevronRight,
@@ -107,7 +107,7 @@ import {
import { api } from "../lib/api";
import { eventBus } from "../lib/eventBus";
import { isRemoteDataRefreshMessage } from "../lib/remoteDataEvents";
import { tabbyPrefs } from "../components/Tabby/prefs";
import { sagiPrefs } from "../components/Sagi/prefs";
import { fmt, fmtCost, getCurrentLocale } from "../lib/format";
import { subscribeToPush, unsubscribeFromPush } from "../lib/push";
import { Tip } from "../components/Tip";
@@ -136,7 +136,7 @@ const SETTINGS_SECTIONS: {
fallback: "Remote Data Sources",
Icon: Cloud,
},
{ id: "tabby", labelKey: "tabby.title", fallback: "Tabby", Icon: Cat },
{ id: "sagi", labelKey: "sagi.title", fallback: "Sagi", Icon: PiggyBank },
{ id: "notifications", labelKey: "notifications.title", Icon: Bell },
{ id: "alerts", labelKey: "alertsHub.title", Icon: BellRing },
{ id: "data", labelKey: "data.title", Icon: Database },
@@ -457,10 +457,10 @@ export function Settings() {
} | null>(null);
const [confirmAction, setConfirmAction] = useState<string | null>(null);
const [notifPrefs, setNotifPrefs] = useState<NotifPrefs>(loadNotifPrefs);
const [tabbyEnabled, setTabbyEnabled] = useState(() => tabbyPrefs.getEnabled());
const setTabby = useCallback((v: boolean) => {
tabbyPrefs.setEnabled(v);
setTabbyEnabled(v);
const [sagiEnabled, setSagiEnabled] = useState(() => sagiPrefs.getEnabled());
const setSagi = useCallback((v: boolean) => {
sagiPrefs.setEnabled(v);
setSagiEnabled(v);
}, []);
const [abandonHours, setAbandonHours] = useState("24");
const [purgeDays, setPurgeDays] = useState("90");
@@ -1466,37 +1466,37 @@ export function Settings() {
<RemoteSources />
</section>
{/* ─── TABBY COMPANION ─── */}
<section id="tabby" className="scroll-mt-24">
{/* ─── SAGI COMPANION ─── */}
<section id="sagi" className="scroll-mt-24">
<h3 className="text-sm font-medium text-fg-secondary flex items-center gap-2 mb-1">
<span className="text-base leading-none" aria-hidden>
🐾
🦸
</span>
{t("tabby.title", "Tabby companion")}
{t("sagi.title", "Sagi companion")}
</h3>
<p className="text-xs text-fg-muted mb-4">
{t("tabby.description", "A floating cat that reacts to your live sessions.")}
{t("sagi.description", "A floating mascot that reacts to your live sessions.")}
</p>
<div className="card p-5">
<div className="flex items-center gap-3">
<div
className={`w-10 h-10 rounded-xl flex items-center justify-center transition-colors ${
tabbyEnabled
sagiEnabled
? "bg-blue-600/10 border border-blue-600/20"
: "bg-surface-2 border border-border"
}`}
>
<span className="text-lg leading-none" aria-hidden>
🐾
🦸
</span>
</div>
<Toggle
checked={tabbyEnabled}
onChange={setTabby}
label={t("tabby.enable", "Show Tabby")}
checked={sagiEnabled}
onChange={setSagi}
label={t("sagi.enable", "Show Sagi")}
description={t(
"tabby.enableDesc",
"sagi.enableDesc",
"Display the corner companion across the dashboard (⌘B to open)"
)}
/>