Asset Declaration

Declare CSS, JavaScript, fonts, and images for your extension.

Extensions declare static assets (CSS, JS, fonts, images) via declares_assets() and required_assets().

The Methods

fn declares_assets(&self) -> bool {
    true
}

fn required_assets(&self, paths: &dyn AssetPaths) -> Vec<AssetDefinition> {
    vec![
        // Asset definitions
    ]
}

AssetPaths

pub trait AssetPaths: Send + Sync {
    fn storage_files(&self) -> &Path;
    fn output_dir(&self) -> &Path;
}
  • storage_files() - Source directory (storage/files/)
  • output_dir() - Output directory (web/dist/)

AssetDefinition

pub struct AssetDefinition {
    source: PathBuf,
    destination: &'static str,
    asset_type: AssetType,
    required: bool,
}

pub enum AssetType {
    Css,
    JavaScript,
    Font,
    Image,
}

Creating Assets

CSS

AssetDefinition::css(
    paths.storage_files().join("css/main.css"),
    "css/main.css"
)

JavaScript

AssetDefinition::js(
    paths.storage_files().join("js/app.js"),
    "js/app.js"
)

Fonts

AssetDefinition::font(
    paths.storage_files().join("fonts/inter.woff2"),
    "fonts/inter.woff2"
)

Images

AssetDefinition::image(
    paths.storage_files().join("images/logo.svg"),
    "files/images/logo.svg"
)

Builder Pattern

For advanced configuration:

AssetDefinition::builder(
    paths.storage_files().join("css/optional.css"),
    "css/optional.css",
    AssetType::Css
)
.optional()  // Don't fail if missing
.build()

Complete Example

impl Extension for WebExtension {
    fn declares_assets(&self) -> bool {
        true
    }

    fn required_assets(&self, paths: &dyn AssetPaths) -> Vec<AssetDefinition> {
        let css = paths.storage_files().join("css");
        let js = paths.storage_files().join("js");
        let fonts = paths.storage_files().join("fonts");

        vec![
            // Core CSS
            AssetDefinition::css(css.join("core/variables.css"), "css/core/variables.css"),
            AssetDefinition::css(css.join("core/reset.css"), "css/core/reset.css"),
            AssetDefinition::css(css.join("core/typography.css"), "css/core/typography.css"),

            // Component CSS
            AssetDefinition::css(css.join("components/header.css"), "css/components/header.css"),
            AssetDefinition::css(css.join("components/footer.css"), "css/components/footer.css"),
            AssetDefinition::css(css.join("components/cards.css"), "css/components/cards.css"),

            // JavaScript
            AssetDefinition::js(js.join("main.js"), "js/main.js"),
            AssetDefinition::js(js.join("analytics.js"), "js/analytics.js"),

            // Fonts
            AssetDefinition::font(fonts.join("inter-regular.woff2"), "fonts/inter-regular.woff2"),
            AssetDefinition::font(fonts.join("inter-bold.woff2"), "fonts/inter-bold.woff2"),
        ]
    }
}

Asset Pipeline

Assets flow through this pipeline:

storage/files/css/main.css
         |
    Extension declares asset
         |
    Asset validation
         |
    Copy to output
         |
web/dist/css/main.css

URL Routing

Assets are served at these paths:

Type Source URL
CSS storage/files/css/ /css/*
JS storage/files/js/ /js/*
Fonts storage/files/fonts/ /fonts/*
Images storage/files/images/ /files/images/*

CSS Organization

Recommended structure:

storage/files/css/
├── core/
│   ├── variables.css
│   ├── reset.css
│   └── typography.css
├── components/
│   ├── header.css
│   ├── footer.css
│   └── cards.css
├── pages/
│   ├── homepage.css
│   └── blog.css
└── features/
    ├── docs-nav.css
    └── search.css

CLI Commands

# List declared assets
systemprompt web assets list

# Verify assets exist
systemprompt web assets verify

# Copy assets to output
systemprompt web assets publish