Skip to content

Conversation

@Shironex
Copy link
Collaborator

@Shironex Shironex commented Jan 25, 2026

Summary

  • Extract UI-related state and actions from app-store.ts into a dedicated UI slice using Zustand slice pattern
  • Add UISliceState and UISliceActions interfaces to store/types/ui-types.ts
  • First implementation of Zustand slice pattern in the codebase
  • Fix pre-existing bug: fontSans/fontMonofontFamilySans/fontFamilyMono
  • Maintain backward compatibility through re-exports from app-store.ts

Changes

New Files Created (apps/ui/src/store/slices/)

File Contents Lines
ui-slice.ts createUISlice, initialUIState, UISlice type 310
index.ts Re-exports UI slice 1

Files Modified

File Changes
apps/ui/src/store/types/ui-types.ts Added UISliceState and UISliceActions interfaces
apps/ui/src/store/app-store.ts Import and spread UI slice, remove duplicate code, fix font property names

State Extracted to UI Slice

Category Properties
Core UI currentView, sidebarOpen, sidebarStyle, collapsedNavSections, mobileSidebarHidden
Theme theme, previewTheme
Fonts fontFamilySans, fontFamilyMono
Board UI boardViewMode, boardBackgroundByProject
Settings UI keyboardShortcuts, muteDoneSound, disableSplashScreen, showQueryDevtools, chatHistoryOpen
Panel Visibility worktreePanelCollapsed, worktreePanelVisibleByProject, showInitScriptIndicatorByProject, autoDismissInitScriptIndicatorByProject
File Picker lastProjectDir, recentFolders

Actions Extracted to UI Slice

  • View: setCurrentView, toggleSidebar, setSidebarOpen, setSidebarStyle, setCollapsedNavSections, toggleNavSection, toggleMobileSidebarHidden, setMobileSidebarHidden
  • Theme: setTheme, getEffectiveTheme, setPreviewTheme
  • Fonts: setFontSans, setFontMono, getEffectiveFontSans, getEffectiveFontMono
  • Board: setBoardViewMode, setBoardBackground, setCardOpacity, setColumnOpacity, setColumnBorderEnabled, setCardGlassmorphism, setCardBorderEnabled, setCardBorderOpacity, setHideScrollbar, getBoardBackground, clearBoardBackground
  • Settings: setKeyboardShortcut, setKeyboardShortcuts, resetKeyboardShortcuts, setMuteDoneSound, setDisableSplashScreen, setShowQueryDevtools, setChatHistoryOpen, toggleChatHistory
  • Panel: setWorktreePanelCollapsed, setWorktreePanelVisible, getWorktreePanelVisible, setShowInitScriptIndicator, getShowInitScriptIndicator, setAutoDismissInitScriptIndicator, getAutoDismissInitScriptIndicator
  • File Picker: setLastProjectDir, setRecentFolders, addRecentFolder

Actions Kept in Main Store

These actions modify project entities, not just UI state:

  • setProjectTheme - modifies projects[] and currentProject
  • setProjectFontSans - modifies projects[] and currentProject
  • setProjectFontMono - modifies projects[] and currentProject

Bug Fix

Fixed pre-existing bug where code accessed currentProject?.fontSans/fontMono but the Project interface has fontFamilySans/fontFamilyMono.

Backward Compatibility

All existing imports from @/store/app-store continue to work:

// These still work
import { useAppStore } from '@/store/app-store';
const { setCurrentView, setBoardViewMode, theme } = useAppStore();

New types available:

import type { UISliceState, UISliceActions } from '@/store/app-store';

Test Plan

  • Build succeeds (npm run build)
  • Lint passes (npm run lint - 0 errors, pre-existing warnings only)
  • Package tests pass (npm run test:packages - 519 tests)
  • Server tests pass (npm run test:server - 1,415 tests)

Manual Testing Before Merge

View Navigation

  • Navigate between views (Board, Settings, Terminal, Agent, Context, Spec, Ideation)
  • Verify keyboard shortcuts work for navigation (K, S, T, A, C, D, I)

Sidebar

  • Toggle sidebar visibility with backtick key (`)
  • Collapse/expand nav sections in sidebar
  • Switch sidebar style between 'unified' and 'discord' in settings

Theme

  • Change global theme in Settings → Appearance
  • Verify theme preview on hover works in theme selector
  • Set per-project theme override and verify it takes effect

Fonts

  • Change global sans font in Settings → Appearance
  • Change global mono font in Settings → Appearance
  • Set per-project font override and verify it applies

Board View

  • Switch between Kanban and Graph views (K and H keys)
  • Set a board background image
  • Adjust card opacity, column opacity, glassmorphism settings
  • Clear board background

Keyboard Shortcuts

  • Modify a keyboard shortcut in Settings → Keyboard Shortcuts
  • Reset keyboard shortcuts to defaults
  • Verify modified shortcut works

Settings UI

  • Toggle "Mute done sound" setting
  • Toggle "Disable splash screen" setting
  • Toggle "Show Query DevTools" setting (dev mode)

Panel Visibility

  • Collapse/expand worktree panel in board view
  • Toggle worktree panel row visibility per project
  • Toggle init script indicator visibility per project

File Picker

  • Open a project and verify recent folders are tracked
  • Verify last project directory is remembered

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Refactor
    • Internal restructuring of application state management to improve code organization and maintainability.

✏️ Tip: You can customize this high-level summary in your review settings.

- Extract UI-related state and actions into store/slices/ui-slice.ts
- Add UISliceState and UISliceActions interfaces to store/types/ui-types.ts
- First implementation of Zustand slice pattern in the codebase
- Fix pre-existing bug: fontSans/fontMono -> fontFamilySans/fontFamilyMono
- Maintain backward compatibility through re-exports

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Shironex, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refactors the application's state management by extracting all user interface-specific state and actions into a new, modular Zustand slice. This change improves the app-store's maintainability and readability by separating concerns, making it easier to manage and scale the application's state. It also introduces a new pattern for organizing Zustand stores, which can be applied to future state modularization efforts.

Highlights

  • UI State Extraction: All UI-related state and actions have been moved from app-store.ts into a new, dedicated ui-slice.ts using the Zustand slice pattern, significantly modularizing the store.
  • Zustand Slice Pattern Introduction: This pull request introduces the first implementation of the Zustand slice pattern in the codebase, promoting better organization and maintainability for future state management.
  • Font Property Bug Fix: A pre-existing bug where fontSans/fontMono were incorrectly accessed instead of fontFamilySans/fontFamilyMono in project settings has been corrected.
  • Backward Compatibility Maintained: Existing imports and usage patterns from app-store.ts continue to function as before, ensuring a smooth transition for consumers of the store without requiring immediate changes.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link

coderabbitai bot commented Jan 25, 2026

📝 Walkthrough

Walkthrough

Extracts all UI-related state and actions into a new Zustand UI slice (UISlice) and integrates it into the root app store by spreading initialUIState and createUISlice(set, get, store). Public types UISliceState and UISliceActions are exported and many root UI actions are relocated into the slice.

Changes

Cohort / File(s) Summary
UI Types & Slice
apps/ui/src/store/types/ui-types.ts, apps/ui/src/store/slices/ui-slice.ts
New UISliceState and UISliceActions types; initialUIState constant; createUISlice StateCreator implementing view, theme, font, board, settings, panel, and file-picker state/actions plus persistence helpers and computed getters.
Slices Re-exports
apps/ui/src/store/slices/index.ts
Re-exports createUISlice, initialUIState, and UISlice from the new UI slice.
Root Store Integration
apps/ui/src/store/app-store.ts
Root store updated to spread ...initialUIState and ...createUISlice(set, get, store) into the top-level store; useAppStore initializer now receives store parameter; numerous former root UI actions removed and delegated to the UI slice.

Sequence Diagram(s)

sequenceDiagram
  participant AppInit as App Init
  participant RootStore as useAppStore (root)
  participant UISlice as createUISlice
  participant Storage as LocalStorage

  AppInit->>RootStore: call create<AppState & AppActions>((set,get,store) => ...)
  RootStore->>UISlice: call createUISlice(set,get,store)
  UISlice-->>RootStore: returns UI state & actions (merged into root)
  Note right of UISlice: UI actions call persistence helpers
  UISlice->>Storage: saveTheme / saveFont... (on action)
  AppInit->>RootStore: components use store selectors/actions
  RootStore->>UISlice: routed UI action calls delegate to slice handlers
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰
I hop and I code, a tidy little trick,
A UISlice assembled, all actions quick,
Themes, fonts, and boards tucked in a row,
Root store lighter — watch the clean flow go! 🎨🐇

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor(store): Extract UI slice from app-store.ts' clearly and concisely summarizes the main change—extracting UI-related state and actions into a dedicated Zustand slice, matching the primary objective of the PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This is an excellent pull request that significantly improves the structure and maintainability of the application's state management by introducing the Zustand slice pattern for UI state. The refactoring is clean, well-documented, and successfully isolates UI-related concerns into a dedicated slice. The inclusion of a bug fix for font property names is also a great addition. I've added a couple of suggestions to further enhance code clarity and reduce duplication, but overall, this is a very solid contribution.

Comment on lines +166 to +253
setBoardBackground: (projectPath: string, imagePath: string | null) =>
set((state) => ({
boardBackgroundByProject: {
...state.boardBackgroundByProject,
[projectPath]: {
...(state.boardBackgroundByProject[projectPath] ?? defaultBackgroundSettings),
imagePath,
imageVersion: Date.now(), // Bust cache on image change
},
},
})),

setCardOpacity: (projectPath: string, opacity: number) =>
set((state) => ({
boardBackgroundByProject: {
...state.boardBackgroundByProject,
[projectPath]: {
...(state.boardBackgroundByProject[projectPath] ?? defaultBackgroundSettings),
cardOpacity: opacity,
},
},
})),

setColumnOpacity: (projectPath: string, opacity: number) =>
set((state) => ({
boardBackgroundByProject: {
...state.boardBackgroundByProject,
[projectPath]: {
...(state.boardBackgroundByProject[projectPath] ?? defaultBackgroundSettings),
columnOpacity: opacity,
},
},
})),

setColumnBorderEnabled: (projectPath: string, enabled: boolean) =>
set((state) => ({
boardBackgroundByProject: {
...state.boardBackgroundByProject,
[projectPath]: {
...(state.boardBackgroundByProject[projectPath] ?? defaultBackgroundSettings),
columnBorderEnabled: enabled,
},
},
})),

setCardGlassmorphism: (projectPath: string, enabled: boolean) =>
set((state) => ({
boardBackgroundByProject: {
...state.boardBackgroundByProject,
[projectPath]: {
...(state.boardBackgroundByProject[projectPath] ?? defaultBackgroundSettings),
cardGlassmorphism: enabled,
},
},
})),

setCardBorderEnabled: (projectPath: string, enabled: boolean) =>
set((state) => ({
boardBackgroundByProject: {
...state.boardBackgroundByProject,
[projectPath]: {
...(state.boardBackgroundByProject[projectPath] ?? defaultBackgroundSettings),
cardBorderEnabled: enabled,
},
},
})),

setCardBorderOpacity: (projectPath: string, opacity: number) =>
set((state) => ({
boardBackgroundByProject: {
...state.boardBackgroundByProject,
[projectPath]: {
...(state.boardBackgroundByProject[projectPath] ?? defaultBackgroundSettings),
cardBorderOpacity: opacity,
},
},
})),

setHideScrollbar: (projectPath: string, hide: boolean) =>
set((state) => ({
boardBackgroundByProject: {
...state.boardBackgroundByProject,
[projectPath]: {
...(state.boardBackgroundByProject[projectPath] ?? defaultBackgroundSettings),
hideScrollbar: hide,
},
},
})),
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This is a great implementation of the UI slice! To improve maintainability and reduce code duplication, you could extract the logic for updating boardBackgroundByProject into a helper function. Many of the set... actions for board backgrounds follow the same pattern.

For example, you could create a helper like this:

const updateBoardBackground = (
  projectPath: string,
  updates: Partial<BackgroundSettings>
) => (state: UISliceState) => ({
  boardBackgroundByProject: {
    ...state.boardBackgroundByProject,
    [projectPath]: {
      ...(state.boardBackgroundByProject[projectPath] ?? defaultBackgroundSettings),
      ...updates,
    },
  },
});

And then use it in your actions:

setCardOpacity: (projectPath: string, opacity: number) =>
  set(updateBoardBackground(projectPath, { cardOpacity: opacity })),

setColumnOpacity: (projectPath: string, opacity: number) =>
  set(updateBoardBackground(projectPath, { columnOpacity: opacity })),

// etc.

This would make the actions more concise and easier to manage.

@Shironex Shironex self-assigned this Jan 25, 2026
@Shironex Shironex added Testers-Requested Request for others to test an enhancement or bug fix/etc. Refactor A complete logic rewrite is requested or being performed for an issue. Do Not Merge Use this label if something should not be merged. cleanup remove unused files in the codebase labels Jan 25, 2026
Resolve merge conflict in app-store.ts by keeping UI slice implementation
of getEffectiveFontSans/getEffectiveFontMono (already provided by ui-slice.ts)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/ui/src/store/app-store.ts (1)

1-8: Remove unused imports from line 8.

The imports UI_SANS_FONT_OPTIONS and UI_MONO_FONT_OPTIONS are not used in this file and should be removed.

Remove unused imports
-import { UI_SANS_FONT_OPTIONS, UI_MONO_FONT_OPTIONS } from '@/config/ui-font-options';

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cleanup remove unused files in the codebase Do Not Merge Use this label if something should not be merged. Refactor A complete logic rewrite is requested or being performed for an issue. Testers-Requested Request for others to test an enhancement or bug fix/etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants