Skip to content

Add resizable home screen tiles with grid layout#7575

Draft
hawkrives wants to merge 25 commits into
masterfrom
claude/resizable-home-buttons-tAQN6
Draft

Add resizable home screen tiles with grid layout#7575
hawkrives wants to merge 25 commits into
masterfrom
claude/resizable-home-buttons-tAQN6

Conversation

@hawkrives

Copy link
Copy Markdown
Member

Summary

This PR implements a resizable tile system for the home screen, allowing users to customize tile sizes (Small, Medium, Large, Wide) via long-press context menus. The layout uses a grid-based packing algorithm to intelligently position tiles of varying dimensions.

Key Changes

Core Features

  • Tile Resizing: Added four tile size options (1x1, 1x2, 2x2, 2x4) accessible via long-press context menu on each tile
  • Grid Layout System: Replaced the previous two-column layout with a 4-column grid that dynamically packs tiles based on their selected sizes
  • Persistence: Tile size preferences are stored in Redux and survive app restarts
  • Reset Functionality: Added "Reset home screen layout" option to the unofficial app notice menu to restore default tile sizes

New Components & Files

  • HomeScreenGrid: Manages grid layout and tile positioning using absolute positioning
  • HomeScreenTile: Wraps tiles with context menu and size selection logic
  • HomeScreenButton: Enhanced to support four size variants with appropriate icon sizes, text visibility, and layouts
  • packTiles(): Algorithm that packs tiles into a 4-column grid, respecting multi-row tile dimensions and flowing around occupied cells
  • types.ts: Defines tile size types, dimensions, and grid constants

Redux Integration

  • Extended settings slice with homescreenSizes state to track per-tile size preferences
  • Added setHomescreenTileSize and resetHomescreenSizes actions
  • Added selectHomescreenSize selector with DEFAULT_TILE_SIZE fallback

View System Updates

  • Added id field to all ViewType entries (kebab-case, unique identifiers)
  • Updated AllViews() to include IDs for all 16 home screen tiles
  • Locked title-to-id mapping to preserve user persistence across updates

Context Menu Enhancement

  • Extended ContextMenu component to support object-based actions with custom titles
  • Added selectedAction prop to indicate currently selected menu item

Testing

  • Comprehensive unit tests for tile packing algorithm covering edge cases (mixed sizes, wrapping, multi-row tiles)
  • Tests for HomeScreenButton size variants and layout behavior
  • Tests for HomeScreenTile context menu integration and Redux dispatch
  • Tests for settings slice actions and selectors
  • Tests for view ID uniqueness and title-to-id mapping stability
  • UI tests for long-press resizing, persistence across launches, and layout reset

UI Tests (iOS)

  • testLongPressTileChangesSize(): Verifies tile resizing, persistence, accessibility, and reset functionality
  • testGapBehaviorWithOrphanSmallTile(): Validates grid packing with mixed tile sizes
  • testRotationReflowsLayout(): Ensures layout recomputes correctly on device rotation

Implementation Details

  • Grid uses absolute positioning with calculated cell dimensions based on screen width
  • Packing algorithm maintains a set of occupied cells to prevent overlaps
  • 2x4 (Wide) tiles must start at column 0; other sizes flow naturally
  • Tile heights are fixed at 80 units; widths scale with screen dimensions
  • Small (1x1) tiles hide text but preserve accessibility labels for screen readers
  • Wide (2x4) tiles use horizontal layout with icon and text side-by-side

https://claude.ai/code/session_017WkVVojFsm38sp89pFQkj8

claude and others added 25 commits April 26, 2026 15:31
Spec covers per-tile size selection (1x1, 1x2, 2x2, 2x4) via long-press
context menu with redux-persist-backed storage, strict-order packing on a
4-column grid, size-aware button variants, and a "Reset home screen
layout" action on the existing notice menu. Drag-to-reorder and live
content previews are explicitly deferred to follow-up specs.

Also unignore /docs/superpowers/ so future specs commit cleanly, and
ignore .superpowers/ where brainstorm session files live.

https://claude.ai/code/session_017WkVVojFsm38sp89pFQkj8
Eleven sequential tasks (TDD where applicable) covering: TileSize types,
the packTiles function, stable view ids, settings slice extension, the
context-menu module's structured-action support, size-aware
HomeScreenButton, the HomeScreenTile orchestrator, HomeScreenGrid layout,
HomePage wiring, the notice-menu reset action, and XCUITests for resize,
persistence, gap behavior, rotation, and reset.

https://claude.ai/code/session_017WkVVojFsm38sp89pFQkj8
Implements a pure, strict-order packer that places variable-size tiles
onto a 4-column grid with left-to-right, top-down cursor advancement and
multi-row occupancy reservation for tall tiles.

https://claude.ai/code/session_017WkVVojFsm38sp89pFQkj8
Adds a kebab-case id to every entry in AllViews(), adds uniqueness/format
tests, removes deep view-module imports from views.ts in favour of inline
keyof RootViewsParamList literals (making the module independently
testable), and cleans up the as-unknown casts in pack-tiles.test.ts.
Also adds @react-navigation to the Jest transform list so that modules
reachable from views.ts are correctly handled.

https://claude.ai/code/session_017WkVVojFsm38sp89pFQkj8
Restores the runtime NavigationKey imports and local aliases in
views.ts and removes the `@react-navigation` esmPackages entry from
jest.config.ts. Replaces the refactor's testing benefit with
jest.mock() calls in views.test.ts so the new test loads without
pulling the React Native component tree. Also adds a hard table
assertion that locks the title→id mapping, protecting the persistence
contract that ships in Task 4.

https://claude.ai/code/session_017WkVVojFsm38sp89pFQkj8
Adds HomeScreenTile which reads per-tile size from Redux, wires the
@frogpond/context-menu for resize actions, and dispatches
setHomescreenTileSize on selection. Also adds react-redux to Jest ESM
transform list so Provider-based tests can run.

https://claude.ai/code/session_017WkVVojFsm38sp89pFQkj8
…e has shipped

The branch documents (spec at docs/superpowers/specs/...-design.md, plan at
docs/superpowers/plans/...md) served their purpose during brainstorm and
execution. The implemented code is the source of truth going forward.

https://claude.ai/code/session_017WkVVojFsm38sp89pFQkj8
- views.test: add /u flag to the kebab-case regex (require-unicode-regexp)
- tile.test: type the ContextMenu mock factory to drop unsafe-any return
- button.test: replace empty () => {} stubs with jest.fn() stubs
- button.tsx: add an exhaustive `default` case to variantFor's switch
- index.tsx + tile.tsx: alphabetize JSX props (eslint --fix autopass)

https://claude.ai/code/session_017WkVVojFsm38sp89pFQkj8
- Use useAppSelector instead of useSelector for proper RootState typing
- Guard homescreenSizes ?? {} in selector and reducer against stale persisted state
- Add buttonContainer (flex: 1) to Touchable.containerStyle so Pressable fills grid cell
- Add buttonStyle (flex: 1) to ContextMenuButton so it fills the grid cell height
The RefreshControl component from react-native-gesture-handler requires a
GestureHandlerRootView ancestor. This fix wraps the WebcamsView return value
with GestureHandlerRootView to provide the required context and prevent the
'nativegesturehandler must be used as a descendant of gesturehandlerrootview'
error.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…iles

iOS accessibility-collapses the ContextMenuButton wrapper into its single
accessible child (the inner Touchable with accessibilityLabel + role), so the
testID we set on <ContextMenu> never surfaces in the accessibility hierarchy
and `app.element(matching: \"home-tile-menus\")` returns nothing. The CI UI
hierarchy snapshot confirmed this: 'home-notice' (whose child is a plain View)
shows up as an Other element with the identifier, but no 'home-tile-...'
identifier appears for the home tiles.

Forwarding testID through HomeScreenButton to the inner Touchable puts the
identifier on the same element as the accessibility label, which iOS keeps in
the tree. The ContextMenu still receives testID for any iOS version that does
expose the wrapper.

Fixes the CI failures of testGapBehaviorWithOrphanSmallTile,
testLongPressTileChangesSize, and testRotationReflowsLayout.

https://claude.ai/code/session_017WkVVojFsm38sp89pFQkj8
The previous fix tried to forward testID through HomeScreenButton's Touchable
so 'home-tile-{id}' would land on a queryable accessibility element, but iOS
still drops the identifier when the Touchable is wrapped in
react-native-ios-context-menu's ContextMenuButton — the UI hierarchy snapshot
on commit c29f6c0 still showed home tile Buttons without any 'identifier'
field, while the same Touchable+testID pattern on button-open-settings (in a
plain navigation header) does surface the identifier.

Switch the failing tests to query by accessibility label (app.buttons['Menus']
etc.). The accessibility label is preserved on every tile in the snapshot, and
this is the same pattern the existing testShowsTheHomeScreen test already uses
to find the Menus button.

Reverts the dead testID forwarding from HomeScreenButton.

https://claude.ai/code/session_017WkVVojFsm38sp89pFQkj8
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.

3 participants