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) 로 필요할 때 불러와 쓰시면 됩니다.
+