Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

### Improvements

- **`plugin-starter.md` follows `magic_starter` off the alpha rail.** That package's next release is `0.0.27` rather than `0.0.1-alpha.27`, so the reference stamp moves with it and the release markers inside the file now read `0.0.27` where they described that release. The `magic_notifications` requirement it states moves to `^0.3.0`, which is the floor the starter release carries, and the notification section gains the five `notifications.*` keys the mounted screens read and no package supplies (`bulk_title`, `bulk_description`, `delete`, `delete_failed`, `channel_sms`): an adopter upgrading with a hand-written catalogue sees each of them rendered as its own key. The configuration section also gains `notifications.external_id_prefix`, which 0.0.27 introduced: the provider now declares `<prefix><user id>` as the push external id off `Auth.stateNotifier` and the value has to equal the backend's own, since OneSignal accepts a mismatch and delivers to nobody. This file is the only agent-facing document for that package, since `.pubignore` keeps its `CLAUDE.md` out of the published archive, and `magic_starter`'s own `skill_reference_stamp_test.dart` fails against a stale stamp whenever a sibling checkout exists. (`skills/magic-framework/references/plugin-starter.md`, `skills/magic-framework/SKILL.md`)

## [0.0.10] - 2026-09-11

### BREAKING
Expand Down
4 changes: 2 additions & 2 deletions skills/magic-framework/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
name: magic-framework
description: "Write correct, idiomatic code in a Flutter app that depends on the `magic` framework (Laravel-inspired: IoC container, 18 facades, Eloquent-style ORM, service providers, reactive controllers, GoRouter routing, validation, auth, broadcasting). Use whenever code imports `package:magic/magic.dart` or `package:magic/testing.dart`, or the work touches Magic.init, MagicApp, a facade (Auth/Http/Cache/DB/Echo/Event/Gate/Config/Lang/Launch/Log/Pick/MagicRoute/Schema/Session/Storage/Vault/Crypt), a Model, MagicController, a MagicView, MagicFormData, FormRequest, a ServiceProvider, a migration, or the artisan make:* CLI. UI styling is Wind (separate wind-ui skill). Do NOT use for plain Flutter or Wind-only work with no magic import."
when_to_use: "Use proactively when editing or scaffolding a magic app: Magic.init / a facade / a Model / a MagicController or MagicView / a form (MagicFormData, FormRequest, Validator) / a ServiceProvider / a route or MagicMiddleware / a migration / MagicStateMixin + RxStatus + fetchList / Session flash + old() + trans() / testing with MagicTest + Http.fake/Auth.fake / the artisan make:* CLI / the magic_deeplink, magic_notifications, magic_social_auth, magic_starter, magic_payments, or magic_devtools plugins. Trigger even when the user does not say the word 'magic'. Do NOT trigger for plain Flutter or Wind-only UI with no package:magic import."
version: 0.1.16
version: 0.1.17
---

<!-- magic 0.0.10 | Skill v0.1.16 (2026-09-10). API surface verified against lib/src. -->
<!-- magic 0.0.10 | Skill v0.1.17 (2026-09-11). API surface verified against lib/src. -->

# Magic Framework

Expand Down
25 changes: 22 additions & 3 deletions skills/magic-framework/references/plugin-starter.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<!-- magic_starter v0.0.1-alpha.27 | Updated: 2026-09-09 -->
<!-- magic_starter v0.0.27 | Updated: 2026-09-11 -->

# magic_starter Plugin

Full-stack Flutter starter kit for Magic Framework: pre-built auth flows, team management, profile settings, billing, and responsive app/guest layouts with an opt-in feature flag system. The notification UI moved to `magic_notifications` in alpha.25; this package mounts it and requires `magic_notifications ^0.2.0`.
Full-stack Flutter starter kit for Magic Framework: pre-built auth flows, team management, profile settings, billing, and responsive app/guest layouts with an opt-in feature flag system. The notification UI moved to `magic_notifications` in alpha.25; this package mounts it and requires `magic_notifications ^0.3.0`.

Versions leave the alpha rail at this release: `0.0.1-alpha.26` is followed by `0.0.27`, carrying the counter rather than resetting it. An existing `^0.0.1-alpha.N` pin already covers it, since a caret on a zero major ends at `0.1.0`, and `flutter pub add magic_starter` now takes the current release without a prerelease pin.

## Contents

Expand Down Expand Up @@ -223,6 +225,8 @@ Notify.view.slot(NotificationViewRegistry.typeIconSlotView, 'monitor_down',

What stays here: `registerMagicStarterNotificationRoutes()` mounts `/notifications` and `/settings/notifications` in the `layout.app` shell and re-registers both screens wrapped in `MSPageContainer`, so they inherit the host's page geometry. The delete row asks first, through this package's `MSConfirmDialog`. See `plugin-notifications.md` for the `Notify` facade API.

Those screens read five `notifications.*` keys no package supplies: `bulk_title` and `bulk_description` (the bulk channel card, `magic_notifications` 0.3.0), `delete` (the row's delete glyph, which a screen reader otherwise announces as "button"), `delete_failed`, and `channel_sms`. `starter:install` scaffolds all five into `en.stub`; an app upgrading with a hand-written catalogue adds them itself, or `Translator.get` renders each key as its own text.

### Access

| Property | Type | Description |
Expand Down Expand Up @@ -274,6 +278,9 @@ Copy from `lib/config/magic_starter.dart` into your app config:
'billing': {
'web_origin': null, // REQUIRED once billing is on, and no default exists
},
'notifications': {
'external_id_prefix': 'user_', // must equal the backend's own prefix (0.0.27+)
},
'legal': {
'terms_url': null, // Shows ToS link on register page when set
'privacy_url': null, // Shows Privacy link on register page when set
Expand All @@ -288,6 +295,18 @@ concatenates it into Stripe's `successUrl`, `cancelUrl` and the portal `returnUr
relative url, and the resulting `BillingException` is logged rather than shown, so the customer sees a
checkout button that does nothing. `starter:doctor` reports the missing key (alpha.23+).

`notifications.external_id_prefix` is new in 0.0.27 and needs no host code: `MagicStarterServiceProvider`
listens to `Auth.stateNotifier` and declares `<prefix><user id>` as the push external id when a session
begins, releases it when one ends, and reads the current state once at boot so a session restored before
this provider boots is declared too. The default `user_` is what `magic-starter-laravel`'s `HasNotifications`
composes, and the two must agree EXACTLY: OneSignal accepts a mismatch and delivers to nobody, so the only
trace is a zero-recipient report on the server. A blank value resolves to `user_` rather than to no prefix.
`starter:doctor` prints the prefix it resolves to (0.0.27+) and never fails on it, since any value is valid
as long as the backend uses the same one; three of the four sites that compose the id live in the backend,
where no check inside this package can reach them. The release also stops `Notify` polling on the two
sign-outs the auth controller never sees (account deletion, and magic's `AuthInterceptor` failing a token
refresh); `MagicStarterAppLayout.initState` re-arms it when the shell remounts.

## View Registry

Override any pre-built screen by registering a custom builder under its string key. Call `MagicStarter.view.register()` in a service provider `boot()`.
Expand Down Expand Up @@ -514,7 +533,7 @@ Two guards ship ready to register as the `auth` and `guest` aliases in the app's

Both override `redirectTarget` (a pre-build synchronous redirect) rather than `handle` (a post-build remount), so a guarded page never mounts for someone who is about to be sent away. Each one guards its own destination so the redirect cannot loop, which matters because go_router raises after more than five successive redirects.

`EnsureAuthenticated` also records the requested location with `MagicRouter.setIntendedUrl` before bouncing (alpha.27), and the `NavigatesRoutes.navigateHome()` every post-auth path calls reads it back with `pullIntendedUrl`, falling back to `MagicStarterConfig.homeRoute()`. So a deep link that lands on a signed-out device survives the login bounce. Nothing is recorded for the guest-only auth routes themselves, and `redirectTarget` only sees `state.matchedLocation`, so a recorded intent loses the original query string.
`EnsureAuthenticated` also records the requested location with `MagicRouter.setIntendedUrl` before bouncing (0.0.27), and the `NavigatesRoutes.navigateHome()` every post-auth path calls reads it back with `pullIntendedUrl`, falling back to `MagicStarterConfig.homeRoute()`. So a deep link that lands on a signed-out device survives the login bounce. Nothing is recorded for the guest-only auth routes themselves, and `redirectTarget` only sees `state.matchedLocation`, so a recorded intent loses the original query string.

## Plan upgrade wall

Expand Down
Loading