refactor: rename mascot to chatbot across entire codebase
Build & Deploy / build-and-push (discord-gateway) (push) Failing after 30s
Build & Deploy / build-and-push (backend) (push) Failing after 2m28s
Build & Deploy / build-and-push (proxy) (push) Successful in 2m28s
Build & Deploy / build-and-push (discord-gateway) (push) Failing after 30s
Build & Deploy / build-and-push (backend) (push) Failing after 2m28s
Build & Deploy / build-and-push (proxy) (push) Successful in 2m28s
- Backend: mascot-chat module → chatbot, routes /mascot/chat → /chat - Shared schema: pgMascotChatMessagesTable → pgChatbotMessagesTable - Frontend: MascotProvider/useMascot → ChatbotProvider/useChatbot - Gateway schema: update exports to match shared schema - Docs: update all .md references (CLAUDE.md, README, specs, plans) - All API routes, controller names, service classes, types renamed Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
bd9e7d8151
commit
977a6f9653
@@ -2,7 +2,7 @@
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Full frontend redesign with glassmorphic dark theme, floating top nav, Live2D mascot, and rich analytics dashboard.
|
||||
**Goal:** Full frontend redesign with glassmorphic dark theme, floating top nav, Live2D chatbot, and rich analytics dashboard.
|
||||
|
||||
**Architecture:** No routing or state management changes — same Next.js App Router, TanStack Query, WebSocket context. Only visual layer and component structure rewritten. New glass component system wraps existing logic.
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
| `src/app/globals.css` | Complete rewrite — new tokens, glass system, animations |
|
||||
| `src/app/layout.tsx` | Fonts (Inter + JetBrains Mono), metadata |
|
||||
| `src/app/page.tsx` | Redirect `/dashboard` not `/messages` |
|
||||
| `src/app/(dashboard)/layout.tsx` | Top nav, no sidebar, mascot context, media context, WS provider |
|
||||
| `src/app/(dashboard)/layout.tsx` | Top nav, no sidebar, chatbot context, media context, WS provider |
|
||||
| `src/lib/navigation.ts` | New nav items (no Search link), mobile items updated |
|
||||
| `src/app/(dashboard)/dashboard/page.tsx` | Full rewrite — Ops Center |
|
||||
| `src/app/(dashboard)/messages/page.tsx` | Full rewrite — Split pane |
|
||||
@@ -69,23 +69,23 @@
|
||||
| `src/components/voice/activity-timeline.tsx` | Voice activity chart |
|
||||
| `src/components/recordings/recording-card.tsx` | Glass card + waveform preview |
|
||||
| `src/components/recordings/recording-player.tsx` | Inline audio player |
|
||||
| `src/components/mascot/mascot-container.tsx` | Floating L2D container |
|
||||
| `src/components/mascot/mascot-canvas.tsx` | WebGL Live2D renderer |
|
||||
| `src/components/mascot/chat-panel.tsx` | Chat input + history |
|
||||
| `src/components/mascot/mascot-context.tsx` | Context provider |
|
||||
| `src/components/chatbot/chatbot-container.tsx` | Floating L2D container |
|
||||
| `src/components/chatbot/chatbot-canvas.tsx` | WebGL Live2D renderer |
|
||||
| `src/components/chatbot/chat-panel.tsx` | Chat input + history |
|
||||
| `src/components/chatbot/chatbot-context.tsx` | Context provider |
|
||||
| `src/components/media/mini-player.tsx` | Floating media player |
|
||||
| `src/components/shared/error-boundary.tsx` | Per-page error boundary |
|
||||
| `src/components/shared/loading-skeleton.tsx` | Glass shimmer skeleton |
|
||||
| `src/components/shared/empty-state.tsx` | Empty state |
|
||||
| `src/lib/hooks/use-media-player.ts` | Global media player context |
|
||||
| `src/lib/hooks/use-mascot.ts` | Mascot context hook |
|
||||
| `src/lib/hooks/use-chatbot.ts` | Chatbot context hook |
|
||||
|
||||
### Deleted files (replaced by new components):
|
||||
| File | Replaced by |
|
||||
|------|-------------|
|
||||
| `src/components/layout/app-sidebar.tsx` | `top-nav.tsx` + `hidden-sidebar.tsx` |
|
||||
| `src/components/layout/app-header.tsx` | `top-nav.tsx` + `sub-nav.tsx` |
|
||||
| `src/components/chatbot/chatbot.tsx` | `mascot/` components |
|
||||
| `src/components/chatbot/chatbot.tsx` | `chatbot/` components |
|
||||
| `src/components/shared/stat-card.tsx` | `dashboard/stat-card.tsx` |
|
||||
| `src/components/shared/detail-stat.tsx` | inline in detail views |
|
||||
| `src/components/messages/images-grid.tsx` | `attachments-grid.tsx` |
|
||||
@@ -749,8 +749,8 @@ import { Suspense } from "react";
|
||||
import { TopNav } from "@/components/layout/top-nav";
|
||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import { WsProvider } from "@/lib/ws/context";
|
||||
import { MascotProvider } from "@/components/mascot/mascot-context";
|
||||
import { MascotContainer } from "@/components/mascot/mascot-container";
|
||||
import { ChatbotProvider } from "@/components/chatbot/chatbot-context";
|
||||
import { ChatbotContainer } from "@/components/chatbot/chatbot-container";
|
||||
import { MiniPlayer } from "@/components/media/mini-player";
|
||||
import { MediaPlayerProvider } from "@/lib/hooks/use-media-player";
|
||||
import { HiddenSidebar } from "@/components/layout/hidden-sidebar";
|
||||
@@ -777,7 +777,7 @@ export default function DashboardLayout({
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<WsProvider>
|
||||
<MediaPlayerProvider>
|
||||
<MascotProvider>
|
||||
<ChatbotProvider>
|
||||
<div className="min-h-screen bg-canvas">
|
||||
<TopNav />
|
||||
<HiddenSidebar guildId={guildId} onGuildChange={(g) => setGuildId(g ?? "")} />
|
||||
@@ -799,9 +799,9 @@ export default function DashboardLayout({
|
||||
|
||||
<MobileNav />
|
||||
<MiniPlayer />
|
||||
<MascotContainer />
|
||||
<ChatbotContainer />
|
||||
</div>
|
||||
</MascotProvider>
|
||||
</ChatbotProvider>
|
||||
</MediaPlayerProvider>
|
||||
</WsProvider>
|
||||
</QueryClientProvider>
|
||||
@@ -822,7 +822,7 @@ rm src/components/chatbot/chatbot.tsx
|
||||
```bash
|
||||
git add src/app/\(dashboard\)/layout.tsx
|
||||
git rm src/components/layout/app-sidebar.tsx src/components/layout/app-header.tsx src/components/chatbot/chatbot.tsx
|
||||
git commit -m "feat: rewrite dashboard layout with top nav, hidden sidebar, mascot, mini-player"
|
||||
git commit -m "feat: rewrite dashboard layout with top nav, hidden sidebar, chatbot, mini-player"
|
||||
```
|
||||
|
||||
---
|
||||
@@ -3117,38 +3117,38 @@ git commit -m "feat: add media player context and floating mini player"
|
||||
|
||||
---
|
||||
|
||||
### Task 21: Mascot — Context, Container & Canvas
|
||||
### Task 21: Chatbot — Context, Container & Canvas
|
||||
|
||||
**Files:**
|
||||
- Create: `src/components/mascot/mascot-context.tsx`
|
||||
- Create: `src/components/mascot/mascot-container.tsx`
|
||||
- Create: `src/components/mascot/mascot-canvas.tsx`
|
||||
- Create: `src/components/mascot/chat-panel.tsx`
|
||||
- Create: `src/components/chatbot/chatbot-context.tsx`
|
||||
- Create: `src/components/chatbot/chatbot-container.tsx`
|
||||
- Create: `src/components/chatbot/chatbot-canvas.tsx`
|
||||
- Create: `src/components/chatbot/chat-panel.tsx`
|
||||
|
||||
- [ ] **Step 1: Create MascotContext**
|
||||
- [ ] **Step 1: Create ChatbotContext**
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import { createContext, useContext, useState, type ReactNode } from "react";
|
||||
|
||||
type MascotExpression = "idle" | "listening" | "surprise" | "happy" | "sad" | "talking";
|
||||
type ChatbotExpression = "idle" | "listening" | "surprise" | "happy" | "sad" | "talking";
|
||||
|
||||
interface MascotContextType {
|
||||
expression: MascotExpression;
|
||||
interface ChatbotContextType {
|
||||
expression: ChatbotExpression;
|
||||
minimized: boolean;
|
||||
chatOpen: boolean;
|
||||
chatHistory: { role: "user" | "assistant"; text: string }[];
|
||||
setExpression: (expr: MascotExpression) => void;
|
||||
setExpression: (expr: ChatbotExpression) => void;
|
||||
setMinimized: (v: boolean) => void;
|
||||
setChatOpen: (v: boolean) => void;
|
||||
addChat: (role: "user" | "assistant", text: string) => void;
|
||||
}
|
||||
|
||||
const MascotContext = createContext<MascotContextType | null>(null);
|
||||
const ChatbotContext = createContext<ChatbotContextType | null>(null);
|
||||
|
||||
export function MascotProvider({ children }: { children: ReactNode }) {
|
||||
const [expression, setExpression] = useState<MascotExpression>("idle");
|
||||
export function ChatbotProvider({ children }: { children: ReactNode }) {
|
||||
const [expression, setExpression] = useState<ChatbotExpression>("idle");
|
||||
const [minimized, setMinimized] = useState(true);
|
||||
const [chatOpen, setChatOpen] = useState(false);
|
||||
const [chatHistory, setChatHistory] = useState<{ role: "user" | "assistant"; text: string }[]>([]);
|
||||
@@ -3158,28 +3158,28 @@ export function MascotProvider({ children }: { children: ReactNode }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<MascotContext.Provider
|
||||
<ChatbotContext.Provider
|
||||
value={{ expression, minimized, chatOpen, chatHistory, setExpression, setMinimized, setChatOpen, addChat }}
|
||||
>
|
||||
{children}
|
||||
</MascotContext.Provider>
|
||||
</ChatbotContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useMascot() {
|
||||
const ctx = useContext(MascotContext);
|
||||
if (!ctx) throw new Error("useMascot must be used within MascotProvider");
|
||||
export function useChatbot() {
|
||||
const ctx = useContext(ChatbotContext);
|
||||
if (!ctx) throw new Error("useChatbot must be used within ChatbotProvider");
|
||||
return ctx;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Create MascotCanvas (Live2D placeholder)**
|
||||
- [ ] **Step 2: Create ChatbotCanvas (Live2D placeholder)**
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useMascot } from "./mascot-context";
|
||||
import { useChatbot } from "./chatbot-context";
|
||||
|
||||
/**
|
||||
* Live2D Cubism WebGL canvas.
|
||||
@@ -3188,15 +3188,15 @@ import { useMascot } from "./mascot-context";
|
||||
* Integration requires:
|
||||
* 1. Live2D Cubism SDK for Web (npm: @live2d/cubism)
|
||||
* 2. Model files: .model3.json, .moc3, .physics3.json, textures
|
||||
* 3. Place model files in public/mascot/
|
||||
* 3. Place model files in public/chatbot/
|
||||
*
|
||||
* The current implementation shows a placeholder character.
|
||||
* Replace with actual Cubism SDK integration when model files are available.
|
||||
*/
|
||||
|
||||
export function MascotCanvas() {
|
||||
export function ChatbotCanvas() {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const { expression } = useMascot();
|
||||
const { expression } = useChatbot();
|
||||
|
||||
// Placeholder: draw a simple avatar face that responds to expression
|
||||
useEffect(() => {
|
||||
@@ -3308,19 +3308,19 @@ export function MascotCanvas() {
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Create MascotContainer**
|
||||
- [ ] **Step 3: Create ChatbotContainer**
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import { MessageCircle, X, Minimize2, Maximize2 } from "lucide-react";
|
||||
import { useMascot } from "./mascot-context";
|
||||
import { MascotCanvas } from "./mascot-canvas";
|
||||
import { useChatbot } from "./chatbot-context";
|
||||
import { ChatbotCanvas } from "./chatbot-canvas";
|
||||
import { ChatPanel } from "./chat-panel";
|
||||
import { useState } from "react";
|
||||
|
||||
export function MascotContainer() {
|
||||
const { minimized, setMinimized, chatOpen, setChatOpen } = useMascot();
|
||||
export function ChatbotContainer() {
|
||||
const { minimized, setMinimized, chatOpen, setChatOpen } = useChatbot();
|
||||
const [position, setPosition] = useState({ x: 0, y: 0 });
|
||||
const [dragging, setDragging] = useState(false);
|
||||
const [dragStart, setDragStart] = useState({ x: 0, y: 0 });
|
||||
@@ -3345,7 +3345,7 @@ export function MascotContainer() {
|
||||
onMouseUp={handleMouseUp}
|
||||
onMouseLeave={handleMouseUp}
|
||||
>
|
||||
{/* Main mascot bubble */}
|
||||
{/* Main chatbot bubble */}
|
||||
<div
|
||||
className={`glass-intense rounded-2xl overflow-hidden transition-all duration-200 ${
|
||||
minimized ? "w-16 h-16 cursor-pointer" : "w-[200px]"
|
||||
@@ -3368,7 +3368,7 @@ export function MascotContainer() {
|
||||
className="flex items-center justify-between px-3 py-1.5 border-b border-glass-border cursor-grab active:cursor-grabbing"
|
||||
onMouseDown={handleMouseDown}
|
||||
>
|
||||
<span className="text-[10px] font-semibold text-text-secondary tracking-wide uppercase">Mascot</span>
|
||||
<span className="text-[10px] font-semibold text-text-secondary tracking-wide uppercase">Chatbot</span>
|
||||
<div className="flex items-center gap-1">
|
||||
<button type="button" onClick={() => setChatOpen(!chatOpen)}>
|
||||
<MessageCircle className="size-3 text-text-secondary/60 hover:text-text-primary" />
|
||||
@@ -3381,7 +3381,7 @@ export function MascotContainer() {
|
||||
|
||||
{/* Canvas area */}
|
||||
<div className="h-[140px] flex items-center justify-center">
|
||||
<MascotCanvas />
|
||||
<ChatbotCanvas />
|
||||
</div>
|
||||
|
||||
{/* Chat panel (expandable) */}
|
||||
@@ -3403,10 +3403,10 @@ export function MascotContainer() {
|
||||
|
||||
import { Send } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { useMascot } from "./mascot-context";
|
||||
import { useChatbot } from "./chatbot-context";
|
||||
|
||||
export function ChatPanel() {
|
||||
const { chatHistory, addChat, setExpression } = useMascot();
|
||||
const { chatHistory, addChat, setExpression } = useChatbot();
|
||||
const [input, setInput] = useState("");
|
||||
|
||||
const handleSend = () => {
|
||||
@@ -3414,7 +3414,7 @@ export function ChatPanel() {
|
||||
addChat("user", input);
|
||||
setExpression("listening");
|
||||
|
||||
// Simulated bot response — replace with actual mascot-chat API call
|
||||
// Simulated bot response — replace with actual chatbot-chat API call
|
||||
setTimeout(() => {
|
||||
addChat("assistant", `I'm monitoring this server for you!`);
|
||||
setExpression("happy");
|
||||
@@ -3444,7 +3444,7 @@ export function ChatPanel() {
|
||||
value={input}
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
onKeyDown={(e) => e.key === "Enter" && handleSend()}
|
||||
placeholder="Ask mascot..."
|
||||
placeholder="Ask chatbot..."
|
||||
className="flex-1 bg-transparent text-[10px] text-text-primary placeholder-text-secondary/30 outline-none"
|
||||
/>
|
||||
<button type="button" onClick={handleSend} className="size-5 flex items-center justify-center">
|
||||
@@ -3459,17 +3459,17 @@ export function ChatPanel() {
|
||||
- [ ] **Step 5: Create barrel export**
|
||||
|
||||
```tsx
|
||||
// src/components/mascot/index.ts
|
||||
export { MascotProvider } from "./mascot-context";
|
||||
export { MascotContainer } from "./mascot-container";
|
||||
export { useMascot } from "./mascot-context";
|
||||
// src/components/chatbot/index.ts
|
||||
export { ChatbotProvider } from "./chatbot-context";
|
||||
export { ChatbotContainer } from "./chatbot-container";
|
||||
export { useChatbot } from "./chatbot-context";
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add src/components/mascot/
|
||||
git commit -m "feat: add Live2D mascot container with canvas, chat panel, and context"
|
||||
git add src/components/chatbot/
|
||||
git commit -m "feat: add Live2D chatbot container with canvas, chat panel, and context"
|
||||
```
|
||||
|
||||
---
|
||||
@@ -3477,21 +3477,21 @@ git commit -m "feat: add Live2D mascot container with canvas, chat panel, and co
|
||||
### Task 22: WS Expression Triggers
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/app/(dashboard)/layout.tsx` — add WS → mascot expression bindings
|
||||
- Modify: `src/app/(dashboard)/layout.tsx` — add WS → chatbot expression bindings
|
||||
|
||||
- [ ] **Step 1: Add WebSocket expression triggers**
|
||||
|
||||
In the dashboard layout, add a side-effect that connects WebSocket events to mascot expressions:
|
||||
In the dashboard layout, add a side-effect that connects WebSocket events to chatbot expressions:
|
||||
|
||||
```tsx
|
||||
// Add to dashboard layout before the return:
|
||||
import { useEffect } from "react";
|
||||
import { useMascot } from "@/components/mascot/mascot-context";
|
||||
import { useChatbot } from "@/components/chatbot/chatbot-context";
|
||||
import { useWebSocket } from "@/lib/ws/context";
|
||||
|
||||
function MascotExpressionSync() {
|
||||
function ChatbotExpressionSync() {
|
||||
const ws = useWebSocket();
|
||||
const { setExpression } = useMascot();
|
||||
const { setExpression } = useChatbot();
|
||||
|
||||
useEffect(() => {
|
||||
const unsub1 = ws.on("message_created", (data: any) => {
|
||||
@@ -3512,13 +3512,13 @@ function MascotExpressionSync() {
|
||||
}
|
||||
```
|
||||
|
||||
Then render `<MascotExpressionSync />` inside the layout tree.
|
||||
Then render `<ChatbotExpressionSync />` inside the layout tree.
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add src/app/\(dashboard\)/layout.tsx
|
||||
git commit -m "feat: connect WS events to mascot expression triggers"
|
||||
git commit -m "feat: connect WS events to chatbot expression triggers"
|
||||
```
|
||||
|
||||
---
|
||||
@@ -3570,12 +3570,12 @@ git commit -m "chore: remove old components replaced by redesign"
|
||||
- Section 8 (Tech Stack) → Task 2 (fonts)
|
||||
- Section 9 (File Structure) → All tasks
|
||||
- Section 10 (Implementation Order) → Followed as-is
|
||||
- Mascot → Tasks 21, 22
|
||||
- Chatbot → Tasks 21, 22
|
||||
- Media Player → Task 20
|
||||
- No gaps found.
|
||||
|
||||
- [ ] **Placeholder check:** No TBD, TODO, or "implement later" found. Every task has specific code. The only note is the Live2D canvas is a placeholder with Canvas2D drawing — this is intentional since the actual Live2D model file isn't available yet.
|
||||
|
||||
- [ ] **Type consistency:** All component props match what consuming pages expect. Hook interfaces consistent (useMascot, useMediaPlayer). No type drift between tasks.
|
||||
- [ ] **Type consistency:** All component props match what consuming pages expect. Hook interfaces consistent (useChatbot, useMediaPlayer). No type drift between tasks.
|
||||
|
||||
- [ ] **No contradictions:** nav items match top nav links. Page layouts match sub-nav tabs. No file referenced before being created.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Frontend Refactor: Cleanup, API Alignment & Rebrand
|
||||
|
||||
## Goal
|
||||
Refactor the frontend (`services/frontend/`) to be cleaner, more maintainable, properly aligned with backend API, and rebranded from "bete/GMW" to "Discord Automod" and from "mascot" to "chatbot".
|
||||
Refactor the frontend (`services/frontend/`) to be cleaner, more maintainable, properly aligned with backend API, and rebranded from "bete/GMW" to "Discord Automod" and from "chatbot" to "chatbot".
|
||||
|
||||
## Scope
|
||||
|
||||
@@ -23,7 +23,7 @@ Refactor the frontend (`services/frontend/`) to be cleaner, more maintainable, p
|
||||
|
||||
### D. Rebrand
|
||||
- **bete/GMW → Discord Automod**: page title, sidebar, settings, comments
|
||||
- **mascot → chatbot**: the frontend already uses "chatbot" naming for the component and API module; backend paths (`/api/mascot/chat`) stay unchanged on frontend since they reference the actual backend path
|
||||
- **chatbot → chatbot**: the frontend already uses "chatbot" naming for the component and API module; backend paths (`/api/chatbot/chat`) stay unchanged on frontend since they reference the actual backend path
|
||||
|
||||
### E. Dead Code Removal
|
||||
- Remove `components/landing/` (including `live-stats.tsx`)
|
||||
|
||||
@@ -13,7 +13,7 @@ Comprehensive refactoring of `services/backend` (4.2k lines) and `services/disco
|
||||
|
||||
**Problem:** Two competing controller patterns.
|
||||
|
||||
- `messages.controller.ts`, `mascot-chat.controller.ts` use convoluted `asyncHandler` inside function body (Gaya A)
|
||||
- `messages.controller.ts`, `chatbot-chat.controller.ts` use convoluted `asyncHandler` inside function body (Gaya A)
|
||||
- `voice.controller.ts`, `health.controller.ts` use clean `asyncHandler` decorator (Gaya B)
|
||||
|
||||
**Fix:** Convert all controllers to **Gaya B** (decorator pattern).
|
||||
@@ -36,7 +36,7 @@ export const handleListMessages = asyncHandler(async (req, res) => {
|
||||
|
||||
**Files affected:**
|
||||
- `modules/messages/messages.controller.ts`
|
||||
- `modules/mascot-chat/mascot-chat.controller.ts`
|
||||
- `modules/chatbot-chat/chatbot-chat.controller.ts`
|
||||
|
||||
### Phase 2 — Backend `response.ts` Cleanup
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ Full frontend redesign for Discord Automod, a Discord moderation watcher dashboa
|
||||
Key pillars:
|
||||
- **Immersion** — Full-viewport canvas with ambient motion, glass panels float over content
|
||||
- **Awareness** — Live data streams, real-time voice waveforms, animated moderation alerts
|
||||
- **Presence** — Live2D vtuber mascot character as chatbot interface, reacts to server events
|
||||
- **Presence** — Live2D vtuber chatbot character as chatbot interface, reacts to server events
|
||||
|
||||
---
|
||||
|
||||
@@ -69,7 +69,7 @@ Key pillars:
|
||||
│ └─────────┘ └──────────────┘ │
|
||||
│ │
|
||||
├──────────────────────────────────────────────────────┤
|
||||
│ 🎵 [Mini-player] ← bottom-left 🎭 [Mascot] ← BR │
|
||||
│ 🎵 [Mini-player] ← bottom-left 🎭 [Chatbot] ← BR │
|
||||
└──────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
@@ -196,9 +196,9 @@ Custom theme matching design tokens:
|
||||
- Heatmap: activity by hour × weekday
|
||||
- Radar: multi-axis for moderation categories
|
||||
|
||||
### 4.5 Live2D Mascot / Chatbot
|
||||
### 4.5 Live2D Chatbot / Chatbot
|
||||
|
||||
- Replaces the existing `Chatbot` component entirely — mascot panel is the new chat interface
|
||||
- Replaces the existing `Chatbot` component entirely — chatbot panel is the new chat interface
|
||||
- **Location:** Floating panel, bottom-right corner, draggable
|
||||
- **Default size:** Compact — upper body visible (~200×280px)
|
||||
- **Click character:** Expand with full chat panel
|
||||
@@ -232,7 +232,7 @@ Full-viewport command center:
|
||||
- **Message Trend Chart:** 7-day area chart
|
||||
- **Activity Heatmap:** Hour × day-of-week, moderation event density
|
||||
- **Top Channels:** Bar chart with channel names
|
||||
- **Mascot visible** floating bottom-right
|
||||
- **Chatbot visible** floating bottom-right
|
||||
|
||||
### 5.2 Messages — Split Pane
|
||||
|
||||
@@ -334,8 +334,8 @@ Optimistic cache updates for real-time data
|
||||
### 7.4 Global State (React Context)
|
||||
|
||||
- `useMediaPlayer()` — current track, queue, play/skip/stop/volume
|
||||
- `useMascot()` — expression, minimized, chatHistory, setExpression
|
||||
- Externally triggerable: `mascot.setExpression("surprise")` on flagged message, `("listening")` on voice activity
|
||||
- `useChatbot()` — expression, minimized, chatHistory, setExpression
|
||||
- Externally triggerable: `chatbot.setExpression("surprise")` on flagged message, `("listening")` on voice activity
|
||||
|
||||
### 7.5 URL State
|
||||
|
||||
@@ -359,7 +359,7 @@ Each page has its own error boundary. One page failure doesn't affect others.
|
||||
- **Icons:** lucide-react
|
||||
- **State/data:** @tanstack/react-query v5
|
||||
- **Charts:** Recharts 3.8 (with custom theme)
|
||||
- **3D/Mascot:** Live2D Cubism SDK WebGL (pixi.js wrapper)
|
||||
- **3D/Chatbot:** Live2D Cubism SDK WebGL (pixi.js wrapper)
|
||||
- **Audio:** Web Audio API for waveform visualization
|
||||
- **Animation:** CSS animations + transitions (no GSAP/framer-motion dependency unless specifically needed)
|
||||
|
||||
@@ -374,7 +374,7 @@ src/
|
||||
│ ├── page.tsx # Redirect → /dashboard
|
||||
│ ├── globals.css # Complete redesign CSS (tokens, glass, animations)
|
||||
│ └── (dashboard)/
|
||||
│ ├── layout.tsx # Dashboard layout (top nav, QueryClient, WS, mascot)
|
||||
│ ├── layout.tsx # Dashboard layout (top nav, QueryClient, WS, chatbot)
|
||||
│ ├── dashboard/
|
||||
│ │ └── page.tsx # Ops Center
|
||||
│ ├── messages/
|
||||
@@ -424,11 +424,11 @@ src/
|
||||
│ │ ├── recording-card.tsx # Glass card with waveform preview
|
||||
│ │ └── recording-player.tsx # Inline audio player
|
||||
│ │
|
||||
│ ├── mascot/
|
||||
│ │ ├── mascot-container.tsx # Floating L2D container
|
||||
│ │ ├── mascot-canvas.tsx # WebGL canvas for L2D rendering
|
||||
│ ├── chatbot/
|
||||
│ │ ├── chatbot-container.tsx # Floating L2D container
|
||||
│ │ ├── chatbot-canvas.tsx # WebGL canvas for L2D rendering
|
||||
│ │ ├── chat-panel.tsx # Chat input + history
|
||||
│ │ └── mascot-context.tsx # Context provider
|
||||
│ │ └── chatbot-context.tsx # Context provider
|
||||
│ │
|
||||
│ ├── media/
|
||||
│ │ └── mini-player.tsx # Floating mini media player
|
||||
@@ -448,7 +448,7 @@ src/
|
||||
│ │ └── types.ts # WS event types
|
||||
│ ├── hooks/ # Existing hooks + new ones
|
||||
│ │ ├── use-media-player.ts # Global media state
|
||||
│ │ ├── use-mascot.ts # Mascot context hook
|
||||
│ │ ├── use-chatbot.ts # Chatbot context hook
|
||||
│ │ └── use-heatmap.ts # Heatmap data hook
|
||||
│ ├── types/ # Existing types (unchanged)
|
||||
│ ├── navigation.ts # Nav items (updated)
|
||||
@@ -486,7 +486,7 @@ src/
|
||||
|
||||
### Phase 5 — Floating Elements
|
||||
18. Build `mini-player.tsx` for media
|
||||
19. Build mascot components (L2D integration)
|
||||
19. Build chatbot components (L2D integration)
|
||||
|
||||
---
|
||||
|
||||
@@ -495,7 +495,7 @@ src/
|
||||
- Visual regression checks per component
|
||||
- WS integration tests for cache updates
|
||||
- Responsive breakpoint testing (mobile bottom nav)
|
||||
- L2D mascot load + expression trigger
|
||||
- L2D chatbot load + expression trigger
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user