← 목록
기타 2026-06-25 6KB 읽기 6분

크라우니 스티커 — macOS 스티커(Stickies) 대용 프로그램

개요

사용자 요청: "맥용 스티커 프로그램 대용 프로그램 만들기, 색상 더 고를 수 있게, 꺼졌다 켜져도 멀티 스크린 위치에 다시 배치되도록."

macOS 기본 스티커를 대체하는 네이티브 데일리드라이버 앱을 제작. 3대 요구사항(떠다니는 메모 / 확장 색상 / 멀티스크린 위치 영속) 모두 구현·검증 완료.

분별 판정

  • 신규 기능(티) + GUI 플랫폼락 → 재구축 독트린 #4(플랫폼락=더블 작성) 적용.
  • crownyc_window.m은 VM이 단일 프레임버퍼를 그리는 단일 캔버스 → 메모마다 독립 NSWindow가 필요한 스티커는 네이티브 멀티윈도우 작업.
  • 결정: 네이티브 Swift/AppKit 데일리드라이버 + 한선씨 동반(위치해결/클램프 로직).

무엇을 했는지

1. 네이티브 앱 (CrownySticky.swift)

  • 메모 1개 = borderless NSWindow(StickyWindow, canBecomeKey/Main override). 헤더 드래그 이동, 가장자리 리사이즈.
  • 16색 팔레트 메뉴 + "사용자 지정 색…"(시스템 색상패널). 배경 밝기(luminance>0.55)로 글자색 자동 대비.
  • 메뉴: ⌘N 새 메모, ⌘W 닫기(내용 있으면 삭제 확인), 편집(undo/redo/cut/copy/paste/selectAll).

2. 멀티스크린 위치 영속 (핵심)

  • 저장 모델(NoteData): text · rgba · displayUUID · offset(x,y) · size(w,h)~/Library/Application Support/CrownySticky/notes.json.
  • displayUUID = CGDisplayCreateUUIDFromDisplayID(재연결해도 유지). 오프셋은 그 화면 원점 기준 상대좌표.
  • 복원(resolvePlacement): 디스플레이 연결됨→그 화면 복원 / 사라짐→메인 폴백. 두 경우 모두 visibleFrame 클램프.
  • 사라진 모니터 메모는 원래 UUID 보존(무상호작용 재실행 시 미저장) → 모니터 복귀 시 제자리.
  • didChangeScreenParameters로 화면밖 메모 자동 재클램프.
  • 저장 트리거: 이동/리사이즈/입력(0.4s 디바운스)·종료·화면변경.

3. 한선씨 동반 (CrownySticky.한선)

  • 클램프 + 위치해결 순수 로직. crownyc 컴파일·실행 검증(4사례: 정상/넘침클램프/외부모니터복원/사라짐폴백) 통과.
  • 학습 등록: intent 스티커_멀티스크린_위치해결_클램프.

검증 결과 (라이브 실측)

항목결과
색상 영속노랑 r=1,g=0.96,b=0.62 정확 라운드트립
색 피드백 루프해결(applyColor 폭증 0). NSColorWell 배타활성으로 차단
메모 A(실 UUID, off 300,300)실제 window.frame {{300,300},{260,200}} 정확 복원
메모 B(가짜 UUID DEAD, off 99999)메인 폴백 + 클램프 {{1620,809},{300,240}} (maxX=1920-300, maxY=1049-240)
한선씨 동반4/4 사례 기대값 일치

관련 파일

  • /Users/ef/crowny-sticky/CrownySticky.swift — 네이티브 앱
  • /Users/ef/crowny-sticky/CrownySticky.한선 — 동반 위치해결 로직
  • /Users/ef/crowny-sticky/build.sh — 빌드
  • /Users/ef/crowny-sticky/README.md
  • 산출 앱: /Users/ef/crowny-sticky/크라우니스티커.app
  • 저장소: ~/Library/Application Support/CrownySticky/notes.json

함정 (재사용 지식)

  • 색상 피드백 루프: borderless 창 + 전역 NSColorPanel 액션 가로채기 시, textView.textColor 설정이 패널에 동기화→액션 재발火→무한루프(배경이 텍스트색으로 덮임). 해결=NSColorWell.activate(true) 배타 활성화.
  • AX 좌표 측정 아티팩트: borderless 창은 System Events position이 실제 window.frame과 다르게 보고됨 → 검증은 NSStringFromRect(window.frame) 직접 로깅으로.
  • borderless 창 텍스트 편집엔 canBecomeKey/Main override 필수.

2차 추가 기능 (2026-06-25, 동일 세션)

사용자 요청: "커맨드 플러스 마이너스로 폰트와 상단바까지 크기 조절. 첫 줄 작성하고 엔터치면 제목. 스티커끼리 연계성 연결."

  1. 폰트 + 상단바 크기 조절(⌘+/⌘-)setFontSize로 9~48pt. headerHeight = max(22, fontSize+12)로 상단바·버튼·제목 글자 동반 스케일. 메뉴 글자/상단바 크게(⌘+, ⌘= 대체) / 작게(⌘-). 포커스된 메모(keyNote)에 적용.
  2. 첫 줄 = 제목firstLine()이 본문 첫 줄을 헤더 제목 라벨에 굵게 표시, textDidChange로 실시간. 첫 줄은 본문에 그대로 남고 제목으로 미러(Apple Notes 모델).
  3. 스티커 연결(연계성)links: Set<String>. 헤더 🔗 버튼 메뉴로 토글, 양방향(toggleLink이 상대 메모 links도 갱신). 제목 옆 🔗N 배지. revealLinks로 연결 메모 앞으로+파란 테두리 flash(). 삭제 시 타 메모 links에서 자동 제거. NoteData에 fontSize?·links? 옵셔널 추가(구버전 json 호환).

검증 — --selftest (UI 무의존 셀프테스트, ALL PASS)

  • 첫줄→제목 '제목A' / 폰트 14→18 / 상단바 26→30 / A→B·양방향 B→A / 인코드·디코드 왕복.
  • 라이브 ⌘+ 자동화 검증은 System Events Accessibility 권한·borderless AX 좌표 한계로 미수행 → 동일 코드경로를 셀프테스트로 대체 검증.

잔여 이슈

  • 코드 서명은 ad-hoc(로컬 전용). 배포 시 Developer ID 서명·공증 필요.
  • 멀티 디스플레이 실기기 2화면 동시 테스트는 단일화면 환경에서 가짜 UUID 폴백으로 대체 검증(로직 동일).
  • 추후: 항상 위(float) 토글, 메뉴바 아이콘. (폰트 크기 조절은 2차에서 구현 완료)