3de56316bb
# Conflicts: # client/src/ui/CommandPanel.ts
173 lines
4.4 KiB
TypeScript
173 lines
4.4 KiB
TypeScript
// EarthBound-inspired color palette (matches NpcInfoPanel)
|
|
const EB = {
|
|
bgDeep: '#0c0824',
|
|
bgPanel: '#141038',
|
|
borderOuter: '#7878d8',
|
|
borderInner: '#5858b8',
|
|
borderGap: '#1c1450',
|
|
textPrimary: '#f0f0ff',
|
|
textSecondary: '#9898d0',
|
|
textMuted: '#6868a8',
|
|
shine: 'rgba(200,200,255,0.08)',
|
|
};
|
|
|
|
interface Command {
|
|
key: string;
|
|
label: string;
|
|
}
|
|
|
|
export class CommandPanel {
|
|
private outerFrame: HTMLDivElement;
|
|
private listContainer: HTMLDivElement;
|
|
private visible = false;
|
|
|
|
constructor(id = 'command-panel', title = 'COMMANDS', commands: Command[] = [{ key: '1', label: 'Spawn NPC' }]) {
|
|
// Outer frame (doubled border, same as NpcInfoPanel)
|
|
this.outerFrame = document.createElement('div');
|
|
this.outerFrame.id = id;
|
|
this.outerFrame.style.cssText = `
|
|
position: fixed;
|
|
bottom: 16px;
|
|
right: 16px;
|
|
min-width: 160px;
|
|
border-radius: 12px;
|
|
border: 3px solid ${EB.borderOuter};
|
|
background: ${EB.borderGap};
|
|
z-index: 1000;
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
transition: opacity 0.3s cubic-bezier(0.22, 1, 0.36, 1),
|
|
transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
|
|
pointer-events: none;
|
|
box-shadow:
|
|
0 0 16px rgba(80, 60, 160, 0.3),
|
|
0 4px 20px rgba(0, 0, 0, 0.5),
|
|
inset 0 1px 0 rgba(160, 160, 255, 0.15);
|
|
`;
|
|
|
|
// Inner container
|
|
const container = document.createElement('div');
|
|
container.style.cssText = `
|
|
margin: 3px;
|
|
border-radius: 8px;
|
|
border: 2px solid ${EB.borderInner};
|
|
background: ${EB.bgPanel};
|
|
overflow: hidden;
|
|
position: relative;
|
|
padding: 8px 12px 10px;
|
|
font-family: 'Press Start 2P', monospace;
|
|
`;
|
|
|
|
// Shine overlay
|
|
const shine = document.createElement('div');
|
|
shine.style.cssText = `
|
|
position: absolute;
|
|
top: 0; left: 0; right: 0; bottom: 0;
|
|
background: linear-gradient(
|
|
135deg,
|
|
${EB.shine} 0%,
|
|
transparent 40%,
|
|
transparent 60%,
|
|
rgba(0,0,0,0.05) 100%
|
|
);
|
|
pointer-events: none;
|
|
z-index: 1;
|
|
`;
|
|
container.appendChild(shine);
|
|
|
|
// Title
|
|
const titleEl = document.createElement('div');
|
|
titleEl.style.cssText = `
|
|
font-size: 9px;
|
|
color: ${EB.textMuted};
|
|
text-align: center;
|
|
letter-spacing: 4px;
|
|
padding-bottom: 6px;
|
|
user-select: none;
|
|
position: relative;
|
|
z-index: 2;
|
|
`;
|
|
titleEl.textContent = `\u25C6 ${title} \u25C6`;
|
|
container.appendChild(titleEl);
|
|
|
|
// Commands list
|
|
this.listContainer = document.createElement('div');
|
|
this.listContainer.style.cssText = `
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
position: relative;
|
|
z-index: 2;
|
|
`;
|
|
container.appendChild(this.listContainer);
|
|
|
|
this.outerFrame.appendChild(container);
|
|
document.body.appendChild(this.outerFrame);
|
|
|
|
// Add commands
|
|
for (const cmd of commands) {
|
|
this.addCommand(cmd);
|
|
}
|
|
}
|
|
|
|
private addCommand(cmd: Command): void {
|
|
const row = document.createElement('div');
|
|
row.style.cssText = `
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
`;
|
|
|
|
const keyBadge = document.createElement('div');
|
|
keyBadge.style.cssText = `
|
|
font-size: 10px;
|
|
color: ${EB.bgDeep};
|
|
background: ${EB.borderOuter};
|
|
border-radius: 3px;
|
|
padding: 2px 5px;
|
|
min-width: 12px;
|
|
text-align: center;
|
|
text-shadow: none;
|
|
font-weight: bold;
|
|
box-shadow: 0 1px 0 rgba(0,0,0,0.4), inset 0 1px 0 rgba(255,255,255,0.2);
|
|
`;
|
|
keyBadge.textContent = cmd.key;
|
|
|
|
const label = document.createElement('div');
|
|
label.style.cssText = `
|
|
font-size: 10px;
|
|
color: ${EB.textSecondary};
|
|
letter-spacing: 0.5px;
|
|
`;
|
|
label.textContent = cmd.label;
|
|
|
|
row.appendChild(keyBadge);
|
|
row.appendChild(label);
|
|
this.listContainer.appendChild(row);
|
|
}
|
|
|
|
show(): void {
|
|
if (this.visible) return;
|
|
this.visible = true;
|
|
this.outerFrame.style.opacity = '1';
|
|
this.outerFrame.style.transform = 'translateY(0)';
|
|
this.outerFrame.style.pointerEvents = 'auto';
|
|
}
|
|
|
|
hide(): void {
|
|
if (!this.visible) return;
|
|
this.visible = false;
|
|
this.outerFrame.style.opacity = '0';
|
|
this.outerFrame.style.transform = 'translateY(20px)';
|
|
this.outerFrame.style.pointerEvents = 'none';
|
|
}
|
|
|
|
isVisible(): boolean {
|
|
return this.visible;
|
|
}
|
|
|
|
destroy(): void {
|
|
this.outerFrame.remove();
|
|
}
|
|
}
|