feat(v2): Phases 2-6 — trails, heatmap, portals, windows, effects
Phase 2 — Map overlays: - TrailsSVG: SVG polylines per character from /trails, polled 2s - HeatmapCanvas: canvas radial gradients from /spawns/heatmap - PortalMarkers: emoji markers from /portals - Sidebar toggles for heatmap and portals Phase 3 — Draggable windows: - WindowManagerContext: z-index stack for open windows - DraggableWindow: generic shell with drag-header, close btn, z-stack - ChatWindow: color-coded messages + input form (1000 msg buffer) - CharacterWindow: combat stats with monster damage table - InventoryWindow: item table with material/set/AL/dmg/workmanship - WindowRenderer: reads context, renders all open windows - Action buttons (Chat/Stats/Inv/Char/Radar) now open windows Phase 4 — Window types share same DraggableWindow shell with character-specific content. Combat stats and inventory via API. Phase 5 — Effects: - RareNotification: slide-in/slide-out banner with gold border - Fireworks: 30-particle explosion with CSS custom property animation - Notification queue with 6s display + 0.5s exit animation Phase 6 — Polish: - Window header uses modern blue gradient (not solid purple) - Chat uses monospace font - All overlay layers properly stacked (heatmap → trails → dots → portals) - Mobile: sidebar stacks above map at 768px breakpoint - Chat messages tracked per-character in useLiveData Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
183d662bb9
commit
de7b547349
20 changed files with 1040 additions and 193 deletions
49
frontend/src/contexts/WindowManagerContext.tsx
Normal file
49
frontend/src/contexts/WindowManagerContext.tsx
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import React, { createContext, useContext, useState, useCallback, useRef } from 'react';
|
||||
|
||||
interface WindowState {
|
||||
id: string;
|
||||
title: string;
|
||||
charName?: string;
|
||||
zIndex: number;
|
||||
}
|
||||
|
||||
interface WindowManagerValue {
|
||||
windows: WindowState[];
|
||||
openWindow: (id: string, title: string, charName?: string) => void;
|
||||
closeWindow: (id: string) => void;
|
||||
bringToFront: (id: string) => void;
|
||||
}
|
||||
|
||||
const Ctx = createContext<WindowManagerValue>({
|
||||
windows: [],
|
||||
openWindow: () => {},
|
||||
closeWindow: () => {},
|
||||
bringToFront: () => {},
|
||||
});
|
||||
|
||||
export const WindowManagerProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const [windows, setWindows] = useState<WindowState[]>([]);
|
||||
const zRef = useRef(10000);
|
||||
|
||||
const openWindow = useCallback((id: string, title: string, charName?: string) => {
|
||||
setWindows(prev => {
|
||||
const existing = prev.find(w => w.id === id);
|
||||
if (existing) {
|
||||
return prev.map(w => w.id === id ? { ...w, zIndex: ++zRef.current } : w);
|
||||
}
|
||||
return [...prev, { id, title, charName, zIndex: ++zRef.current }];
|
||||
});
|
||||
}, []);
|
||||
|
||||
const closeWindow = useCallback((id: string) => {
|
||||
setWindows(prev => prev.filter(w => w.id !== id));
|
||||
}, []);
|
||||
|
||||
const bringToFront = useCallback((id: string) => {
|
||||
setWindows(prev => prev.map(w => w.id === id ? { ...w, zIndex: ++zRef.current } : w));
|
||||
}, []);
|
||||
|
||||
return <Ctx.Provider value={{ windows, openWindow, closeWindow, bringToFront }}>{children}</Ctx.Provider>;
|
||||
};
|
||||
|
||||
export const useWindowManager = () => useContext(Ctx);
|
||||
Loading…
Add table
Add a link
Reference in a new issue