This commit is contained in:
33333-33333 2026-07-18 13:06:02 +09:00
commit f657b6a4a4
94 changed files with 3970 additions and 1241 deletions

View file

@ -4,6 +4,7 @@ function bindUI() {
renderToolPalette();
loadLogPushSettings();
syncLogPushControls();
if (uiCache.logPushEnabled) window.setTimeout(() => window.TarinaiAchievements?.recordPushNotificationsEnabled?.({ world, source: "push-setting-load" }), 0);
syncAudioControls();
syncVisualSettingsPanel();
window.TarinaiTooltips.applyToolPalette();
@ -12,8 +13,17 @@ function bindUI() {
function isActiveToolMode() {
return Boolean(world?.tool && world.tool !== "observe");
}
function clickedInsideGameOrToolUi(target) {
return Boolean((canvas && canvas.contains?.(target)) || ui.toolPalette?.contains?.(target) || ui.panelQuickTabs?.contains?.(target) || ui.mobileModeControls?.contains?.(target) || ui.mobileRotateControls?.contains?.(target) || ui.backToGameBtn?.contains?.(target));
function clearSelectedToolSilently() {
if (!isActiveToolMode()) return false;
world.pendingLinkEndpoint = null;
world.copyBuffer = null;
selectTool("observe");
for (const button of uiCache.toolButtons || []) button.classList.remove("selected");
return true;
}
function isInteractiveUiTarget(target) {
if (!target?.closest) return false;
return Boolean(target.closest("button, a, input, select, textarea, label, summary, [role='button'], [contenteditable='true'], [data-tool]"));
}
function panelSectionForTab(tab = "") {
@ -160,14 +170,19 @@ function bindUI() {
const colonyLimitControls = (kind) => kind === "tarinai"
? { slider: ui.tarinaiPopulationLimitSlider, input: ui.tarinaiPopulationLimitInput }
: { slider: ui.objectLimitSlider, input: ui.objectLimitInput };
const COLONY_LIMIT_MAX = 300;
const COLONY_LIMIT_INFINITY_SLOT = 301;
const syncColonyLimitControls = (kind, value) => {
const { slider, input } = colonyLimitControls(kind);
const limit = Math.max(0, Math.floor(Number(value || 0) || 0));
if (input && document.activeElement !== input) input.value = limit > 0 ? String(limit) : "";
const rawLimit = Math.max(0, Math.floor(Number(value || 0) || 0));
const limit = rawLimit > 0 ? Math.min(COLONY_LIMIT_MAX, rawLimit) : 0;
if (input && document.activeElement !== input) {
input.value = limit > 0 ? String(limit) : "";
input.setAttribute("aria-valuetext", limit > 0 ? String(limit) : "\u221e");
}
if (slider && document.activeElement !== slider) {
const max = Math.max(0, Number(slider.max || 0) || 0);
slider.value = String(max > 0 ? Math.min(limit, max) : limit);
slider.setAttribute("aria-valuetext", limit > 0 ? String(limit) : "\u4e0a\u9650\u306a\u3057");
slider.value = String(limit > 0 ? limit : COLONY_LIMIT_INFINITY_SLOT);
slider.setAttribute("aria-valuetext", limit > 0 ? String(limit) : "\u221e");
}
};
window.syncColonyLimitControls = syncColonyLimitControls;
@ -176,8 +191,9 @@ function bindUI() {
const { slider, input } = colonyLimitControls(kind);
const sourceEl = String(source).startsWith("slider") ? slider : input;
if (!sourceEl) return;
const raw = sourceEl.value?.trim?.() === "" ? 0 : Number(sourceEl.value);
const value = Math.max(0, Math.floor(Number(raw) || 0));
const raw = sourceEl.value?.trim?.() === "" ? COLONY_LIMIT_INFINITY_SLOT : Number(sourceEl.value);
const requested = Math.floor(Number(raw) || 0);
const value = requested <= 0 || requested >= COLONY_LIMIT_INFINITY_SLOT ? 0 : Math.min(COLONY_LIMIT_MAX, requested);
if (kind === "tarinai") world.setTarinaiPopulationLimit?.(value);
else world.setObjectLimit?.(value);
syncColonyLimitControls(kind, value);
@ -350,12 +366,14 @@ function bindUI() {
audio.uiFold?.();
const collapsed = !card.classList.contains("collapsed");
markCollapsibleCardState(card, collapsed, { user: true });
if (!collapsed && card.classList.contains("colony-card")) renderStats?.({ force: true });
});
}
ui.colonyChartTabs?.addEventListener("click", (e) => {
const btn = e.target.closest("button[data-chart]");
if (!btn) return;
uiCache.chartMode = btn.dataset.chart || "population";
window.TarinaiAchievements?.recordStatsButtonPress?.({ world, control: `chart:${uiCache.chartMode}` });
for (const b of ui.colonyChartTabs.querySelectorAll("button")) b.classList.toggle("active", b === btn);
uiCache.lastChartDraw = "";
renderStats();
@ -368,6 +386,7 @@ function bindUI() {
if (!(uiCache.chartHiddenSeries instanceof Set)) uiCache.chartHiddenSeries = new Set();
if (uiCache.chartHiddenSeries.has(key)) uiCache.chartHiddenSeries.delete(key);
else uiCache.chartHiddenSeries.add(key);
window.TarinaiAchievements?.recordStatsButtonPress?.({ world, control: `series:${key}` });
uiCache.lastChartDraw = "";
audio.uiClick?.();
renderStats();
@ -378,6 +397,7 @@ function bindUI() {
uiCache.logPushEnabled = Boolean(ui.logPushEnabled.checked);
audio.notify?.();
saveLogPushSettings();
if (uiCache.logPushEnabled) window.TarinaiAchievements?.recordPushNotificationsEnabled?.({ world, source: "push-toggle" });
showToast(uiCache.logPushEnabled ? "\u30ed\u30b0\u306e\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u3092ON\u306b\u3057\u307e\u3057\u305f\u3002" : "\u30ed\u30b0\u306e\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u3092OFF\u306b\u3057\u307e\u3057\u305f\u3002");
});
ui.logPushControls?.addEventListener("change", (e) => {
@ -534,15 +554,17 @@ function bindUI() {
showToast(on ? "\u52b9\u679c\u97f3\u3092ON\u306b\u3057\u307e\u3057\u305f\u3002" : "\u52b9\u679c\u97f3\u3092OFF\u306b\u3057\u307e\u3057\u305f\u3002");
});
document.addEventListener("click", (e) => {
if (ui.soundMenu?.contains(e.target) || ui.visualSettingsMenu?.contains(e.target)) return;
if (typeof closeSoundPanel === "function") closeSoundPanel();
closeVisualSettingsPanel();
if (!ui.topMenu?.contains(e.target)) closeTopMenu();
});
document.addEventListener("click", (e) => {
if (!isActiveToolMode()) return;
if (clickedInsideGameOrToolUi(e.target)) return;
selectTool("observe"); // \u9053\u5177\u8a2d\u7f6e\u30e2\u30fc\u30c9\u3092\u89e3\u9664
const target = e.target;
if (!(ui.soundMenu?.contains(target) || ui.visualSettingsMenu?.contains(target))) {
if (typeof closeSoundPanel === "function") closeSoundPanel();
closeVisualSettingsPanel();
if (!ui.topMenu?.contains(target)) closeTopMenu();
}
// Normal controls keep the selected tool. A blank UI click exits placement
// mode; canvas clicks are still handled as tool actions.
if (isActiveToolMode() && !canvas?.contains?.(target) && !isInteractiveUiTarget(target)) {
clearSelectedToolSilently();
}
});
ui.creditsBtn?.addEventListener("click", () => { audio.uiClick?.(); closeTopMenu(); ui.creditsDialog?.classList.remove("hidden"); });
ui.creditsCloseBtn?.addEventListener("click", () => ui.creditsDialog?.classList.add("hidden"));
@ -627,10 +649,7 @@ function bindUI() {
return true;
}
if (tool === "clear_tool") {
world.pendingLinkEndpoint = null;
world.copyBuffer = null;
selectTool("observe");
for (const b of uiCache.toolButtons || []) b.classList.remove("selected");
clearSelectedToolSilently();
showToast("\u9053\u5177\u9078\u629E\u3092\u89E3\u9664\u3057\u307E\u3057\u305F\u3002");
return true;
}