fret 0.1.0

Batteries-included meta crate for the Fret UI framework (golden path entry point).
docs.rs failed to build fret-0.1.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

fret

[!WARNING] Experimental — under heavy development.

This project is an experiment in AI-driven software development. The vast majority of the code, tests, and documentation were written by AI (Codex). Humans direct architecture, priorities, and design decisions, but have not reviewed most of the code line-by-line. Treat this accordingly — there will be bugs, rough edges, and things that don't work. Use at your own risk.

Desktop-first, batteries-included entry points for building UI apps with Fret.

This is an ecosystem-level crate. It intentionally provides a small, ergonomic surface for applications while keeping the framework/kernel crates (crates/*) policy-light.

Boundary note

fret is the golden-path authoring facade for application code. It is intentionally not the repo's canonical example host.

  • Use docs/examples/README.md for the canonical learning/index path.
  • Keep runnable lessons in apps/fret-cookbook/examples/, component coverage in apps/fret-ui-gallery, and heavier platform/app demos in their owning app crates.

This keeps the facade teachable while leaving example/tooling ownership outside the crate. Editor/workspace-shell composition stays on owning crates such as fret-workspace instead of growing the default fret facade into an editor-specific shell surface.

For repository overview / architecture docs, see the monorepo README: https://github.com/Latias94/fret

Quick start (in this repo)

If you are learning the repo's default path, follow this ladder in order:

  1. hello
  2. simple-todo
  3. todo
  • Index: docs/examples/README.md
  • The generated template READMEs repeat the same ladder and explain where each rung fits.
  • Use fretboard new todo when you want the richer third-rung product baseline with explicit selector/query seams, not as a replacement for the first two rungs.

Generate a runnable starter (minimal baseline first):

cargo run -p fretboard -- new simple-todo --name my-simple-todo
cargo run --manifest-path local/my-simple-todo/Cargo.toml

Then move to the richer third rung when you actually want selectors + queries on top of a product baseline:

cargo run -p fretboard -- new todo --name my-todo
cargo run --manifest-path local/my-todo/Cargo.toml

Keep the default authoring model intentionally small:

  • use LocalState<T> / LocalState<Vec<_>> for view-owned state,
  • use local.layout_value(cx) / local.paint_value(cx) for ordinary LocalState reads, and local.layout_read_ref(cx, |value| ...) / local.paint_read_ref(cx, |value| ...) when a derived projection should avoid cloning the full slot,
  • keep one or two trivial locals inline; when a view owns several related LocalState<T> slots, prefer a small *Locals bundle with new(cx) and optional bind_actions(&self, cx), then use cx.actions().locals_with((...)).on::<A>(...) for coordinated LocalState-first typed UI actions,
  • use cx.actions().local(&local).set::<A>(...) / .update::<A>(...) / .toggle_bool::<A>() for single-local writes,
  • for view-owned keyed rows, bind payloads with .action_payload(...), prefer cx.actions().local(&rows_state).payload_update_if::<A>(...) as the default row-write path,
  • use cx.actions().transient::<A>(...) when the real effect must happen with &mut App in render(),
  • drop to cx.actions().models::<A>(...) only when coordinating shared Model<T> graphs,
  • if you intentionally need the raw model handle itself, treat that as the advanced lane via use fret::advanced::AppUiRawModelExt; + cx.raw_model::<T>(),
  • keep widget-local .action(...) / .action_payload(...) / .listen(...) for activation-only surfaces that do not already expose a narrower widget-owned app-facing helper.

Quick start (Cargo)

With defaults (desktop + app):

[dependencies]
fret = { path = "../fret" } # path is relative to your Cargo.toml

Enable selector/query helpers (optional):

[dependencies]
fret = { path = "../fret", features = ["state"] }

Enable the explicit router extension surface (optional):

[dependencies]
fret = { path = "../fret", features = ["router"] }

For editor-grade docking workflows, depend on fret-docking directly:

[dependencies]
fret = { path = "../fret" }
fret-docking = { path = "../fret-docking" }

If your crate lives under apps/ in this repository:

[dependencies]
fret = { path = "../../ecosystem/fret" }

Or explicitly opt into a smaller surface:

[dependencies]
fret = { path = "../fret", default-features = false, features = ["desktop", "shadcn"] }

Minimal app skeleton

use fret::app::prelude::*;

struct HelloView;

impl View for HelloView {
    fn render(&mut self, _ui: &mut AppUi<'_, '_>) -> Ui {
        shadcn::Label::new("Hello from Fret!").into()
    }
}

fn main() -> fret::Result<()> {
    FretApp::new("hello")
        .window("Hello", (560.0, 360.0))
        .view::<HelloView>()?
        .run()
}

If app code needs explicit style/token nouns or icon helpers/IDs beyond the default lane, import them from fret::style::{...} and fret::icons::{icon, IconId} instead of expecting them from fret::app::prelude::*. If app code needs explicit ThemeSnapshot, LocalState, or CommandId nouns for helper signatures / command registration, import them intentionally from fret::style::ThemeSnapshot, fret::app::LocalState, and fret::actions::CommandId. If app code needs explicit semantic-role nouns, import them from fret::semantics::SemanticsRole instead of expecting them from fret::app::prelude::*. If app code needs explicit selector/query helper nouns beyond the grouped cx.data() story (cx.data().query*(...), handle.read_layout(cx), cx.data().invalidate_query(...), cx.data().invalidate_query_namespace(...)), import them intentionally from fret::selector::ui::DepsBuilder, fret::selector::DepsSignature, and fret::query::{QueryError, QueryKey, QueryPolicy, QueryState, ...}. For adaptive UI helpers such as breakpoints, safe-area insets, pointer/media preferences, or Tailwind breakpoint probes, use fret::env::{...} explicitly. For logical assets, use fret::assets::{...} and prefer AssetBundleId::app(...) / AssetBundleId::package(...) plus AssetLocator::bundle(...) / register_bundle_entries(...); keep AssetLocator::file(...) and AssetLocator::url(...) as capability-gated escape hatches. For startup that needs one explicit development-vs-packaged decision, use AssetStartupPlan + AssetStartupMode from fret::assets::{AssetStartupPlan, AssetStartupMode} plus FretApp::asset_startup(...) / UiAppBuilder::with_asset_startup(...): keep native/package-dev inputs on development_dir(...) / development_manifest(...), and keep packaged/web/mobile-ready bytes on packaged_entries(...), packaged_bundle_entries(...), or packaged_embedded_entries(...). Generated modules still fit this packaged lane because they already expose ENTRIES, bundle_id(), Bundle, install(app), and mount(builder). Keep FileAssetManifestResolver::from_bundle_dir(...) / FileAssetManifestResolver::from_manifest_path(...) plus register_resolver(...) as the lower-level host-path escape hatch when app/bootstrap code intentionally installs file-backed resolver layers directly instead of staying on the builder startup contract. When native/dev-only UI helpers still need file reload ergonomics, keep app/widget code on logical bundle locators and let fret-ui-assets::ui::ImageSourceElementContextExt::use_image_source_state_from_asset_request(...) or fret-ui-assets::ui::SvgAssetElementContextExt::svg_source_state_from_asset_request(...) consume the resolver's bundle/reference bridge instead of introducing direct raw file-path widget loading. Keep resolve_image_source_from_host_locator(...) / resolve_svg_source_from_host_locator(...) as the lower-level UI-ready source seams, and use fret::assets::resolve_reference(...) / resolve_locator_reference(...) when a non-UI integration truly needs the raw external reference itself. On the app-facing builder path, prefer FretApp::asset_startup(...) / UiAppBuilder::with_asset_startup(...) when startup needs one explicit AssetStartupMode switch between development and packaged lanes, and layer additional packaged overrides with FretApp::{asset_entries, bundle_asset_entries, embedded_asset_entries} or UiAppBuilder::{with_bundle_asset_entries, with_embedded_asset_entries} when needed. On the host path, set_primary_resolver(...), register_resolver(...), register_bundle_entries(...), and register_embedded_entries(...) participate in one ordered resolver stack, so later registrations override earlier ones for the same logical locator. The same ordered builder surface now also includes compile-time/static entries through FretApp::{asset_entries, bundle_asset_entries, embedded_asset_entries} and UiAppBuilder::{with_bundle_asset_entries, with_embedded_asset_entries}.

Features

  • desktop: enable the native desktop stack (winit + wgpu) via fret-framework/native-wgpu.
  • app: recommended baseline for apps (shadcn).
  • state: enable selector/query helpers on AppUi (cx.data().selector_layout(...) for LocalState-first derived values, raw cx.data().selector(...) for explicit signatures, and cx.data().query(...) plus handle.read_layout(cx) for the default query read path, plus cx.data().invalidate_query(...) / cx.data().invalidate_query_namespace(...) for grouped app-lane query invalidation), plus the explicit fret::selector::* / fret::query::* secondary lanes when app code needs state helper nouns.
  • router: enable the explicit app-level router surface (fret::router::{app::install, RouterUiStore, RouterOutlet, ...}).
  • batteries: “works out of the box” opt-in bundle (config files + UI assets + icons + preloading + diagnostics).
  • config-files: load layered config files from .fret/ (settings/keymap/menubar).
  • diagnostics: enable default diagnostics wiring (tracing + panic hook; plus extra dev tooling).
  • tracing: advanced/maintainer alias for bootstrap tracing setup; prefer fret-bootstrap/tracing directly for explicit wiring.
  • devloop: advanced/maintainer alias for fret-launch/dev-state; prefer the owning crate directly when you need dev-state hooks.
  • material3: discoverability alias only; depend on fret-ui-material3 directly for Material 3 recipes and tokens.
  • ui-ai: discoverability alias only; depend on fret-ui-ai directly for AI-specific policy surfaces.
  • ui-assets: enable UI render-asset caches (images/SVG) and install default budgets.
  • icons: install the default built-in icon pack (Lucide).
  • preload-icon-svgs: pre-register SVG icons on GPU ready.
  • command-palette: enable the command palette wiring in the golden-path driver, including the default shadcn overlay bridge.

Design-system- or domain-specific ecosystems that do not form a stable fret root authoring story should stay as direct crate dependencies instead of root feature proxies. The root material3 / ui-ai feature names are retained only as discoverability aliases and do not pull those crates into the fret publish closure.

Web / wasm

fret is desktop-first. For web demos in this repository, use tooling:

rustup target add wasm32-unknown-unknown
cargo install trunk
cargo run -p fretboard -- dev web --demo ui_gallery

This runs apps/fret-demo-web via trunk serve.

Related workstream: docs/workstreams/fret-launch-app-surface-fearless-refactor-v1/

Choosing a native entry path

  • App authors (default recommendation): fret::FretApp::new(...).window(...).view::<V>()?
  • App authors with driver hooks: fret::FretApp::new(...).window(...).view_with_hooks::<V>(...)?
  • Advanced integration with fret defaults: fret::advanced::run_native_with_fn_driver(...)
  • Advanced integration with FnDriver hooks preserved: fret::advanced::run_native_with_fn_driver_with_hooks(...)
  • Advanced integration with a preconfigured FnDriver: fret::advanced::run_native_with_configured_fn_driver(...)
  • Advanced low-level interop driver path (compat seam, non-default): fret::advanced::interop::run_native_with_compat_driver(...)
  • Advanced low-level runtime/render/viewport seams: fret::advanced::{kernel::*, interop::*}

If advanced/manual-assembly code also wants the ordinary component authoring vocabulary (ui::*, .ui(), .into_element(...), model/overlay helper traits), import it explicitly with use fret::component::prelude::*;. fret::advanced::prelude::* intentionally stays on the advanced lane and no longer forwards the component prelude implicitly.

What remains first-class on fret

Advanced users do not need to drop to fret-launch immediately. The fret facade keeps the following seams first-class:

  • FretApp::{setup(...), view::<V>(), view_with_hooks::<V>()}
  • FretApp::asset_startup(...) plus fret::assets::{AssetStartupPlan, AssetStartupMode}
  • FretApp::{asset_entries(...), bundle_asset_entries(...), embedded_asset_entries(...)}
  • UiAppBuilder::{configure(...), setup(...), setup_with(...), with_asset_startup(...), with_bundle_asset_entries(...), with_embedded_asset_entries(...)}
  • fret::assets::{FileAssetManifestResolver, register_resolver(...)}
  • fret::advanced::FretAppAdvancedExt::install(...)
  • fret::advanced::UiAppBuilderAdvancedExt::{install(...), on_gpu_ready(...), install_custom_effects(...)}
  • UiAppDriver::{window_create_spec, window_created, before_close_window}
  • UiAppDriver::{record_engine_frame, viewport_input, handle_global_command}
  • fret::advanced::{kernel::*, interop::*}

The default builder chain stays small and app-facing on fret. Advanced users still keep the same extension seams without dropping to fret-launch immediately, but the GPU/effects/bootstrap hooks now live explicitly under fret::advanced instead of the default inherent builder surface.

Optional ecosystems also stay explicit. For example, the router integration lives under fret::router; wire it with FretApp::setup(fret::router::app::install) instead of expecting it to appear in fret::app::prelude::*. Docking and editor theming similarly stay on their owning crates (fret-docking, fret-ui-editor) so advanced apps opt into those ecosystems explicitly instead of learning extra fret root feature proxies. The default design-system surface is similarly curated under fret::shadcn: keep component names at shadcn::Button / shadcn::Card, use shadcn::app::install(...) for app wiring plus environment-aware host-theme syncing, shadcn::themes::apply_shadcn_new_york(...) for explicit one-shot/fixed presets, and shadcn::raw::* only when you intentionally need the full underlying crate surface. Treat shadcn::Button / shadcn::Card as the only first-contact component-family lane: shadcn::app::* and shadcn::themes::* are setup lanes, not peer discovery lanes. Environment / UiServices-boundary hooks stay off the curated lane: if you only depend on fret, reach them through fret::shadcn::raw::advanced::*; if you depend on the recipe crate directly, use fret_ui_shadcn::advanced::*. Reusable ecosystem bundles can share the same .setup(...) seam by implementing fret::integration::InstallIntoApp; ordinary app docs/examples should still teach plain installer functions first. For small app-local composition, it is also acceptable to write .setup((install_a, install_b)); prefer a named bundle type once that composition becomes reusable or crate-facing API. Because Rust does not let a trait-bound-only fn(&mut App) implementation accept plain function items without explicit casts, InstallIntoApp stays broad in implementation. Treat that as an internal accommodation: keep .setup(...) on named installer functions, tuples, or named bundles, and reserve .setup_with(...) for one-off inline closures or runtime-captured values. The same rule should apply to shipped resources: app-owned bytes normally live under AssetBundleId::app(...), ecosystem/package-owned shipped bytes normally live under AssetBundleId::package(...), and reusable crates should publish installer or mount helpers rather than asking apps to mirror internal bundle registrations. Icon packs are still installed through explicit crate::app::install seams backed by the global IconRegistry; reusable components should prefer semantic IconId / ui.* ids instead of baking one vendor pack into their public contract unless that dependency is intentionally explicit. The same explicit-lane rule applies to optional state helpers: keep grouped cx.data().selector_layout(...) as the default LocalState-first selector story, keep raw cx.data().selector(...) for explicit shared Model<T> / global signatures, and keep cx.data().query* plus handle.read_layout(cx) as the default query story. Import DepsBuilder / QueryKey-style nouns from fret::selector::* / fret::query::* only when app code actually needs to spell them. Editor-themed apps should depend on fret-ui-editor directly and install the desired preset in their own app setup. That keeps editor policy on the owning crate instead of teaching fret root feature aliases for app-specific theme replay.

That makes fret suitable for both general-purpose desktop apps and many editor-style customizations before you need to depend on fret-bootstrap or fret-launch directly.

When to drop down to fret-framework + fret-bootstrap

fret is designed to keep the “first app” and “small app” story simple. Prefer dropping down to manual assembly when you need:

  • a custom runner/event loop integration (fret-launch),
  • non-default settings/keymap/config file layering,
  • different icon/asset wiring policies than the kit defaults,
  • experimenting with alternate component surfaces without the kit defaults.

Mapping (rough):

  • fret::UiAppBuilder -> fret_bootstrap::UiAppBootstrapBuilder
  • fret::UiAppDriver -> fret_bootstrap::ui_app_driver::UiAppDriver
  • fret::advanced::run_native_with_fn_driver(...) -> fret_bootstrap::BootstrapBuilder::new_fn(...)
  • fret::advanced::run_native_with_fn_driver_with_hooks(...) -> fret_bootstrap::BootstrapBuilder::new_fn_with_hooks(...)
  • fret::advanced::run_native_with_configured_fn_driver(...) -> fret_bootstrap::BootstrapBuilder::new(...) with a preconfigured FnDriver
  • fret::advanced::interop::run_native_with_compat_driver(...) -> fret_bootstrap::BootstrapBuilder::new(...) for advanced low-level interop / retained driver cases
  • fret::advanced::kernel::* -> fret-framework::*

The recommended manual-assembly entry point remains fret-bootstrap, keeping the underlying driver hotpatch-friendly (function-pointer FnDriver surface, per ADR 0105 / 0110).