Skip to content

feat: Hearth theme restyle - #49

Merged
bgultekin merged 22 commits into
mainfrom
hearth-restyle
May 11, 2026
Merged

bgultekin merged 22 commits into
mainfrom
hearth-restyle

Conversation

@bgultekin

@bgultekin bgultekin commented May 11, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Introduces the Hearth theme alongside Dark and Light themes, replacing the previous auto mode
  • Adds an animated EmberDot status indicator with glow, ring for marked terminals, and diagonal slash for done state
  • Applies Hearth colors throughout the sidebar, terminal pane (warm gradient header, deep background), group cards, and settings window
  • Replaces the app wordmark with an Instrument Serif title card featuring a flame icon
  • Moves fonts to assets/fonts/ as the canonical source, synced to Swift package resources at build time via a sync-assets Makefile target

Test plan

  • Toggle between Dark, Light, and Hearth themes in settings and verify correct colors in all surfaces
  • Verify EmberDot animates correctly for working/needs_input/done states, shows ring for marked terminals, and slash for done
  • Confirm make build, make run, and make build-wrapped all sync fonts before compiling
  • Confirm a fresh clone + make build picks up fonts from assets/fonts/ correctly
  • Check sidebar cards, terminal pane header, and settings window render correctly in both light and dark system appearance with each theme

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • New "Hearth" appearance mode and themed visual system
    • Animated ember-style session status indicators
  • Improvements

    • Revamped card and terminal styling with hearth visual effects
    • Updated typography and icon/header designs for sidebar and lists
    • Adjusted panel sizing and row layouts for improved spacing and readability
    • Settings and about screens themed to match Hearth visuals
  • Chores

    • Fonts synchronized into app resources and registered at startup
    • Build scripts/Makefile updated to include font sync; fonts ignored in VCS

Review Change Stack

bgultekin and others added 20 commits May 9, 2026 23:20
…bar margins

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…and row glow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use a tighter shadowRadius (10) on the terminal pane hearthCard so the
shadow fades out within the available clearance; expose shadowRadius as a
hearthCard parameter (default 25) so other cards are unaffected. Also fix
SessionListView trailing/top insets that were set to hardcoded values from
a previous iteration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the left-edge vertical bar with a thin circular ring around
the status dot. The ring uses the same status color/glow and the dot
container is fixed-size so centers stay aligned across all rows.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lower the default shadowRadius (25 → 15) and shadow opacity (0.55 → 0.30)
on hearthCard so the drop shadow is less heavy. Pass shadowRadius: 10 on
sidebar cards (app header, group card, group drag preview, UpdateAvailableBox,
HookSetupBox) and bump their surrounding padding from 8pt to 14pt so the
halo fades within the available clearance instead of being clipped by the
panel edge and ScrollView bounds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces full Dark and Light theme support by making all OcakTheme
colors dynamic based on AppearanceConfigStore mode. Replaces the
previous auto/system mode with Hearth as the default, adds
visualEffectAppearance routing so HearthCard adapts across themes,
and pins the selection ring to the ember color regardless of theme.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bump terminal pane top padding from 8→12 to match the session list's
top inset so both cards share the same horizontal baseline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the native macOS sidebar with a custom warm-dark sidebar when
Hearth is active, and applies OcakTheme background and text colors
throughout all settings tabs so the settings window matches the drawer's
Hearth aesthetic.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fonts are now canonical in assets/fonts/ (alongside images in assets/images/).
A sync-assets Makefile target copies them to macos/Sources/Ocak/Resources/Fonts/
before every build, run, and build-wrapped invocation. Resources/Fonts/ is
gitignored since it is now a generated directory.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 11, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR adds a Hearth appearance mode, refactors OcakTheme into Hearth-aware computed colors, introduces HearthCard and EmberDot UI primitives, updates many views to use the new theme and typography, and adds a font asset pipeline with build-time syncing and runtime font registration.

Changes

Hearth Theme System & UI Component Integration

Layer / File(s) Summary
Appearance Model & Config
macos/Sources/Ocak/Models/AppearanceConfigStore.swift, macos/Sources/Ocak/App/AppDelegate.swift
AppearanceMode replaces .auto with .hearth; AppearanceConfigStore defaults to .hearth and simplifies effectiveMode to a direct passthrough; AppDelegate.resolvedAppearance() treats .hearth like .dark.
Theme Color System Expansion
macos/Sources/Ocak/Theme/Colors.swift
Adds mode/terminalMode plumbing and isHearth/isDark/isLight; converts many fixed colors into Hearth-aware computed properties (card/terminal surfaces, text hierarchy, accent/status palette, selection/glow, header overlay, visualEffectAppearance) and updates statusColor(for:) accordingly.
HearthCard Styled Card
macos/Sources/Ocak/Theme/HearthCard.swift
Adds ThemedVisualEffectView (NSVisualEffectView wrapper) and HearthCardModifier plus hearthCard(...) extension to render themed blur + background, rounded clipping, adaptive shadow, and gradient stroke highlight.
EmberDot Status Component
macos/Sources/Ocak/Views/EmberDot.swift
New animated two-circle status indicator mapping SessionStatus to base/glow colors and opacity; supports repeating animations for certain statuses and respects accessibilityReduceMotion.
Layout & Sizing Constants
macos/Sources/Ocak/Models/PanelSizeStore.swift, macos/Sources/Ocak/Views/DrawerView.swift
Adds sessionListLeadingPad and updates terminalChrome/sessionListChrome; adjusts DrawerView resize handle padding to use the new leading pad depending on panel edge.
Session List Row Styling
macos/Sources/Ocak/Views/SessionListRowView.swift
Increases approximateRowHeight to 38, adds accessibilityReduceMotion to skip typing animation, replaces old status dot with EmberDot, refactors status badge typography/styling, and simplifies row background/selection using theme tokens.
Session List Sidebar Redesign
macos/Sources/Ocak/Views/SessionListView.swift
Removes ocakTextLogo; adds FlameIcon with flicker animation; rebuilds app header with FlameIcon + title; applies hearthCard styling; updates typography (JetBrainsMono) and spacing, group headers, previews, and empty state; updates GroupNameTextField typing attributes.
Terminal Pane View Updates
macos/Sources/Ocak/Views/TerminalPaneView.swift
Wraps terminal in .hearthCard() with OcakTheme.termBg, switches header divider to cardEdge, replaces inline status indicator with EmberDot, applies gradient header overlay, and updates header typography and tracking.
Update & Setup Box Card Styling
macos/Sources/Ocak/Views/UpdateAvailableBox.swift, macos/Sources/Ocak/Views/HookSetupBox.swift
Replaces explicit background + clipShape card styling with hearthCard(radius: 12, shadowRadius: 10).
Settings View Appearance Adaptation
macos/Sources/Ocak/Views/SettingsView.swift
Adds isHearth computed state; conditionally renders a custom hearthSidebar for Hearth mode; applies OcakTheme colors across tabs for dividers, backgrounds, text, and section headers; adds Hearth description text.

Font Asset Pipeline & Registration

Layer / File(s) Summary
Font Build Configuration
.gitignore, Makefile, scripts/build-macos-app.sh
Adds .gitignore entry to exclude generated fonts; Makefile defines FONTS_SRC/FONTS_DST and sync-assets target using rsync to copy only .ttf/.otf files with deletion; build, run, and build-wrapped targets depend on sync-assets; build script also pre-stages fonts.
SPM Package Resources
macos/Package.swift
Adds Resources/Fonts/ to the Ocak executable target resources so SPM bundles the font files.
App Startup Font Registration
macos/Sources/Ocak/App/OcakApp.swift
Imports CoreText; adds an init() that calls registerBundledFonts() to resolve font resource URLs from Bundle.module (JetBrainsMono and InstrumentSerif) and registers them via CTFontManagerRegisterFontsForURL for process-wide use.

Sequence Diagram(s)

sequenceDiagram
  participant Makefile
  participant Filesystem as Resources_Fonts
  participant Package as Package.swift
  participant OcakApp
  participant CTFontManager

  Makefile->>Resources_Fonts: rsync TTF/OTF into macos/Sources/Ocak/Resources/Fonts (sync-assets)
  Package->>Resources_Fonts: include Resources/Fonts/ in target resources
  OcakApp->>Resources_Fonts: Bundle.module.url(forResource: fontName)
  OcakApp->>CTFontManager: CTFontManagerRegisterFontsForURL(fontURL)
  CTFontManager-->>OcakApp: fonts available to process
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 By hearthlight fonts march in a row,
Ember dots and card frames glow—
A flicker, a gradient, buttons dressed anew,
Bundled type and themed hues through and through.
Hop, patch, and build — the UI feels cozy too.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat: Hearth theme restyle' clearly and accurately summarizes the main objective of the changeset: introducing a Hearth theme and comprehensively restyling the application around it.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch hearth-restyle

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@bgultekin
bgultekin marked this pull request as ready for review May 11, 2026 08:42

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
macos/Sources/Ocak/Views/DrawerView.swift (1)

135-135: ⚡ Quick win

Consider extracting the session-list padding constant.

The padding value 8 is hardcoded here and also embedded in PanelSizeStore's chrome calculations (lines 19, 21). If this spacing needs adjustment in the future, it would require updates in 4 locations across 2 files.

♻️ Suggested approach

In PanelSizeStore, expose the padding as a constant:

static let sessionListLeadingPad: CGFloat = 8
static let terminalChrome: CGFloat = 6 + sessionListLeadingPad + 6
static let sessionListChrome: CGFloat = sessionListLeadingPad + 6

Then reference it in DrawerView:

if edge == .right { sessionResizeHandle.padding(.leading, PanelSizeStore.sessionListLeadingPad) }
// ...
if edge == .left { sessionResizeHandle.padding(.trailing, PanelSizeStore.sessionListLeadingPad) }

Also applies to: 151-151

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@macos/Sources/Ocak/Views/DrawerView.swift` at line 135, Extract the hardcoded
padding value (8) into a named constant on PanelSizeStore (e.g.,
sessionListLeadingPad or similar) and update PanelSizeStore's chrome
calculations (terminalChrome, sessionListChrome) to use that constant; then
replace the inline 8 in DrawerView's sessionResizeHandle.padding calls (both the
right and left edge branches where sessionResizeHandle.padding(.leading, 8) and
.padding(.trailing, 8) appear) to reference PanelSizeStore.sessionListLeadingPad
so all usages share the same source of truth.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@macos/Sources/Ocak/Views/SessionListView.swift`:
- Around line 25-33: FlameIcon currently hardcodes gradient and shadow hex
colors in SessionListView (LinearGradient colors and shadow color); move those
color values into OcakTheme color tokens in Theme/Colors.swift (e.g., add tokens
like ocakFlameGradientStart, ocakFlameGradientMid, ocakFlameGradientEnd, and
ocakFlameShadow), then update FlameIcon/SessionListView to reference
OcakTheme.<colorTokenName> instead of Color(hex: ...) so all color usage is
centralized in Theme/Colors.swift and no hex literals remain in the view.
- Around line 35-40: The current flicker logic only checks reduceMotion in the
onAppear closure, so toggling Reduce Motion at runtime won't stop or start the
animation; update the view to react to changes by adding an onChange(of:
reduceMotion) handler (in addition to the existing onAppear) that mirrors the
onAppear behavior: when reduceMotion becomes false, start the flicker animation
(withAnimation(.easeInOut(duration: 1.6).repeatForever(autoreverses: true)) {
flickering = true }) and when reduceMotion becomes true, stop the animation by
setting flickering = false (and optionally wrap in withAnimation if you want a
smooth stop). Ensure you reference the existing flickering and reduceMotion
properties so the runtime toggle updates the animation state.

In `@macos/Sources/Ocak/Views/SettingsView.swift`:
- Around line 27-30: The view currently hardcodes Color(hex: 0x0C0A08) in
SettingsView (background modifiers tied to isHearth) — move that color into the
centralized theme tokens in Theme/Colors.swift (e.g., add a token on OcakTheme
such as ocakHearthBackground or hearthBackgroundColor) and then replace the
inline Color(hex: 0x0C0A08) occurrences in SettingsView.swift (the
.background(...) calls that use isHearth) with references to the new OcakTheme
color token; ensure the token exposes a SwiftUI Color and update all three
hardcoded instances so the view reads OcakTheme.<tokenName> instead of
Color(hex: ...).

---

Nitpick comments:
In `@macos/Sources/Ocak/Views/DrawerView.swift`:
- Line 135: Extract the hardcoded padding value (8) into a named constant on
PanelSizeStore (e.g., sessionListLeadingPad or similar) and update
PanelSizeStore's chrome calculations (terminalChrome, sessionListChrome) to use
that constant; then replace the inline 8 in DrawerView's
sessionResizeHandle.padding calls (both the right and left edge branches where
sessionResizeHandle.padding(.leading, 8) and .padding(.trailing, 8) appear) to
reference PanelSizeStore.sessionListLeadingPad so all usages share the same
source of truth.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5655a9c2-d6ec-4e88-8958-70923f8d108d

📥 Commits

Reviewing files that changed from the base of the PR and between 0c4436b and 74ca2d2.

⛔ Files ignored due to path filters (3)
  • assets/fonts/InstrumentSerif-Italic.ttf is excluded by !**/*.ttf
  • assets/fonts/JetBrainsMono-Medium.ttf is excluded by !**/*.ttf
  • assets/fonts/JetBrainsMono-Regular.ttf is excluded by !**/*.ttf
📒 Files selected for processing (19)
  • .gitignore
  • Makefile
  • assets/fonts/.gitkeep
  • macos/Package.swift
  • macos/Sources/Ocak/App/AppDelegate.swift
  • macos/Sources/Ocak/App/OcakApp.swift
  • macos/Sources/Ocak/Models/AppearanceConfigStore.swift
  • macos/Sources/Ocak/Models/PanelSizeStore.swift
  • macos/Sources/Ocak/Theme/Colors.swift
  • macos/Sources/Ocak/Theme/HearthCard.swift
  • macos/Sources/Ocak/Views/DrawerView.swift
  • macos/Sources/Ocak/Views/EmberDot.swift
  • macos/Sources/Ocak/Views/HookSetupBox.swift
  • macos/Sources/Ocak/Views/SessionListRowView.swift
  • macos/Sources/Ocak/Views/SessionListView.swift
  • macos/Sources/Ocak/Views/SettingsView.swift
  • macos/Sources/Ocak/Views/TerminalPaneView.swift
  • macos/Sources/Ocak/Views/UpdateAvailableBox.swift
  • scripts/build-macos-app.sh

Comment thread macos/Sources/Ocak/Views/SessionListView.swift
Comment thread macos/Sources/Ocak/Views/SessionListView.swift
Comment thread macos/Sources/Ocak/Views/SettingsView.swift Outdated
bgultekin and others added 2 commits May 11, 2026 14:40
- Add hearthBackground, hearthSidebarBackground, flame gradient, and flameShadow tokens to Colors.swift
- Replace all inline Color(hex:) literals in SettingsView and FlameIcon with OcakTheme references
- Add onChange(of: reduceMotion) to FlameIcon so toggling Reduce Motion at runtime starts/stops animation
- Extract PanelSizeStore.sessionListLeadingPad constant and use it in chrome calculations and DrawerView padding

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@macos/Sources/Ocak/Views/EmberDot.swift`:
- Around line 38-52: The pulsing opacity is applied to the entire ZStack (in
EmberDot.body) so the outer stroked ring pulses with the inner dot; move the
animation-only opacity from the ZStack to the inner filled Circle so only the
dot uses .opacity(reduceMotion ? 1.0 : phase) while the outer Circle stroke
remains controlled by .opacity(isMarked ? 1 : 0); update EmberDot.body to apply
the phase-based opacity to the inner Circle (the one using .fill(dotColor) and
.shadow(...)) and remove the phase opacity from the ZStack so the marked ring
stays visually stable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 29d7a87b-dbcb-4ae1-831b-eff0026e966c

📥 Commits

Reviewing files that changed from the base of the PR and between 74ca2d2 and 22e5e68.

📒 Files selected for processing (6)
  • macos/Sources/Ocak/Models/PanelSizeStore.swift
  • macos/Sources/Ocak/Theme/Colors.swift
  • macos/Sources/Ocak/Views/DrawerView.swift
  • macos/Sources/Ocak/Views/EmberDot.swift
  • macos/Sources/Ocak/Views/SessionListView.swift
  • macos/Sources/Ocak/Views/SettingsView.swift
✅ Files skipped from review due to trivial changes (1)
  • macos/Sources/Ocak/Views/DrawerView.swift
🚧 Files skipped from review as they are similar to previous changes (4)
  • macos/Sources/Ocak/Models/PanelSizeStore.swift
  • macos/Sources/Ocak/Views/SettingsView.swift
  • macos/Sources/Ocak/Views/SessionListView.swift
  • macos/Sources/Ocak/Theme/Colors.swift

Comment thread macos/Sources/Ocak/Views/EmberDot.swift
@bgultekin
bgultekin merged commit e12ebe5 into main May 11, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant