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
|
|
@ -373,6 +373,235 @@
|
|||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* ── Map toggles ──────────────────────────────────────── */
|
||||
.ml-toggles {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
.ml-toggle-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
color: #aaa;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ml-toggle-label input { accent-color: #4488ff; }
|
||||
|
||||
/* ── Trail SVG overlay ────────────────────────────────── */
|
||||
.ml-trails-svg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ── Heatmap canvas overlay ───────────────────────────── */
|
||||
.ml-heatmap-canvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* ── Portal markers ───────────────────────────────────── */
|
||||
.ml-portals-layer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ml-portal-icon {
|
||||
position: absolute;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
transform: translate(-50%, -50%);
|
||||
pointer-events: all;
|
||||
cursor: help;
|
||||
}
|
||||
.ml-portal-icon::before {
|
||||
content: '🌀';
|
||||
font-size: 10px;
|
||||
position: absolute;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
/* ── Draggable windows ────────────────────────────────── */
|
||||
.ml-window {
|
||||
position: fixed;
|
||||
background: #1a1a1a;
|
||||
border: 1px solid #444;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.ml-window-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 6px 12px;
|
||||
background: linear-gradient(135deg, #2a3a5a, #1a2a40);
|
||||
cursor: move;
|
||||
user-select: none;
|
||||
border-bottom: 1px solid #334;
|
||||
}
|
||||
|
||||
.ml-window-title {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
color: #aaccff;
|
||||
}
|
||||
|
||||
.ml-window-close {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #888;
|
||||
font-size: 1.1rem;
|
||||
cursor: pointer;
|
||||
line-height: 1;
|
||||
padding: 0 4px;
|
||||
}
|
||||
.ml-window-close:hover { color: #f66; }
|
||||
|
||||
.ml-window-content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* ── Chat window ──────────────────────────────────────── */
|
||||
.ml-chat-messages {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 6px 10px;
|
||||
font-size: 0.75rem;
|
||||
font-family: 'Consolas', 'Courier New', monospace;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.ml-chat-line {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.ml-chat-form {
|
||||
display: flex;
|
||||
border-top: 1px solid #333;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.ml-chat-input {
|
||||
flex: 1;
|
||||
background: #222;
|
||||
color: #eee;
|
||||
border: 1px solid #444;
|
||||
border-radius: 3px;
|
||||
padding: 4px 8px;
|
||||
font-size: 0.78rem;
|
||||
outline: none;
|
||||
}
|
||||
.ml-chat-input:focus { border-color: #4488ff; }
|
||||
.ml-chat-input::placeholder { color: #666; }
|
||||
|
||||
/* ── Rare notifications ───────────────────────────────── */
|
||||
.ml-rare-notifications {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 99999;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ml-rare-notif {
|
||||
background: linear-gradient(135deg, #1a0a2e, #2a1040);
|
||||
border: 2px solid #ffcc00;
|
||||
border-radius: 8px;
|
||||
padding: 16px 32px;
|
||||
text-align: center;
|
||||
animation: ml-notif-in 0.5s ease-out;
|
||||
box-shadow: 0 0 40px rgba(255, 204, 0, 0.3);
|
||||
}
|
||||
|
||||
.ml-rare-notif.exiting {
|
||||
animation: ml-notif-out 0.5s ease-in forwards;
|
||||
}
|
||||
|
||||
.ml-rare-notif-title {
|
||||
font-size: 1.4rem;
|
||||
font-weight: 800;
|
||||
color: #ffcc00;
|
||||
text-shadow: 0 0 20px rgba(255, 204, 0, 0.5);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.ml-rare-notif-name {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.ml-rare-notif-by {
|
||||
font-size: 0.75rem;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.ml-rare-notif-char {
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
color: #ffcc00;
|
||||
}
|
||||
|
||||
@keyframes ml-notif-in {
|
||||
from { transform: translateY(-40px); opacity: 0; }
|
||||
to { transform: translateY(0); opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes ml-notif-out {
|
||||
to { transform: translateY(-60px); opacity: 0; }
|
||||
}
|
||||
|
||||
/* ── Fireworks ────────────────────────────────────────── */
|
||||
.ml-fireworks {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 99998;
|
||||
}
|
||||
|
||||
.ml-firework-particle {
|
||||
position: absolute;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
animation: ml-particle 2s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
|
||||
}
|
||||
|
||||
@keyframes ml-particle {
|
||||
0% { transform: translate(0, 0) scale(1); opacity: 1; }
|
||||
100% { transform: translate(var(--dx), var(--dy)) scale(0); opacity: 0; }
|
||||
}
|
||||
|
||||
/* ── Mobile ───────────────────────────────────────────── */
|
||||
@media (max-width: 768px) {
|
||||
.ml-layout { flex-direction: column; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue