// Adjustable panel gap controls // Feature Registration Pattern implementation import type { ReaderContext } from "../core/reader-context"; export function init(context: ReaderContext): void { const state = createPanelGapState(); applyPanelGap(state.gapSize, state.showBorders); context.events.on("panel-gap:set", (detail: { gap: number }) => { setPanelGap(state, detail.gap); }); context.events.on("panel-gap:increase", (detail?: { amount: number }) => { increasePanelGap(state, detail?.amount); }); context.events.on("panel-gap:decrease", (detail?: { amount: number }) => { decreasePanelGap(state, detail?.amount); }); context.events.on("panel-gap:borders:toggle", () => { togglePanelBorders(state); }); context.events.on("ui:show-settings", (detail: { container: HTMLElement }) => { renderPanelGapControls(detail.container, state); }); context.events.on("reader:unload", () => { const controls = document.querySelector(".panel-gap-controls"); controls?.remove(); }); } interface PanelGapState { gapSize: number; showBorders: boolean; } function createPanelGapState(initialGap: number = 4): PanelGapState { const saved = localStorage.getItem("reader-panel-gap"); return { gapSize: saved ? parseInt(saved) : initialGap, showBorders: false, }; } function applyPanelGap(gap: number, showBorders: boolean): void { document.documentElement.style.setProperty("--panel-gap", `${gap}px`); document.documentElement.style.setProperty( "--panel-border-width", showBorders ? "1px" : "0px", ); localStorage.setItem("reader-panel-gap", String(gap)); } function setPanelGap(state: PanelGapState, gap: number): PanelGapState { const clampedGap = Math.max(0, Math.min(20, gap)); state.gapSize = clampedGap; document.documentElement.style.setProperty("--panel-gap", `${clampedGap}px`); localStorage.setItem("reader-panel-gap", String(clampedGap)); const controls = document.querySelector(".panel-gap-controls"); if (controls) { updatePanelGapUI(controls as HTMLElement, state); } return state; } function increasePanelGap(state: PanelGapState, amount: number = 2): PanelGapState { return setPanelGap(state, state.gapSize + amount); } function decreasePanelGap(state: PanelGapState, amount: number = 2): PanelGapState { return setPanelGap(state, state.gapSize - amount); } function togglePanelBorders(state: PanelGapState): PanelGapState { state.showBorders = !state.showBorders; document.documentElement.style.setProperty( "--panel-border-width", state.showBorders ? "1px" : "0px", ); const controls = document.querySelector(".panel-gap-controls"); if (controls) { updatePanelGapUI(controls as HTMLElement, state); } return state; } function renderPanelGapControls( container: HTMLElement, state: PanelGapState, ): void { const existing = container.querySelector(".panel-gap-controls"); existing?.remove(); const controls = document.createElement("div"); controls.className = "panel-gap-controls fixed bottom-24 right-4 bg-gray-900 bg-opacity-90 rounded-lg p-2 flex flex-col gap-2 z-40"; controls.innerHTML = ` ${state.gapSize}px `; controls .querySelector(".panel-gap-increase") ?.addEventListener("click", () => { increasePanelGap(state); updatePanelGapUI(controls, state); }); controls .querySelector(".panel-gap-decrease") ?.addEventListener("click", () => { decreasePanelGap(state); updatePanelGapUI(controls, state); }); controls .querySelector(".panel-gap-borders") ?.addEventListener("click", () => { togglePanelBorders(state); updatePanelGapUI(controls, state); }); container.appendChild(controls); } function updatePanelGapUI(container: HTMLElement, state: PanelGapState): void { const gapLabel = container.querySelector("span"); if (gapLabel) { gapLabel.textContent = `${state.gapSize}px`; } const bordersBtn = container.querySelector(".panel-gap-borders"); if (bordersBtn) { bordersBtn.textContent = state.showBorders ? "▦" : "▢"; } }