From a68feae20eb9d5c73eb5558fa54cff13c5b81374 Mon Sep 17 00:00:00 2001 From: th-kim0823 Date: Thu, 14 May 2026 13:11:17 +0900 Subject: [PATCH] =?UTF-8?q?fix(macos):=20hidden=20autostart=20dock=20indic?= =?UTF-8?q?ator=20+=20=EC=9E=90=EB=8F=99=EC=8B=A4=ED=96=89=20mismatch=20fa?= =?UTF-8?q?lse=20positive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 두 macOS 한정 버그 묶음: 1. autostart --hidden 으로 spawn 시 quickCapture (NSPanel) 만 떠 있어 dock running indicator (점) 가 표출 안 됨 — NSPanel 은 NSApp main window 로 register 안 됨. inboxWindow 를 hidden 상태로 미리 create + ready-to-show 시점에 showInactive → hide trick 으로 NSApp 에 register, 사용자 화면 깜빡임 없이 dock 점 켜짐. 2. SettingsPage 의 자동실행 mismatch 경고가 macOS 에서 false positive. macOS 13+ 의 SMAppService API 가 args 옵션 무시 + unsigned/Electron 앱에 대해 executableWillLaunchAtLogin 을 자주 false 로 반환 → 정상 등록 상태에서도 경고 떠 있음. AutostartDiagnostic 결과에 platform 필드 추가, willLaunch 신호는 win32 에서만 mismatch 판정에 사용. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/main/index.ts | 7 +-- src/main/services/AutostartDiagnostic.ts | 9 +++- src/main/windows/inboxWindow.ts | 24 ++++++++-- .../components/settings/AutostartSection.tsx | 16 +++++-- src/shared/types.ts | 2 + tests/unit/AutostartDiagnostic.test.ts | 1 + tests/unit/AutostartSection.test.tsx | 48 +++++++++++++++++-- 7 files changed, 89 insertions(+), 18 deletions(-) diff --git a/src/main/index.ts b/src/main/index.ts index ee3577c..a9f5133 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -193,9 +193,10 @@ app.whenReady().then(async () => { }); if (!reg.ok) logger.warn('hotkey.register.failed', { reason: reg.reason }); - if (!startedHidden) { - createInboxWindow(); - } + // macOS LoginItems autostart 시 startedHidden=true 로 spawn — 그대로 두면 quickCapture + // (NSPanel) 만 떠 있어 dock running indicator 미표출. inboxWindow 를 hidden 상태로 + // 미리 create 하면 NSApp register → 점 표출 + 사용자가 dock 아이콘 확인으로 앱 살아있음 인지. + createInboxWindow({ visible: !startedHidden }); createQuickCaptureWindow(); await worker.loadFromDb(); diff --git a/src/main/services/AutostartDiagnostic.ts b/src/main/services/AutostartDiagnostic.ts index c11a975..02c48f1 100644 --- a/src/main/services/AutostartDiagnostic.ts +++ b/src/main/services/AutostartDiagnostic.ts @@ -13,6 +13,12 @@ export interface AutostartState { withArgs: { openAtLogin: boolean; executableWillLaunchAtLogin: boolean }; noArgs: { openAtLogin: boolean; executableWillLaunchAtLogin: boolean }; execPath: string; + /** + * 플랫폼 분기용. macOS 13+ 의 SMAppService API 는 args 옵션 무시 + unsigned/Electron + * 앱에 대해 executableWillLaunchAtLogin 이 false 를 반환할 수 있어, mismatch 판정에서 + * 해당 신호를 제외해야 false positive 방지 가능. + */ + platform: NodeJS.Platform; registryPath?: string; registryValue?: string | null; } @@ -26,7 +32,8 @@ export async function collectAutostartState(): Promise { const state: AutostartState = { withArgs: { openAtLogin: w.openAtLogin, executableWillLaunchAtLogin: w.executableWillLaunchAtLogin }, noArgs: { openAtLogin: n.openAtLogin, executableWillLaunchAtLogin: n.executableWillLaunchAtLogin }, - execPath: process.execPath + execPath: process.execPath, + platform: process.platform }; if (process.platform === 'win32') { state.registryPath = `${WIN_REGISTRY_PATH}\\${WIN_REGISTRY_KEY}`; diff --git a/src/main/windows/inboxWindow.ts b/src/main/windows/inboxWindow.ts index 5329121..8b58ca0 100644 --- a/src/main/windows/inboxWindow.ts +++ b/src/main/windows/inboxWindow.ts @@ -11,10 +11,13 @@ export function getInboxWindow(): BrowserWindowType | null { return inboxWindow; } -export function createInboxWindow(): BrowserWindowType { +export function createInboxWindow(opts: { visible?: boolean } = {}): BrowserWindowType { + const visible = opts.visible ?? true; if (inboxWindow && !inboxWindow.isDestroyed()) { - inboxWindow.show(); - inboxWindow.focus(); + if (visible) { + inboxWindow.show(); + inboxWindow.focus(); + } return inboxWindow; } @@ -43,6 +46,19 @@ export function createInboxWindow(): BrowserWindowType { } }); - inboxWindow.once('ready-to-show', () => inboxWindow?.show()); + inboxWindow.once('ready-to-show', () => { + if (visible) { + inboxWindow?.show(); + return; + } + // macOS hidden autostart: regular NSWindow 를 NSApp 에 register 해야 dock running + // indicator (점) 가 표출된다. panel type 의 quickCapture 만 있으면 NSPanel 미인지 → + // dock 점이 안 보여 "앱이 안 떠 있는 것처럼" 보이는 버그. showInactive 로 focus 점유 + // 없이 짧게 표출 후 즉시 hide — 사용자 화면 깜빡임 최소화. + if (process.platform === 'darwin') { + inboxWindow?.showInactive(); + inboxWindow?.hide(); + } + }); return inboxWindow; } diff --git a/src/renderer/inbox/components/settings/AutostartSection.tsx b/src/renderer/inbox/components/settings/AutostartSection.tsx index 509a686..0378da6 100644 --- a/src/renderer/inbox/components/settings/AutostartSection.tsx +++ b/src/renderer/inbox/components/settings/AutostartSection.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from 'react'; import type { AutostartResponse } from '@shared/types'; import { inboxApi } from '../../api.js'; +import { SectionIntro } from './SectionIntro.js'; export function AutostartSection(): React.ReactElement { const [data, setData] = useState(null); @@ -31,14 +32,19 @@ export function AutostartSection(): React.ReactElement { } const d = data.diagnostic; - // v0.2.7 F12 deeper fix — withArgs vs noArgs 의 openAtLogin 불일치, 또는 - // executableWillLaunchAtLogin = false 면 mismatch 로 간주 (등록은 됐지만 실제론 - // 로그인 시 실행되지 않을 수 있는 상태). - const mismatch = d.withArgs.openAtLogin !== d.noArgs.openAtLogin - || (data.openAtLogin && !d.withArgs.executableWillLaunchAtLogin); + // withArgs vs noArgs 의 openAtLogin 불일치는 양 플랫폼에서 진짜 mismatch 시그널. + // executableWillLaunchAtLogin 은 Win 에서만 신뢰 — macOS 13+ SMAppService API 는 + // LoginItems 에 등록되어 있어도 unsigned/Electron 앱에 대해 false 를 자주 반환해 + // false positive 가 발생함. macOS 는 이 신호를 mismatch 판정에서 제외. + const willLaunchSignal = d.platform === 'win32' && data.openAtLogin && !d.withArgs.executableWillLaunchAtLogin; + const mismatch = d.withArgs.openAtLogin !== d.noArgs.openAtLogin || willLaunchSignal; return (
+ + 시스템에 로그인하면 Inkling 이 백그라운드로 함께 시작합니다. 메인 창은 뜨지 않고, + Cmd+Shift+J (macOS) / Ctrl+Shift+J (Windows) 로 필요할 때 불러와 쓰시면 됩니다. +