diff --git a/src/game/AdminPanel.jsx b/src/game/AdminPanel.jsx
index 3e288c8..223128f 100644
--- a/src/game/AdminPanel.jsx
+++ b/src/game/AdminPanel.jsx
@@ -5,7 +5,7 @@
import React, { useState } from 'react';
import { loadProgress, saveProgress, resetProgress } from './gameState.js';
-export default function AdminPanel({ worlds, onClose }) {
+export default function AdminPanel({ worlds, onClose, adminMode, onToggleAdmin }) {
const [, refresh] = useState(0);
const p = loadProgress();
const totalStars = Object.values(p.completedLevels).reduce((s, l) => s + (l.stars || 0), 0);
@@ -63,6 +63,12 @@ export default function AdminPanel({ worlds, onClose }) {
+
diff --git a/src/game/GameApp.jsx b/src/game/GameApp.jsx
index 82d0f11..3872f1a 100644
--- a/src/game/GameApp.jsx
+++ b/src/game/GameApp.jsx
@@ -23,6 +23,7 @@ export default function GameApp({ onSwitchToSandbox }) {
const [currentLevelIndex, setCurrentLevelIndex] = useState(0);
const [currentWorld, setCurrentWorld] = useState(null);
const [showAdmin, setShowAdmin] = useState(false);
+ const [adminMode, setAdminMode] = useState(false);
const handleSelectLevel = useCallback((level, world) => {
const idx = world.levels.findIndex(l => l.id === level.id);
@@ -67,6 +68,7 @@ export default function GameApp({ onSwitchToSandbox }) {
worldLevels={currentWorld.levels}
onBack={handleBack}
onNextLevel={handleNextLevel}
+ adminMode={adminMode}
/>
);
}
@@ -82,6 +84,8 @@ export default function GameApp({ onSwitchToSandbox }) {
setShowAdmin(false)}
+ adminMode={adminMode}
+ onToggleAdmin={() => setAdminMode(a => !a)}
/>
)}
>
diff --git a/src/game/PuzzleView.jsx b/src/game/PuzzleView.jsx
index 23f0c31..e9b9684 100644
--- a/src/game/PuzzleView.jsx
+++ b/src/game/PuzzleView.jsx
@@ -9,7 +9,7 @@ import LevelComplete from './LevelComplete.jsx';
import { completeLevel, saveLevelPatch, getLevelPatch, markHintUsed, wasHintUsed } from './gameState.js';
import { playLevelComplete, playFail, playHint, playEngineStart, playEngineStop, playNav } from '../engine/uiSounds.js';
-export default function PuzzleView({ level, levelIndex, worldLevels, onBack, onNextLevel }) {
+export default function PuzzleView({ level, levelIndex, worldLevels, onBack, onNextLevel, adminMode }) {
const [, forceUpdate] = useState(0);
const containerRef = useRef(null);
const portPositions = useRef({});
@@ -285,6 +285,17 @@ export default function PuzzleView({ level, levelIndex, worldLevels, onBack, onN
}
};
+ // Admin auto-solve — gives 3 stars instantly
+ const handleAutoSolve = () => {
+ const checks = level.checks.map(check => ({
+ ...check,
+ passed: true,
+ }));
+ completeLevel(level.id, 3);
+ setResult({ stars: 3, checks, hintPenalty: false });
+ playLevelComplete();
+ };
+
const isLastLevel = levelIndex >= worldLevels.length - 1;
return (
@@ -312,6 +323,11 @@ export default function PuzzleView({ level, levelIndex, worldLevels, onBack, onN
+ {adminMode && (
+
+ )}
diff --git a/src/index.css b/src/index.css
index 62f18f8..ffd49cc 100644
--- a/src/index.css
+++ b/src/index.css
@@ -715,9 +715,17 @@ html, body, #root {
}
.admin-action-btn.gold { border-color: var(--yellow); color: var(--yellow); }
.admin-action-btn.gold:hover { background: var(--yellow); color: var(--bg); }
+.admin-action-btn.active { border-color: var(--green); color: var(--green); background: rgba(68, 255, 136, 0.1); }
+.admin-action-btn.active:hover { background: var(--green); color: var(--bg); }
.admin-action-btn.danger { border-color: var(--red); color: var(--red); }
.admin-action-btn.danger:hover { background: var(--red); color: #fff; }
+/* Admin auto-solve button in puzzle bar */
+.gm-btn.admin-solve {
+ background: rgba(170, 85, 255, 0.15); border-color: var(--purple); color: var(--purple);
+}
+.gm-btn.admin-solve:hover { background: var(--purple); color: #fff; }
+
.admin-world { margin-bottom: 16px; }
.admin-world-header {
display: flex; align-items: center; gap: 8px;