Files
TurboSu/static/js/components/sidebar.js
T
AskaEth e28be26ab3 fix: 7 bug fixes + port config with auto-restart
- HTTPException import added (was causing 500 on missing dashboard)
- game_manager.remove_plugin() restored (was AttributeError)
- api.js checks HTTP status codes, fails fast on errors
- sidebar game highlight only matches selected gameId
- scene resize handler uses stored canvas dims (was NaN)
- forwarder socket properly closed on listener stop
- telemetry port: use_unified_port toggle, off = per-game port, auto-restart on switch
2026-07-26 17:23:14 +08:00

128 lines
4.6 KiB
JavaScript

const Sidebar = {
_el: null,
_gameListEl: null,
_gameToggleEl: null,
_gameLabelEl: null,
_gameData: [],
init() {
this._el = document.getElementById('sidebar');
this._gameListEl = document.getElementById('game-list');
this._gameToggleEl = document.getElementById('game-selector-toggle');
this._gameLabelEl = document.getElementById('game-selector-label');
this._topbarLogo = document.getElementById('topbar-logo-title');
document.getElementById('sidebar-toggle').addEventListener('click', () => this.toggle());
this._gameToggleEl.addEventListener('click', () => this._toggleGameList());
const saved = localStorage.getItem('turbosu-sidebar-collapsed');
if (saved === 'true') this.collapse(true);
this._loadGames();
},
toggle() {
this._el.classList.toggle('collapsed');
this._el.classList.toggle('expanded');
const isCollapsed = this._el.classList.contains('collapsed');
this._topbarLogo.classList.toggle('hidden', !isCollapsed);
localStorage.setItem('turbosu-sidebar-collapsed', isCollapsed);
},
collapse(silent) {
this._el.classList.add('collapsed');
this._el.classList.remove('expanded');
this._topbarLogo.classList.remove('hidden');
if (!silent) localStorage.setItem('turbosu-sidebar-collapsed', 'true');
},
expand(silent) {
this._el.classList.remove('collapsed');
this._el.classList.add('expanded');
this._topbarLogo.classList.add('hidden');
if (!silent) localStorage.setItem('turbosu-sidebar-collapsed', 'false');
},
async _loadGames() {
const games = await API.getGames();
this._gameData = games;
this._gameListEl.innerHTML = '';
if (!Array.isArray(games) || games.length === 0) {
this._gameListEl.innerHTML = '<div style="padding:8px 12px;color:var(--text-tertiary);font-size:12px;">暂无可用的游戏插件</div>';
return;
}
const selectedId = (await API.getConfig()).selected_game_id;
games.forEach(game => {
const item = document.createElement('button');
item.className = 'nav-item';
if (selectedId === game.id) item.classList.add('active');
item.innerHTML = `
<span style="font-size:16px;flex-shrink:0;">&#x25C9;</span>
<span class="nav-label">${game.name}</span>
`;
item.addEventListener('click', () => this._selectGame(game.id));
this._gameListEl.appendChild(item);
});
this._updateGameLabel(selectedId, games);
},
_updateGameLabel(selectedId, games) {
if (selectedId && games) {
const game = games.find(g => g.id === selectedId);
if (game) {
this._gameLabelEl.textContent = game.name;
return;
}
}
this._gameLabelEl.textContent = '选择游戏';
},
async _selectGame(gameId) {
await API.updateConfig({ selected_game_id: gameId });
const cfg = await API.getConfig();
const game = this._gameData.find(g => g.id === gameId);
if (game && !cfg.use_unified_port && game.default_port) {
await API.updateConfig({ telemetry_port: game.default_port });
}
await API.stopTelemetry();
await API.startTelemetry();
this._gameListEl.querySelectorAll('.nav-item').forEach(el => el.classList.remove('active'));
const items = this._gameListEl.querySelectorAll('.nav-item');
items.forEach((item, idx) => {
if (this._gameData[idx] && this._gameData[idx].id === gameId) item.classList.add('active');
});
const games = await API.getGames();
this._updateGameLabel(gameId, games);
this._loadGames();
if (typeof Router !== 'undefined' && Router.currentPage === 'scene') {
Router.navigate('scene');
}
},
_toggleGameList() {
const toggle = this._gameToggleEl;
const list = this._gameListEl;
const isCollapsed = toggle.classList.contains('collapsed');
if (isCollapsed) {
toggle.classList.remove('collapsed');
list.classList.remove('collapsed');
} else {
toggle.classList.add('collapsed');
list.classList.add('collapsed');
}
},
setActive(route) {
this._el.querySelectorAll('.nav-item[data-route]').forEach(el => {
el.classList.toggle('active', el.getAttribute('data-route') === route);
});
}
};
window.Sidebar = Sidebar;