···99 */
10101111import { DEBUG } from './config.js';
1212-import { checkModeConditions, type MajorModeId, type MinorModeId } from './datastore.js';
1212+import { checkModeConditions, type MajorModeId } from './datastore.js';
13131414// Lazy-load Electron modules to allow testing without Electron
1515let globalShortcut: typeof import('electron').globalShortcut | null = null;
···39394040interface ModeConditions {
4141 majorMode?: MajorModeId;
4242- minorModes?: MinorModeId[];
4342}
44434544interface LocalShortcutEntry {
···226225227226 // If mode-conditional, add to array (allows same key with different modes)
228227 // If not mode-conditional, replace any existing non-conditional entry
229229- if (modeConditions?.majorMode || modeConditions?.minorModes?.length) {
228228+ if (modeConditions?.majorMode) {
230229 // Mode-conditional: add to array
231230 entries.push(entry);
232231 } else {
233232 // Non-conditional: find and replace any existing non-conditional entry
234234- const nonConditionalIndex = entries.findIndex(e => !e.modeConditions?.majorMode && !e.modeConditions?.minorModes?.length);
233233+ const nonConditionalIndex = entries.findIndex(e => !e.modeConditions?.majorMode);
235234 if (nonConditionalIndex >= 0) {
236235 entries[nonConditionalIndex] = entry;
237236 } else {
···266265 }
267266268267 // No mode conditions - remove non-conditional entries
269269- return entry.modeConditions?.majorMode || entry.modeConditions?.minorModes?.length;
268268+ return !!entry.modeConditions?.majorMode;
270269 });
271270272271 if (filtered.length > 0) {
···291290 for (const entry of entries) {
292291 if (inputMatchesShortcut(input, entry.parsed)) {
293292 // Check mode conditions if specified
294294- if (entry.modeConditions?.majorMode || entry.modeConditions?.minorModes?.length) {
293293+ if (entry.modeConditions?.majorMode) {
295294 // Need a window ID to check mode
296295 if (focusedWindowId === undefined && BrowserWindow) {
297296 // Try to get focused window
···302301 if (focusedWindowId !== undefined) {
303302 const modeMatches = checkModeConditions(
304303 focusedWindowId,
305305- entry.modeConditions.majorMode,
306306- entry.modeConditions.minorModes
304304+ entry.modeConditions.majorMode
307305 );
308306309307 if (modeMatches) {
+2-24
backend/types/api.ts
···4242 * Only one major mode can be active per window at a time.
4343 * Inspired by Emacs major modes.
4444 */
4545-export type MajorModeId = 'page' | 'group' | 'settings' | 'default';
4646-4747-/**
4848- * Minor modes are optional features that can be combined.
4949- * Multiple minor modes can be active simultaneously.
5050- * Inspired by Emacs minor modes.
5151- */
5252-export type MinorModeId = 'preview' | 'edit' | 'annotate' | 'search';
4545+export type MajorModeId = 'page' | 'group' | 'default';
53465447/**
5548 * Mode metadata for display and behavior
5649 */
5750export interface ModeInfo {
5858- id: MajorModeId | MinorModeId;
5151+ id: MajorModeId;
5952 name: string;
6053 description?: string;
6161- /** Whether this is a major (exclusive) or minor (stackable) mode */
6262- type: 'major' | 'minor';
6354}
64556556/**
···6859export interface WindowModeState {
6960 /** Current major mode */
7061 major: MajorModeId;
7171- /** Active minor modes */
7272- minors: MinorModeId[];
7362}
74637564/**
···9887 /** Set the major mode for a window */
9988 setMajorMode(mode: MajorModeId, windowId?: number | null): Promise<ApiResult<void>>;
10089101101- /** Enable a minor mode for a window */
102102- enableMinorMode(mode: MinorModeId, windowId?: number | null): Promise<ApiResult<void>>;
103103-104104- /** Disable a minor mode for a window */
105105- disableMinorMode(mode: MinorModeId, windowId?: number | null): Promise<ApiResult<void>>;
106106-107107- /** Toggle a minor mode for a window */
108108- toggleMinorMode(mode: MinorModeId, windowId?: number | null): Promise<ApiResult<boolean>>;
109109-11090 /** Get all available modes */
11191 listModes(): Promise<ApiResult<ModeInfo[]>>;
11292···121101 global?: boolean;
122102 /** Only trigger shortcut when in this major mode */
123103 mode?: MajorModeId;
124124- /** Only trigger shortcut when these minor modes are active */
125125- minorModes?: MinorModeId[];
126104}
127105128106export interface IShortcutsApi {
+9-42
preload.js
···113113 * @param {function} cb - Callback function when shortcut is triggered
114114 * @param {object} options - Optional configuration
115115 * @param {boolean} options.global - If true, shortcut works even when app doesn't have focus (default: false)
116116- * @param {string} options.mode - Only trigger in this major mode ('page', 'group', 'settings', 'default')
117117- * @param {string[]} options.minorModes - Only trigger when these minor modes are active
116116+ * @param {string} options.mode - Only trigger in this major mode ('page', 'group', 'default')
118117 */
119118 register: (shortcut, cb, options = {}) => {
120119 const isGlobal = options.global === true;
···128127 shortcut,
129128 replyTopic,
130129 global: isGlobal,
131131- mode: options.mode,
132132- minorModes: options.minorModes
130130+ mode: options.mode
133131 });
134132135133 ipcRenderer.on(replyTopic, (ev, msg) => {
···152150 source: sourceAddress,
153151 shortcut,
154152 global: isGlobal,
155155- mode: options.mode,
156156- minorModes: options.minorModes
153153+ mode: options.mode
157154 });
158155 }
159156};
···15791576});
1580157715811578// ==================== Modes API ====================
15821582-// Context-aware command system with major/minor modes
15791579+// Context-aware command system
15831580api.modes = {
15841581 /**
15851582 * Get the current mode state for a window
15861583 * @param {number|null} windowId - Window ID (null = current/last focused)
15871587- * @returns {Promise<{success: boolean, data?: {major: string, minors: string[]}, error?: string}>}
15841584+ * @returns {Promise<{success: boolean, data?: {major: string}, error?: string}>}
15881585 */
15891586 getWindowMode: (windowId = null) => {
15901587 return ipcRenderer.invoke('modes:getWindowMode', { windowId });
···1592158915931590 /**
15941591 * Set the major mode for a window
15951595- * @param {string} mode - Major mode ID ('page', 'group', 'settings', 'default')
15921592+ * @param {string} mode - Major mode ID ('page', 'group', 'default')
15961593 * @param {number|null} windowId - Window ID (null = current/last focused)
15971594 * @returns {Promise<{success: boolean, error?: string}>}
15981595 */
···16011598 },
1602159916031600 /**
16041604- * Enable a minor mode for a window
16051605- * @param {string} mode - Minor mode ID ('preview', 'edit', 'annotate', 'search')
16061606- * @param {number|null} windowId - Window ID (null = current/last focused)
16071607- * @returns {Promise<{success: boolean, error?: string}>}
16081608- */
16091609- enableMinorMode: (mode, windowId = null) => {
16101610- return ipcRenderer.invoke('modes:enableMinorMode', { mode, windowId });
16111611- },
16121612-16131613- /**
16141614- * Disable a minor mode for a window
16151615- * @param {string} mode - Minor mode ID
16161616- * @param {number|null} windowId - Window ID (null = current/last focused)
16171617- * @returns {Promise<{success: boolean, error?: string}>}
16181618- */
16191619- disableMinorMode: (mode, windowId = null) => {
16201620- return ipcRenderer.invoke('modes:disableMinorMode', { mode, windowId });
16211621- },
16221622-16231623- /**
16241624- * Toggle a minor mode for a window
16251625- * @param {string} mode - Minor mode ID
16261626- * @param {number|null} windowId - Window ID (null = current/last focused)
16271627- * @returns {Promise<{success: boolean, data?: boolean, error?: string}>} data is true if now enabled
16281628- */
16291629- toggleMinorMode: (mode, windowId = null) => {
16301630- return ipcRenderer.invoke('modes:toggleMinorMode', { mode, windowId });
16311631- },
16321632-16331633- /**
16341634- * Get all available modes (major and minor)
16351635- * @returns {Promise<{success: boolean, data?: Array<{id: string, name: string, description: string, type: string}>, error?: string}>}
16011601+ * Get all available modes
16021602+ * @returns {Promise<{success: boolean, data?: Array<{id: string, name: string, description: string}>, error?: string}>}
16361603 */
16371604 listModes: () => {
16381605 return ipcRenderer.invoke('modes:listModes');
···16531620 */
16541621 onModeChange: (callback) => {
16551622 api.subscribe('modes:changed', (msg) => {
16561656- callback({ major: msg.major, minors: msg.minors }, msg.windowId);
16231623+ callback({ major: msg.major }, msg.windowId);
16571624 }, api.scopes.GLOBAL);
16581625 }
16591626};