MishkaCmsCore.Runtime.Stylesheet (Mishka CMS Core v0.0.1)

Copy Markdown View Source

Manages CSS stylesheets in the MishkaCMS runtime system.

Stylesheets are runtime CSS content stored in the database — kit themes (e.g. Chelekom's @theme block + CSS variables), reusable utility layers, and per-site custom user CSS. Each row is inlined into the Tailwind v4 compilation pipeline at buildcss_content*. Order is controlled by kind:

  • :app_base — mirrors the developer's assets/css/app.css. First.
  • :theme — @theme {} blocks and :root CSS variables. Must
              come BEFORE Tailwind class generation references them.
  • :base — kit-shipped utility layers, animations, etc.
  • :custom — user-added overrides. Last (highest precedence).

Multi-tenancy mirrors JsHook — site_id == nil is global (kit themes default here), site_id == <uuid> is scoped to one site (user customizations default here).

Summary

Functions

Apply the full GuardedStruct pipeline (sanitize → validate → derive → main_validator) to attrs and return either {:ok, transformed_attrs} or {:error, errors}. Wire this into an Ash.Resource.Change to plug guardedstruct rules into Ash's changeset pipeline.

O(1) lookup of a field's compile-time metadata by name. Returns nil for unknown fields.

Compile-time-baked MapSet of every field name owned by the guardedstruct block. Used by GuardedStruct.AshResource.Change to decide, in O(1), whether a key in changeset.atomics belongs to the pipeline.

Validates that the keys in the provided input are valid for at least one action on the resource.

Same as input/1, except restricts the keys to values accepted by the action provided.

The sort key deciding which sheet wins when two of them set the same thing.

Types

t()

@type t() :: %MishkaCmsCore.Runtime.Stylesheet{
  __lateral_join_source__: term(),
  __meta__: term(),
  __metadata__: term(),
  __order__: term(),
  active: term(),
  aggregates: term(),
  archived_at: term(),
  calculations: term(),
  content: term(),
  description: term(),
  extra: term(),
  format: term(),
  id: term(),
  inserted_at: term(),
  is_global: term(),
  kind: term(),
  kind_order: term(),
  name: term(),
  paper_trail_versions: term(),
  permissions: term(),
  priority: term(),
  site: term(),
  site_id: term(),
  sorted_versions: term(),
  updated_at: term()
}

Functions

__guarded_change__(attrs, error? \\ false)

Apply the full GuardedStruct pipeline (sanitize → validate → derive → main_validator) to attrs and return either {:ok, transformed_attrs} or {:error, errors}. Wire this into an Ash.Resource.Change to plug guardedstruct rules into Ash's changeset pipeline.

The function is named __guarded_change__ because it does more than validate — it can also transform values (trim, downcase, slugify, auto-fill, etc.) before they reach the data layer.

__guarded_field_meta__(name)

O(1) lookup of a field's compile-time metadata by name. Returns nil for unknown fields.

__guarded_field_name_set__()

Compile-time-baked MapSet of every field name owned by the guardedstruct block. Used by GuardedStruct.AshResource.Change to decide, in O(1), whether a key in changeset.atomics belongs to the pipeline.

default_short_name()

input(opts)

@spec input(values :: map() | Keyword.t()) :: map() | no_return()

Validates that the keys in the provided input are valid for at least one action on the resource.

Raises a KeyError error at compile time if not. This exists because generally a struct should only ever be created by Ash as a result of a successful action. You should not be creating records manually in code, e.g %MyResource{value: 1, value: 2}. Generally that is fine, but often with embedded resources it is nice to be able to validate the keys that are being provided, e.g

Resource
|> Ash.Changeset.for_create(:create, %{embedded: EmbeddedResource.input(foo: 1, bar: 2)})
|> Ash.create()

input(opts, action)

@spec input(values :: map() | Keyword.t(), action :: atom()) :: map() | no_return()

Same as input/1, except restricts the keys to values accepted by the action provided.

json_api_match_route(_, _)

load_order(map)

@spec load_order(struct()) :: {integer(), integer(), integer()}

The sort key deciding which sheet wins when two of them set the same thing.

CSS is last-wins at equal specificity, so every component of this key ASCENDS: whichever sheet sorts last is the one the browser obeys. priority says so itself — lower loads first, so a higher value wins — and :for_compiler already sorted by it that way.

Both consumers then re-sorted on -priority, which emitted the highest value FIRST and therefore let the LOWEST one win. They agreed with each other, so nothing looked wrong: Studio showed the same value the page rendered. Both were the opposite of what an operator setting priority: 90 asked for, and there was no way to find that out except by reading two sorts in two modules.

Owning the key here is what keeps them from drifting apart again — TailwindCompiler decides what the browser sees and PageBuilder.Tokens decides what Studio shows, and they must not disagree.

A global sheet sorts before the site's own, so a site can always override what it inherited.

primary_key_matches?(left, right)