A Flicker.Provider that derives navigable destinations from a host's own
Phoenix.Router (Spec 011).
This is the other half of the ⌘K experience: a palette that can take you to a screen, not only to a record, with one line of config and nothing to keep in sync when a route changes.
<Flicker.palette source={{Flicker.Providers.Routes, router: MyAppWeb.Router}} />Results carry meta.href, which is Spec 008's navigate-on-select convention,
and group: "Pages" so they read as a section when federated alongside
records.
Authorisation: this provider is not policy-scoped
A router knows paths, not permissions. Every other Flicker provider reads
through actor: and Ash policies
(ADR-004),
and this one structurally cannot: a route is a compile-time fact with nothing
to authorise against. It lists what it is given.
That makes the default deliberately conservative — only GET/live routes,
no parameterised paths, and framework mounts excluded — and it makes
:visible? the mechanism that matters. Pass visible?: fn route, actor -> boolean end to gate destinations for the current actor, and treat any
sensitive path as needing that gate rather than assuming the search hid it.
A palette entry is not an authorisation boundary in any case: it reveals that
a path exists, and the page behind it must still refuse.
Options
:router— required, thePhoenix.Routermodule to introspect.:only— path prefixes to include (list of strings). Everything else is dropped.:except— path prefixes to exclude. Applied after:only.:visible?—fn route, actor -> boolean end, the authorisation hook described above.:label—fn route -> String.t() end, overriding the derived label.:group— the group label. Defaults to"Pages";nilopts out of grouping.:extra— additional destinations the router can't supply, as[%{label: ..., href: ...}]. This is where a parameterised favourite ("My profile" →/users/42) belongs.
Label derivation
Dumb but predictable: path segments are humanised and joined with ·, so
/user-settings/billing becomes "User settings · Billing" and / becomes
"Home". Where that reads badly, :label overrides it — the derivation never
tries to be clever, because a surprising label is worse than a plain one.
Summary
Functions
The label derived from a path — humanised segments joined with ·.
Every destination this provider would offer, unfiltered by text.
Resolves hrefs back to results, so a palette restoring state from a URL shows the destination's label rather than its path.
Returns routes whose label matches query.text, case-insensitively.
Functions
The label derived from a path — humanised segments joined with ·.
Examples
iex> Flicker.Providers.Routes.derive_label("/")
"Home"
iex> Flicker.Providers.Routes.derive_label("/user-settings/billing")
"User settings · Billing"
iex> Flicker.Providers.Routes.derive_label("/artists")
"Artists"
@spec destinations(keyword()) :: [Flicker.Result.t()]
Every destination this provider would offer, unfiltered by text.
Exposed so a host can inspect what a given configuration actually exposes — worth doing once for anything sensitive, given the authorisation caveat above.
Examples
iex> Flicker.Providers.Routes.destinations(router: Flicker.Test.Router)
...> |> Enum.all?(&(&1.meta.href =~ "/"))
true
@spec fetch([term()], keyword()) :: {:ok, [Flicker.Result.t()]} | {:error, term()}
Resolves hrefs back to results, so a palette restoring state from a URL shows the destination's label rather than its path.
Values not present are simply absent, as ADR-003 requires of every provider.
@spec search(Flicker.Query.t(), keyword()) :: {:ok, [Flicker.Result.t()]} | {:error, term()}
Returns routes whose label matches query.text, case-insensitively.
query.facets is ignored: a route has no attributes to facet on. Free text is
matched against the derived (or overridden) label, not the raw path, since the
label is what the user sees.