Skip to content

feat: dev-only MagicPreview catalog framework#7

Open
anilcancakir wants to merge 9 commits into
masterfrom
feat/design-first-component-system
Open

feat: dev-only MagicPreview catalog framework#7
anilcancakir wants to merge 9 commits into
masterfrom
feat/design-first-component-system

Conversation

@anilcancakir

Copy link
Copy Markdown
Contributor

Adds the MagicPreview catalog (PreviewEntry contract, sidebar nav, side-by-side light/dark panes bound to WindThemeController) + the /preview page and /preview/:component deep link. Dev-only: gated by kReleaseMode + PREVIEW_ENABLED, tree-shaken from release (verified). Adds fluttersdk_wind dep. analyze 0, 95 tests.

Cross-repo: this design-first component system spans 5 repos. Land in dependency order: wind (WindRecipe + 5 primitives) first, then magic (design:sync/lint, previews:refresh, make:component) and magic_devtools (preview catalog) which consume wind, then magic_starter (component library + view rewrite), then magic_example (consumer app). Branches are all feat/design-first-component-system. Verified locally end-to-end (analyze 0, full suites green, real-browser e2e of the /preview catalog, release-strip confirmed).

Add the MagicPreview framework: a PreviewEntry contract, a
MagicPreviewCatalog (sidebar nav + per-preview side-by-side light/dark
panes + a global toggle bound to wind's WindThemeController), and a
/preview ShellRoute with :component children registered through magic's
router. The catalog is dev-only: registerRoutes() is gated by
kReleaseMode + a const bool.fromEnvironment('PREVIEW_ENABLED') and snapshots
entries inside the function body, so release builds tree-shake the whole
catalog (verified in Step 19). Adds the fluttersdk_wind dependency.
Three runtime defects the analyze/build/unit-test gates missed, caught by
actually running the app in a browser:

- The catalog group's index child path '/' composed to '/preview/', which
  trips go_router's trailing-slash assertion and blanked the WHOLE app on
  every route. Index child path is now '' (composes to '/preview').
- Each light/dark preview pane used a bare WindThemeData with no aliases, so
  component semantic tokens resolved to no-ops and previews rendered Flutter's
  red unstyled-text fallback. Panes now copyWith(brightness:) the ambient app
  theme, keeping aliases + brand colors.
- The preview surface and panes now scroll (vertical surface, horizontal
  panes) so wide variant matrices no longer overflow the side-by-side layout.

Release strip re-verified: the catalog still tree-shakes from release.
The catalog used a persistent ShellRoute whose _PreviewShell read stale
global pathParameters and never rebuilt when only the child route swapped,
so /preview/<slug> always showed the first entry. Replace it with two plain
pages: /preview (index) and /preview/:component, the latter receiving the
slug in its builder and rebuilding on navigation. Add a regression test
asserting both paths register and none ends with '/' (the boot-crash guard).
…ADME

Keep a Changelog section order; replace em-dashes with ':'/';'/',' in the
README to satisfy the no-em-dash convention. Content unchanged.
Copilot AI review requested due to automatic review settings June 25, 2026 22:53
@codecov

codecov Bot commented Jun 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.13924% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
lib/src/preview/preview_routes.dart 68.75% 5 Missing ⚠️
lib/src/preview/magic_preview.dart 96.82% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copilot AI 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.

Pull request overview

Adds a dev-only MagicPreview component catalog to magic_devtools, exposing a /preview index and /preview/:component deep link along with the PreviewEntry contract and catalog UI backed by fluttersdk_wind.

Changes:

  • Introduces MagicPreview registration + route wiring, gated behind kReleaseMode and PREVIEW_ENABLED.
  • Adds MagicPreviewCatalog UI that renders each selected preview in side-by-side light/dark panes and binds a global theme toggle to WindThemeController.
  • Adds widget/unit tests for route registration and catalog behavior; updates docs/changelog and adds the fluttersdk_wind dependency.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
test/preview/preview_routes_test.dart Exercises MagicPreview.register() and registerRoutes() behavior and guards against bad route paths.
test/preview/magic_preview_test.dart Widget-tests catalog rendering in both brightnesses and the wind theme toggle binding.
lib/src/preview/preview_routes.dart Implements MagicPreview registry and registers /preview + /preview/:component routes behind release/dev guards.
lib/src/preview/magic_preview.dart Defines PreviewEntry and the MagicPreviewCatalog sidebar + dual-pane preview UI.
lib/preview.dart Adds public barrel export for the preview catalog framework.
README.md Documents the new preview catalog barrel and wiring guidance.
CHANGELOG.md Records the new preview catalog framework and related fixes/behavior.
pubspec.yaml Adds fluttersdk_wind dependency required by the catalog UI.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/src/preview/preview_routes.dart Outdated
Comment on lines +8 to +13
/// Defaults to [kDebugMode]: the catalog is reachable in debug and profile
/// builds, never in release. A host can force it off in any mode with
/// `--dart-define=PREVIEW_ENABLED=false`. Because this is a `const`, the
/// release-mode optimizer can fold the guarded branch in
/// [MagicPreview.registerRoutes] dead and tree-shake the entire catalog,
/// every [PreviewEntry], and every builder it transitively references.
Comment on lines +40 to +54
/// The registered previews. Populated by [register]; empty until then.
static List<PreviewEntry> _entries = const <PreviewEntry>[];

/// The currently registered previews (read-only view).
static List<PreviewEntry> get entries => List.unmodifiable(_entries);

/// Register the catalog [entries].
///
/// Call this BEFORE [registerRoutes], typically from the consumer's
/// `RouteServiceProvider.boot()`. In release builds the guard in
/// [registerRoutes] short-circuits, so even if entries are registered they
/// are never wired into a route and stay tree-shakeable.
static void register(List<PreviewEntry> entries) {
_entries = entries;
}
Comment thread README.md Outdated
Comment on lines +32 to +36
Three import barrels:

- `package:magic_devtools/dusk.dart` — `MagicDuskIntegration` registers 14 Magic-aware enrichers into fluttersdk_dusk's snapshot pipeline.
- `package:magic_devtools/telescope.dart` — `MagicTelescopeIntegration` registers 5 Magic watchers and `MagicHttpFacadeAdapter` into fluttersdk_telescope.
- `package:magic_devtools/dusk.dart`: `MagicDuskIntegration` registers 14 Magic-aware enrichers into fluttersdk_dusk's snapshot pipeline.
- `package:magic_devtools/telescope.dart`: `MagicTelescopeIntegration` registers 5 Magic watchers and `MagicHttpFacadeAdapter` into fluttersdk_telescope.
- `package:magic_devtools/preview.dart`: `MagicPreview` hosts a dev-only component preview catalog behind a `/preview` ShellRoute, tree-shaken from release builds.
Comment thread lib/preview.dart Outdated
Comment on lines +3 to +7
/// Import this file to host the auto-discovered component previews behind a
/// `/preview` ShellRoute. The whole surface is dev-only: it is reachable only
/// through [MagicPreview.registerRoutes], which is guarded by `kReleaseMode` +
/// `bool.fromEnvironment('PREVIEW_ENABLED')` and tree-shaken from release
/// builds.
Comment thread lib/src/preview/magic_preview.dart Outdated
Comment on lines +74 to +76
/// Invoked when a sidebar item is tapped. The `/preview` ShellRoute wires
/// this to navigation; when null, selection updates local state only.
final ValueChanged<PreviewEntry>? onSelect;
Comment thread test/preview/preview_routes_test.dart Outdated
Comment on lines +6 to +12
/// Tests for [MagicPreview]'s registration entrypoint and the `/preview`
/// ShellRoute it wires into [MagicRouter].
///
/// The release boundary itself (the `kReleaseMode` early return + tree-shaking)
/// is asserted by Step 19 (a release-bundle symbol grep); these tests cover the
/// debug-mode behavior: entries round-trip, and `registerRoutes` adds exactly
/// one `magic-preview` layout BEFORE the router locks.
Comment thread CHANGELOG.md Outdated
Comment on lines +12 to +16
- `MagicPreview` framework: a dev-only component preview catalog hosted behind a
`/preview` ShellRoute. New `package:magic_devtools/preview.dart` barrel exports
the `PreviewEntry` contract (`label`, `slug`, `builder`), the
`MagicPreviewCatalog` widget (sidebar nav plus a light/dark pair per preview,
with a global theme toggle bound to wind's `WindTheme.of(context).toggleTheme()`),
The catalog now shows one preview pane under the ambient wind theme; the
header "Toggle theme" button flips brightness for the whole catalog. A
single pane also mounts each controller-backed feature-screen preview
once, avoiding the duplicate-mount churn of the two-pane layout.
Rework MagicPreviewCatalog to match the idea-design reference catalog: a
single vertically scrolling page that stacks every registered preview as
its own labeled section (heading + bordered card), instead of showing one
preview at a time. The left sidebar is now jump-to-section navigation -
tapping an item (or deep-linking /preview/<slug>) scrolls that section
into view via Scrollable.ensureVisible. Because controller-backed screen
previews defer-mount and settle their heights over a few frames, the
scroll re-runs once after a short, dispose-cancelled delay so the section
lands at the top precisely. Header keeps the global light/dark toggle.
The jump-link sidebar listed every entry in a fixed flex column; with a
large component set (~39 entries) it overflowed the viewport (RenderFlex
bottom overflow). The nav list now scrolls independently under a fixed
header.
Stacking all sections on one scrolling page janked once the catalog grew
to ~39 entries including heavy controller-backed screen previews (all
mounted at once). Revert to a single active pane: the scrollable sidebar
selects, and only the chosen preview is built. Heavy screens mount only
when selected, so the catalog stays responsive (index now shows zero
exceptions vs the prior setState-during-build churn). Sidebar stays
scrollable for the large entry set; header shows the active label.
…harden register

- Replace every 'ShellRoute' reference (barrel doc, README, CHANGELOG, test
  doc) with the actual two-plain-pages wiring (/preview + /preview/:component);
  the persistent-ShellRoute rationale comment stays where it explains the
  avoidance.
- Correct kPreviewEnabled doc: ON in debug, OFF in profile+release by default
  (kDebugMode is false in profile); profile opts in via --dart-define.
- register() now snapshots entries into List.unmodifiable so later caller
  mutation cannot change the registered catalog.
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.

2 participants