mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-05-27 12:55:20 -07:00
fix: settings window z-order on Hyprland and Linux app detach (#85)
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: desktop
|
||||||
|
|
||||||
|
- Fixed Hyprland settings windows opening behind the subtitle overlay by promoting SubMiner and Yomitan settings above the overlay without hiding subtitles.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: launcher
|
||||||
|
|
||||||
|
- Fixed `subminer app` on Linux so launching the tray app returns control to the terminal immediately instead of waiting for the tray process to exit.
|
||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
import type { LauncherCommandContext } from './context.js';
|
import type { LauncherCommandContext } from './context.js';
|
||||||
|
|
||||||
type AppCommandDeps = {
|
type AppCommandDeps = {
|
||||||
platform: () => NodeJS.Platform;
|
|
||||||
runAppCommandWithInherit: (appPath: string, appArgs: string[]) => void;
|
runAppCommandWithInherit: (appPath: string, appArgs: string[]) => void;
|
||||||
launchAppBackgroundDetached: (
|
launchAppBackgroundDetached: (
|
||||||
appPath: string,
|
appPath: string,
|
||||||
@@ -15,7 +14,6 @@ type AppCommandDeps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const defaultAppCommandDeps: AppCommandDeps = {
|
const defaultAppCommandDeps: AppCommandDeps = {
|
||||||
platform: () => process.platform,
|
|
||||||
runAppCommandWithInherit,
|
runAppCommandWithInherit,
|
||||||
launchAppBackgroundDetached,
|
launchAppBackgroundDetached,
|
||||||
};
|
};
|
||||||
@@ -35,7 +33,7 @@ export function runAppPassthroughCommand(
|
|||||||
if (!args.appPassthrough) {
|
if (!args.appPassthrough) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (deps.platform() === 'darwin' && args.appArgs.length === 0) {
|
if (args.appArgs.length === 0) {
|
||||||
deps.launchAppBackgroundDetached(appPath, args.logLevel);
|
deps.launchAppBackgroundDetached(appPath, args.logLevel);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,7 +176,25 @@ test('app command starts default macOS background app detached from launcher', (
|
|||||||
const calls: string[] = [];
|
const calls: string[] = [];
|
||||||
|
|
||||||
const handled = runAppPassthroughCommand(context, {
|
const handled = runAppPassthroughCommand(context, {
|
||||||
platform: () => 'darwin',
|
runAppCommandWithInherit: () => {
|
||||||
|
calls.push('attached');
|
||||||
|
},
|
||||||
|
launchAppBackgroundDetached: (appPath, logLevel) => {
|
||||||
|
calls.push(`detached:${appPath}:${logLevel}`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(handled, true);
|
||||||
|
assert.deepEqual(calls, ['detached:/tmp/subminer.app:info']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('app command starts default Linux background app detached from launcher', () => {
|
||||||
|
const context = createContext();
|
||||||
|
context.args.appPassthrough = true;
|
||||||
|
context.args.appArgs = [];
|
||||||
|
const calls: string[] = [];
|
||||||
|
|
||||||
|
const handled = runAppPassthroughCommand(context, {
|
||||||
runAppCommandWithInherit: () => {
|
runAppCommandWithInherit: () => {
|
||||||
calls.push('attached');
|
calls.push('attached');
|
||||||
},
|
},
|
||||||
@@ -197,7 +215,6 @@ test('app command keeps explicit passthrough args attached', () => {
|
|||||||
const detached: string[] = [];
|
const detached: string[] = [];
|
||||||
|
|
||||||
const handled = runAppPassthroughCommand(context, {
|
const handled = runAppPassthroughCommand(context, {
|
||||||
platform: () => 'darwin',
|
|
||||||
runAppCommandWithInherit: (_appPath, appArgs) => {
|
runAppCommandWithInherit: (_appPath, appArgs) => {
|
||||||
forwarded.push(appArgs);
|
forwarded.push(appArgs);
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
@@ -50,7 +50,7 @@
|
|||||||
"test:plugin:src": "lua scripts/test-plugin-lua-compat.lua && lua scripts/test-plugin-start-gate.lua && lua scripts/test-plugin-session-bindings.lua && lua scripts/test-plugin-binary-windows.lua",
|
"test:plugin:src": "lua scripts/test-plugin-lua-compat.lua && lua scripts/test-plugin-start-gate.lua && lua scripts/test-plugin-session-bindings.lua && lua scripts/test-plugin-binary-windows.lua",
|
||||||
"test:launcher:smoke:src": "bun test launcher/smoke.e2e.test.ts",
|
"test:launcher:smoke:src": "bun test launcher/smoke.e2e.test.ts",
|
||||||
"test:launcher:src": "bun test launcher/config.test.ts launcher/config-domain-parsers.test.ts launcher/config/cli-parser-builder.test.ts launcher/config/args-normalizer.test.ts launcher/mpv.test.ts launcher/picker.test.ts launcher/parse-args.test.ts launcher/main.test.ts launcher/commands/command-modules.test.ts launcher/commands/update-command.test.ts launcher/smoke.e2e.test.ts && bun run test:plugin:src",
|
"test:launcher:src": "bun test launcher/config.test.ts launcher/config-domain-parsers.test.ts launcher/config/cli-parser-builder.test.ts launcher/config/args-normalizer.test.ts launcher/mpv.test.ts launcher/picker.test.ts launcher/parse-args.test.ts launcher/main.test.ts launcher/commands/command-modules.test.ts launcher/commands/update-command.test.ts launcher/smoke.e2e.test.ts && bun run test:plugin:src",
|
||||||
"test:core:src": "bun test src/preload-settings.test.ts src/settings/settings-anki-controls.test.ts src/settings/settings-model.test.ts src/settings/settings-field-layout.test.ts src/cli/args.test.ts src/cli/help.test.ts src/shared/setup-state.test.ts src/core/services/cli-command.test.ts src/core/services/ipc.test.ts src/core/services/anki-jimaku-ipc.test.ts src/core/services/field-grouping-overlay.test.ts src/core/services/numeric-shortcut-session.test.ts src/core/services/secondary-subtitle.test.ts src/core/services/mpv-render-metrics.test.ts src/core/services/overlay-content-measurement.test.ts src/core/services/mpv-control.test.ts src/core/services/mpv.test.ts src/core/services/runtime-options-ipc.test.ts src/core/services/runtime-config.test.ts src/core/services/yomitan-extension-paths.test.ts src/core/services/yomitan-extension-loader.test.ts src/core/services/yomitan-settings.test.ts src/core/services/config-hot-reload.test.ts src/core/services/discord-presence.test.ts src/core/services/tokenizer.test.ts src/core/services/tokenizer/annotation-stage.test.ts src/core/services/tokenizer/parser-selection-stage.test.ts src/core/services/tokenizer/parser-enrichment-stage.test.ts src/core/services/subsync.test.ts src/core/services/overlay-bridge.test.ts src/core/services/overlay-shortcut-handler.test.ts src/core/services/stats-window.test.ts src/core/services/stats-window-lifecycle.test.ts src/core/services/__tests__/stats-server.test.ts src/main/runtime/stats-server-routing.test.ts src/core/services/mining.test.ts src/core/services/anki-jimaku.test.ts src/core/services/jimaku-download-path.test.ts src/core/services/jellyfin.test.ts src/core/services/jellyfin-remote.test.ts src/core/services/immersion-tracker-service.test.ts src/core/services/overlay-runtime-init.test.ts src/core/services/app-ready.test.ts src/core/services/startup-bootstrap.test.ts src/core/services/subtitle-processing-controller.test.ts src/main/runtime/current-subtitle-snapshot.test.ts src/main/runtime/autoplay-tokenization-warm-release.test.ts src/main/runtime/autoplay-subtitle-primer.test.ts src/core/services/anilist/anilist-update-queue.test.ts src/core/services/anilist/rate-limiter.test.ts src/core/services/jlpt-token-filter.test.ts src/core/services/subtitle-position.test.ts src/core/utils/shortcut-config.test.ts src/main/runtime/startup-mode-flags.test.ts src/main/runtime/first-run-setup-plugin.test.ts src/main/runtime/first-run-setup-service.test.ts src/main/runtime/first-run-setup-window.test.ts src/main/runtime/command-line-launcher.test.ts src/main/runtime/tray-runtime.test.ts src/main/runtime/tray-main-actions.test.ts src/main/runtime/tray-main-deps.test.ts src/main/runtime/tray-runtime-handlers.test.ts src/main/runtime/cli-command-context-main-deps.test.ts src/main/runtime/app-ready-main-deps.test.ts src/main/runtime/update/appimage-updater.test.ts src/main/runtime/update/fetch-adapter.test.ts src/main/runtime/update/release-metadata-policy.test.ts src/main/runtime/update/update-dialogs.test.ts src/main/runtime/update/support-assets.test.ts src/renderer/error-recovery.test.ts src/renderer/subtitle-render.test.ts src/renderer/subtitle-render-word-class.test.ts src/renderer/handlers/mouse.test.ts src/renderer/handlers/keyboard.test.ts src/renderer/modals/jimaku.test.ts src/subsync/utils.test.ts src/main/anilist-url-guard.test.ts src/window-trackers/hyprland-tracker.test.ts src/window-trackers/x11-tracker.test.ts src/window-trackers/windows-helper.test.ts src/window-trackers/windows-tracker.test.ts launcher/config.test.ts launcher/config-domain-parsers.test.ts launcher/config/cli-parser-builder.test.ts launcher/config/args-normalizer.test.ts launcher/parse-args.test.ts launcher/main.test.ts launcher/commands/command-modules.test.ts launcher/commands/update-command.test.ts launcher/setup-gate.test.ts stats/src/lib/api-client.test.ts stats/src/hooks/useExcludedWords.test.ts",
|
"test:core:src": "bun test src/preload-settings.test.ts src/settings/settings-anki-controls.test.ts src/settings/settings-model.test.ts src/settings/settings-field-layout.test.ts src/cli/args.test.ts src/cli/help.test.ts src/shared/setup-state.test.ts src/core/services/cli-command.test.ts src/core/services/ipc.test.ts src/core/services/anki-jimaku-ipc.test.ts src/core/services/field-grouping-overlay.test.ts src/core/services/numeric-shortcut-session.test.ts src/core/services/secondary-subtitle.test.ts src/core/services/mpv-render-metrics.test.ts src/core/services/overlay-content-measurement.test.ts src/core/services/mpv-control.test.ts src/core/services/mpv.test.ts src/core/services/runtime-options-ipc.test.ts src/core/services/runtime-config.test.ts src/core/services/yomitan-extension-paths.test.ts src/core/services/yomitan-extension-loader.test.ts src/core/services/yomitan-settings.test.ts src/core/services/settings-window-z-order.test.ts src/core/services/config-hot-reload.test.ts src/core/services/discord-presence.test.ts src/core/services/tokenizer.test.ts src/core/services/tokenizer/annotation-stage.test.ts src/core/services/tokenizer/parser-selection-stage.test.ts src/core/services/tokenizer/parser-enrichment-stage.test.ts src/core/services/subsync.test.ts src/core/services/overlay-bridge.test.ts src/core/services/overlay-shortcut-handler.test.ts src/core/services/stats-window.test.ts src/core/services/stats-window-lifecycle.test.ts src/core/services/__tests__/stats-server.test.ts src/main/runtime/stats-server-routing.test.ts src/core/services/mining.test.ts src/core/services/anki-jimaku.test.ts src/core/services/jimaku-download-path.test.ts src/core/services/jellyfin.test.ts src/core/services/jellyfin-remote.test.ts src/core/services/immersion-tracker-service.test.ts src/core/services/overlay-runtime-init.test.ts src/core/services/app-ready.test.ts src/core/services/startup-bootstrap.test.ts src/core/services/subtitle-processing-controller.test.ts src/main/runtime/current-subtitle-snapshot.test.ts src/main/runtime/autoplay-tokenization-warm-release.test.ts src/main/runtime/autoplay-subtitle-primer.test.ts src/core/services/anilist/anilist-update-queue.test.ts src/core/services/anilist/rate-limiter.test.ts src/core/services/jlpt-token-filter.test.ts src/core/services/subtitle-position.test.ts src/core/utils/shortcut-config.test.ts src/main/runtime/startup-mode-flags.test.ts src/main/runtime/config-settings-window.test.ts src/main/runtime/settings-window-z-order.test.ts src/main/runtime/setup-window-factory.test.ts src/main/runtime/first-run-setup-plugin.test.ts src/main/runtime/first-run-setup-service.test.ts src/main/runtime/first-run-setup-window.test.ts src/main/runtime/command-line-launcher.test.ts src/main/runtime/tray-runtime.test.ts src/main/runtime/tray-main-actions.test.ts src/main/runtime/tray-main-deps.test.ts src/main/runtime/tray-runtime-handlers.test.ts src/main/runtime/cli-command-context-main-deps.test.ts src/main/runtime/app-ready-main-deps.test.ts src/main/runtime/update/appimage-updater.test.ts src/main/runtime/update/fetch-adapter.test.ts src/main/runtime/update/release-metadata-policy.test.ts src/main/runtime/update/update-dialogs.test.ts src/main/runtime/update/support-assets.test.ts src/renderer/error-recovery.test.ts src/renderer/subtitle-render.test.ts src/renderer/subtitle-render-word-class.test.ts src/renderer/handlers/mouse.test.ts src/renderer/handlers/keyboard.test.ts src/renderer/modals/jimaku.test.ts src/subsync/utils.test.ts src/main/anilist-url-guard.test.ts src/window-trackers/hyprland-tracker.test.ts src/window-trackers/x11-tracker.test.ts src/window-trackers/windows-helper.test.ts src/window-trackers/windows-tracker.test.ts launcher/config.test.ts launcher/config-domain-parsers.test.ts launcher/config/cli-parser-builder.test.ts launcher/config/args-normalizer.test.ts launcher/parse-args.test.ts launcher/main.test.ts launcher/commands/command-modules.test.ts launcher/commands/update-command.test.ts launcher/setup-gate.test.ts stats/src/lib/api-client.test.ts stats/src/hooks/useExcludedWords.test.ts",
|
||||||
"test:core:dist": "bun test dist/preload-settings.test.js dist/settings/settings-anki-controls.test.js dist/settings/settings-model.test.js dist/settings/settings-field-layout.test.js dist/cli/args.test.js dist/cli/help.test.js dist/core/services/cli-command.test.js dist/core/services/ipc.test.js dist/core/services/anki-jimaku-ipc.test.js dist/core/services/field-grouping-overlay.test.js dist/core/services/numeric-shortcut-session.test.js dist/core/services/secondary-subtitle.test.js dist/core/services/mpv-render-metrics.test.js dist/core/services/overlay-content-measurement.test.js dist/core/services/mpv-control.test.js dist/core/services/mpv.test.js dist/core/services/runtime-options-ipc.test.js dist/core/services/runtime-config.test.js dist/core/services/yomitan-extension-paths.test.js dist/core/services/config-hot-reload.test.js dist/core/services/discord-presence.test.js dist/core/services/tokenizer.test.js dist/core/services/tokenizer/annotation-stage.test.js dist/core/services/tokenizer/parser-selection-stage.test.js dist/core/services/tokenizer/parser-enrichment-stage.test.js dist/core/services/subsync.test.js dist/core/services/overlay-bridge.test.js dist/core/services/overlay-manager.test.js dist/core/services/overlay-shortcut-handler.test.js dist/core/services/stats-window-lifecycle.test.js dist/core/services/mining.test.js dist/core/services/anki-jimaku.test.js dist/core/services/jimaku-download-path.test.js dist/core/services/jellyfin.test.js dist/core/services/jellyfin-remote.test.js dist/core/services/immersion-tracker-service.test.js dist/core/services/overlay-runtime-init.test.js dist/core/services/app-ready.test.js dist/core/services/startup-bootstrap.test.js dist/core/services/subtitle-processing-controller.test.js dist/main/runtime/current-subtitle-snapshot.test.js dist/main/runtime/autoplay-tokenization-warm-release.test.js dist/main/runtime/autoplay-subtitle-primer.test.js dist/core/services/anilist/anilist-token-store.test.js dist/core/services/anilist/anilist-update-queue.test.js dist/core/services/anilist/rate-limiter.test.js dist/core/services/jlpt-token-filter.test.js dist/core/services/subtitle-position.test.js dist/renderer/error-recovery.test.js dist/renderer/subtitle-render.test.js dist/renderer/subtitle-render-word-class.test.js dist/renderer/handlers/mouse.test.js dist/renderer/handlers/keyboard.test.js dist/renderer/modals/jimaku.test.js dist/subsync/utils.test.js dist/main/anilist-url-guard.test.js dist/window-trackers/hyprland-tracker.test.js dist/window-trackers/x11-tracker.test.js dist/window-trackers/windows-helper.test.js dist/window-trackers/windows-tracker.test.js",
|
"test:core:dist": "bun test dist/preload-settings.test.js dist/settings/settings-anki-controls.test.js dist/settings/settings-model.test.js dist/settings/settings-field-layout.test.js dist/cli/args.test.js dist/cli/help.test.js dist/core/services/cli-command.test.js dist/core/services/ipc.test.js dist/core/services/anki-jimaku-ipc.test.js dist/core/services/field-grouping-overlay.test.js dist/core/services/numeric-shortcut-session.test.js dist/core/services/secondary-subtitle.test.js dist/core/services/mpv-render-metrics.test.js dist/core/services/overlay-content-measurement.test.js dist/core/services/mpv-control.test.js dist/core/services/mpv.test.js dist/core/services/runtime-options-ipc.test.js dist/core/services/runtime-config.test.js dist/core/services/yomitan-extension-paths.test.js dist/core/services/config-hot-reload.test.js dist/core/services/discord-presence.test.js dist/core/services/tokenizer.test.js dist/core/services/tokenizer/annotation-stage.test.js dist/core/services/tokenizer/parser-selection-stage.test.js dist/core/services/tokenizer/parser-enrichment-stage.test.js dist/core/services/subsync.test.js dist/core/services/overlay-bridge.test.js dist/core/services/overlay-manager.test.js dist/core/services/overlay-shortcut-handler.test.js dist/core/services/stats-window-lifecycle.test.js dist/core/services/mining.test.js dist/core/services/anki-jimaku.test.js dist/core/services/jimaku-download-path.test.js dist/core/services/jellyfin.test.js dist/core/services/jellyfin-remote.test.js dist/core/services/immersion-tracker-service.test.js dist/core/services/overlay-runtime-init.test.js dist/core/services/app-ready.test.js dist/core/services/startup-bootstrap.test.js dist/core/services/subtitle-processing-controller.test.js dist/main/runtime/current-subtitle-snapshot.test.js dist/main/runtime/autoplay-tokenization-warm-release.test.js dist/main/runtime/autoplay-subtitle-primer.test.js dist/core/services/anilist/anilist-token-store.test.js dist/core/services/anilist/anilist-update-queue.test.js dist/core/services/anilist/rate-limiter.test.js dist/core/services/jlpt-token-filter.test.js dist/core/services/subtitle-position.test.js dist/renderer/error-recovery.test.js dist/renderer/subtitle-render.test.js dist/renderer/subtitle-render-word-class.test.js dist/renderer/handlers/mouse.test.js dist/renderer/handlers/keyboard.test.js dist/renderer/modals/jimaku.test.js dist/subsync/utils.test.js dist/main/anilist-url-guard.test.js dist/window-trackers/hyprland-tracker.test.js dist/window-trackers/x11-tracker.test.js dist/window-trackers/windows-helper.test.js dist/window-trackers/windows-tracker.test.js",
|
||||||
"test:core:smoke:dist": "bun test dist/cli/help.test.js dist/core/services/runtime-config.test.js dist/core/services/ipc.test.js dist/core/services/overlay-manager.test.js dist/core/services/anilist/anilist-token-store.test.js dist/core/services/startup-bootstrap.test.js dist/renderer/error-recovery.test.js dist/main/anilist-url-guard.test.js dist/window-trackers/x11-tracker.test.js",
|
"test:core:smoke:dist": "bun test dist/cli/help.test.js dist/core/services/runtime-config.test.js dist/core/services/ipc.test.js dist/core/services/overlay-manager.test.js dist/core/services/anilist/anilist-token-store.test.js dist/core/services/startup-bootstrap.test.js dist/renderer/error-recovery.test.js dist/main/anilist-url-guard.test.js dist/window-trackers/x11-tracker.test.js",
|
||||||
"test:smoke:dist": "bun run test:config:smoke:dist && bun run test:core:smoke:dist",
|
"test:smoke:dist": "bun run test:config:smoke:dist && bun run test:core:smoke:dist",
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export {
|
|||||||
shouldAutoInitializeOverlayRuntimeFromConfig,
|
shouldAutoInitializeOverlayRuntimeFromConfig,
|
||||||
} from './startup';
|
} from './startup';
|
||||||
export { destroyYomitanSettingsWindow, openYomitanSettingsWindow } from './yomitan-settings';
|
export { destroyYomitanSettingsWindow, openYomitanSettingsWindow } from './yomitan-settings';
|
||||||
|
export { promoteSettingsWindowAboveOverlay } from './settings-window-z-order';
|
||||||
export { createTokenizerDepsRuntime, tokenizeSubtitle } from './tokenizer';
|
export { createTokenizerDepsRuntime, tokenizeSubtitle } from './tokenizer';
|
||||||
export {
|
export {
|
||||||
addYomitanNoteViaSearch,
|
addYomitanNoteViaSearch,
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
import {
|
||||||
|
promoteSettingsWindowAboveOverlay,
|
||||||
|
shouldPromoteSettingsWindowAboveOverlay,
|
||||||
|
} from './settings-window-z-order';
|
||||||
|
|
||||||
|
test('settings window promotion only applies to Hyprland sessions', () => {
|
||||||
|
assert.equal(
|
||||||
|
shouldPromoteSettingsWindowAboveOverlay('linux', { HYPRLAND_INSTANCE_SIGNATURE: 'hypr' }),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
shouldPromoteSettingsWindowAboveOverlay('linux', { WAYLAND_DISPLAY: 'wayland-1' }),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
shouldPromoteSettingsWindowAboveOverlay('darwin', { HYPRLAND_INSTANCE_SIGNATURE: 'hypr' }),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('promoteSettingsWindowAboveOverlay raises Hyprland settings windows above the overlay', () => {
|
||||||
|
const calls: string[] = [];
|
||||||
|
|
||||||
|
const promoted = promoteSettingsWindowAboveOverlay(
|
||||||
|
{
|
||||||
|
isDestroyed: () => false,
|
||||||
|
getTitle: () => 'SubMiner Settings',
|
||||||
|
setAlwaysOnTop: (flag: boolean) => calls.push(`always-on-top:${flag}`),
|
||||||
|
moveTop: () => calls.push('move-top'),
|
||||||
|
} as never,
|
||||||
|
{
|
||||||
|
platform: 'linux',
|
||||||
|
env: { HYPRLAND_INSTANCE_SIGNATURE: 'hypr' },
|
||||||
|
ensureHyprlandWindowFloatingByTitle: ({ title }) => {
|
||||||
|
calls.push(`hyprland-top:${title}`);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(promoted, true);
|
||||||
|
assert.deepEqual(calls, ['always-on-top:true', 'move-top', 'hyprland-top:SubMiner Settings']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('promoteSettingsWindowAboveOverlay skips destroyed windows', () => {
|
||||||
|
const calls: string[] = [];
|
||||||
|
|
||||||
|
const promoted = promoteSettingsWindowAboveOverlay(
|
||||||
|
{
|
||||||
|
isDestroyed: () => true,
|
||||||
|
getTitle: () => 'SubMiner Settings',
|
||||||
|
setAlwaysOnTop: () => calls.push('always-on-top'),
|
||||||
|
moveTop: () => calls.push('move-top'),
|
||||||
|
} as never,
|
||||||
|
{
|
||||||
|
platform: 'linux',
|
||||||
|
env: { HYPRLAND_INSTANCE_SIGNATURE: 'hypr' },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(promoted, false);
|
||||||
|
assert.deepEqual(calls, []);
|
||||||
|
});
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import type { BrowserWindow } from 'electron';
|
||||||
|
import {
|
||||||
|
ensureHyprlandWindowFloatingByTitle,
|
||||||
|
shouldAttemptHyprlandWindowPlacement,
|
||||||
|
} from './hyprland-window-placement';
|
||||||
|
|
||||||
|
type SettingsWindowLevelController = Pick<
|
||||||
|
BrowserWindow,
|
||||||
|
'getTitle' | 'isDestroyed' | 'moveTop' | 'setAlwaysOnTop'
|
||||||
|
>;
|
||||||
|
|
||||||
|
type PromoteSettingsWindowOptions = {
|
||||||
|
platform?: NodeJS.Platform;
|
||||||
|
env?: NodeJS.ProcessEnv;
|
||||||
|
ensureHyprlandWindowFloatingByTitle?: typeof ensureHyprlandWindowFloatingByTitle;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function shouldPromoteSettingsWindowAboveOverlay(
|
||||||
|
platform: NodeJS.Platform = process.platform,
|
||||||
|
env: NodeJS.ProcessEnv = process.env,
|
||||||
|
): boolean {
|
||||||
|
return shouldAttemptHyprlandWindowPlacement(platform, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function promoteSettingsWindowAboveOverlay(
|
||||||
|
window: SettingsWindowLevelController,
|
||||||
|
options: PromoteSettingsWindowOptions = {},
|
||||||
|
): boolean {
|
||||||
|
const platform = options.platform ?? process.platform;
|
||||||
|
const env = options.env ?? process.env;
|
||||||
|
if (window.isDestroyed() || !shouldPromoteSettingsWindowAboveOverlay(platform, env)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.setAlwaysOnTop(true);
|
||||||
|
window.moveTop();
|
||||||
|
|
||||||
|
const title = window.getTitle().trim();
|
||||||
|
if (title) {
|
||||||
|
(options.ensureHyprlandWindowFloatingByTitle ?? ensureHyprlandWindowFloatingByTitle)({
|
||||||
|
title,
|
||||||
|
platform,
|
||||||
|
env,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -106,20 +106,32 @@ test('yomitan settings URL disables the embedded popup preview', () => {
|
|||||||
test('showYomitanSettingsWindow restores, repaints, shows, and focuses an existing window', () => {
|
test('showYomitanSettingsWindow restores, repaints, shows, and focuses an existing window', () => {
|
||||||
const calls: string[] = [];
|
const calls: string[] = [];
|
||||||
|
|
||||||
showYomitanSettingsWindow({
|
showYomitanSettingsWindow(
|
||||||
isDestroyed: () => false,
|
{
|
||||||
isMinimized: () => true,
|
isDestroyed: () => false,
|
||||||
restore: () => calls.push('restore'),
|
isMinimized: () => true,
|
||||||
getSize: () => [1200, 800],
|
restore: () => calls.push('restore'),
|
||||||
setSize: (width: number, height: number) => calls.push(`set-size:${width}x${height}`),
|
getSize: () => [1200, 800],
|
||||||
webContents: {
|
setSize: (width: number, height: number) => calls.push(`set-size:${width}x${height}`),
|
||||||
invalidate: () => calls.push('invalidate'),
|
webContents: {
|
||||||
|
invalidate: () => calls.push('invalidate'),
|
||||||
|
},
|
||||||
|
show: () => calls.push('show'),
|
||||||
|
focus: () => calls.push('focus'),
|
||||||
|
} as never,
|
||||||
|
{
|
||||||
|
promoteSettingsWindowAboveOverlay: () => calls.push('promote'),
|
||||||
},
|
},
|
||||||
show: () => calls.push('show'),
|
);
|
||||||
focus: () => calls.push('focus'),
|
|
||||||
} as never);
|
|
||||||
|
|
||||||
assert.deepEqual(calls, ['restore', 'set-size:1200x800', 'invalidate', 'show', 'focus']);
|
assert.deepEqual(calls, [
|
||||||
|
'restore',
|
||||||
|
'set-size:1200x800',
|
||||||
|
'invalidate',
|
||||||
|
'show',
|
||||||
|
'focus',
|
||||||
|
'promote',
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('destroyYomitanSettingsWindow destroys a live settings window before app quit', () => {
|
test('destroyYomitanSettingsWindow destroys a live settings window before app quit', () => {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import electron from 'electron';
|
import electron from 'electron';
|
||||||
import type { BrowserWindow, Extension, Menu, MenuItemConstructorOptions, Session } from 'electron';
|
import type { BrowserWindow, Extension, Menu, MenuItemConstructorOptions, Session } from 'electron';
|
||||||
import { createLogger } from '../../logger';
|
import { createLogger } from '../../logger';
|
||||||
|
import { promoteSettingsWindowAboveOverlay } from './settings-window-z-order';
|
||||||
|
|
||||||
const { BrowserWindow: ElectronBrowserWindow, Menu: ElectronMenu, session } = electron;
|
const { BrowserWindow: ElectronBrowserWindow, Menu: ElectronMenu, session } = electron;
|
||||||
const logger = createLogger('main:yomitan-settings');
|
const logger = createLogger('main:yomitan-settings');
|
||||||
@@ -136,7 +137,12 @@ export function buildYomitanSettingsUrl(extensionId: string): string {
|
|||||||
return `chrome-extension://${extensionId}/settings.html?popup-preview=false`;
|
return `chrome-extension://${extensionId}/settings.html?popup-preview=false`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showYomitanSettingsWindow(settingsWindow: BrowserWindow): void {
|
export function showYomitanSettingsWindow(
|
||||||
|
settingsWindow: BrowserWindow,
|
||||||
|
options: {
|
||||||
|
promoteSettingsWindowAboveOverlay?: (settingsWindow: BrowserWindow) => void;
|
||||||
|
} = {},
|
||||||
|
): void {
|
||||||
if (settingsWindow.isDestroyed()) {
|
if (settingsWindow.isDestroyed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -148,6 +154,7 @@ export function showYomitanSettingsWindow(settingsWindow: BrowserWindow): void {
|
|||||||
settingsWindow.webContents.invalidate();
|
settingsWindow.webContents.invalidate();
|
||||||
settingsWindow.show();
|
settingsWindow.show();
|
||||||
settingsWindow.focus();
|
settingsWindow.focus();
|
||||||
|
(options.promoteSettingsWindowAboveOverlay ?? promoteSettingsWindowAboveOverlay)(settingsWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function destroyYomitanSettingsWindow(settingsWindow: BrowserWindow | null): boolean {
|
export function destroyYomitanSettingsWindow(settingsWindow: BrowserWindow | null): boolean {
|
||||||
@@ -177,6 +184,7 @@ export function openYomitanSettingsWindow(options: OpenYomitanSettingsWindowOpti
|
|||||||
logger.info('Creating new settings window for extension:', options.yomitanExt.id);
|
logger.info('Creating new settings window for extension:', options.yomitanExt.id);
|
||||||
|
|
||||||
const settingsWindow = new ElectronBrowserWindow({
|
const settingsWindow = new ElectronBrowserWindow({
|
||||||
|
title: 'Yomitan Settings',
|
||||||
width: 1200,
|
width: 1200,
|
||||||
height: 800,
|
height: 800,
|
||||||
show: false,
|
show: false,
|
||||||
|
|||||||
+15
-2
@@ -332,6 +332,7 @@ import {
|
|||||||
mineSentenceCard as mineSentenceCardCore,
|
mineSentenceCard as mineSentenceCardCore,
|
||||||
openYomitanSettingsWindow,
|
openYomitanSettingsWindow,
|
||||||
playNextSubtitleRuntime,
|
playNextSubtitleRuntime,
|
||||||
|
promoteSettingsWindowAboveOverlay,
|
||||||
registerGlobalShortcuts as registerGlobalShortcutsCore,
|
registerGlobalShortcuts as registerGlobalShortcutsCore,
|
||||||
replayCurrentSubtitleRuntime,
|
replayCurrentSubtitleRuntime,
|
||||||
resolveJellyfinPlaybackPlanRuntime,
|
resolveJellyfinPlaybackPlanRuntime,
|
||||||
@@ -565,6 +566,7 @@ import {
|
|||||||
createCreateJellyfinSetupWindowHandler,
|
createCreateJellyfinSetupWindowHandler,
|
||||||
} from './main/runtime/setup-window-factory';
|
} from './main/runtime/setup-window-factory';
|
||||||
import { createConfigSettingsRuntime } from './main/runtime/config-settings-runtime';
|
import { createConfigSettingsRuntime } from './main/runtime/config-settings-runtime';
|
||||||
|
import { shouldSuppressVisibleOverlayRaiseForSeparateWindow } from './main/runtime/settings-window-z-order';
|
||||||
import {
|
import {
|
||||||
isSameYoutubeMediaPath,
|
isSameYoutubeMediaPath,
|
||||||
isYoutubeMediaPath,
|
isYoutubeMediaPath,
|
||||||
@@ -2034,6 +2036,8 @@ const configSettingsRuntime = createConfigSettingsRuntime({
|
|||||||
preloadPath: path.join(__dirname, 'preload-settings.js'),
|
preloadPath: path.join(__dirname, 'preload-settings.js'),
|
||||||
}),
|
}),
|
||||||
settingsHtmlPath: path.join(__dirname, 'settings', 'index.html'),
|
settingsHtmlPath: path.join(__dirname, 'settings', 'index.html'),
|
||||||
|
promoteSettingsWindowAboveOverlay: (window) =>
|
||||||
|
promoteSettingsWindowAboveOverlay(window as BrowserWindow),
|
||||||
openPath: (targetPath) => shell.openPath(targetPath),
|
openPath: (targetPath) => shell.openPath(targetPath),
|
||||||
ipcMain,
|
ipcMain,
|
||||||
ipcChannels: IPC_CHANNELS.request,
|
ipcChannels: IPC_CHANNELS.request,
|
||||||
@@ -4927,8 +4931,17 @@ const updateVisibleOverlayBounds = createUpdateVisibleOverlayBoundsHandler(
|
|||||||
|
|
||||||
const buildEnsureOverlayWindowLevelMainDepsHandler =
|
const buildEnsureOverlayWindowLevelMainDepsHandler =
|
||||||
createBuildEnsureOverlayWindowLevelMainDepsHandler({
|
createBuildEnsureOverlayWindowLevelMainDepsHandler({
|
||||||
shouldSuppressOverlayWindowLevel: (window) =>
|
shouldSuppressOverlayWindowLevel: (window) => {
|
||||||
appState.statsOverlayVisible && window === overlayManager.getMainWindow(),
|
const mainWindow = overlayManager.getMainWindow();
|
||||||
|
return (
|
||||||
|
(appState.statsOverlayVisible && window === mainWindow) ||
|
||||||
|
shouldSuppressVisibleOverlayRaiseForSeparateWindow({
|
||||||
|
window,
|
||||||
|
mainWindow,
|
||||||
|
separateWindows: [appState.configSettingsWindow, appState.yomitanSettingsWindow],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
ensureOverlayWindowLevelCore: (window) => ensureOverlayWindowLevelCore(window as BrowserWindow),
|
ensureOverlayWindowLevelCore: (window) => ensureOverlayWindowLevelCore(window as BrowserWindow),
|
||||||
afterEnsureOverlayWindowLevel: () => {
|
afterEnsureOverlayWindowLevel: () => {
|
||||||
promoteStatsOverlayAbovePlayback();
|
promoteStatsOverlayAbovePlayback();
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ export interface ConfigSettingsRuntimeDeps<TWindow extends ConfigSettingsWindowL
|
|||||||
setSettingsWindow(window: TWindow | null): void;
|
setSettingsWindow(window: TWindow | null): void;
|
||||||
createSettingsWindow(): TWindow;
|
createSettingsWindow(): TWindow;
|
||||||
settingsHtmlPath: string;
|
settingsHtmlPath: string;
|
||||||
|
promoteSettingsWindowAboveOverlay?: (window: TWindow) => void;
|
||||||
openPath(path: string): Promise<string>;
|
openPath(path: string): Promise<string>;
|
||||||
defaultAnkiConnectUrl: string;
|
defaultAnkiConnectUrl: string;
|
||||||
createAnkiClient(url: string): ConfigSettingsAnkiClient;
|
createAnkiClient(url: string): ConfigSettingsAnkiClient;
|
||||||
@@ -144,6 +145,7 @@ export function createConfigSettingsRuntime<TWindow extends ConfigSettingsWindow
|
|||||||
setSettingsWindow: deps.setSettingsWindow,
|
setSettingsWindow: deps.setSettingsWindow,
|
||||||
createSettingsWindow: deps.createSettingsWindow,
|
createSettingsWindow: deps.createSettingsWindow,
|
||||||
settingsHtmlPath: deps.settingsHtmlPath,
|
settingsHtmlPath: deps.settingsHtmlPath,
|
||||||
|
promoteSettingsWindowAboveOverlay: deps.promoteSettingsWindowAboveOverlay,
|
||||||
log: deps.log,
|
log: deps.log,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ test('createOpenConfigSettingsWindowHandler focuses existing settings window', (
|
|||||||
const calls: string[] = [];
|
const calls: string[] = [];
|
||||||
const existing = {
|
const existing = {
|
||||||
isDestroyed: () => false,
|
isDestroyed: () => false,
|
||||||
|
show: () => calls.push('show'),
|
||||||
focus: () => calls.push('focus'),
|
focus: () => calls.push('focus'),
|
||||||
loadFile: () => calls.push('load'),
|
loadFile: () => calls.push('load'),
|
||||||
on: () => {},
|
on: () => {},
|
||||||
@@ -18,10 +19,11 @@ test('createOpenConfigSettingsWindowHandler focuses existing settings window', (
|
|||||||
throw new Error('Should not create a second window.');
|
throw new Error('Should not create a second window.');
|
||||||
},
|
},
|
||||||
settingsHtmlPath: '/tmp/settings.html',
|
settingsHtmlPath: '/tmp/settings.html',
|
||||||
|
promoteSettingsWindowAboveOverlay: () => calls.push('promote'),
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(open(), true);
|
assert.equal(open(), true);
|
||||||
assert.deepEqual(calls, ['focus']);
|
assert.deepEqual(calls, ['show', 'focus', 'promote']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('createOpenConfigSettingsWindowHandler creates window and clears closed state', () => {
|
test('createOpenConfigSettingsWindowHandler creates window and clears closed state', () => {
|
||||||
@@ -29,6 +31,7 @@ test('createOpenConfigSettingsWindowHandler creates window and clears closed sta
|
|||||||
const handlers: { closed?: () => void } = {};
|
const handlers: { closed?: () => void } = {};
|
||||||
const created = {
|
const created = {
|
||||||
isDestroyed: () => false,
|
isDestroyed: () => false,
|
||||||
|
show: () => calls.push('show'),
|
||||||
focus: () => calls.push('focus'),
|
focus: () => calls.push('focus'),
|
||||||
loadFile: (path: string) => calls.push(`load:${path}`),
|
loadFile: (path: string) => calls.push(`load:${path}`),
|
||||||
on: (event: string, handler: () => void) => {
|
on: (event: string, handler: () => void) => {
|
||||||
@@ -41,10 +44,11 @@ test('createOpenConfigSettingsWindowHandler creates window and clears closed sta
|
|||||||
setSettingsWindow: (window) => calls.push(window ? 'set:window' : 'set:null'),
|
setSettingsWindow: (window) => calls.push(window ? 'set:window' : 'set:null'),
|
||||||
createSettingsWindow: () => created,
|
createSettingsWindow: () => created,
|
||||||
settingsHtmlPath: '/tmp/settings.html',
|
settingsHtmlPath: '/tmp/settings.html',
|
||||||
|
promoteSettingsWindowAboveOverlay: () => calls.push('promote'),
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(open(), true);
|
assert.equal(open(), true);
|
||||||
assert.deepEqual(calls, ['load:/tmp/settings.html', 'set:window', 'focus']);
|
assert.deepEqual(calls, ['load:/tmp/settings.html', 'set:window', 'show', 'focus', 'promote']);
|
||||||
assert.ok(handlers.closed);
|
assert.ok(handlers.closed);
|
||||||
handlers.closed();
|
handlers.closed();
|
||||||
assert.equal(calls.at(-1), 'set:null');
|
assert.equal(calls.at(-1), 'set:null');
|
||||||
@@ -54,6 +58,7 @@ test('createOpenConfigSettingsWindowHandler clears failed load window state', as
|
|||||||
const calls: string[] = [];
|
const calls: string[] = [];
|
||||||
const created = {
|
const created = {
|
||||||
isDestroyed: () => false,
|
isDestroyed: () => false,
|
||||||
|
show: () => calls.push('show'),
|
||||||
focus: () => calls.push('focus'),
|
focus: () => calls.push('focus'),
|
||||||
loadFile: (path: string) => {
|
loadFile: (path: string) => {
|
||||||
calls.push(`load:${path}`);
|
calls.push(`load:${path}`);
|
||||||
@@ -76,6 +81,7 @@ test('createOpenConfigSettingsWindowHandler clears failed load window state', as
|
|||||||
assert.deepEqual(calls, [
|
assert.deepEqual(calls, [
|
||||||
'load:/tmp/missing-settings.html',
|
'load:/tmp/missing-settings.html',
|
||||||
'set:window',
|
'set:window',
|
||||||
|
'show',
|
||||||
'focus',
|
'focus',
|
||||||
'set:null',
|
'set:null',
|
||||||
'destroy',
|
'destroy',
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export interface ConfigSettingsWindowLike {
|
export interface ConfigSettingsWindowLike {
|
||||||
isDestroyed(): boolean;
|
isDestroyed(): boolean;
|
||||||
|
show(): void;
|
||||||
focus(): void;
|
focus(): void;
|
||||||
loadFile(path: string): unknown;
|
loadFile(path: string): unknown;
|
||||||
on(event: 'closed', handler: () => void): unknown;
|
on(event: 'closed', handler: () => void): unknown;
|
||||||
@@ -11,6 +12,7 @@ export interface OpenConfigSettingsWindowDeps<TWindow extends ConfigSettingsWind
|
|||||||
setSettingsWindow(window: TWindow | null): void;
|
setSettingsWindow(window: TWindow | null): void;
|
||||||
createSettingsWindow(): TWindow;
|
createSettingsWindow(): TWindow;
|
||||||
settingsHtmlPath: string;
|
settingsHtmlPath: string;
|
||||||
|
promoteSettingsWindowAboveOverlay?: (window: TWindow) => void;
|
||||||
log?: (message: string) => void;
|
log?: (message: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,9 +20,15 @@ export function createOpenConfigSettingsWindowHandler<TWindow extends ConfigSett
|
|||||||
deps: OpenConfigSettingsWindowDeps<TWindow>,
|
deps: OpenConfigSettingsWindowDeps<TWindow>,
|
||||||
): () => boolean {
|
): () => boolean {
|
||||||
return () => {
|
return () => {
|
||||||
|
const showAndFocus = (window: TWindow): void => {
|
||||||
|
window.show();
|
||||||
|
window.focus();
|
||||||
|
deps.promoteSettingsWindowAboveOverlay?.(window);
|
||||||
|
};
|
||||||
|
|
||||||
const existing = deps.getSettingsWindow();
|
const existing = deps.getSettingsWindow();
|
||||||
if (existing && !existing.isDestroyed()) {
|
if (existing && !existing.isDestroyed()) {
|
||||||
existing.focus();
|
showAndFocus(existing);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +43,7 @@ export function createOpenConfigSettingsWindowHandler<TWindow extends ConfigSett
|
|||||||
window.on('closed', () => {
|
window.on('closed', () => {
|
||||||
deps.setSettingsWindow(null);
|
deps.setSettingsWindow(null);
|
||||||
});
|
});
|
||||||
window.focus();
|
showAndFocus(window);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
import { shouldSuppressVisibleOverlayRaiseForSeparateWindow } from './settings-window-z-order';
|
||||||
|
|
||||||
|
test('separate settings windows suppress visible overlay restacking', () => {
|
||||||
|
const mainWindow = { id: 'overlay', isDestroyed: () => false };
|
||||||
|
const settingsWindow = { id: 'settings', isDestroyed: () => false };
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
shouldSuppressVisibleOverlayRaiseForSeparateWindow({
|
||||||
|
window: mainWindow,
|
||||||
|
mainWindow,
|
||||||
|
separateWindows: [settingsWindow],
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('separate settings windows do not suppress unrelated or closed overlay work', () => {
|
||||||
|
const mainWindow = { id: 'overlay', isDestroyed: () => false };
|
||||||
|
const modalWindow = { id: 'modal', isDestroyed: () => false };
|
||||||
|
const closedSettingsWindow = { id: 'settings', isDestroyed: () => true };
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
shouldSuppressVisibleOverlayRaiseForSeparateWindow({
|
||||||
|
window: modalWindow,
|
||||||
|
mainWindow,
|
||||||
|
separateWindows: [{ isDestroyed: () => false }],
|
||||||
|
}),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
shouldSuppressVisibleOverlayRaiseForSeparateWindow({
|
||||||
|
window: mainWindow,
|
||||||
|
mainWindow,
|
||||||
|
separateWindows: [closedSettingsWindow, null],
|
||||||
|
}),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
type SeparateWindowLike = {
|
||||||
|
isDestroyed(): boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
function hasLiveSeparateWindow(windows: Array<SeparateWindowLike | null | undefined>): boolean {
|
||||||
|
return windows.some((window) => Boolean(window && !window.isDestroyed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shouldSuppressVisibleOverlayRaiseForSeparateWindow(options: {
|
||||||
|
window: unknown;
|
||||||
|
mainWindow: unknown;
|
||||||
|
separateWindows: Array<SeparateWindowLike | null | undefined>;
|
||||||
|
}): boolean {
|
||||||
|
if (!options.mainWindow || options.window !== options.mainWindow) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasLiveSeparateWindow(options.separateWindows);
|
||||||
|
}
|
||||||
@@ -111,7 +111,7 @@ test('createCreateConfigSettingsWindowHandler builds configuration settings wind
|
|||||||
width: 1040,
|
width: 1040,
|
||||||
height: 760,
|
height: 760,
|
||||||
title: 'SubMiner Settings',
|
title: 'SubMiner Settings',
|
||||||
show: true,
|
show: false,
|
||||||
autoHideMenuBar: true,
|
autoHideMenuBar: true,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
backgroundColor: '#24273a',
|
backgroundColor: '#24273a',
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ interface SetupWindowConfig {
|
|||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
title: string;
|
title: string;
|
||||||
|
show?: boolean;
|
||||||
resizable?: boolean;
|
resizable?: boolean;
|
||||||
minimizable?: boolean;
|
minimizable?: boolean;
|
||||||
maximizable?: boolean;
|
maximizable?: boolean;
|
||||||
@@ -19,7 +20,7 @@ function createSetupWindowHandler<TWindow>(
|
|||||||
width: config.width,
|
width: config.width,
|
||||||
height: config.height,
|
height: config.height,
|
||||||
title: config.title,
|
title: config.title,
|
||||||
show: true,
|
show: config.show ?? true,
|
||||||
autoHideMenuBar: true,
|
autoHideMenuBar: true,
|
||||||
...(config.resizable === undefined ? {} : { resizable: config.resizable }),
|
...(config.resizable === undefined ? {} : { resizable: config.resizable }),
|
||||||
...(config.minimizable === undefined ? {} : { minimizable: config.minimizable }),
|
...(config.minimizable === undefined ? {} : { minimizable: config.minimizable }),
|
||||||
@@ -77,6 +78,7 @@ export function createCreateConfigSettingsWindowHandler<TWindow>(deps: {
|
|||||||
width: 1040,
|
width: 1040,
|
||||||
height: 760,
|
height: 760,
|
||||||
title: 'SubMiner Settings',
|
title: 'SubMiner Settings',
|
||||||
|
show: false,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
preloadPath: deps.preloadPath,
|
preloadPath: deps.preloadPath,
|
||||||
backgroundColor: '#24273a',
|
backgroundColor: '#24273a',
|
||||||
|
|||||||
Reference in New Issue
Block a user