feat(subtitle-sidebar): add sidebar config surface (#28)

This commit is contained in:
2026-03-21 23:37:42 -07:00
committed by GitHub
parent eddf6f0456
commit 3a01cffc6b
66 changed files with 5241 additions and 426 deletions

View File

@@ -34,10 +34,12 @@ import { createControllerSelectModal } from './modals/controller-select.js';
import { createJimakuModal } from './modals/jimaku.js';
import { createKikuModal } from './modals/kiku.js';
import { createSessionHelpModal } from './modals/session-help.js';
import { createSubtitleSidebarModal } from './modals/subtitle-sidebar.js';
import { createRuntimeOptionsModal } from './modals/runtime-options.js';
import { createSubsyncModal } from './modals/subsync.js';
import { createPositioningController } from './positioning.js';
import { createOverlayContentMeasurementReporter } from './overlay-content-measurement.js';
import { syncOverlayMouseIgnoreState } from './overlay-mouse-ignore.js';
import { createRendererState } from './state.js';
import { createSubtitleRenderer } from './subtitle-render.js';
import { isYomitanPopupVisible, registerYomitanLookupListener } from './yomitan-popup.js';
@@ -78,7 +80,8 @@ function isAnyModalOpen(): boolean {
ctx.state.kikuModalOpen ||
ctx.state.runtimeOptionsModalOpen ||
ctx.state.subsyncModalOpen ||
ctx.state.sessionHelpModalOpen
ctx.state.sessionHelpModalOpen ||
ctx.state.subtitleSidebarModalOpen
);
}
@@ -114,6 +117,9 @@ const sessionHelpModal = createSessionHelpModal(ctx, {
modalStateReader: { isAnyModalOpen },
syncSettingsModalSubtitleSuppression,
});
const subtitleSidebarModal = createSubtitleSidebarModal(ctx, {
modalStateReader: { isAnyModalOpen },
});
const kikuModal = createKikuModal(ctx, {
modalStateReader: { isAnyModalOpen },
syncSettingsModalSubtitleSuppression,
@@ -143,6 +149,9 @@ const keyboardHandlers = createKeyboardHandlers(ctx, {
controllerDebugModal.openControllerDebugModal();
window.electronAPI.notifyOverlayModalOpened('controller-debug');
},
toggleSubtitleSidebarModal: () => {
void subtitleSidebarModal.toggleSubtitleSidebarModal();
},
});
const mouseHandlers = createMouseHandlers(ctx, {
modalStateReader: { isAnySettingsModalOpen, isAnyModalOpen },
@@ -183,6 +192,7 @@ function getSubtitleTextForPreview(data: SubtitleData | string): string {
function getActiveModal(): string | null {
if (ctx.state.controllerSelectModalOpen) return 'controller-select';
if (ctx.state.controllerDebugModalOpen) return 'controller-debug';
if (ctx.state.subtitleSidebarModalOpen) return 'subtitle-sidebar';
if (ctx.state.jimakuModalOpen) return 'jimaku';
if (ctx.state.kikuModalOpen) return 'kiku';
if (ctx.state.runtimeOptionsModalOpen) return 'runtime-options';
@@ -198,6 +208,9 @@ function dismissActiveUiAfterError(): void {
if (ctx.state.controllerDebugModalOpen) {
controllerDebugModal.closeControllerDebugModal();
}
if (ctx.state.subtitleSidebarModalOpen) {
subtitleSidebarModal.closeSubtitleSidebarModal();
}
if (ctx.state.jimakuModalOpen) {
jimakuModal.closeJimakuModal();
}
@@ -468,6 +481,7 @@ async function init(): Promise<void> {
lastSubtitlePreview = truncateForErrorLog(getSubtitleTextForPreview(data));
keyboardHandlers.handleSubtitleContentUpdated();
subtitleRenderer.renderSubtitle(data);
subtitleSidebarModal.handleSubtitleUpdated(data);
measurementReporter.schedule();
});
});
@@ -508,10 +522,10 @@ async function init(): Promise<void> {
subtitleRenderer.renderSecondarySub(await window.electronAPI.getCurrentSecondarySub());
measurementReporter.schedule();
ctx.dom.subtitleContainer.addEventListener('mouseenter', mouseHandlers.handleMouseEnter);
ctx.dom.subtitleContainer.addEventListener('mouseleave', mouseHandlers.handleMouseLeave);
ctx.dom.secondarySubContainer.addEventListener('mouseenter', mouseHandlers.handleMouseEnter);
ctx.dom.secondarySubContainer.addEventListener('mouseleave', mouseHandlers.handleMouseLeave);
ctx.dom.subtitleContainer.addEventListener('mouseenter', mouseHandlers.handlePrimaryMouseEnter);
ctx.dom.subtitleContainer.addEventListener('mouseleave', mouseHandlers.handlePrimaryMouseLeave);
ctx.dom.secondarySubContainer.addEventListener('mouseenter', mouseHandlers.handleSecondaryMouseEnter);
ctx.dom.secondarySubContainer.addEventListener('mouseleave', mouseHandlers.handleSecondaryMouseLeave);
mouseHandlers.setupResizeHandler();
mouseHandlers.setupSelectionObserver();
@@ -528,6 +542,10 @@ async function init(): Promise<void> {
controllerSelectModal.wireDomEvents();
controllerDebugModal.wireDomEvents();
sessionHelpModal.wireDomEvents();
subtitleSidebarModal.wireDomEvents();
window.addEventListener('beforeunload', () => {
subtitleSidebarModal.disposeDomEvents();
});
window.electronAPI.onRuntimeOptionsChanged((options: RuntimeOptionState[]) => {
runGuarded('runtime-options:changed', () => {
@@ -539,6 +557,11 @@ async function init(): Promise<void> {
keyboardHandlers.updateKeybindings(payload.keybindings);
subtitleRenderer.applySubtitleStyle(payload.subtitleStyle);
subtitleRenderer.updateSecondarySubMode(payload.secondarySubMode);
ctx.state.subtitleSidebarConfig = payload.subtitleSidebar;
ctx.state.subtitleSidebarToggleKey = payload.subtitleSidebar.toggleKey;
ctx.state.subtitleSidebarPauseVideoOnHover = payload.subtitleSidebar.pauseVideoOnHover;
ctx.state.subtitleSidebarAutoScroll = payload.subtitleSidebar.autoScroll;
void subtitleSidebarModal.refreshSubtitleSidebarSnapshot();
measurementReporter.schedule();
});
});
@@ -555,6 +578,8 @@ async function init(): Promise<void> {
const initialSubtitleStyle = await window.electronAPI.getSubtitleStyle();
subtitleRenderer.applySubtitleStyle(initialSubtitleStyle);
await subtitleSidebarModal.refreshSubtitleSidebarSnapshot();
await subtitleSidebarModal.autoOpenSubtitleSidebarOnStartup();
positioning.applyStoredSubtitlePosition(
await window.electronAPI.getSubtitlePosition(),
@@ -563,7 +588,7 @@ async function init(): Promise<void> {
measurementReporter.schedule();
if (ctx.platform.shouldToggleMouseIgnore) {
window.electronAPI.setIgnoreMouseEvents(true, { forward: true });
syncOverlayMouseIgnoreState(ctx);
}
measurementReporter.emitNow();