commits
This is way nicer than the Syntect API, is quite fast, and is also
actually up to date.
It basically doesn’t do what I want, unfortunately, because `minijinja`
needs the underlying type to implement `Serialize` if this is going to
work:
use minijinja::{Environment, Error};
use serde::Serialize;
pub(crate) trait Component: Serialize + Sized {
const VIEW_NAME: &'static str;
fn view(&self, env: &Environment) -> Result<String, Error> {
env.get_template(&Self::template())?.render(self)
}
fn template() -> String {
format!("components/{}.jinja", Self::VIEW_NAME)
}
}
The key bit is that `.render(self)`: it will *only* work if `self` there
has the `Serialize` constraint on it, because that’s how `mininjinja`
currently approaches things. (Future versions might not have that issue,
but future versions are not today.) I still like the idea, but as things
stand today, the result is that if you invoke an item that implements
`Serialize` but does *not* implement `Object` (and, for my purposes,
specifically `Object::call` as a way of invoking `Component::view`), you
get the “native” `Serialize` implementation instead. Which is fine for
how people generally use this, but doesn’t afford the “component” model
I was reaching for. Alas.
I’ll need to rethink this.
I will follow this with updates to the layout of templates as well:
these are really what I had done with “components” historically via
macros.
Instead of having a `view::template_for(view: impl View)` with internal
type shenanigans to support it, just use `Self::template()` relying on
the types that are *necessarily* available to support that via a default
`impl` for `View::template`. Works much nicer.
Some pages should reasonably have things that are not things like
length/duration/etc.
- Introduce a new `View` abstraction that lets me conveniently name the
pattern of mapping a given data type to a concrete and specific way of
rendering it: a view of that data! For now, this assumes there is only
a single such implementation. If I later need different views of the
same data (e.g. because a feed item and an on-page item should render
differently), I can easily make `FeedBookView` types to do that. This
feels decent so far!
Each such type has an associated item defining what template to
render, and the render function will need access to the environment.
This can be supplied directly or by way of e.g. an `impl Object` using
`call`, which has a `State` and can thus get the environment. This is
a mildly annoying bit of indirection, but means that invocations like
`{{some_archive()}}` or `{{some_book()}}` or whatever else should Just
Work™ when all is said and done.
- Refactor the existing `Archive` type to be a general-purpose tool that
can be used in the context of any `View` that needs it, because all
such views have access to the requisite inputs: all the items in the
project (or some pre-narrowed subset). Accordingly, update it to
account for the `Page`/`Post` distinction in its constructor.
Create basic `View` implementations for `Book`, `Image`, `Item`,
`NavItem`, `Qualifiers`, and `Label` as the starting points to guide
this implementation and to be useful first things to get rendering
this way.
- Leave some notes about how I am thinking about taxonomies more
generally, mostly for my future self but possibly also for guiding any
LLM codegen I experiment with.
- Switch to using `task::spawn_blocking` to trigger the rebuild task.
Wrap some of the reused data for site info in `Arc`s so they can be
shared across the thread pool.
- Perform a rebuild when the rebuild channel lagged as well as when it
sent changes. In support of that, add the ability to distinguish
between those cases throughout.
AI note: some of this was initiall implemented with Claude Code, then
rewritten by me. Some of it was also in response to prompting Claude
Code for critical review of the function.
This is a good project for experimenting on and seeing what works well
and where things go off the rails.
I realized that `Archive` really wanted to be applied only to posts, not
to pages, because an `Archive` will never include an item that does not
have a date attached. Thinking about how best to plumb that through led
me to realize that although the date is notionally a kind of metadata,
it properly distinguishes not *kinds of metadata* but *kinds of item*.
Thus, introduce a `Post` type which *includes* a `Page`, because every
`Post` has *at least* the same set of fields and thus can delegate to
the underlying `Page`.
This implementation is still a *bit* annoying in some spots, so I may
add more helper methods that directly reference the underlying fields,
but it gets the job done fairly well and avoids needing to do further
filtering in the `Archive`.
Previously, I was solving this by filtering the glob results down to
files that did not start with `_`, but because I have moved away from
SCSS to normal CSS, using LightningCSS to optimize/bundle, and have also
renamed the files to be easier to work with (`_index.css` for “entry
points” to directories, normal `content.css` and similar for regular
files in the project), that filtering no longer makes sense. Instead, I
want to treat files in the root of `_styles` as entry points that the
bundler can chase, and *not* do duplicate work.
This will need further iteration to use `build::Mode` later, but for now
it gives me a hook for *where* to do that when I am ready.
For clarity’s sake, rename `style::Mode` to `style::OutputMode`. This
probably also needs to become a top-level consideration, or perhaps also
a `build`-level consideration, because CSS is not the only thing that
could be affected by build mode. But this is a useful intermediate step.
Need to figure out how to get RustRover to do this correctly if I am
going to keep using it!
May use these in the future, but for now, make Clippy happy!
- Implement some functionality on the `Qualifiers` type for rendering.
- Keep `Retraction` a richer data structure until later.
This does not yet go far enough to fully render them, but it steps in
the right direction. It looks like I will probably be moving away from
using `Serialize` and `Deserialize` and to `impl Object for Type`.
This is always known statically in the case I am using it so might as well!
I want to make it possible to do template substitution or something that
accomplishes the same goal to enable data files to do things like set a
default slug pattern for all files in a directory.
I’m not yet sure how I want to handle “components” like the formatting
of blockquotes, notes, etc. within `.content`, because they *are* scoped
to that context. Maybe a good spot for a cascade layer? Or maybe “just”
doing the old-fashioned thing of making them be `.content blockquote`
and the like.
These are pulled over ~exactly as-is from v5.
Still not *great*, but better. The Sass `_index` → folder thing was, in
fact, pretty nice. CSS is, like JS, a language designed by people with
very, very weird taste.
Nesting seems nice, but in practice I am finding it doesn't work quite
as robustly as I had hoped.
- Update `lx` to use LightningCSS instead of the `grass` Sass compiler.
- Rename all `.scss` files to `.css`.
- Update all internal imports to use `@import` instead of `@use`.
It basically doesn’t do what I want, unfortunately, because `minijinja`
needs the underlying type to implement `Serialize` if this is going to
work:
use minijinja::{Environment, Error};
use serde::Serialize;
pub(crate) trait Component: Serialize + Sized {
const VIEW_NAME: &'static str;
fn view(&self, env: &Environment) -> Result<String, Error> {
env.get_template(&Self::template())?.render(self)
}
fn template() -> String {
format!("components/{}.jinja", Self::VIEW_NAME)
}
}
The key bit is that `.render(self)`: it will *only* work if `self` there
has the `Serialize` constraint on it, because that’s how `mininjinja`
currently approaches things. (Future versions might not have that issue,
but future versions are not today.) I still like the idea, but as things
stand today, the result is that if you invoke an item that implements
`Serialize` but does *not* implement `Object` (and, for my purposes,
specifically `Object::call` as a way of invoking `Component::view`), you
get the “native” `Serialize` implementation instead. Which is fine for
how people generally use this, but doesn’t afford the “component” model
I was reaching for. Alas.
I’ll need to rethink this.
- Introduce a new `View` abstraction that lets me conveniently name the
pattern of mapping a given data type to a concrete and specific way of
rendering it: a view of that data! For now, this assumes there is only
a single such implementation. If I later need different views of the
same data (e.g. because a feed item and an on-page item should render
differently), I can easily make `FeedBookView` types to do that. This
feels decent so far!
Each such type has an associated item defining what template to
render, and the render function will need access to the environment.
This can be supplied directly or by way of e.g. an `impl Object` using
`call`, which has a `State` and can thus get the environment. This is
a mildly annoying bit of indirection, but means that invocations like
`{{some_archive()}}` or `{{some_book()}}` or whatever else should Just
Work™ when all is said and done.
- Refactor the existing `Archive` type to be a general-purpose tool that
can be used in the context of any `View` that needs it, because all
such views have access to the requisite inputs: all the items in the
project (or some pre-narrowed subset). Accordingly, update it to
account for the `Page`/`Post` distinction in its constructor.
Create basic `View` implementations for `Book`, `Image`, `Item`,
`NavItem`, `Qualifiers`, and `Label` as the starting points to guide
this implementation and to be useful first things to get rendering
this way.
- Leave some notes about how I am thinking about taxonomies more
generally, mostly for my future self but possibly also for guiding any
LLM codegen I experiment with.
- Switch to using `task::spawn_blocking` to trigger the rebuild task.
Wrap some of the reused data for site info in `Arc`s so they can be
shared across the thread pool.
- Perform a rebuild when the rebuild channel lagged as well as when it
sent changes. In support of that, add the ability to distinguish
between those cases throughout.
AI note: some of this was initiall implemented with Claude Code, then
rewritten by me. Some of it was also in response to prompting Claude
Code for critical review of the function.
I realized that `Archive` really wanted to be applied only to posts, not
to pages, because an `Archive` will never include an item that does not
have a date attached. Thinking about how best to plumb that through led
me to realize that although the date is notionally a kind of metadata,
it properly distinguishes not *kinds of metadata* but *kinds of item*.
Thus, introduce a `Post` type which *includes* a `Page`, because every
`Post` has *at least* the same set of fields and thus can delegate to
the underlying `Page`.
This implementation is still a *bit* annoying in some spots, so I may
add more helper methods that directly reference the underlying fields,
but it gets the job done fairly well and avoids needing to do further
filtering in the `Archive`.
Previously, I was solving this by filtering the glob results down to
files that did not start with `_`, but because I have moved away from
SCSS to normal CSS, using LightningCSS to optimize/bundle, and have also
renamed the files to be easier to work with (`_index.css` for “entry
points” to directories, normal `content.css` and similar for regular
files in the project), that filtering no longer makes sense. Instead, I
want to treat files in the root of `_styles` as entry points that the
bundler can chase, and *not* do duplicate work.
- Implement some functionality on the `Qualifiers` type for rendering.
- Keep `Retraction` a richer data structure until later.
This does not yet go far enough to fully render them, but it steps in
the right direction. It looks like I will probably be moving away from
using `Serialize` and `Deserialize` and to `impl Object for Type`.