mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-20 12:11:28 -07:00
refactor: remove root node and npm workflow deps
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { ipcMain } from 'electron';
|
||||
import electron from 'electron';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
@@ -25,6 +25,8 @@ import {
|
||||
} from '../../shared/ipc/validators';
|
||||
import { buildJimakuSubtitleFilenameFromMediaPath } from './jimaku-download-path';
|
||||
|
||||
const { ipcMain } = electron;
|
||||
|
||||
const logger = createLogger('main:anki-jimaku-ipc');
|
||||
|
||||
export interface AnkiJimakuIpcDeps {
|
||||
|
||||
@@ -3,9 +3,9 @@ import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import type { DatabaseSync as NodeDatabaseSync } from 'node:sqlite';
|
||||
import { toMonthKey } from './immersion-tracker/maintenance';
|
||||
import { enqueueWrite } from './immersion-tracker/queue';
|
||||
import { Database, type DatabaseSync } from './immersion-tracker/sqlite';
|
||||
import {
|
||||
deriveCanonicalTitle,
|
||||
normalizeText,
|
||||
@@ -17,22 +17,6 @@ type ImmersionTrackerService = import('./immersion-tracker-service').ImmersionTr
|
||||
type ImmersionTrackerServiceCtor =
|
||||
typeof import('./immersion-tracker-service').ImmersionTrackerService;
|
||||
|
||||
type DatabaseSyncCtor = typeof NodeDatabaseSync;
|
||||
const DatabaseSync: DatabaseSyncCtor | null = (() => {
|
||||
try {
|
||||
return (require('node:sqlite') as { DatabaseSync?: DatabaseSyncCtor }).DatabaseSync ?? null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
const testIfSqlite = DatabaseSync ? test : test.skip;
|
||||
|
||||
if (!DatabaseSync) {
|
||||
console.warn(
|
||||
'Skipping SQLite-backed immersion tracker persistence tests in this runtime; run `bun run test:immersion:sqlite` for real DB coverage.',
|
||||
);
|
||||
}
|
||||
|
||||
let trackerCtor: ImmersionTrackerServiceCtor | null = null;
|
||||
|
||||
async function loadTrackerCtor(): Promise<ImmersionTrackerServiceCtor> {
|
||||
@@ -89,7 +73,7 @@ test('seam: toMonthKey uses UTC calendar month', () => {
|
||||
assert.equal(toMonthKey(Date.UTC(2026, 1, 1, 0, 0, 0, 0)), 202602);
|
||||
});
|
||||
|
||||
testIfSqlite('startSession generates UUID-like session identifiers', async () => {
|
||||
test('startSession generates UUID-like session identifiers', async () => {
|
||||
const dbPath = makeDbPath();
|
||||
let tracker: ImmersionTrackerService | null = null;
|
||||
|
||||
@@ -105,7 +89,7 @@ testIfSqlite('startSession generates UUID-like session identifiers', async () =>
|
||||
privateApi.flushTelemetry(true);
|
||||
privateApi.flushNow();
|
||||
|
||||
const db = new DatabaseSync!(dbPath);
|
||||
const db = new Database(dbPath);
|
||||
const row = db.prepare('SELECT session_uuid FROM imm_sessions LIMIT 1').get() as {
|
||||
session_uuid: string;
|
||||
} | null;
|
||||
@@ -120,7 +104,7 @@ testIfSqlite('startSession generates UUID-like session identifiers', async () =>
|
||||
}
|
||||
});
|
||||
|
||||
testIfSqlite('destroy finalizes active session and persists final telemetry', async () => {
|
||||
test('destroy finalizes active session and persists final telemetry', async () => {
|
||||
const dbPath = makeDbPath();
|
||||
let tracker: ImmersionTrackerService | null = null;
|
||||
|
||||
@@ -132,7 +116,7 @@ testIfSqlite('destroy finalizes active session and persists final telemetry', as
|
||||
tracker.recordSubtitleLine('Hello immersion', 0, 1);
|
||||
tracker.destroy();
|
||||
|
||||
const db = new DatabaseSync!(dbPath);
|
||||
const db = new Database(dbPath);
|
||||
const sessionRow = db.prepare('SELECT ended_at_ms FROM imm_sessions LIMIT 1').get() as {
|
||||
ended_at_ms: number | null;
|
||||
} | null;
|
||||
@@ -150,7 +134,7 @@ testIfSqlite('destroy finalizes active session and persists final telemetry', as
|
||||
}
|
||||
});
|
||||
|
||||
testIfSqlite('persists and retrieves minimum immersion tracking fields', async () => {
|
||||
test('persists and retrieves minimum immersion tracking fields', async () => {
|
||||
const dbPath = makeDbPath();
|
||||
let tracker: ImmersionTrackerService | null = null;
|
||||
|
||||
@@ -178,7 +162,7 @@ testIfSqlite('persists and retrieves minimum immersion tracking fields', async (
|
||||
|
||||
tracker.destroy();
|
||||
|
||||
const db = new DatabaseSync!(dbPath);
|
||||
const db = new Database(dbPath);
|
||||
const videoRow = db
|
||||
.prepare('SELECT canonical_title, source_path, duration_ms FROM imm_videos LIMIT 1')
|
||||
.get() as {
|
||||
@@ -190,7 +174,7 @@ testIfSqlite('persists and retrieves minimum immersion tracking fields', async (
|
||||
.prepare(
|
||||
`SELECT lines_seen, words_seen, tokens_seen, cards_mined
|
||||
FROM imm_session_telemetry
|
||||
ORDER BY sample_ms DESC
|
||||
ORDER BY sample_ms DESC, telemetry_id DESC
|
||||
LIMIT 1`,
|
||||
)
|
||||
.get() as {
|
||||
@@ -217,7 +201,7 @@ testIfSqlite('persists and retrieves minimum immersion tracking fields', async (
|
||||
}
|
||||
});
|
||||
|
||||
testIfSqlite('applies configurable queue, flush, and retention policy', async () => {
|
||||
test('applies configurable queue, flush, and retention policy', async () => {
|
||||
const dbPath = makeDbPath();
|
||||
let tracker: ImmersionTrackerService | null = null;
|
||||
|
||||
@@ -270,7 +254,7 @@ testIfSqlite('applies configurable queue, flush, and retention policy', async ()
|
||||
}
|
||||
});
|
||||
|
||||
testIfSqlite('monthly rollups are grouped by calendar month', async () => {
|
||||
test('monthly rollups are grouped by calendar month', async () => {
|
||||
const dbPath = makeDbPath();
|
||||
let tracker: ImmersionTrackerService | null = null;
|
||||
|
||||
@@ -278,7 +262,7 @@ testIfSqlite('monthly rollups are grouped by calendar month', async () => {
|
||||
const Ctor = await loadTrackerCtor();
|
||||
tracker = new Ctor({ dbPath });
|
||||
const privateApi = tracker as unknown as {
|
||||
db: NodeDatabaseSync;
|
||||
db: DatabaseSync;
|
||||
runRollupMaintenance: () => void;
|
||||
};
|
||||
|
||||
@@ -433,16 +417,16 @@ testIfSqlite('monthly rollups are grouped by calendar month', async () => {
|
||||
}
|
||||
});
|
||||
|
||||
testIfSqlite('flushSingle reuses cached prepared statements', async () => {
|
||||
test('flushSingle reuses cached prepared statements', async () => {
|
||||
const dbPath = makeDbPath();
|
||||
let tracker: ImmersionTrackerService | null = null;
|
||||
let originalPrepare: NodeDatabaseSync['prepare'] | null = null;
|
||||
let originalPrepare: DatabaseSync['prepare'] | null = null;
|
||||
|
||||
try {
|
||||
const Ctor = await loadTrackerCtor();
|
||||
tracker = new Ctor({ dbPath });
|
||||
const privateApi = tracker as unknown as {
|
||||
db: NodeDatabaseSync;
|
||||
db: DatabaseSync;
|
||||
flushSingle: (write: {
|
||||
kind: 'telemetry' | 'event';
|
||||
sessionId: number;
|
||||
@@ -472,7 +456,7 @@ testIfSqlite('flushSingle reuses cached prepared statements', async () => {
|
||||
|
||||
originalPrepare = privateApi.db.prepare;
|
||||
let prepareCalls = 0;
|
||||
privateApi.db.prepare = (...args: Parameters<NodeDatabaseSync['prepare']>) => {
|
||||
privateApi.db.prepare = (...args: Parameters<DatabaseSync['prepare']>) => {
|
||||
prepareCalls += 1;
|
||||
return originalPrepare!.apply(privateApi.db, args);
|
||||
};
|
||||
@@ -557,7 +541,7 @@ testIfSqlite('flushSingle reuses cached prepared statements', async () => {
|
||||
assert.equal(prepareCalls, 0);
|
||||
} finally {
|
||||
if (tracker && originalPrepare) {
|
||||
const privateApi = tracker as unknown as { db: NodeDatabaseSync };
|
||||
const privateApi = tracker as unknown as { db: DatabaseSync };
|
||||
privateApi.db.prepare = originalPrepare;
|
||||
}
|
||||
tracker?.destroy();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import path from 'node:path';
|
||||
import { DatabaseSync } from 'node:sqlite';
|
||||
import * as fs from 'node:fs';
|
||||
import { createLogger } from '../../logger';
|
||||
import { getLocalVideoMetadata } from './immersion-tracker/metadata';
|
||||
import { pruneRetention, runRollupMaintenance } from './immersion-tracker/maintenance';
|
||||
import { Database, type DatabaseSync } from './immersion-tracker/sqlite';
|
||||
import { finalizeSessionRecord, startSessionRecord } from './immersion-tracker/session';
|
||||
import {
|
||||
applyPragmas,
|
||||
@@ -164,7 +164,7 @@ export class ImmersionTrackerService {
|
||||
1,
|
||||
3650,
|
||||
) * 86_400_000;
|
||||
this.db = new DatabaseSync(this.dbPath);
|
||||
this.db = new Database(this.dbPath);
|
||||
applyPragmas(this.db);
|
||||
ensureSchema(this.db);
|
||||
this.preparedStatements = createTrackerPreparedStatements(this.db);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { DatabaseSync } from 'node:sqlite';
|
||||
import type { DatabaseSync } from './sqlite';
|
||||
|
||||
const ROLLUP_STATE_KEY = 'last_rollup_sample_ms';
|
||||
const DAILY_MS = 86_400_000;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { DatabaseSync } from 'node:sqlite';
|
||||
import type { DatabaseSync } from './sqlite';
|
||||
import type {
|
||||
ImmersionSessionRollupRow,
|
||||
SessionSummaryQueryRow,
|
||||
@@ -44,7 +44,7 @@ export function getSessionTimeline(
|
||||
cards_mined AS cardsMined
|
||||
FROM imm_session_telemetry
|
||||
WHERE session_id = ?
|
||||
ORDER BY sample_ms DESC
|
||||
ORDER BY sample_ms DESC, telemetry_id DESC
|
||||
LIMIT ?
|
||||
`);
|
||||
return prepared.all(sessionId, limit) as unknown as SessionTimelineRow[];
|
||||
@@ -56,8 +56,8 @@ export function getQueryHints(db: DatabaseSync): {
|
||||
} {
|
||||
const sessions = db.prepare('SELECT COUNT(*) AS total FROM imm_sessions');
|
||||
const active = db.prepare('SELECT COUNT(*) AS total FROM imm_sessions WHERE ended_at_ms IS NULL');
|
||||
const totalSessions = Number(sessions.get()?.total ?? 0);
|
||||
const activeSessions = Number(active.get()?.total ?? 0);
|
||||
const totalSessions = Number((sessions.get() as { total?: number } | null)?.total ?? 0);
|
||||
const activeSessions = Number((active.get() as { total?: number } | null)?.total ?? 0);
|
||||
return { totalSessions, activeSessions };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import crypto from 'node:crypto';
|
||||
import type { DatabaseSync } from 'node:sqlite';
|
||||
import type { DatabaseSync } from './sqlite';
|
||||
import { createInitialSessionState } from './reducer';
|
||||
import { SESSION_STATUS_ACTIVE, SESSION_STATUS_ENDED } from './types';
|
||||
import type { SessionState } from './types';
|
||||
|
||||
20
src/core/services/immersion-tracker/sqlite.ts
Normal file
20
src/core/services/immersion-tracker/sqlite.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import Database = require('libsql');
|
||||
|
||||
export { Database };
|
||||
|
||||
export interface DatabaseRunResult {
|
||||
changes: number;
|
||||
lastInsertRowid: number | bigint;
|
||||
}
|
||||
|
||||
export interface DatabaseStatement {
|
||||
run(...params: unknown[]): DatabaseRunResult;
|
||||
get(...params: unknown[]): unknown;
|
||||
all(...params: unknown[]): unknown[];
|
||||
}
|
||||
|
||||
export interface DatabaseSync {
|
||||
prepare(source: string): DatabaseStatement;
|
||||
exec(source: string): DatabaseSync;
|
||||
close(): DatabaseSync;
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
import type { DatabaseSync as NodeDatabaseSync } from 'node:sqlite';
|
||||
import { Database } from './sqlite';
|
||||
import { finalizeSessionRecord, startSessionRecord } from './session';
|
||||
import {
|
||||
createTrackerPreparedStatements,
|
||||
@@ -13,22 +13,6 @@ import {
|
||||
} from './storage';
|
||||
import { EVENT_SUBTITLE_LINE, SESSION_STATUS_ENDED, SOURCE_TYPE_LOCAL } from './types';
|
||||
|
||||
type DatabaseSyncCtor = typeof NodeDatabaseSync;
|
||||
const DatabaseSync: DatabaseSyncCtor | null = (() => {
|
||||
try {
|
||||
return (require('node:sqlite') as { DatabaseSync?: DatabaseSyncCtor }).DatabaseSync ?? null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
const testIfSqlite = DatabaseSync ? test : test.skip;
|
||||
|
||||
if (!DatabaseSync) {
|
||||
console.warn(
|
||||
'Skipping SQLite-backed immersion tracker storage/session tests in this runtime; run `bun run test:immersion:sqlite` for real DB coverage.',
|
||||
);
|
||||
}
|
||||
|
||||
function makeDbPath(): string {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-imm-storage-session-'));
|
||||
return path.join(dir, 'immersion.sqlite');
|
||||
@@ -41,9 +25,9 @@ function cleanupDbPath(dbPath: string): void {
|
||||
}
|
||||
}
|
||||
|
||||
testIfSqlite('ensureSchema creates immersion core tables', () => {
|
||||
test('ensureSchema creates immersion core tables', () => {
|
||||
const dbPath = makeDbPath();
|
||||
const db = new DatabaseSync!(dbPath);
|
||||
const db = new Database(dbPath);
|
||||
|
||||
try {
|
||||
ensureSchema(db);
|
||||
@@ -77,9 +61,9 @@ testIfSqlite('ensureSchema creates immersion core tables', () => {
|
||||
}
|
||||
});
|
||||
|
||||
testIfSqlite('start/finalize session updates ended_at and status', () => {
|
||||
test('start/finalize session updates ended_at and status', () => {
|
||||
const dbPath = makeDbPath();
|
||||
const db = new DatabaseSync!(dbPath);
|
||||
const db = new Database(dbPath);
|
||||
|
||||
try {
|
||||
ensureSchema(db);
|
||||
@@ -111,9 +95,9 @@ testIfSqlite('start/finalize session updates ended_at and status', () => {
|
||||
}
|
||||
});
|
||||
|
||||
testIfSqlite('executeQueuedWrite inserts event and telemetry rows', () => {
|
||||
test('executeQueuedWrite inserts event and telemetry rows', () => {
|
||||
const dbPath = makeDbPath();
|
||||
const db = new DatabaseSync!(dbPath);
|
||||
const db = new Database(dbPath);
|
||||
|
||||
try {
|
||||
ensureSchema(db);
|
||||
@@ -178,9 +162,9 @@ testIfSqlite('executeQueuedWrite inserts event and telemetry rows', () => {
|
||||
}
|
||||
});
|
||||
|
||||
testIfSqlite('executeQueuedWrite inserts and upserts word and kanji rows', () => {
|
||||
test('executeQueuedWrite inserts and upserts word and kanji rows', () => {
|
||||
const dbPath = makeDbPath();
|
||||
const db = new DatabaseSync!(dbPath);
|
||||
const db = new Database(dbPath);
|
||||
|
||||
try {
|
||||
ensureSchema(db);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { DatabaseSync } from 'node:sqlite';
|
||||
import type { DatabaseSync } from './sqlite';
|
||||
import { SCHEMA_VERSION } from './types';
|
||||
import type { QueuedWrite, VideoMetadata } from './types';
|
||||
|
||||
@@ -13,7 +13,7 @@ function hasColumn(db: DatabaseSync, tableName: string, columnName: string): boo
|
||||
return db
|
||||
.prepare(`PRAGMA table_info(${tableName})`)
|
||||
.all()
|
||||
.some((row) => (row as { name: string }).name === columnName);
|
||||
.some((row: unknown) => (row as { name: string }).name === columnName);
|
||||
}
|
||||
|
||||
function addColumnIfMissing(db: DatabaseSync, tableName: string, columnName: string): void {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { BrowserWindow, ipcMain, IpcMainEvent } from 'electron';
|
||||
import electron from 'electron';
|
||||
import type { IpcMainEvent } from 'electron';
|
||||
import type {
|
||||
RuntimeOptionId,
|
||||
RuntimeOptionValue,
|
||||
@@ -18,6 +19,8 @@ import {
|
||||
parseSubsyncManualRunRequest,
|
||||
} from '../../shared/ipc/validators';
|
||||
|
||||
const { BrowserWindow, ipcMain } = electron;
|
||||
|
||||
export interface IpcServiceDeps {
|
||||
onOverlayModalClosed: (modal: OverlayHostedModal) => void;
|
||||
onOverlayModalOpened?: (modal: OverlayHostedModal) => void;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { safeStorage } from 'electron';
|
||||
import electron from 'electron';
|
||||
|
||||
const { safeStorage } = electron;
|
||||
|
||||
interface PersistedSessionPayload {
|
||||
encryptedSession?: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { KikuFieldGroupingChoice, KikuFieldGroupingRequestData } from '../../types';
|
||||
import { createFieldGroupingCallback } from './field-grouping';
|
||||
import { BrowserWindow } from 'electron';
|
||||
import type { BrowserWindow } from 'electron';
|
||||
|
||||
export function sendToVisibleOverlayRuntime<T extends string>(options: {
|
||||
mainWindow: BrowserWindow | null;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { BrowserWindow } from 'electron';
|
||||
import type { BrowserWindow } from 'electron';
|
||||
import { BaseWindowTracker, createWindowTracker } from '../../window-trackers';
|
||||
import {
|
||||
AiConfig,
|
||||
AnkiConnectConfig,
|
||||
KikuFieldGroupingChoice,
|
||||
KikuFieldGroupingRequestData,
|
||||
@@ -13,6 +14,7 @@ type AnkiIntegrationLike = {
|
||||
|
||||
type CreateAnkiIntegrationArgs = {
|
||||
config: AnkiConnectConfig;
|
||||
aiConfig: AiConfig;
|
||||
subtitleTimingTracker: unknown;
|
||||
mpvClient: { send?: (payload: { command: string[] }) => void };
|
||||
showDesktopNotification: (title: string, options: { body?: string; icon?: string }) => void;
|
||||
@@ -39,6 +41,7 @@ function createDefaultAnkiIntegration(args: CreateAnkiIntegrationArgs): AnkiInte
|
||||
args.showDesktopNotification,
|
||||
args.createFieldGroupingCallback(),
|
||||
args.knownWordCacheStatePath,
|
||||
args.aiConfig,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,7 +60,7 @@ export function initializeOverlayRuntime(options: {
|
||||
targetMpvSocketPath?: string | null,
|
||||
) => BaseWindowTracker | null;
|
||||
getMpvSocketPath: () => string;
|
||||
getResolvedConfig: () => { ankiConnect?: AnkiConnectConfig };
|
||||
getResolvedConfig: () => { ankiConnect?: AnkiConnectConfig; ai?: AiConfig };
|
||||
getSubtitleTimingTracker: () => unknown | null;
|
||||
getMpvClient: () => {
|
||||
send?: (payload: { command: string[] }) => void;
|
||||
@@ -118,6 +121,7 @@ export function initializeOverlayRuntime(options: {
|
||||
const createAnkiIntegration = options.createAnkiIntegration ?? createDefaultAnkiIntegration;
|
||||
const integration = createAnkiIntegration({
|
||||
config: effectiveAnkiConfig,
|
||||
aiConfig: config.ai ?? {},
|
||||
subtitleTimingTracker,
|
||||
mpvClient,
|
||||
showDesktopNotification: options.showDesktopNotification,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { globalShortcut } from 'electron';
|
||||
import electron from 'electron';
|
||||
import { ConfiguredShortcuts } from '../utils/shortcut-config';
|
||||
import { isGlobalShortcutRegisteredSafe } from './shortcut-fallback';
|
||||
import { createLogger } from '../../logger';
|
||||
|
||||
const { globalShortcut } = electron;
|
||||
const logger = createLogger('main:overlay-shortcut-service');
|
||||
|
||||
export interface OverlayShortcutHandlers {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { BrowserWindow } from 'electron';
|
||||
import electron from 'electron';
|
||||
import type { BrowserWindow } from 'electron';
|
||||
import * as path from 'path';
|
||||
import { WindowGeometry } from '../../types';
|
||||
import { createLogger } from '../../logger';
|
||||
import { IPC_CHANNELS } from '../../shared/ipc/contracts';
|
||||
|
||||
const { BrowserWindow: ElectronBrowserWindow } = electron;
|
||||
const logger = createLogger('main:overlay-window');
|
||||
const overlayWindowLayerByInstance = new WeakMap<BrowserWindow, OverlayWindowKind>();
|
||||
|
||||
@@ -18,7 +20,7 @@ function loadOverlayWindowLayer(window: BrowserWindow, layer: OverlayWindowKind)
|
||||
.loadFile(htmlPath, {
|
||||
query: { layer },
|
||||
})
|
||||
.catch((err) => {
|
||||
.catch((err: unknown) => {
|
||||
logger.error('Failed to load HTML file:', err);
|
||||
});
|
||||
}
|
||||
@@ -90,7 +92,7 @@ export function createOverlayWindow(
|
||||
onWindowClosed: (kind: OverlayWindowKind) => void;
|
||||
},
|
||||
): BrowserWindow {
|
||||
const window = new BrowserWindow({
|
||||
const window = new ElectronBrowserWindow({
|
||||
show: false,
|
||||
width: 800,
|
||||
height: 600,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { globalShortcut } from 'electron';
|
||||
import electron from 'electron';
|
||||
|
||||
const { globalShortcut } = electron;
|
||||
|
||||
export function isGlobalShortcutRegisteredSafe(accelerator: string): boolean {
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { BrowserWindow, globalShortcut } from 'electron';
|
||||
import electron from 'electron';
|
||||
import type { BrowserWindow } from 'electron';
|
||||
import { createLogger } from '../../logger';
|
||||
|
||||
const { globalShortcut } = electron;
|
||||
const logger = createLogger('main:shortcut');
|
||||
|
||||
export interface GlobalShortcutConfig {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { BrowserWindow, Extension, session } from 'electron';
|
||||
import electron from 'electron';
|
||||
import type { BrowserWindow, Extension } from 'electron';
|
||||
import * as fs from 'fs';
|
||||
import { createLogger } from '../../logger';
|
||||
import { ensureExtensionCopy } from './yomitan-extension-copy';
|
||||
@@ -7,6 +8,7 @@ import {
|
||||
resolveExistingYomitanExtensionPath,
|
||||
} from './yomitan-extension-paths';
|
||||
|
||||
const { session } = electron;
|
||||
const logger = createLogger('main:yomitan-extension-loader');
|
||||
|
||||
export interface YomitanExtensionLoaderDeps {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { BrowserWindow, Extension, session } from 'electron';
|
||||
import electron from 'electron';
|
||||
import type { BrowserWindow, Extension } from 'electron';
|
||||
import { createLogger } from '../../logger';
|
||||
|
||||
const { BrowserWindow: ElectronBrowserWindow, session } = electron;
|
||||
const logger = createLogger('main:yomitan-settings');
|
||||
|
||||
export interface OpenYomitanSettingsWindowOptions {
|
||||
@@ -28,7 +30,7 @@ export function openYomitanSettingsWindow(options: OpenYomitanSettingsWindowOpti
|
||||
|
||||
logger.info('Creating new settings window for extension:', options.yomitanExt.id);
|
||||
|
||||
const settingsWindow = new BrowserWindow({
|
||||
const settingsWindow = new ElectronBrowserWindow({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
show: false,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Notification, nativeImage } from 'electron';
|
||||
import electron from 'electron';
|
||||
import * as fs from 'fs';
|
||||
import { createLogger } from '../../logger';
|
||||
|
||||
const { Notification, nativeImage } = electron;
|
||||
const logger = createLogger('core:notification');
|
||||
|
||||
export function showDesktopNotification(
|
||||
|
||||
Reference in New Issue
Block a user