477 Commits

Author SHA1 Message Date
songyc macbook
4c3b5a6c03 feat(diary): show selected calendar events in the timetable, per date
- DiaryEntry.calendarEventIDs: 날짜별로 어떤 캘린더(EventKit) 일정을 넣을지
  따로 저장 — 오늘은 포함, 내일은 미포함 같은 선택이 가능
- 타임테이블 카드 우상단 캘린더 버튼(+선택 개수 배지) → 일정 선택 팝업:
  그날의 일정 목록에서 전체 선택/해제 또는 개별 체크, 권한 미허용 시 설정
  이동 안내. 종일 일정은 제외, 하루 경계에 걸치면 잘라서 표시
- 일정은 행동 블록과 구분되는 외곽선+은은한 채움 스타일(캘린더 색)로 렌더,
  일기 내보내기(PDF/이미지)에도 동일하게 포함 — 내보내기 요약도 화면과
  같은 24시간 타임테이블로 통일
- NSCalendarsFullAccessUsageDescription 추가, 새 문자열 en/ja 번역
- 검증 인자: -diarySeedEvents YES, -diaryShowCalendarPicker YES

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 17:31:09 +09:00
songyc macbook
4f9fdfba11 chore(xcode): normalize Store scheme serialization (Xcode strips comments)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 17:15:37 +09:00
songyc macbook
957618fccd feat(app): add Home tab refresh button and device-specific help guide
- 모음 탭 왼쪽 위 새로고침 버튼(RefreshButton/AppRefresh): 루트 뷰 identity
  리셋으로 모든 @Query를 재조회(앱 재시작과 같은 효과) + 라이브 액티비티·
  위젯·워치 스냅숏 동기화. 다른 기기의 iCloud 변경이 화면에 늦게 보일 때
  재시작 없이 반영. iPhone·iPad 공통, Release(스토어) 빌드에도 포함
- 설정 → 지원 → 도움말(HelpView): 기본 개념/모음 탭/목표/기록·통계/동기화/
  프리미엄 공통 + iPhone은 화면 구성(탭바·버블 메뉴·다이나믹 아일랜드)과
  애플워치(기록·컴플리케이션·동기화) 섹션, iPad는 일기 섹션. 접이식 주제별
  구성, 새 문자열 59개 en/ja 번역
- 검증 인자: -helpPreview YES

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 17:09:11 +09:00
songyc macbook
040198beb4 feat(premium): add store-identical Release run scheme and real-purchase priority
- 'Haru_Danim-Store' 공유 스킴 추가: Release 구성으로 실행해 DEBUG 코드
  (프리미엄 테스트 토글·검증용 런치 인자)가 전부 빠진 '스토어에 올릴
  그대로'의 앱을 실기기에서 확인. StoreKit 로컬 구성은 유지되어 실제 결제
  시트가 뜨되 돈은 청구되지 않는다
- 실구매/복원 시 DEBUG 테스트 토글 강제 모드를 자동 해제(실권한 우선),
  개발용 섹션에 '토글 강제 해제' 버튼과 동작 설명 footer 추가
- Localizable.xcstrings는 Xcode 직렬화 포맷 정규화

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 16:14:11 +09:00
songyc macbook
14fc5cb5bd feat(premium): complete StoreKit 2 purchase experience
기존 StoreKit 2 골격(상품 3종·구매·복원·권한 주입) 위에 실사용 경험 완성:
- PremiumStore.currentPlan: 현재 이용 중인 대표 플랜(평생 > 만료 먼 구독)
  추적, 구매·복원 후 위젯 타임라인 즉시 갱신
- 프리미엄 화면: '이용 중인 플랜' 섹션(플랜 이름·다음 갱신일·평생 안내),
  구독 관리 시트(manageSubscriptionsSheet), 구독 중 평생 이용권 전환 안내,
  년 구독 절약률·평생 '한 번 결제' 배지, 자동 갱신·해지·복원 안내 문구
- 잠금 안내 개선: 행동/목표/다짐 무료 한도 알림에 '프리미엄 알아보기'
  버튼 + PremiumSheetView 모달, 아이패드 일기 잠금 화면에도 버튼 추가
- 기능 소개에 '아이패드 일기' 추가, 새 문자열 en/ja 번역

가격(월 5,000/년 50,000/일회성 80,000)은 HaruDanim.storekit(로컬 테스트)과
App Store Connect에서 각각 수정 가능. DEBUG 강제 토글은 그대로 유지.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 15:52:49 +09:00
songyc macbook
c6959950c2 feat(widget): fill quest list adaptively in goal progress widget
목표 진행률 위젯의 다짐 목록이 고정 3개(중형)/8개(대형) 제한이었던 것을
QuestBarsColumn으로 교체 — 셀의 실제 높이에 들어가는 만큼 최대한 표시
(행 14pt+간격 6pt 기준 계산)하고, 넘치는 개수는 헤더 오른쪽에 '+N' 표기.
기기·패밀리가 커지면 자동으로 더 보인다.

그 외 개수 제한 검토: 다짐 현황 위젯 그리드(4/8/16)는 스펙 고정, 통계
차트 시리즈(4/6)는 가독성 제한, 잠금화면 점(6)은 공간 제한이라 유지.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 15:14:26 +09:00
songyc macbook
5916e17b25 fix(diary): keep pencil strokes sharp at any zoom level
페이지가 논리 768×1024pt 고정 + 스케일 변환이라 PKCanvasView 래스터가
비정수 축소에선 뭉개지고 확대에선 픽셀이 보였음 → 캔버스(내부 렌더 뷰
포함)의 contentScaleFactor를 기본 1.5배 슈퍼샘플링 + 줌 배율 추종(기기
스케일 ×2.5 상한)으로 조정. 레이아웃 변경과 핀치 종료 시점에 갱신.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 15:08:08 +09:00
songyc macbook
8d4d20fc76 feat(diary): make the mood area a large square tile
'오늘 기분'을 카드의 주인공인 200pt 정사각형 타일로 재배치 — 사진은 꽉
채우고(이모지는 우하단 배지), 이모지만 있으면 크게 가운데, 비어 있으면
'탭해서 남기기' 플레이스홀더. 오른쪽에 이모지/사진/지우기 캡슐 버튼 열.
새 문자열 en/ja 번역 추가.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 15:08:08 +09:00
songyc macbook
daf4fc3157 fix(radial): add glass pill background behind bubble tab labels
글래스 버블 메뉴의 탭 이름이 맨 글자(그림자만)라 라이트 모드 밝은 배경
위에서 잘 안 보였음 → 구슬과 같은 ultraThinMaterial 캡슐 배경 + 화이트
보더 + 소프트 섀도를 붙여 라이트/다크 어디서나 읽히게 함.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 14:58:45 +09:00
songyc macbook
939406ac74 feat(brand): logo v4 — walker on a clock planet, with light/dark/tinted icon variants
콘셉트 전면 재디자인: 하루라는 행성(시계)이 화면 아래를 채우고 그 위를
사람이 걸어간다. 시계는 링+3/6/9 눈금+10:10 골드 바늘, 사람 뒤로 링을
따라 남은 발자국, 소프트 섀도로 입체감.

iOS 아이콘 외형 3종(에셋 카탈로그 luminosity 변형):
- 라이트: 웜 화이트 배경 + 초록 시계 + 검정 사람 + 골드 바늘
- 다크: 딥 그린 그라데이션 배경 + 웜 화이트 드로잉 (기존 톤 유지)
- 틴티드: 투명 배경 + 그레이스케일 (시스템이 틴트 적용)
스플래시 로고도 라이트/다크 변형 제공(테마 따라 자동 전환), 워치·워치
위젯은 다크 단일(외형 변형 미지원 플랫폼).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 14:30:01 +09:00
songyc macbook
6b0b6055eb feat(diary): customizable first-page sections — show/hide and reorder
- 일기 상세 메뉴에 '첫 화면 구성' 시트 추가: 7개 섹션(오늘 기분/요약
  지표/이 날의 목표/오늘 할 일/타임테이블/기록 상세/통계 막대)을
  드래그로 순서 변경, 스위치로 표시/숨김 (모든 날짜 공통 적용)
- DiarySummaryPage가 설정 순서대로 렌더링, 숨긴 섹션 제외. 와이드
  레이아웃은 타임테이블 표시 중일 때만 2컬럼(타임테이블 왼쪽 전담)
- 기존 '오늘 기분 표시' 토글은 새 숨김 목록으로 1회 이관
- 검증 인자: -diaryShowConfig YES, -diary.sectionOrder/-diary.hiddenSections
- 새 문자열 en/ja 번역 추가

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 05:57:11 +09:00
songyc macbook
209042575e feat(brand): logo v3 — person walking toward the clock of their day
시계와 사람을 포함해 슬로건('당신의 하루에 다녀가다')과 앱 콘셉트(시간
추적)를 직접 표현: 우상단 웜 화이트 시계 링(골드 10:10 바늘) + 그곳을
향해 걷는 웜 화이트 픽토그램 + 지나온 발자국 점 2개. 색 구성은 유지.
iOS·워치·워치 위젯 아이콘과 스플래시 로고 일괄 교체.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 05:57:11 +09:00
songyc macbook
921eb4bccf feat(brand): redesign logo around the slogan — footsteps visiting the sun of your day
'당신의 하루에 다녀가다'를 직접 표현: 골드 해(하루)를 향해 대각선으로
걸어가는 웜 화이트 발자국 캡슐 3개(멀수록 작아지는 깊이감) + 은은한 링.
기존 색 구성(딥 그린 그라데이션/웜 화이트/머스터드 골드) 유지.
iOS·워치·워치 위젯 앱 아이콘과 스플래시 로고에 일괄 적용.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 03:47:12 +09:00
songyc macbook
ecb7b43d31 feat(diary): redesign summary page as daily review and add note pinch-zoom, line spacing
- 첫 페이지를 '하루 정리' 화면으로 개편: 기록 유무와 무관한 24시간 고정
  타임테이블(trimHours 옵션), 그 날짜에 진행 중이던 목표들의 다짐별 진행
  현황 카드(하루 다짐은 그날 실적, 주간/월간/기간 다짐은 그 날짜까지 누적,
  '이하 유지'는 한도 게이지로 구분), 기록 상세·통계 막대 재배치
- 노트 페이지: UIScrollView 기반 핀치줌(두 손가락, fit~4배, 배치 모드에선 잠금)
- 줄 노트 줄 간격 설정(좁게/보통/넓게/아주 넓게, 페이지별 저장·내보내기 반영)
- 새 문자열 en/ja 번역 추가

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 03:46:58 +09:00
songyc macbook
1f77264852 fix(startup): survive store open failures instead of crashing before splash
일기 기능의 스키마 변경(모델 4종 추가) 후 기존 데이터가 있는 기기에서 첫 실행 시
마이그레이션이 필요한데, 이때 위젯 프로세스와의 스토어 경합이나 마이그레이션 실패가
발생하면 makeContainer()가 곧장 fatalError로 죽어 스플래시도 못 띄우는 문제 방어:
- 일시적 경합 대비 최대 3회 재시도(점증 대기)
- 그래도 실패하면 스토어를 타임스탬프 백업으로 보존 후 새로 생성해 앱은 반드시 켜지게
- 검증: 스토어 파일 고의 손상 → 백업 생성 + 정상 부팅 확인

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 03:15:38 +09:00
songyc macbook
8c803d4d6e feat(brand): new geometric logo — day ring + footsteps, applied to all app icons and splash
- DesignAssets/AppLogo.svg 벡터 원본 (하루=열린 링, 지금=골드 점, 다님=발자국 캡슐 2개)
- iOS·워치 앱·워치 위젯 AppIcon + 스플래시 AppLogo 전부 교체 (기존 플레이스홀더 삭제)
- CoreGraphics 렌더로 불투명(알파 없음) 1024px PNG 생성 — 앱 아이콘 알파 채널 이슈 해소

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 03:02:00 +09:00
songyc macbook
40d917d4e2 feat(diary): add iPad-only premium diary tab with calendar, pencil notes, and export
- 달력(작성일 마커: 기분 이모지/연필 점) → 날짜별 일기, 이번 달 작성 수 표시
- 첫 페이지: 날짜·오늘 기분(이모지/사진, 끄기 옵션)·할 일 체크리스트(스크리블 입력)·
  타임테이블·통계(내보내기 스냅숏 재사용으로 화면·내보내기 일치)
- 노트 페이지: PencilKit 캔버스(도구 팔레트) + 줄 노트 배경 + 사진/도형(사각형·원·화살표·선)
  배치 모드(드래그·핀치), 페이지 무한 추가 — 논리 크기 768×1024 고정으로 WYSIWYG 내보내기
- 내보내기: 구간/복수 날짜(MultiDatePicker) → PDF 1개 또는 날짜별 세로 스티치 PNG
- SwiftData 모델 4종(CloudKit 호환 패턴) 스키마 추가, PremiumFeature.diary,
  iPhone 탭 컨텍스트(탭바·더보기·라디얼)에서 일기 제외, en/ja 번역 39건
- 검증 인자: -diarySeed/-diaryOpenToday/-diaryPage N/-diaryShowExport/-diaryExportRun pdf|image

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 02:51:30 +09:00
songyc macbook
5c3017aeaa fix(l10n): complete ko/en/ja coverage — sync catalogs, translate 123 strings, localize stats titles and filter sheet
- 카탈로그 4종 xcstringstool sync + stale 키 64개 제거
- 버블 메뉴·위젯 5종·이미지 내보내기 신규 문자열 en/ja 전량 번역
- StatsTabView 카드 제목·RecordFilterSheet 제목이 String 파라미터라 미현지화되던 것을 LocalizedStringKey로 수정
- HistoryView 필터 칩 문자열 String(localized:) 누락 수정
- 위젯 익스텐션 InfoPlist.xcstrings 신설(표시 이름 현지화), iOS에 사진 권한 문구 ko/en/ja 추가, 워치 ja 앱 이름 'Haru Danim'으로 통일

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 02:24:28 +09:00
songyc macbook
cde4c4001b feat(export): add high-res image export of records and stats as report posters
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 02:05:16 +09:00
songyc macbook
b897db47db fix(widget): resolve white-on-white rendering bug in Liquid Glass and tinted themes
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 01:55:54 +09:00
songyc macbook
221dce1259 refactor(ui): redesign floating menu as one-hand glass bubble arc
The vertical capsule stack reached ~414pt up the screen, putting the top
tabs out of thumb range. Replace it with 'Glass Bubbles': liquid-glass
orbs that fan out from the FAB along a semicircular arc whose radius
(~152pt, clamped to screen width) keeps every tab inside the natural
thumb sweep from the bottom-center pivot.

- Each bubble: ultraThinMaterial circle + top white gradient (specular
  light) + white 0.4 hairline + soft shadow; label with shadow below.
  Current tab rendered as green glass with white symbol.
- Entrance: bubbles fly from the FAB to their arc slot while rotating
  -40deg -> 0 with a snappy spring (response 0.32, damping 0.72) and a
  per-item 0.03s stagger, like droplets bursting in sequence; collapse
  snaps back with no delay. All transform-only (offset/scale/opacity/
  rotation) so no per-frame blur recomputation.
- Struct renamed FloatingCapsuleMenu -> GlassBubbleMenu; storage key and
  NavStyle.radial rawValue unchanged for compatibility.
- Settings copy updated: nav style now '글래스 버블 메뉴' with a
  description of the fan-out and one-handed reach; order editor renamed
  '버블 메뉴 순서' with left-to-right arc wording.

Verified: clean rebuild (stale incremental link produced an old binary
despite BUILD SUCCEEDED; wiped DerivedData), expanded-arc screenshot
shows all 7 bubbles within thumb radius with legible labels, and the
settings screen shows the new name/description.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 01:31:45 +09:00
songyc macbook
49dd1d0ff2 feat(widgets): complete redesign of 5 widget types with modular Intents, fixed floating menu UI/Strings
Widget system rebuilt as 5 independent widgets, each with its own
WidgetConfigurationIntent registered in the WidgetBundle:

- ① ActionRun: multi-action selection, value display option (day/week/
  month/hidden), full tag-color cells; small 1 / medium 4 (2x2) / large
  4 big cells or 8 compact cells (2x4) via largeStyle. Unfilled slots
  render as dashed blank cells.
- ② GoalProgress: multi-goal selection; medium [2 goals | goal+top
  quests], large [4 goals | 2x goal+quests | 1 goal + all quests] via
  medium/largeStyle. New QuestBarRow / GoalWithQuestsCellView.
- ③ QuestRing: multi-quest selection + span; surface-card ring (tag
  color) with centered icon, name and "오늘 N%" text; interactive run
  via RunActionIntent; small 1 / medium 2 / large 4 (2x2).
- ④ QuestStatus (display-only): goal selection + span; rings with name
  only (no %), goal icon+name top-left; small 4 (2x2) / medium 8 (4x2) /
  large [16 (4x4) | 4 goals | 2 goals] via largeStyle.
- ⑤ StatsChart: multi-action selection capped per family (2/4/6) +
  3 span modes; same design scaled by family.

Common foundation:
- Theme option revived on every intent: match app / light fixed / dark
  fixed (WidgetThemeOption AppEnum) applied through widgetAppTheme(_:)
  with proper iOS 17 containerBackground; non-fullColor rendering modes
  (tinted/clear/accented) keep the Color.clear + semantic-color defense
  so content never goes white-on-white.
- Providers are family-aware (capacity by size) and slot-pad selections
  (WidgetStore.slots); defaults fill from existing data so fresh widgets
  never look empty.
- Interactive path unchanged: RunActionIntent(actionID:) +
  IntentStore.performWrite -> reloadAllTimelines.

Floating menu polish:
- All glass capsules now share a uniform width (longest label wins,
  locale-proof) for a tidy popped-up list.
- Settings strings updated: nav style renamed to '플로팅 글래스 캡슐'
  with description matching the capsule popup, order editor retitled
  '캡슐 메뉴 순서' with bottom-to-top wording.

Verified: BUILD SUCCEEDED (iPhone 17 Pro sim); widget preview harness
extended with per-variant scroll anchors and all 15 layout variants
screenshot-checked, including forced accented mode (legible translucent
cells) and blank-slot rendering; capsule menu and settings text
screenshot-checked.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 01:17:39 +09:00
songyc macbook
bd60697419 refactor(ui): redesign floating menu to detached glass capsules for better aesthetics and performance
Replace the single semicircle dome + arc-arranged icons (which distorted and
pushed labels outside the fan) with independent floating glass capsules that pop
up in a vertical stack from the FAB.

- Each tab is now a self-sizing Capsule: icon + label with generous padding so
  text can never overflow; ultraThinMaterial background, thin white translucent
  border (.stroke white 0.4), and a soft drop shadow for a floating glass look.
- Current tab gets a subtle green tint behind the content.
- Removed the heavy DomeShape glass fan and its scale/opacity blur transition
  plus all per-item arc math; appearance is driven purely by offset + scale +
  opacity under one snappy spring (response 0.3, damping 0.7), so no per-item
  blur passes and no progressive frame drops.
- Renamed RadialMenu -> FloatingCapsuleMenu; RadialTabView / settings keys /
  nav-style routing unchanged.

Verified: BUILD SUCCEEDED (iPhone 17 Pro sim) and expanded-state screenshot
confirms all 7 capsules render legibly with text fully inside each pill, no
distortion, current tab highlighted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-13 00:06:55 +09:00
songyc macbook
dd95dcd28f fix(widget): resolve invisible content bug by applying proper containerBackground and dynamic contrast for transparent/tinted themes
On a tinted/transparent (or iOS 18 accented) home screen, the Action Run
widget turned into a solid white block with invisible content, while the
other widgets rendered fine. Cause: only the Action Run cell paints an opaque
tag-color background (WidgetTintedCellBackground) with hardcoded
.foregroundStyle(.white) text on top. In any non-.fullColor rendering mode
the system re-interprets that opaque fill toward white and the white text
disappears — classic white-on-white. The other widgets already use the
adaptive AppTheme.surface plus semantic .primary/.secondary text, which is
why they were unaffected.

Fix, confined to ActionRunCellView:
- Read @Environment(\.widgetRenderingMode). In .fullColor keep the tag-color
  fill + white text (unchanged). In accented/vibrant, drop the opaque fill to
  a faint translucent tint and render content with the semantic .primary color
  so the system's tint keeps a clear foreground/background contrast.
- No hardcoded .white survives on the tinted path; the container background is
  still owned by widgetAppTheme()'s .containerBackground(for: .widget) (clear
  in non-fullColor so the system glass shows through), so this composes safely
  with iOS 18 home-screen customization.

The Live Activity's white-on-tint icon (HaruDanimWidgets) is intentionally
left as-is — Live Activities always render full color and aren't subject to
home-screen widgetRenderingMode.

Verified in the in-app widget preview with a new DEBUG affordance
(-widgetRenderMode accented|vibrant, added to WidgetPreviewScreen): forcing
accented mode, the Action Run cells now show legible dark icons/labels/values
on a faint tint instead of blank white. Build succeeds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-12 23:35:09 +09:00
songyc macbook
dc9434a241 refactor(ui): aggressively prune widget config parameters and optimize floating menu performance/glass styling
Widget configuration — delete the multi-slot pickers entirely
--------------------------------------------------------------
Every home/lock widget exposed 3–4 separate entity slots (action1–4,
goal1–4, quest1–4, secondGoal) regardless of family, cluttering the
long-press config screen with slots a given size never renders. Replaced
that with a single anchor selection per widget that self-configures by size
family (the view already prefixes by family; providers now build the
candidate list from one selection):

- ActionRun (A): one anchor Action → fills following actions (main-tab order)
  up to the family's count.
- GoalBars (B): one anchor Goal → fills following goals up to the count.
- QuestRing (C): one Goal → renders that goal's quests, sized by family.
- GoalQuestGrid (D): one Goal (dropped secondGoal) → its quests; large now
  shows up to 16 rings of the single goal.
- Stats: one anchor Action → plus following actions on the same chart.

New WidgetStore helpers actionsFrom/goalsFrom/questsOfGoal centralize the
"anchor + following / goal's quests" logic; the None sentinel is ignored
gracefully so clearing falls back to sensible defaults. No @Model, timeline,
or reload/sync logic touched.

Floating radial menu (iPhone) — performance + glassmorphism
-----------------------------------------------------------
Each of the ~7 icons and ~7 labels had its own .ultraThinMaterial, so the
expanded menu ran ~15 separate blur passes that re-rendered every frame as
content scrolled behind them — the source of the progressive frame drops.
Now a single unified glass dome (one DomeShape filled with .ultraThinMaterial)
sits behind the whole fan and is the only blur; icons are lightweight symbols
(only the current tab gets a solid green chip) and labels are plain text with
a legibility shadow. The dome is styled per glassmorphism: a soft white
top-to-bottom gradient overlay, a thin white.opacity(0.2) border, and a soft
shadow; the FAB gets the same gradient/border treatment.

Animation is snappier and lighter: the per-item implicit spring animations
with staggered delays are gone — one withAnimation(.easeOut ~0.24 open /
.easeIn ~0.18 close) drives the whole fan, and the press style uses a short
easeOut instead of a spring.

Verified in the simulator: build succeeds; the expanded menu renders the
single glass dome with all tabs and no crash (light/dark, expanded/collapsed);
the refactored C/D widgets render from a single selected goal, self-sized by
family.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-12 23:26:59 +09:00
songyc macbook
4a03868163 fix(goals): cap daily progress contributions to 100% to prevent weekly and monthly progress distortion
Weekly/monthly progress for a daily quest summed each day's raw value into
the numerator, so one over-achieving day leaked its overage into the span
total. Example: two daily quests with a 5-minute target; on one day quest A
did 5 min (100%) and quest B did 10 min (200%). Both are "done" for that day,
but the weekly ratio counted quest B as 600s vs quest A's 300s — B carried
double the weight, and its over-achievement masked other days' shortfalls and
could push per-quest weekly display past 100%.

spanProgress now aggregates a daily 'atLeast' quest's week/month value as the
sum of per-day values each capped at that day's target (min(dayValue, target)),
via a new private spanValue(for:range:now:). This equals averaging per-day
capped ratios, so a single 200% day contributes exactly one day's worth — no
day can cover for another, and per-quest weekly/monthly display never exceeds
100%. Scope is deliberately narrow:

- day span is untouched — a single day still shows 200% over-achievement.
- 'atMost' (stay-under) quests keep their existing per-day 100%/0% logic;
  capping the value would make them always pass, so they use the raw value
  unchanged — the two rules coexist without contradiction.
- weekly/monthly/custom quests accumulate freely within their period, so no
  per-day cap applies to them.

Only the progress calculation changed; raw records are never altered, so the
records/stats screens still show the true 10-minute total.

Verified in the simulator with a temporary isolated in-memory self-test
(removed before commit): quest A (100%) and quest B (200%) produce identical
weekly ratios (0.1429 each), quest B's weekly display stays <= 100%, and its
day-span display remains 200%. Build succeeds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-12 22:38:35 +09:00
songyc macbook
c7024ea612 fix(widget): fix AppIntent button interactions and streamline widget configuration options while preserving real-time sync
Two independent widget issues, both confined to the widget/intent layer.
The app→widget real-time sync path (IntentStore.refresh + performWrite +
reloadAllTimelines) is intentionally left untouched.

1. Interactive button taps were unreliable ("touch feedback but nothing
   changes; works only after several taps"). The run buttons passed a full
   AppEntity parameter — RunActionIntent(action: cell.entity). On every tap
   WidgetKit re-resolves that AppEntity through ActionEntityQuery.entities(for:),
   which opens a brand-new ModelContainer from disk each time. That heavy
   entity-resolution round-trip on the tap hot path is the classic cause of
   dropped/laggy interactive-widget taps — the write logic itself was already
   proven reliable last session. Replaced RunActionIntent's AppEntity parameter
   with a plain UUID String (actionID) that deserializes instantly with no
   query; perform() looks the action up inside the existing single-context
   performWrite path. One tap now fires immediately. RunActionIntent is
   widget-only (not a Siri AppShortcut), so this touches no shortcut. Both call
   sites (ActionRunCellView, QuestRingActionCellView) updated; the now-unused
   ActionCellSnapshot.entity helper removed.

2. Configuration screens were cluttered — every widget exposed action/goal/
   quest slots 1–4 regardless of family, so a Small widget's long-press config
   listed slots it never renders. Added a parameterSummary to all six
   WidgetConfigurationIntents so the config reads as one clean sentence, and
   labeled secondary slots with the size that uses them (e.g. "행동 2 (중형·대형)",
   "목표 3 (대형)"). WidgetKit can't hide parameters by family, so this makes the
   optional slots self-explanatory instead of noise.

Verified: BUILD SUCCEEDED (app + embedded Haru_DanimWidgets.appex); cold launch
with the widget preview renders without crashing. Physical home/lock-screen
button taps still warrant a real-device pass (the simulator can't tap widget
overlays).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-12 21:50:27 +09:00
songyc macbook
32306510c8 fix(widget): make interactive AppIntent writes reliable and crash-proof
Interactive home/lock-screen widget buttons (count +1, timer start/stop)
gave touch feedback but often didn't persist, only working after several
rapid taps. Two compounding defects in the widget-extension write path:

- The extension could crash mid-write. refresh()/makeContainer() opens a
  new ModelContainer on every intent perform and every timeline read; in
  the extension that hit a local-store branch whose failure was fatalError.
  A transient App Group store contention with the running main app crashed
  the whole appex — the tap looked dead and only a later retry won the race.
  Split makeContainer into a throwing makeContainerThrowing(); refresh() now
  wraps it in try? and keeps the previous container on failure, so the
  extension degrades instead of crashing. The app's DataStore.shared keeps
  its crash-on-failure semantics.

- Fetch → mutate → save could span different contexts and save errors were
  swallowed. IntentStore.context is a computed container.mainContext, and
  commit() re-read it for a try? save(), so a mid-flight container swap left
  the mutation on a dead context and any save failure vanished silently.
  Added IntentStore.performWrite(_:) which captures one context for the whole
  fetch+mutate+save, saves with a retry (no silent try?), then fires the
  Live Activity hook and reloadAllTimelines. All four mutating intents
  (RunAction/StartTime/StopTime/AddCount) now fetch in that single context.

No @Model or main-app logic changed — fix is confined to the widget/intent
data layer. Verified: 5 rapid RunActionIntent.perform() calls persist +5 with
no lost writes across three cold launches (ok=true each). Build succeeds
(app + embedded Haru_DanimWidgets.appex).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-12 11:01:47 +09:00
songyc macbook
a02e06a9df feat: add reordering to floating radial menu and fix weekly timetable step
Radial menu icon order (iPhone floating nav only):
- Settings → 내비게이션 gains a '반원 메뉴 순서 편집' link (shown only when the
  radial style is selected) that opens a drag-to-reorder list of all 7 tabs.
- Order is stored device-local via @AppStorage(SettingsKeys.radialTabOrder)
  (not CloudKit-synced); RadialMenu reads it through AppTab.radialOrder(from:),
  which always yields the full tab set and appends any missing/unknown value in
  default spec order — so partial or legacy strings can never drop a tab.
- iPad/Mac sidebars and the watch target are untouched (they never read the key).

History timetable date step:
- The date header </> buttons now jump by a full week (7 days) when the
  timetable is in '일주일' view, and by 1 day otherwise (list / daily timetable).
- The calendar popover and '오늘' still set the date directly, so those paths and
  record loading are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-12 09:13:20 +09:00
songyc macbook
8f2b5fab93 feat(nav): add experimental iPhone radial floating navigation menu
Adds an alternative iPhone navigation style, switchable in Settings, that
replaces the bottom tab bar with a single center floating button which fans
all tabs out into an upper 180° semicircle.

- Settings → 내비게이션: pick 기본 하단 탭 바 / 중앙 플로팅 반원 메뉴.
  Stored device-local via @AppStorage(SettingsKeys.navStyle) (not synced);
  the 탭바 구성 section auto-hides in radial mode since all tabs are shown.
- iPhone (Compact) only: RootNavigationView branches on NavStyle. iPad/Mac
  sidebar and the watch target are untouched.
- Radial menu (RadialNavigationView): glass FAB at bottom-center over a
  tab-bar-hidden TabView (per-tab navigation state preserved). Tapping the
  FAB blooms all 7 tabs into an upper 180° arc with per-icon name labels;
  tapping an icon routes immediately and closes; tapping the FAB or the
  dimming scrim closes. Current tab is highlighted.
- Drops the 더보기 indirection in radial mode via AppRouter.allTabsDirect,
  so cross-tab routing (openHistory/openStats) selects tabs directly.
- Liquid Glass (.ultraThinMaterial) styling on the FAB, icons and labels.
- Overlay is kept top-most in the ZStack with hit-testing gated to the
  expanded state, so scrolling grid content can never intercept the button.
- DEBUG: -navStyle radial / -radialExpanded YES launch args for simulator
  verification (no way to tap widgets/overlays via simctl).

Scope: iPhone navigation only; no changes to iPad/Mac sidebar, the watch
app, core data/models, or widget code.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-12 09:01:50 +09:00
songyc macbook
f7ddd42260 refactor(widget): complete widget architecture overhaul for reliable interactions and removed bloated configurations
- IntentStore now recreates the ModelContainer on demand in the widget
  extension (IntentStore.refresh()), called before every AppIntent
  perform() and every provider's makeEntry(). Fixes stale/blank widget
  buttons and double-toggle bugs caused by the extension process
  reading an old in-memory snapshot after CloudKit remote changes.
- Replaced the fixed 15/30-minute polling Timeline policy (which burns
  WidgetKit's daily reload budget across 6 widget kinds) with
  WidgetRefresh.timeline: idle widgets wait until the next logical-day
  boundary since value changes are already pushed via
  reloadAllTimelines, while widgets showing a live ratio (a currently
  running quest/goal) get pre-computed future entries so progress
  rings and bars keep advancing without extra reloads.
- Removed the per-widget "위젯 테마" (match app / light / dark / liquid
  glass) configuration option from all 5 home-screen widgets. It
  duplicated the same enum five times, and the liquid-glass variant's
  custom background/scheme override fought the system's own
  vibrant/tinted rendering in tinted Home Screens, which was part of
  the "블랙 화면"/렌더링 오류 reports. Widgets now simply follow the
  app's own theme setting (WidgetAppearance.appScheme), which is what
  "앱 테마와 일치" already defaulted to.
- Added shared gallery/placeholder sample data (ActionCellSnapshot,
  QuestCellSnapshot, GoalSnapshot, StatsChart sample points) so the
  widget gallery and redacted placeholders show a realistic shape
  instead of an empty/blank card.
- Unified empty-state UI via WidgetEmptyView across all 5 widgets.

Scope: Widgets/*, Shared/HaruDanimIntents.swift (widget-facing
IntentStore only), IOS/Views/WidgetPreviewScreen.swift (DEBUG preview
tool). No changes to app core data models, sync, or view logic.
2026-07-11 22:41:54 +09:00
songyc macbook
fbd4810628 fix: resolve widget sync crashes via local AppGroup container and improve goal widget to show all sub-goals
위젯 동기화 크래시 수정:
- DataStore: CloudKit 미러링을 메인 앱 프로세스 전담으로 분리.
  위젯 등 앱 확장(.appex)은 CloudKit 엔타이틀먼트가 없고 메모리·수명
  제약으로 미러링 설정이 크래시/행을 유발하므로, 확장 프로세스는 항상
  같은 App Group 스토어 파일을 로컬 전용(cloudKitDatabase: .none)으로
  연다. 확장이 쓴 변경은 메인 앱이 히스토리로 집어 CloudKit에 내보냄
- CloudSyncMonitor 신설: 메인 앱이 NSPersistentStoreRemoteChange
  (다른 기기의 CloudKit 가져오기·위젯 인텐트의 쓰기)를 디바운스로 받아
  라이브 액티비티 → 위젯 타임라인 → 워치 스냅숏 순으로 즉시 갱신
- 인터랙티브 위젯(AppIntent)의 컨테이너 매핑 점검: IntentStore.commit이
  저장 직후 reloadAllTimelines를 이미 보장함을 확인·문서화

모음 탭 목표 진행 현황 카드 개선:
- '다짐별로 보기'의 상위 3개 제한 제거 — 모든 다짐 확인 가능
- 화면 균형을 위해 접힘/펼침 UX 적용: iPhone은 3개, iPad는 6개까지
  접힌 상태로 보여주고 '다짐 N개 더 보기' 버튼으로 카드 안에서 펼침
  (카드 탭 이동과 충돌하지 않는 독립 버튼, 스냅 애니메이션)
- DebugSeed: 접힘/펼침 검증용 4번째 다짐 추가, -expandGoalCard 인자 추가

검증: iPhone 17 Pro·iPad Pro 11 시뮬레이터에서 접힘/펼침 렌더링과
사이드바 레이아웃 확인, 전체 타깃(iOS·워치·위젯) 빌드 성공

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-11 21:35:20 +09:00
songyc macbook
3dd53c4fc6 refactor: decouple all UI preferences and layout settings from CloudKit sync to local device storage
- Add Shared/LocalPrefs.swift: device-local UI preferences stored in App Group
  UserDefaults (never synced by iCloud), read identically by app, widgets,
  watch snapshot, and App Intents on the same device
- Move to local storage: main-tab action button order (UUID array), pinned
  goal-progress cards on the main tab, goal quest-list collapse state, and
  per-action memo-prompt popup — reordering or repinning on iPhone no longer
  touches the iPad and vice versa
- Defense logic: actions not in the local order list (e.g. newly received via
  CloudKit from another device) are automatically appended to the end, sorted
  by legacy sortOrder then creation date
- One-time migration adopts existing @Model values (sortOrder, showsOnMain,
  isCollapsed, promptsForNote) into local prefs at launch; model properties
  stay in place so the CloudKit schema is untouched and records keep syncing
- Data remains fully synced: actions, goals, quests, sessions, count entries,
  statistics, and goal/tag/quest list order are untouched by this change
- Verified in simulator: legacy data migrates losslessly, grid renders from
  local order, unknown action lands at the end; iOS+watch+widgets build clean

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-11 20:58:34 +09:00
songyc macbook
9e81cd3d03 feat: auto-adapt layout for iPadOS/macOS with NavigationSplitView and hide irrelevant settings
- Add SidebarRootView: iPad/Mac (Designed for iPad) uses a NavigationSplitView
  sidebar listing all 7 tabs, sharing every existing screen (rootView) untouched
- Branch by device idiom (DeviceLayout.isPad) so iPhone keeps its tab bar UI,
  logic, and Live Activity behavior pixel-for-pixel, including landscape
- AppRouter gains sidebar mode: cross-tab routing (기록 확인/통계 보기) selects
  the target tab directly instead of detouring through the More tab
- Hide iPhone-only settings on iPad/Mac: 탭바 구성 section and the main grid's
  per-row column picker are meaningless with a sidebar/adaptive grid
- Main tab wide-screen layout: pinned goal cards flow in an adaptive grid
  (340-520pt cards) instead of full-width paging; action buttons auto-fill
  columns at 150-220pt so nothing stretches on large displays
- Verified on iPad Pro 11" simulator (sidebar, settings, stats routing,
  timetable) and iPhone 17 Pro (unchanged tab bar); both build clean

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-11 09:28:06 +09:00
songyc macbook
2567b53b71 fix: correct daily goal progress calculation and optimize widget refresh logic
- 목표 하루 진행률은 오늘이 수행일인 다짐만 모수로 계산 (요일·날짜 지정으로
  오늘 쉬는 다짐은 분모에서 제외, QuestProgress.isScheduled 신설)
- 오늘 수행할 다짐이 하나도 없는 날은 0% 대신 100%(완료 상태)로 표기
- 주간/월간 진행률은 기존대로 전체 다짐 기준 유지
- 이벤트 기반 위젯 새로 고침: 앱 내 모든 핵심 Mutation(측정 시작/종료,
  횟수 추가, 기록 추가·수정·삭제, 행동·꼬리표·목표·다짐 CRUD, 순서 변경,
  목표 상태 변경)의 끝단에서 DataChange.commit() 호출 — 저장 즉시 확정 후
  Live Activity·위젯 타임라인·워치 스냅숏을 한 번에 동기화
- 주기적 백그라운드 새로 고침 없이 변경 시점에만 갱신해 WidgetKit
  업데이트 예산 절약 (타이머 표시는 기존 Text(style: .timer)로 시스템이
  자체 렌더링하므로 타임라인 소모 없음)
- 하루 시작 시간·주 시작 요일 설정 변경 시에도 위젯 집계 갱신
- 시뮬레이터 검증: 월·수·금 다짐이 있는 목표의 토요일 하루 진행률이
  33%가 아닌 50%(오늘 수행 다짐 2개 평균)로 표시됨 확인

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-11 08:30:12 +09:00
songyc macbook
081c388f00 fix: 워치-라이브 액티비티 동기화, 앱 이름 띄어쓰기, 차트 라벨 겹침, 위젯 '선택 안 함' 옵션
라이브 액티비티(다이내믹 아일랜드) 워치 연동:
- 워치 실행 명령이 세션 활성화 전이거나 sendMessage 실패 시 유실되던 문제를
  transferUserInfo 큐 폴백으로 보강 (iPhone didReceiveUserInfo 수신 처리 추가)
- 큐로 지연 도착한 실행 명령이 엉뚱한 토글을 일으키지 않도록 5분 유효 시간 부여
- 백그라운드에서 Live Activity 시작이 거부되면 pendingStartRetry로 기억했다가
  앱 포그라운드 복귀(scenePhase active) 시 재시도 + DB 상태 재동기화
- WatchSync 진단용 os_log(notice) 추가, DEBUG -autoRunFirstAction 검증 훅 추가
- 시뮬레이터 검증: 워치 시작 → 아이폰 세션 시작 + 다이내믹 아일랜드 생성,
  워치 종료 → 세션 종료 + 아일랜드 소멸 확인 (큐 폴백 채널은 시뮬레이터
  transferUserInfo 한계로 실기기 확인 필요)

앱 이름 전역 띄어쓰기 ('하루다님' → '하루 다님'):
- pbxproj CFBundleDisplayName 6곳, InfoPlist.xcstrings 3개(ko), 스플래시(ContentView),
  워치 내비게이션 타이틀, 컴플리케이션 문구, 문자열 카탈로그 키 일괄 변경
- 워치 카탈로그 영문 값 HaruDanim → Haru Danim 통일

통계 위젯 X축 라벨 겹침 방지:
- 날짜 축 포인트가 7개를 초과하면(한 달·일별 등) 라벨을 숨기고 눈금만 표시

위젯 설정 '선택 안 함' 옵션:
- Action/Goal/Quest 엔티티에 zero-UUID 센티널 추가, 제안 목록 최상단 노출
- 2·3번째 슬롯 선택을 되돌릴 수 있고, 위젯 조회 단계에서 자연스럽게 걸러짐
- ko/en/ja 번역 추가 (None / 選択しない)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-10 21:47:42 +09:00
songyc macbook
bc6cd010b7 fix(widgets): 리퀴드 글라스 테마 시인성 개선 및 틴트/클리어 렌더링 모드 대응
- 글라스 테마: 틴트 셀 불투명도 0.5→0.72, 테두리 강화로 흰 텍스트 대비 확보
- 글라스 테마 전용 텍스트 그림자(glassTextShadow) 추가 — 홈 위젯 5종 셀 콘텐츠에 적용
- widgetRenderingMode != .fullColor(홈 화면 틴트/클리어)일 때 커스텀 배경·강제
  컬러 스킴을 끄고 시스템 바이브런트 렌더링에 맡기도록 분기
- 잠금화면 위젯: AccessoryWidgetBackground + widgetAccentable + .primary 전경색으로
  비브런트/악센트 렌더링에서 시인성 확보
- 미리보기 하네스: 글라스 테마가 시스템 컬러 스킴을 따르도록 수정

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-10 21:18:43 +09:00
songyc macbook
bef503465c chore(i18n): 문자열 카탈로그를 Xcode 표준 포맷으로 재직렬화
빌드 과정에서 Xcode가 정규화한 포맷 반영 (내용 변화 없음,
DEBUG 전용 문자열 2건 stale 표기 유지)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-10 15:43:42 +09:00
songyc macbook
c57c39002b feat(widgets): 홈 위젯 풀블리드 적용 및 다짐 현황 간격 개선
- 홈 위젯 5종에 contentMarginsDisabled() 적용: 시스템 기본 베젤 제거,
  여백은 각 뷰가 직접 관리 (다중 셀 10pt, 콘텐츠형 13pt)
- 소형(단일 셀) 위젯은 셀이 위젯 전체를 여백 없이 채움 — 셀 배경/테두리가
  ContainerRelativeShape로 위젯 모서리에 정확히 맞음 (A·B·C 위젯)
- 다짐 현황(D) 위젯 간격 개선: 링 그리드 행 간격 6→12, 열 간격 4→8,
  섹션 간격 6→10, 대형 목표 2개 간격 10→16, 링-이름 간격 확대
- 미리보기 하니스도 여백 제거를 반영해 실제 렌더링과 일치

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-10 15:42:37 +09:00
songyc macbook
c0c59af0ac fix(watch): 컴플리케이션 페이스 목록 미노출 수정 + 워치 앱 이름 다국어화
- 컴플리케이션이 페이스 편집 목록에 아예 안 나오던 원인: 추천 구성
  (recommendations)의 description에 문자열 보간을 사용해 WidgetKit이
  "Formatted text for AppIntentRecommendation is not supported" assertion으로
  익스텐션을 즉사시킴 → 시스템이 위젯 목록 조회 자체를 실패
  → Text(verbatim:)으로 교체. 시뮬레이터 chronod 로그로 3종 kind
  (HaruWatchStatus/GoalRate/QuestRate) 정상 등록 확인 (크래시 0)
- 추천 구성을 캐시된 목표/다짐 기반으로 개선 (모든 파라미터 채움)
- 워치 앱·워치 위젯 익스텐션 표시 이름 현지화: InfoPlist.xcstrings 추가
  (ko 하루다님 / en Haru Danim / ja ハルダニム)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-10 15:42:37 +09:00
songyc macbook
a6c040cd6a feat(i18n): 전체 UI 다국어 지원 (한국어·영어·일본어)
- 언어 설정 실적용: 변경 시 AppleLanguages 오버라이드 저장, 재시작 안내 footer
- 자동 추출되지 않던 일반 문자열(탭 이름, 픽커 라벨, 상태 뱃지, 포매터,
  삼항 문구, 심볼 카테고리 등)을 String(localized:)로 전환
- 위젯·워치 앱·컴플리케이션 타깃에 Localizable.xcstrings 신설
  (중복 리소스 방지 pbxproj 예외 추가), xcstringstool sync로 키 추출
- 4개 카탈로그 전체 키(고유 430개)에 en/ja 번역 주입
- 영어의 긴 횟수 단위("times")가 잘리지 않도록 모음 탭 셀 축소 허용
- 검증: -AppleLanguages "(en)"/"(ja)"로 모음 탭·설정 화면 확인

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-10 15:03:24 +09:00
songyc macbook
1e62c082d3 feat(premium): StoreKit 2 결제 레이어 구현 (실결제 연결 직전 단계)
- StoreKitEntitlementProvider: Transaction.currentEntitlements 기반 실권한
  조회 + Transaction.updates 이벤트 반영. 앱 시작 시 PremiumManager.bootstrap
  으로 주입 (확장 타깃은 기존 PremiumGate 캐시 그대로)
- PremiumStore: 상품 로드/구매/복원 (월·년 구독 + 평생 이용권 3종)
- PremiumView: 실구매 UI (상품 목록·가격·구매 복원, 미로드 시 재시도 폴백)
- DEBUG 강제 프리미엄(-premium, 테스트 토글) 중에는 StoreKit 결과가
  덮어쓰지 않도록 보호
- HaruDanim.storekit 로컬 테스트 구성(월 5,000 / 년 50,000 / 일회성 80,000원,
  ko·en·ja 현지화) + 메인 스킴에 연결 → Xcode 실행으로 결제 흐름 테스트 가능
- 남은 것: App Store Connect에 productID 3종 등록만 하면 실결제 연결 완료

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-10 14:49:32 +09:00
songyc macbook
0cdb566ad5 fix(watch): 컴플리케이션이 워치 앱을 열지 않아도 갱신되도록 수정
- 워치 앱 시작(App.init) 시점에 WCSession 활성화: 백그라운드로 깨어나
  스냅숏을 받을 수 있도록 델리게이트를 미리 설정
- iPhone이 transferCurrentComplicationUserInfo 전용 채널로도 스냅숏 전송
  (전송 예산 고려: 측정 상태 변화 또는 30분 경과 시에만)
- 워치 didReceiveUserInfo 수신 → 캐시 저장 + 컴플리케이션 타임라인 갱신
- ComplicationStore.defaults 단일 인스턴스화

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-10 14:44:48 +09:00
songyc macbook
e5550eb106 fix(theme): 테마 즉시 적용 및 위젯 테마 실적용 + 리퀴드 글라스 구현
- AppGroup.defaults를 단일 인스턴스(static let)로 변경: 접근마다 새
  UserDefaults를 만들어 @AppStorage(store:) 관찰이 끊기던 문제 수정
  → 설정에서 테마를 바꾸면 재시작 없이 즉시 적용됨
- 위젯 containerBackground에 강제 컬러 스킴을 직접 지정: 시스템이
  배경을 위젯 환경으로 그려 라이트/다크 고정·앱 테마 일치가 실제
  위젯에서 무시되던 문제 수정
- 테마 변경 시 위젯 타임라인 갱신 (앱 테마와 일치 옵션 즉시 반영)
- 리퀴드 글라스 실구현: 머티리얼+하이라이트 배경(GlassWidgetBackground),
  유리 테두리 셀(WidgetCellBackground), 색이 비치는 틴트 셀
  (WidgetTintedCellBackground, 행동 실행 위젯 적용)
- 검증 인자 -themeAutoToggle YES 추가 (3초 후 테마 반전)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-10 14:44:36 +09:00
songyc macbook
828cf2e906 feat(watch): 워치 앱 아이콘 추가 (메인 앱 아이콘 재사용)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-10 09:19:17 +09:00
songyc macbook
9bf617f3e5 feat(widgets): 위젯 테마 옵션 추가 — 앱 테마 일치/라이트·다크 고정/리퀴드 글라스 (CLAUDE.md §8.1)
- 홈 위젯 5종(행동 실행/목표 진행률/다짐 진행률/다짐 현황/통계) 설정에
  '위젯 테마' 옵션 추가
- 앱 테마와 일치: 앱의 라이트/다크 설정을 따름 (테마 키를 App Group으로 이관해
  위젯이 읽을 수 있게 함)
- 라이트/다크 고정: 컬러 스킴 강제 + 해당 모드 팔레트
- 리퀴드 글라스: 반투명 머티리얼 배경 + 표면 셀도 머티리얼로 변형해
  유리 질감 위에서 가독성 유지
- DEBUG 미리보기가 테마를 재현하도록 개선 (-widgetTheme light|dark|glass,
  글라스는 그라데이션 배경 위에 렌더링)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-10 09:14:31 +09:00
songyc macbook
8e69bbb25f chore: 스펙 문서·로컬라이제이션 갱신 및 xcuserdata를 gitignore로 이동
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-10 09:04:33 +09:00
songyc macbook
1758cc3a25 feat(complications): 워치 페이스 컴플리케이션 3종 (CLAUDE.md §9.2)
- 현재 현황: 측정 중 대표 행동 + 실시간 타이머 + 추가 측정 +N(없으면 생략),
  미측정 시 '실행 중 아님'
- 목표 달성률: 목표·기간(하루/주간/월간) 선택, 원형 게이지/진행바/인라인
- 다짐 달성률: rectangular는 세 기간 모두 표기, '이하 유지(한도형)' 다짐은
  게이지 대신 한도 안/초과 상태로 별도 표현
- accessoryCircular/Rectangular/Inline/Corner 지원 + 페이스 갤러리 추천(recommendations)
- 워치 앱이 캐시한 스냅숏 사용, 갱신 시 reloadTimelines로 실시간 리프레시
- pbxproj: WatchShared 3개 타깃 연결, 위젯·컴플리케이션 뷰를 앱 타깃에도 포함
  (DEBUG 미리보기용, @main 제외), WATCHOS_DEPLOYMENT_TARGET 26.0
- DEBUG 미리보기: -complicationPreview YES, -complicationScroll YES

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-10 09:00:15 +09:00
songyc macbook
f9f1898d15 feat(watch): 애플워치 앱 + WCSession 실시간 연동 (CLAUDE.md §9.1)
- WatchShared/WatchPayload: iOS·워치·워치위젯 3개 타깃이 공유하는 Codable 스냅숏
  (꼬리표/행동/목표·다짐 진행률/측정 중 대표 행동 + 추가 개수/프리미엄)
- iOS WatchSyncManager: 워치의 실행·새로 고침 명령 처리(ActionRunner 공용) 후
  Live Activity·홈 위젯 갱신, 데이터 변경 시 updateApplicationContext로 푸시
- 워치 앱: 꼬리표 목록 → 행동 목록 → 탭으로 실행(시간형 토글/횟수형 +1),
  측정 중 실시간 타이머, 대표 행동 표시는 다이나믹 아일랜드 설정과 동일 기준
- 미결제 시 프리미엄 잠금, iPhone 연결 불가 안내
- 수신 스냅숏을 워치 App Group에 캐시 (컴플리케이션용)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-10 09:00:01 +09:00
songyc macbook
ed8b52792a feat(widgets): 홈/잠금화면 위젯 6종 + Interactive Widgets (CLAUDE.md §8)
- 행동 실행(A): 소형1/중형3/대형4칸, Button(intent:)로 앱 없이 즉시 실행,
  측정 중 실시간 타이머 + 노란 테두리, 표시 기간(오늘/이번 주/이번 달) 옵션
- 목표 진행률(B): 하루/주간/월간 가로 진행바, 소형1/중형2/대형4
- 다짐 진행률(C): 원형 링 + 기간 옵션, 단일 행동 다짐은 눌러서 실행,
  '이하 유지'는 한도 지킴/초과로 표현
- 다짐 현황(D): 목표명 + 원형 링 그리드 (소형 2x2 / 중형 최대 8 / 대형 목표 2개)
- 통계: 행동 최대 3개 꺾은선 그래프, 앱 통계 탭과 동일 산식·색,
  소형은 '한 달 일별' 제외 (스펙 §8.3)
- 잠금화면: circular/rectangular/inline, 표시 방식 3종(게이지/숫자/다짐 점)
- 전 위젯 프리미엄 게이트 (미결제 시 잠금 안내)
- DEBUG 미리보기 화면: -widgetPreview YES|lock, -widgetPreviewScroll B|C|D|S

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-10 08:59:47 +09:00