"use strict"; function bindUI() { renderToolPalette(); loadLogPushSettings(); syncLogPushControls(); syncAudioControls(); applyToolTips(); window.bindGroundUI?.(world); renderEcologyCards(); window.TarinaiSaveSystem?.bindSaveSystem?.(); window.TarinaiFreezeSystem?.bindFreezeSystem?.(); uiCache.toolButtons = Array.from(ui.toolPalette?.querySelectorAll(".tool") || []); function isActivePlacementTool() { const def = typeof toolDefinition === "function" ? toolDefinition(world.tool) : null; return Boolean(def?.placeable && typeof toolItemType === "function" && toolItemType(world.tool)); } function clickedInsideGameOrToolUi(target) { return Boolean((canvas && canvas.contains?.(target)) || ui.toolPalette?.contains?.(target) || ui.panelQuickTabs?.contains?.(target)); } function panelSectionForTab(tab = "") { if (tab === "selected") return ui.selectedCard; if (tab === "tools") return ui.toolCard; if (tab === "colony") return document.querySelector(".colony-card"); if (tab === "events") return document.querySelector(".log-card"); return null; } function setActivePanelTab(tab = "") { for (const btn of ui.panelQuickTabs?.querySelectorAll("[data-panel-tab]") || []) { btn.classList.toggle("active", btn.dataset.panelTab === tab); } } function scrollPanelToTab(tab = "") { const panel = ui.panel; if (!panel) return false; if (tab === "selected" && (!world.selected || world.selected.dead || !world.tarinai.includes(world.selected))) { selectTool("observe"); uiCache.showSelectedEmpty = true; uiCache.selectedEmpty = false; renderSelected(); showToast("たりないが未選択です。観察モードにしました。"); } const target = panelSectionForTab(tab); if (!target) return false; target.classList?.remove?.("hidden"); const tabsH = ui.panelQuickTabs?.offsetHeight || 0; const top = Math.max(0, target.offsetTop - tabsH - 8); panel.scrollTo({ top, behavior: "smooth" }); setActivePanelTab(tab); return true; } ui.panelQuickTabs?.addEventListener("click", (e) => { const btn = e.target.closest("[data-panel-tab]"); if (!btn) return; e.preventDefault(); audio.uiClick?.(); scrollPanelToTab(btn.dataset.panelTab || ""); }); ui.panel?.addEventListener("scroll", () => { const panel = ui.panel; if (!panel || !ui.panelQuickTabs) return; const tabsH = ui.panelQuickTabs.offsetHeight || 0; const entries = [ ["selected", ui.selectedCard], ["tools", ui.toolCard], ["colony", document.querySelector(".colony-card")], ["events", document.querySelector(".log-card")], ].filter(([, el]) => el && !el.classList.contains("hidden")); let active = entries[0]?.[0] || ""; const y = panel.scrollTop + tabsH + 18; for (const [key, el] of entries) { if (el.offsetTop <= y) active = key; } setActivePanelTab(active); }, { passive: true }); syncToolSizeBadges(); setActivePanelTab(ui.selectedCard?.classList.contains("hidden") ? "tools" : "selected"); function activeRotatableToolType() { const type = typeof toolItemType === "function" ? toolItemType(world?.tool || "") : ""; return type && typeof isRotatableItemType === "function" && isRotatableItemType(type) ? type : ""; } window.addEventListener("keydown", (e) => { const tag = String(e.target?.tagName || "").toLowerCase(); if (tag === "input" || tag === "textarea" || e.target?.isContentEditable) return; const type = activeRotatableToolType(); if (!type || !world?.rotateToolAngle) return; const key = String(e.key || "").toLowerCase(); const step = (e.shiftKey ? 5 : 45) * Math.PI / 180; if (key === "q" || key === "[") { e.preventDefault(); world.rotateToolAngle(type, -step); render(); } else if (key === "e" || key === "]") { e.preventDefault(); world.rotateToolAngle(type, step); render(); } else if (key === "r") { e.preventDefault(); world.setToolAngle?.(type, typeof defaultItemAngle === "function" ? defaultItemAngle(type) : 0); render(); } }); for (const toggle of document.querySelectorAll(".panel-card-toggle")) { toggle.addEventListener("click", () => { const card = toggle.closest(".collapsible-card"); if (!card) return; audio.uiFold?.(); const collapsed = card.classList.toggle("collapsed"); toggle.setAttribute("aria-expanded", collapsed ? "false" : "true"); const state = toggle.querySelector(".panel-card-state"); if (state) state.textContent = collapsed ? "\u5c55\u958b" : "\u6298\u308a\u7573\u307f"; }); } ui.colonyChartTabs?.addEventListener("click", (e) => { const btn = e.target.closest("button[data-chart]"); if (!btn) return; uiCache.chartMode = btn.dataset.chart || "population"; for (const b of ui.colonyChartTabs.querySelectorAll("button")) b.classList.toggle("active", b === btn); uiCache.lastChartDraw = ""; uiCache.lastChartDrawAt = 0; renderStats(); }); ui.logPushEnabled?.addEventListener("change", () => { uiCache.logPushEnabled = Boolean(ui.logPushEnabled.checked); audio.notify?.(); saveLogPushSettings(); 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) => { const input = e.target.closest("[data-push-kind]"); if (!input) return; const kind = input.dataset.pushKind || ""; audio.uiClick?.(); if (input.checked) uiCache.logPushKinds.add(kind); else uiCache.logPushKinds.delete(kind); saveLogPushSettings(); }); ui.soundCategoryControls?.addEventListener("change", (e) => { const input = e.target.closest("[data-sound-category]"); if (!input) return; const category = input.dataset.soundCategory || ""; audio.setCategory?.(category, Boolean(input.checked)); if (audio.enabled && input.checked) audio.uiClick?.(); syncAudioControls(); showToast(`${audio.soundCategories?.[category] || category} ${input.checked ? "ON" : "OFF"}`); }); ui.archiveContent?.addEventListener("scroll", scheduleArchiveWindowRender, { passive: true }); window.addEventListener("scroll", schedulePendingArchiveRender, { passive: true }); window.addEventListener("resize", schedulePendingArchiveRender, { passive: true }); const bindArchiveHorizontal = (root) => { if (!root) return; root.addEventListener("wheel", (e) => { const scroller = e.target.closest(".lineage-family-tree, .lineage-main-content"); if (!scroller) return; const canX = scroller.scrollWidth > scroller.clientWidth + 4; if (!canX) return; const dominant = Math.abs(e.deltaX) > Math.abs(e.deltaY) ? e.deltaX : (e.shiftKey ? e.deltaY : 0); if (!dominant) return; e.preventDefault(); scroller.scrollLeft += dominant; }, { passive: false }); }; bindArchiveHorizontal(ui.archiveContent); ui.archiveContent?.addEventListener("click", (e) => { const node = e.target.closest("[data-family-tarinai-id]"); if (!node) return; const id = node.getAttribute("data-family-tarinai-id"); const t = world.tarinai.find(x => (x.id === id || x.familyKey === id) && !x.dead); if (!t) { showToast("\u305d\u306e\u500b\u4f53\u306f\u3082\u3046\u9078\u629e\u3067\u304d\u307e\u305b\u3093\u3002"); return; } window.TarinaiCommands?.dispatch?.(world, { type: "selection.focus", target: t, options: { pulse: 1.15 } }); selectTool("observe"); renderStats(); showToast(`${t.name}\u3092\u9078\u629e\u3057\u307e\u3057\u305f\u3002`); }); if (!uiCache.archiveAutoRefreshTimer && typeof window !== "undefined") { uiCache.archiveAutoRefreshTimer = window.setInterval(() => { if (!ui.archiveContent || !archiveAutoUpdateEnabled()) return; scheduleArchiveWindowRender(); }, 5000); } ui.selectedInfo?.addEventListener("click", (e) => { const categoryToggle = e.target.closest(".selected-category-toggle"); if (categoryToggle) { const category = categoryToggle.closest(".selected-category"); const id = category?.dataset?.selectedCategory || ""; if (!category || !id) return; if (!uiCache.selectedCollapsedCategories) uiCache.selectedCollapsedCategories = new Set(); audio.uiFold?.(); category.classList.toggle("collapsed"); const collapsed = category.classList.contains("collapsed"); if (collapsed) uiCache.selectedCollapsedCategories.add(id); else uiCache.selectedCollapsedCategories.delete(id); categoryToggle.setAttribute("aria-expanded", collapsed ? "false" : "true"); return; } const focusBtn = e.target.closest("[data-focus-tarinai-id]"); if (focusBtn) { e.preventDefault(); focusTarinaiById(focusBtn.getAttribute("data-focus-tarinai-id")); return; } const btn = e.target.closest("[data-selected-action=\"favorite\"]"); if (!btn || !world.selected || world.selected.dead) return; const favResult = window.TarinaiCommands?.dispatch?.(world, { type: "selection.favorite.toggle" }); if (!favResult?.ok) return; uiCache.selectedSnapshot = ""; renderSelected(); render(); showToast(favResult.favorite ? "\u304a\u6c17\u306b\u5165\u308a\u306b\u3057\u307e\u3057\u305f\u3002" : "\u304a\u6c17\u306b\u5165\u308a\u3092\u89e3\u9664\u3057\u307e\u3057\u305f\u3002"); }); ui.selectedInfo?.addEventListener("change", (e) => { const input = e.target.closest("[data-selected-name]"); if (!input) return; renameSelectedTarinai(input.value); }); ui.selectedInfo?.addEventListener("keydown", (e) => { const input = e.target.closest("[data-selected-name]"); if (!input) return; if (e.key === "Enter") { e.preventDefault(); input.blur(); } if (e.key === "Escape") { e.preventDefault(); input.value = world.selected?.name || ""; input.blur(); } }); ui.selectedCloseBtn?.addEventListener("click", () => { window.TarinaiCommands?.dispatch?.(world, { type: "selection.clear" }); uiCache.showSelectedEmpty = false; uiCache.selectedSnapshot = ""; uiCache.selectedEmpty = false; renderSelected(); render(); }); ui.pauseBtn?.addEventListener("click", () => { audio.uiClick?.(); const pauseResult = window.TarinaiCommands?.dispatch?.(world, { type: "simulation.paused.toggle" }); const paused = pauseResult?.ok ? pauseResult.paused : world.paused; const archiveStale = uiCache.archiveFamilyVersion !== (world.familyVersion || 0); if (archiveAutoUpdateEnabled() && paused && (uiCache.archivePendingWhileRunning || archiveStale)) { uiCache.archivePendingWhileRunning = false; scheduleArchiveWindowRender(); } syncTopButtons(); }); ui.speedBtn?.addEventListener("click", () => { audio.uiClick?.(); window.TarinaiCommands?.dispatch?.(world, { type: "simulation.speed.cycle", speeds: [1, 2, 4] }); syncTopButtons(); }); ui.ecologyBtn?.addEventListener("click", () => { audio.uiClick?.(); openEcologyDialog(); }); ui.soundBtn?.addEventListener("click", (e) => { e.stopPropagation(); toggleSoundPanel(); }); ui.soundPanel?.addEventListener("click", (e) => e.stopPropagation()); ui.soundMasterToggle?.addEventListener("change", () => { const on = audio.setEnabled ? audio.setEnabled(ui.soundMasterToggle.checked) : audio.toggle(); if (on) audio.uiClick?.(); syncAudioControls(); 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 || ui.soundMenu.contains(e.target)) return; closeSoundPanel(); }); document.addEventListener("click", (e) => { if (!isActivePlacementTool()) return; if (clickedInsideGameOrToolUi(e.target)) return; selectTool("observe"); // \u9053\u5177\u8a2d\u7f6e\u30e2\u30fc\u30c9\u3092\u89e3\u9664 }); ui.creditsBtn?.addEventListener("click", () => { audio.uiClick?.(); openCreditsDialog(); }); ui.creditsCloseBtn?.addEventListener("click", closeCreditsDialog); ui.creditsDialog?.addEventListener("click", (e) => { if (e.target === ui.creditsDialog) closeCreditsDialog(); }); ui.resetBtn?.addEventListener("click", () => { audio.uiClick?.(); openFieldDialog(); }); ui.fieldCancelBtn?.addEventListener("click", closeFieldDialog); ui.fieldDialog?.addEventListener("click", (e) => { if (e.target === ui.fieldDialog) { closeFieldDialog(); return; } const btn = e.target.closest("[data-field-type]"); if (!btn) return; resetWithField(btn.dataset.fieldType || "garden"); }); ui.ecologyCloseBtn?.addEventListener("click", closeEcologyDialog); ui.ecologyDialog?.addEventListener("click", (e) => { if (e.target === ui.ecologyDialog) closeEcologyDialog(); }); ui.archiveUpdateToggleBtn?.addEventListener("click", () => { audio.uiClick?.(); setArchiveUpdateEnabled(!archiveAutoUpdateEnabled()); }); syncArchiveUpdateButton(); ui.archiveResetBtn?.addEventListener("click", () => { audio.uiClick?.(); openArchiveResetDialog(); }); ui.archiveResetCancelBtn?.addEventListener("click", closeArchiveResetDialog); ui.archiveResetDialog?.addEventListener("click", (e) => { if (e.target === ui.archiveResetDialog) closeArchiveResetDialog(); }); ui.archiveResetConfirmBtn?.addEventListener("click", () => { world.resetFamilyTree?.(); window.resetArchiveRenderState?.(); closeArchiveResetDialog(); renderArchive(); renderSelected(); showToast("\u5bb6\u7cfb\u56f3\u3092\u30ea\u30bb\u30c3\u30c8\u3057\u307e\u3057\u305f\u3002"); }); ui.toolPalette?.addEventListener("click", (e) => { const toggle = e.target.closest(".tool-category-toggle"); if (toggle) { const category = toggle.closest(".tool-category"); if (!category) return; audio.uiFold?.(); category.classList.toggle("collapsed"); toggle.setAttribute("aria-expanded", category.classList.contains("collapsed") ? "false" : "true"); return; } const btn = e.target.closest(".tool"); if (!btn) return; const tool = btn.dataset.tool; audio.uiClick?.(); if (world.tool === tool && cycleToolSize(tool)) return; selectTool(tool); }); const chainScroll = (el) => { if (!el) return; el.addEventListener("wheel", (e) => { if (Math.abs(e.deltaY) <= Math.abs(e.deltaX)) return; const atTop = el.scrollTop <= 0; const atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 1; if ((e.deltaY < 0 && atTop) || (e.deltaY > 0 && atBottom)) { const page = document.scrollingElement || document.documentElement; page.scrollTop += e.deltaY; } }, { passive: true }); }; const transferEdgeScroll = (child, parent) => { if (!child || !parent) return; child.addEventListener("wheel", (e) => { if (Math.abs(e.deltaY) <= Math.abs(e.deltaX)) return; const childCanScroll = child.scrollHeight > child.clientHeight + 1; const atTop = child.scrollTop <= 0; const atBottom = child.scrollTop + child.clientHeight >= child.scrollHeight - 1; const movingInside = childCanScroll && ((e.deltaY < 0 && !atTop) || (e.deltaY > 0 && !atBottom)); e.preventDefault(); e.stopPropagation(); if (movingInside) { child.scrollTop += e.deltaY; return; } parent.scrollTop += e.deltaY; }, { passive: false }); }; const rightPanel = document.querySelector(".panel"); chainScroll(rightPanel); chainScroll(ui.archiveContent); transferEdgeScroll(ui.log, rightPanel); const inputContext = window.TarinaiUIInputShared?.createInputContext?.({ canvas, world, ui, uiCache }); if (!inputContext) throw new Error("Tarinai UI input context is not available"); window.TarinaiTouchInput?.bindTouchInput?.(inputContext); window.TarinaiMouseInput?.bindMouseInput?.(inputContext); window.addEventListener("keydown", (e) => { if (e.key !== "Escape") return; closeFieldDialog(); closeEcologyDialog(); closeArchiveResetDialog(); closeCreditsDialog(); }); window.addEventListener("resize", resizeCanvas); }