Files
inkling/docs/superpowers/specs/2026-05-06-v027-cross-platform-design.md

18 KiB
Raw Blame History

v0.2.7 — Cross-Platform 입구 정상화 (Design)

작성일: 2026-05-06 저자: 김태현 (dlsrks0734@gmail.com) 선행 문서:

  • docs/superpowers/specs/2026-04-25-dogfood-feedback.md (F12, F14, F15, F16)
  • docs/superpowers/v024-backlog.md (잔여 24건)
  • docs/superpowers/strategy/dogfood-strategy.md (운영안)

Cut 라벨: v0.2.7 (semver 엄밀히는 MINOR — 새 플랫폼 + 새 surface — 이지만 본 프로젝트 관습상 v0.2.x 를 feature lane 으로 사용 중이므로 v0.2.7 라벨 유지)


1. Cut 정체성

"Cross-platform 입구 정상화" cut. F12 / F14 / F15 / F16 4개 항목을 한 묶음으로 처리. 핵심 동기:

Windows 트레이 의존을 끊고 macOS / Linux 사용자에게 동등한 입구를 제공한다.

현재 13개 트레이 메뉴 항목이 macOS / Linux (특히 모던 GNOME) 에서 발견 / 접근성이 떨어져 핵심 설정 (Ollama endpoint, 자동 실행 등) 진입이 막히는 구조적 문제. 트레이를 deemphasis 하고 inbox 윈도우 안에 통합 설정 페이지를 둔다. 동시에 macOS dock 동작 정상화 (F14) + Linux 앱 빌드 추가 (F15 축소판) + 자동 실행 진단 노출 (F12 deeper fix) 까지 함께 처리한다.

의도적으로 빠진 것:

  • CLI (inkling capture 등) — DB / Ollama 동시접근 race + monorepo 재구성 부담 대비 본인 dogfood metric 직접 기여 적음. v0.2.7 에서 제외. 외부 demand 누적 시 v0.3+ 재거론.

2. 범위

항목 출처 작업
F15 (축소판) dogfood F15 Linux 앱 빌드 (AppImage + deb x64) + better-sqlite3 prebuild linux-x64 매트릭스
F16 dogfood F16 트레이 슬림 (13 → 4) + inbox 안 설정 페이지 (4 섹션)
F14 dogfood F14 macOS dock 클릭 시 hidden 창 show/focus (activate 핸들러 5줄 수정)
F12 deeper fix dogfood F12 (v0.2.6 진단 fallback 후속) 설정 페이지 "자동 실행" 섹션 안에 진단 패널 노출 (withArgs vs noArgs / executableWillLaunchAtLogin / registry path)

3. Architecture 변화

영역 현재 (v0.2.6) v0.2.7
설정 진입 트레이 메뉴 13개 항목 트레이 4개 + 설정 페이지 (inbox 내부 라우트)
Ollama 설정 OllamaSettingsModal (트레이에서만 진입) 설정 페이지 안 "AI 제공자" 섹션 (modal 흡수)
자동 실행 트레이 checkbox + args 명시 설정 페이지 안 섹션 + 진단 패널
macOS dock 클릭 activate 핸들러 no-op (length===0 분기 못 탐) getInboxWindow().show() + focus() 분기 추가
Linux 배포 없음 AppImage + deb 산출물
빌드 매트릭스 win-x64 + mac-arm64 + linux-x64

4. 구현 순서 (Approach 2: Risk-reduction first)

1. Linux 빌드 (가장 unknown — better-sqlite3 prebuild linux-x64 검증)
   ↓ AppImage + deb 산출 + Linux VM/WSL2 smoke test
2. 설정 페이지 (inbox 내부 라우트 + 4 섹션)
   ↓ OllamaSettingsModal 흡수
3. 트레이 슬림 (13 → 4)
   ↓ 제거된 click 핸들러 → 설정 페이지 버튼으로 이동
4. F14 macOS dock 클릭 fix
   ↓ activate 핸들러 5줄
5. F12 deeper fix (자동 실행 진단 노출)
   ↓ IPC settings:autostart-state + 진단 panel UI

Linux 빌드를 먼저 두는 이유: native ABI 트랩 (메모 project_inkling_status.md) 이 linux-x64 에서 재발할 수 있음. 만약 prebuild 가 깔끔히 떨어지지 않으면 v0.2.7 scope 조정 (예: AppImage 만, deb 는 v0.2.8) 여유. 설정 페이지 / 트레이 슬림 / F14 / F12 는 모두 코드 작성 risk 가 낮은 영역이라 후순위로 안전.


5. Linux 빌드 디테일

5-1. electron-builder config 추가

"linux": {
  "target": [
    { "target": "AppImage", "arch": ["x64"] },
    { "target": "deb", "arch": ["x64"] }
  ],
  "category": "Utility",
  "synopsis": "로컬 메모 캡처 + AI 태그",
  "description": "Inkling — 잠깐 스친 생각을 잡아두는 로컬-우선 메모 도구."
}

5-2. npm scripts 추가

"predist:linux": "npm run rebuild:electron && npm run build",
"dist:linux": "electron-builder --linux --x64"

rebuild:electron--target=41.3.0 그대로. prebuild-install 이 linux-x64 prebuild 를 npm 레지스트리에서 받아오는지 검증. 없으면 node-gyp fallback 으로 로컬 컴파일.

5-3. 빌드 호스트 전략

1차: macOS 호스트 (이미 DMG 빌드 호스트). brew 로 도구 설치:

brew install dpkg fakeroot

electron-builder 가 cross-build 지원. AppImage 는 Mac 에서 직접 빌드 가능 (Linux 유저랜드 도구만 필요한 부분은 electron-builder 내장 + AppImageKit 자동 다운로드). deb 는 dpkg-deb 필요.

Fallback (1차 실패 시): Docker on Mac/Windows. electronuserland/builder 이미지로 Linux 빌드 환경 격리. v0.2.7 scope 안에서 결정.

5-4. Smoke test

dist/ 산출물:

  • Inkling-0.2.7.AppImage (x64) — Linux VM 또는 WSL2 에서 chmod +x → 실행 → 마이그레이션 통과 확인 → capture / recall 한 사이클.
  • inkling_0.2.7_amd64.deb — Ubuntu/Debian VM 또는 WSL2 에서 sudo dpkg -iinkling 실행 → 동일 검증.

검증 항목:

  1. better-sqlite3 native module 로드 성공 (마이그레이션 0 → m003 통과)
  2. Ollama 연결 시도 (settings.json 의 endpoint 또는 INKLING_OLLAMA_ENDPOINT env) — 본인 LAN 서버 http://192.168.0.47:11434 사용
  3. capture 한 줄 → AI 처리 → tag 표시
  4. 트레이 (KDE/Cinnamon DE 가정) 4 항목 표시
  5. 트레이 없는 DE (모던 GNOME) — launcher 에서 앱 실행 → inbox 윈도우 → 톱니바퀴 → 설정 페이지 진입

6. 설정 페이지 디테일

6-1. 라우팅 방식

React Router 도입 안 함 (의존성 + 학습 비용). zustand store 의 view: 'inbox' | 'trash' | 'settings' state + 조건부 렌더 — 기존 trash view 와 동일 패턴. 새 의존성 0.

6-2. 진입점

진입 동작
트레이 "설정..." 클릭 main → IPC inbox:navigate 'settings' → renderer store action setView('settings') + inbox 윈도우 show/focus
inbox 헤더 톱니바퀴 아이콘 renderer store action setView('settings')
설정 페이지 안 "← 돌아가기" 버튼 setView('inbox')

6-3. 섹션 4개

6-3-1. AI 제공자

흡수 대상: OllamaSettingsModal 전체 + 트레이 "Ollama 재확인".

UI 요소:

  • Endpoint URL 입력 (zod 검증 — 기존 modal 의 safeParse 재활용)
  • Model 입력 (빈 값 guard)
  • "지금 재확인" 버튼 → ProviderHolder 의 health check trigger
  • 마지막 ping 결과 표시 (성공 시각 또는 실패 사유)
  • "기본값으로 되돌리기" 버튼

저장: 기존 SettingsService (atomic temp+rename + zod) 그대로.

6-3-2. 자동 실행

흡수 대상: 트레이 "윈도우 시작 시 자동 실행" checkbox.

UI 요소:

  • 토글 ("앱 시작 시 자동으로 실행")
  • 진단 패널 (펼치기 가능 — 평소엔 접혀 있음)
  • "재등록" 버튼 (setLoginItemSettings 강제 재호출)

진단 패널 디테일은 §9 (F12 deeper fix) 참조.

6-3-3. 백업 / 복원 / 내보내기

흡수 대상: 트레이의 5개 항목 — "지금 백업" / "내보내기..." / "백업에서 복원..." / "지금 동기화" / "사용 로그 내보내기...".

UI 요소: 5개 버튼 + 각 작업 마지막 실행 시각 (가능하면) + 결과 toast.

IPC 핸들러는 기존 그대로 — 트레이 click 핸들러였던 함수를 IPC 핸들러로 등록 + renderer 에서 invoke.

6-3-4. 정보

흡수 대상: 트레이 "Inkling 정보..." dialog.

UI 요소: 버전 / Electron / Node / OS / 데이터 위치 텍스트 + "데이터 위치 열기" 버튼 + "정보 복사" 버튼.

기존 showAboutDialog 의 detail 문자열 그대로 활용 — clipboard.writeText / shell.openPath 호출도 동일.

6-4. 제외 항목

  • "지금 AI 처리 (실패 N건)" — 이미 inbox FailedBanner 가 surface. 트레이 / 설정 둘 다 제거.
  • "Ollama 재확인" 트레이 메뉴 단독 — OllamaBanner (끊김 시) + 설정 페이지 AI 섹션 "지금 재확인" 버튼이 surface. 트레이 단독 메뉴 제거.

7. 트레이 슬림 디테일

7-1. 잔류 4개 (Win / Mac / Linux 동일)

items.push({ label: '한 줄 적기', click: cb.showCapture });
items.push({ label: '보관한 메모 보기', click: cb.showInbox });
items.push({ type: 'separator' });
items.push({ label: '설정...', click: cb.showSettings });
items.push({ type: 'separator' });
items.push({ label: '종료', click: () => { app.isQuitting = true; app.quit(); } });

todayCount tooltip (Inkling — 오늘 N) 잔류. F4-C 의 "오늘 N번 잡아둠" 비활성 라벨도 잔류 (정체성 신호).

7-2. TrayCallbacks / TrayState 갱신

export interface TrayCallbacks {
  showInbox: () => void;
  showCapture: () => void;
  showSettings: () => void; // NEW — IPC 'inbox:navigate' 'settings' 송출
}

// 메뉴 영향 state 슬림
export interface TrayState {
  todayCount: number;
}

제거 대상:

  • runBackup, runExport, runImport, runSync, runExportTelemetry callback (5개) → 설정 페이지 버튼으로 이동
  • runOllamaRecheck, runRetryAllFailed, runOpenOllamaSettings callback (3개) → 설정 페이지 또는 banner 로 이동
  • ollamaOk, failedCount state field (2개) → 트레이 메뉴 영향 사라짐 (banner 가 surface)
  • refreshTray({ ollamaOk }), refreshTray({ failedCount }) 호출부 (HealthChecker, AiWorker) → 제거. todayCount 만 남음.

v0.2.6 의 Partial 패턴 그대로 활용 — 인터페이스 좁아질 뿐.

7-3. 자동 실행 토글 트레이 잔류 X

기존 트레이 안 checkbox (type: 'checkbox') 는 제거. 설정 페이지 "자동 실행" 섹션 토글이 단일 진입점.

이유: 자동 실행 토글은 빈도 낮은 액션 + F12 진단이 같은 자리에 있어야 의미. 트레이 잔류 시 두 surface mismatch 위험.


8. F14 macOS dock 클릭 fix

src/main/index.ts:411-413 수정:

app.on('activate', () => {
  const win = getInboxWindow();
  if (win && !win.isDestroyed()) {
    if (!win.isVisible()) win.show();
    win.focus();
  } else {
    createInboxWindow();
  }
});

second-instance 핸들러 (B4 #46) 와 패턴 일치 — 양쪽 모두 "살아있으면 show, 죽었으면 create".

테스트: BrowserWindow + activate 이벤트 mocking 비용 ↑ → manual dogfood 검증으로 충분 (macOS 빨간 신호등 → dock 클릭 → 즉시 창 등장).


9. F12 deeper fix — 자동 실행 진단 노출

9-1. 정보 모델

// 신규 IPC: settings:autostart-state
interface AutostartState {
  withArgs: { openAtLogin: boolean; executableWillLaunchAtLogin: boolean };
  noArgs: { openAtLogin: boolean; executableWillLaunchAtLogin: boolean };
  execPath: string; // process.execPath
  registryPath?: string; // Windows only
  registryValue?: string; // Windows only — null 또는 string
}

9-2. main process 핸들러

ipcMain.handle('settings:autostart-state', async () => {
  const withArgs = app.getLoginItemSettings({ args: ['--hidden'] });
  const noArgs = app.getLoginItemSettings();
  const state: AutostartState = {
    withArgs: {
      openAtLogin: withArgs.openAtLogin,
      executableWillLaunchAtLogin: withArgs.executableWillLaunchAtLogin
    },
    noArgs: {
      openAtLogin: noArgs.openAtLogin,
      executableWillLaunchAtLogin: noArgs.executableWillLaunchAtLogin
    },
    execPath: process.execPath
  };
  if (process.platform === 'win32') {
    state.registryPath = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\Inkling';
    state.registryValue = await readRegistryValueSilent(state.registryPath);
  }
  return state;
});

readRegistryValueSilent: child_process.execFile('reg', ['query', path, '/v', 'Inkling']) 1회. 실패 시 null 반환 (silent fallback — 사용자에 에러 노출 X).

새 dependency 추가 X (winreg 등 X) — built-in child_process + Windows reg.exe 만 활용.

9-3. UI

설정 페이지 "자동 실행" 섹션:

[ ] 앱 시작 시 자동으로 실행
   상태: ✅ 등록됨 / ⚠️ 등록 안 됨 / ⚠️ args 미스매치
   ▾ 진단 정보 (펼치기)
     - 표준 조회 (args 명시): openAtLogin=true, willLaunch=true
     - 비교 조회 (args 없이): openAtLogin=false, willLaunch=true   ← mismatch ⚠️
     - 실행 파일 경로: /Applications/Inkling.app/Contents/MacOS/Inkling
     - registry 경로 (Windows): HKCU\...\Run\Inkling
     - registry 값: "C:\Users\...\Inkling.exe" --hidden
   [ 재등록 ] 버튼

9-4. dogfood 시나리오

  1. 토글 ON → 재시작 → 풀려있으면 진단 패널 펼침.
  2. withArgs vs noArgs mismatch 보임 → args canonicalization 문제 확인.
  3. registry 값 vs execPath 비교 — 다르면 path canonicalization 문제 (NSIS 재설치 시 path 바뀜).
  4. "재등록" 버튼 → setLoginItemSettings 재호출 → 다시 재시작 → 효과 측정.

수집된 데이터로 v0.2.8 root cause fix 작성.


10. 테스트 전략

영역 단위 e2e Manual dogfood
Linux 빌드 (F15) - - AppImage + deb 산출 + Linux VM 실행 + 마이그레이션/캡처/recall 한 사이클
설정 페이지 라우팅 zustand store action setView('settings') 단위 (선택) 트레이 "설정..." → IPC → view 전환 e2e 실제 클릭 흐름
Ollama 섹션 흡수 기존 OllamaSettingsModal 단위 + 흡수 후 회귀 - 1회
자동 실행 진단 IPC autostart-state 핸들러 단위 (mock electron API + child_process) - Win 토글 → 재시작 → 진단 패널 mismatch 검출
트레이 슬림 tray.ts buildMenu 단위 (4 항목 검증 + 제거된 항목 부재) - -
F14 dock fix (mock 비용 ↑) — manual 만 - macOS dock 클릭
F12 진단 UI mismatch 시 ⚠️ 렌더 단위 - F12 시나리오 재현

목표: 단위 426 → 약 450 (+24), e2e 1 유지 또는 +1.


11. Risk / Known unknowns

Risk 발생 시 대응
linux-x64 prebuild 부재 → node-gyp 빌드 실패 Docker electronuserland/builder fallback. 그래도 실패 시 v0.2.7 scope 조정: AppImage 만, deb 는 v0.2.8. [2026-05-07 검증: prebuild 존재 — electron-v145 (v41.3.0 ABI) 다운로드 성공, better_sqlite3.node 파일 생성]
ELECTRON_RUN_AS_NODE 함정 (메모) 가 Linux 환경에서 재현 smoke test launch env 에서 strip — 기존 e2e 의 strip 패턴 그대로
AppImage 가 모던 GNOME 에서 트레이 표시 안 됨 의도적 — 그래서 dock/launcher → inbox → 설정 페이지 흐름이 안전망. F14 fix 가 이 흐름의 핵심.
설정 페이지 라우팅이 inbox 의 keyboard shortcut / hotkey 와 충돌 view='settings' 시 inbox-only shortcut 비활성. zustand state 분기.
자동 실행 진단 패널이 Mac/Linux 에선 의미 없는 정보 노출 플랫폼 분기 — Mac/Linux 는 registry 행 숨김 + executableWillLaunchAtLogin 만 표시
트레이 callback 8개 제거 시 import 그래프에서 dead code 잔존 제거 후 typecheck + grep 으로 검증

v0.2.7 Linux 빌드 1차 시도 결과 (2026-05-07, Windows 호스트)

npm run dist:linux 실행 — Windows 11 호스트.

진행 단계:

  • predist:linux (electron rebuild + electron-vite build) — 성공
  • electron-builder linux x64 패키징 prep — 성공
  • electron-v41.3.0-linux-x64.zip 다운로드 (117 MB)
  • dist/linux-unpacked/ 스테이징 생성 — 322 MB (electron + native modules + app)
  • appimage-12.0.1.7z 다운로드 (mksquashfs 등 Linux 유저랜드 도구 캐시)
  • ⚠️ AppImage 패키징 실패mksquashfs 실행 불가
  • ⏭️ deb 패키징은 시도조차 못함 (AppImage 실패로 빌드 중단)

핵심 에러:

 cannot execute  cause=exec: "C:\Users\...\electron-builder\Cache\appimage\appimage-12.0.1\linux-x64\mksquashfs": file does not exist
 failed to build AppImage  error=...app-builder.exe process failed ERR_ELECTRON_BUILDER_CANNOT_EXECUTE
Exit code: 2

진단: mksquashfs 파일은 캐시에 존재하나 (270 KB Linux ELF 바이너리), Windows 가 ELF 를 실행 불가 → electron-builder 가 "file does not exist" 로 보고. AppImage cross-build from Windows 는 근본적으로 불가능 (WSL/Docker/Linux/Mac 호스트 필요).

결론:

  • AppImage: ⚠️ 실패 (Windows 호스트는 mksquashfs ELF 실행 불가 — 환경 제약)
  • deb: ⚠️ 미시도 (dpkg-deb + fakeroot 부재 추정. AppImage 실패로 도달 못함)

Fallback 결정: Mac (사용자 업무 호스트) 또는 Linux/WSL/Docker 핸드오프 필수. Windows 단독으로는 v0.2.7 Linux 산출물 생성 불가. plan Task 3 은 "시도 + 결과 기록" 이 핵심이었고, macOS 후속 시도가 본 빌드 — Windows 시도는 환경 한계 확인용.

권장 후속:

  1. 사용자 macOS 업무 호스트에서 동일 명령 (npm run dist:linux) 재시도. brew 로 dpkg + fakeroot 사전 설치 (brew install dpkg). AppImage 는 macOS 에서 정상 cross-build 가능 (mksquashfs Mach-O 바이너리 caching).
  2. macOS 에서도 deb 가 실패할 경우 — v0.2.7 scope 를 AppImage only 로 축소, deb 는 v0.2.8 또는 Docker electronuserland/builder 환경으로 이동. package.json linux.target 에서 deb 제거하거나 별도 task 로 분리.
  3. 향후 자동화: GitHub Actions / Gitea Actions 에서 ubuntu-latest runner 로 Linux build 자동화 (현 수동 cross-build 환경 의존성 제거).

12. v0.2.7 후

잔여 backlog (24건)docs/superpowers/v024-backlog.md:

  • v0.2.6 final reviewer minor cleanup 6건 — kstDate 의미 정정 / NoteRepository.test.ts as any / store.ts trashCount race 등
  • telemetry data-dependent 14건 — VOCAB_TOP_N 튜닝, recallBanner 임계값 등 — v0.2.8 후보 (v0.2.7 dogfood soak ≥1주 후 telemetry export 누적)
  • v0.2.7 본 cut 안 신규 발견 — F12 root cause 가 진단 데이터 누적 후 결정될 가능성

v0.2.8 트리거: v0.2.7 release 후 dogfood ≥1주 soak + telemetry export + F12 진단 데이터 → v0.2.8 brainstorm.