Skip to content

refactor: adopt Material3 primitives for surface containers - #28

Merged
bengidev merged 7 commits into
mainfrom
refactor/material3-primitive-surfaces
Aug 20, 2026
Merged

bengidev merged 7 commits into
mainfrom
refactor/material3-primitive-surfaces

Conversation

@bengidev

Copy link
Copy Markdown
Owner

Summary

Replace hand-rolled clip + background + border modifier chains with Material3 primitives so surface containers inherit component-level shape, color, and border semantics:

  • chat: Surface for user message bubble, error banner, reasoning card, and output stream card
  • home: AlertDialog for the composer attachment menu
  • onboarding: OutlinedCard for CardChrome
  • sidepanel: OutlinedTextField for the API key field
  • speech: Surface for the recording composer capsule
  • build: bump AGP to 9.3.1 and Gradle wrapper to 9.5.0

Tests

  • ./gradlew :app:testDebugUnitTest passes on the committed tree

Commits

  • build: bump AGP to 9.3.1 and Gradle wrapper to 9.5.0
  • refactor(chat): use Material3 Surface for message bubbles and cards
  • refactor(home): use Material3 AlertDialog for composer attachment menu
  • refactor(onboarding): use Material3 OutlinedCard for CardChrome
  • refactor(sidepanel): use Material3 OutlinedTextField for API key field
  • refactor(speech): use Material3 Surface for recording composer

Replace manual clip+background+border chains with Material3 Surface so
cards and bubbles pick up shape, color, and border semantics from the
component instead of raw modifier composition.
Replace the hand-rolled Dialog with AlertDialog so the attachment picker
gets Material3 semantics for title, message, confirm, and dismiss slots
plus the themed container shape and color.
Replace the Box with clip+background+border using OutlinedCard so card
chrome inherits Material3 outline card semantics and a ColumnScope
content receiver.
Replace the wrapped TextField inside a background+border Row with
OutlinedTextField so the field carries its own container, border, and
leading icon semantics.
Replace the clip+background+border Row with Surface so the voice
recording composer inherits Material3 shape, color, and border
semantics.

@bengidev bengidev left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Review: verified — no blocking issues

I built and tested the branch locally:

  • ./gradlew :app:testDebugUnitTest438 tests, 0 failures, 0 errors
  • :app:compileDebugAndroidTestKotlin fails, but the errors are in SidePanelPresenterTest.kt (untouched by this PR) and reproduce identically on main — pre-existing, not a regression.
  • AGP 9.3.1 ↔ Gradle 9.5.0 is the correct pairing (AGP 9.3 requires Gradle ≥ 9.5), and 9.3.1 is published.

I cross-checked each conversion against the M3 1.4.0 source (Surface, Card/OutlinedCard, AlertDialog/AlertDialogFlowRow, contentColorFor). Shape/color/border/padding are preserved; CardChrome's content receiver change (() -> UnitColumnScope.() -> Unit) is a compatible widening and the sole caller compiles.

Behavioral differences to be aware of (non-blocking)

  1. SidePanelSettingContent API key field is ~24dp shorter. Old: 12dp-padded Row around a 56dp TextField ≈ 80dp tall. New: bare OutlinedTextField at M3's 56dp container height. If the taller field was intentional, add Modifier.height(...)/padding back. Also note M3's focused border is 2dp (was 1dp always) — and since focused/unfocused border colors are identical here, focus now only shows as a thicker stroke rather than a color change.

  2. ComposerAttachmentMenuDialog buttons now flow-wrap. AlertDialog uses AlertDialogFlowRow (not a Row): if Cancel + both option buttons don't fit on one line, confirm actions wrap above dismiss, and button spacing becomes M3 tokens instead of 12dp. Usually identical on typical phone widths, but real behavior on narrow screens / large font scales.

  3. CardChrome content color is unchangedcontentColorFor(surfacePaper) resolves to the ambient LocalContentColor since surfacePaper isn't a scheme color, so no text-color shift. ✅

Pre-existing issue (not from this PR)

SidePanelPresenterTest.kt doesn't compile (longClick unresolved; credentialStore/preferenceStore params missing). Reproduces on main; CI masks it because it only runs unit tests. Worth a separate fix.

Nit

ChatErrorBannerView's inner content isn't re-indented after the Surface wrap — cosmetic.

@bengidev
bengidev merged commit a100ee7 into main Aug 20, 2026
1 check passed
@bengidev
bengidev deleted the refactor/material3-primitive-surfaces branch August 20, 2026 04:15
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