Zine

Devlog

About

This is a non-exhaustive, curated list of changes meant to help users quickly see what has improved since they last checked.

You can subscribe to this page via RSS.

UPGRADING SAFELY

Upgrading Zine can change how your website is rendered because of bugs or breaking changes. Reading the changelog helps you prepare, but to fully ascertain that your website looks as expected, I recommend following this procedure:

  • zine release -o old       (with old Zine)
  • zine release -o new       (with new Zine)
  • diff -ru new old

At this point is up to you to decide if any change in the diff is desireable or not. If you see a change that is not mentioned in the changelog, it might be the result of a bug, in which case a reproducible bug report would be appreciated.

More examples of useful diffing commands

Normal diffing is line based, but occasionally you will want to find more easily which word has changed within a line.

This command highlights individual words:

  • git diff --word-diff=color --no-index new old

This command highlights individual characters, useful for quickly detecting whitespace changes:

  • git diff --no-index --word-diff-regex=. new old

v0.13.0

2026-07-19

This release contains schema changes, make sure to run zine install-schemas after upgrading.

Breaking changes

  • The config file auto_target_blank setting has moved under the new supermd field, as there are now more supermd rendering settings you can tweak.

    Before:

    .zine_version = "0.12.0",
    .auto_target_blank = true,
    // ...
    

    Now:

    .zine_version = "0.12.0",
    .supermd = .{
        .auto_target_blank = true,
    }
    // ...
    
  • The fontmatter skip_subdir setting has been deleted (it never did anything).

  • Starting now, SuperMD documents don’t allow heading level skips anymore. If you have a ‘#’ heading followed by ‘###’, you will now get a build error. Releated to this, SuperMD documents now must start with heading level 1 (’#’). If you were deliberately starting at ‘##’ to render <h2> headings, the aforementioned supermd field in ‘zine.ziggy’ has headings_h2 which, when set to true, will render level 1 headings as h2 and so forth. Note that sectioned content ($section) is exempt from this check for now.

    My recommendation for upgrading is to first understand if you were defaulting to starting each document from ‘##’. If so, then you probably want to set headings_h2 in ‘zine.ziggy’ and you will want to shave off one heading level from all your files. Here’s a one liner that can help with that (make sure to have commited all pending changes before running it!):

    Linux & Mac:

    find . -name "*.smd" -type f -exec perl -pi -e 's/^#//' {} +
    

    Windows:

    Get-ChildItem -Recurse -Filter *.smd | % { $p=$_.FullName; $c=[IO.File]::ReadAllText($p,[Text.Encoding]::UTF8); [IO.File]::WriteAllText($p,($c -replace '(?m)^#',''),[Text.Encoding]::UTF8) }
    

    After running the command, make sure to audit the resulting diff for errors.

    For everybody else nothing should change, except maybe discovering that you occasionally skipped a heading level by mistake. I found a few instances of that when upgrading my own websites.

New features!

  • SuperMD errors have greatly improved. Techatrix started the work in #239 and coincidentally soon after I pushed the work further because I wanted the new “skipped heading level” errors to not be confusing.

  • In SuperHTML, $build.development is true when the build is being served by the Zine live server

  • In SuperHTML, $site.index() returns the root index page (aka the homepage) of a site. It’s the same as $site.page('') but it’s clearer for a reader what your intent was.

  • You can now specify forbid_subsections in the frontmatter of a index.smd file. When set to true, no child page can use the foo/index.smd naming style as that would define a subsection. Users will be prompted to name the file foo.smd. This setting is useful to guarantee that $page.leaves() doesn’t skip pages that were meant to be leaves.

Bugfixes

  • Last release announced $site.custom but a bug prevented scripty from being able to access that data, sorry!
  • auto_target_blank was a bit too eager and ended up adding the attirbute to all links when set to true.
  • SuperHTML would crash when trying to put :text into a self-closing XML element.
  • The live development server would fail to serve the livereloading JS and websocket endpoints when a url_path_prefix was defined.
  • Using Zine from a build.zig would cause Zine to try to parse your Git tags, this was caused by a piece of build functionality that helped me remember to properly create tags and update my zon file when releasing Zine. It’s now disabled when using Zine as a dependency.
  • A bug prevented [](#foo) refs to be validated, you might find new broken deep links after this update.

new-forum

2026-07-09

To give more space to requests for help, and to start disentangling Zine discussion from Discord, I created a new Discourse forum that I invite Zine users to join both to offer and receive help using Zine, and to discuss static sites in general.

https://zine.discourse.group

v0.12.0

2026-06-29

This release contains a few new features and one big breaking change.

The Zine server now works with Firefox

I finally tracked down and solved the issue that would cause Firefox to stop loading pages from the development webserver if you “clicked around” too much. Turns out I forgot to flush, and Firefox forgot to kill half-upgraded websocket connections on page reload (firefox bug report).

You can now add directories to static assets

Tired of having to list all your fonts one by one in static_assets?

You can now put them in a fonts directory and just add that to your config file. Or you could just create assets/static and use that as a convention that is not limited to just fonts.

I’m also pretty proud of my implementation of this feature. Without some care it’s easy to make this feature computationally more hungry than it needs to be.

Also note that Zine will complain if the same file ends up being included twice (either directly or via directory inclusion). This is done to make sure you don’t end up in the situation where you think you removed a file from static assets, but in reality it’s still being installed because of a second reference.

The Zine config file now supports custom fields

The new zine.ziggy definition allows you to specify a .custom Ziggy Dictionary that you can access via $site.custom. See below in the upgrade guide section how that looks like.

Codeberg Pages

The docs now contain a page dedicated to deploying to Codeberg Pages, for those who might be interested.

Setup Zine Version 2

The GitHub / Codeberg kristoff-it/setup-zine@v1 action required you to specify which version of Zine to fetch, but the new Ziggy config file features a zine_version field that should now be considered the authoritative source of truth for which version of Zine your website requires.

Update your CI scripts to use kristoff-it/setup-zine@v2 and remove the input setting to enjoy a more seamless experience.

Note that now Zine will complain about versions not matching between the website config file and the Zine executable version.

SuperHTML Scripty changes!

  • Added $page.subpagesByAuthor() and $page.subpagesByTag(), which will allow you to, well, find all subpages of a given section that have the requested author and tag, respectively. It was already possible to manually iterate subpages and filter them on this property, but the builtin functions can consult an index, offering better performance. The -byAuthor variant of these functions relies on a Ziggy frontmatter change explained in the frontmatter upgrade section further ahead in this changelog entry.

  • Added $page.leaves() which returns a (flat) list of non-section pages in the subtree of the target section page. It accepts an optional argument to limit the length of the iteration. This function is useful to show the latest entries in a structured section.

  • Removed the ability to pass arguments to $site.pages(), which allowed you to iterate over a list of known pages. It was originally created to simplify running the same html snippet over a list of manually selected pages, but it’s fully replaceable by looping over a Ziggy array defined in the frontmatter.

    Old:

    <ul :loop="$site.pages('foo', 'bar')">
      <li :text="$loop.it.title"></li>
    </ul>
    

    New:

    <ul :loop="$page.custom.get('menu-entries')">
      <li :text="$site.page($loop.it).title"></li>
    </ul>
    
  • You can now pass multiple arguments to $site.page, which will become components to compose the final path used to find the target page. This means that the following invocations are equivalent:

    • $site.page('foo/bar/baz')
    • $site.page('foo', 'bar/baz')

    This can be useful when part of your path is fixed while part is parametrized:

    $site.page('speakers', $page.authors.at(0))
    
  • The SuperHTML Scripty reference now uses slightly different syntax to refer to arrays. Previously [String] was notation for an array of strings, while now it’s []String, in order to more closely mirror the new Ziggy Schema syntax.

    Variadic function arguments are still specified as [String...].

New Ziggy Upgrade Guide

Ziggy is the data serialization language used in the Zine config file (zine.ziggy) and the frontmatter in SuperMD files. Up until recently it was probably the weakest point of Zine but, after giving it some love, I’m happy to say that this is not the case anymore. Unfortunately this comes at the cost of some breaking changes, for which I do apologize. I tried to put all breaking changes that I could think of in this release, in order to leave users alone for a while once they’re done upgrading.

Some Ziggy syntax changed, but most importantly both parser and surrounding tooling has matured a lot which enables pushing past previous cul de sacs.

Better error messages

If you ever wrote a bad date, you probably saw this misleading error message:

content/index.smd:3:9:
    .date = @date("1990-01-1T00:00:00"),
            ^
syntax error: '@'

This is now:

content/index.smd:3:9: error: unable to parse date: InvalidCharacter
|    .date = .date("1990-01-1T00:00:00"),
|            ^^^^^^^^^^^^^^^^^^^^^^^^^^^

Unfortunately Zine is not yet able to tell you that there’s a missing digit in the day section of the date, but it’s on my pile of TODOs to eventually take direct control over date parsing and provide even better diagnostics.

In-editor schema intelligence!

Looking at the usual suspects (other SSGs), I believe this is currently a unique feature of Zine. To unlock this ability:

  1. Your editor must be configured to launch the Ziggy LSP for .ziggy, .ziggy-schema and .smd files. In the future SuperMD will have a dedicated LSP but for now the Ziggy one will offer frontmatter support.
  2. You must have the correct Ziggy Schema files installed in your Zine website. Those files contain the schema definition for the Zine config file (zine.ziggy) and SuperMD frontmatter. Use zine install-schemas to have Zine automatically place up-to-date copies of those files at the right locations (also zine init will now generate a project with those files already in place). Note that install-schemas requires you to update your zine.ziggy config file first.

It’s not pictured in the video, but zine.ziggy also has in-editor support now. The Ziggy LSP is not yet at the same level of polish of, say, the SuperHTML one, but it will be improved over time.

Upgrading your website

Ziggy Document syntax changed slightly, checkout the official docs for the full list of changes. The ones that Zine users care the most about are the following two:

  1. @foo literals are gone.
  2. Structs don’t have names anymore.
  3. Both of the above have been functionally replaced by the newly introduced tagged unions.
  4. Structs now have a leading dot like in Zig, so
    { .foo = true } becomes .{ .foo = true }.

Lastly be aware that you can omit every struct field that in their Ziggy Schema definition is:

  • an optional (leading ? in the type expression)
  • a slice (leading [] in the type expression)
  • a dictionary (leading {:} in the type expression)
Upgrading your zine.ziggy

Before:

Site {
  // ...
}

After:

.zine_version = "0.12.0",
.site = .simple(.{ // <-- note the dot!
  // ...
}),

The contents of .simple should remain the same as they were before.

If you have a multilingual website:

.zine_version = "0.12.0",
.site = .multilingual(.{ // <-- note the dot!
  // ...
}),

In this case as well the contents of .simple should be the structurally the same but beware that nested structs (e.g. Locales), will now need a leading dot.

As a reminder, you can now also define custom properties in your config file, which become accessible via $site.custom:

.zine_version = "0.12.0",
.site = .simple(.{ // <-- note the dot!
  // ...
}),
.custom = { // <-- dicts have no dot!
  "my": ["custom", "values!"],
}

After fixing your zine.ziggy, it’s a good idea to run zine install-schemas.

BREAKING CHANGE

The new Zine config file schema has a new setting: auto_target_blank which defines wheder external links generated by SuperMD should have a target blank attribute by default or not.

Zine previously defaulted to adding these attributes automatically while now they are turned off by default. Define the setting to true if you want to preserve the previous behavior.

Note that you can always override this setting for each link by using explicit Scripty syntax when defining the link:

[my link]($link.url(https://example.com).new(true))
Upgrading your frontmatters
  • @date has been replaced by .date and .unix (which accepts a unix timestamp instead of a string)
  • .author has been replaced by .authors which can be omitted and accepts a list of strings
  • Just like for the config file, structs now need a leading dot, which will affect you if:
    • did not use braceless top-level syntax for your frontmatter
    • define alternatives as each is a struct
    • used struct syntax inside of your custom data dictionary
  • Some previously mandatory fields have now become optional, see the frontmatter Ziggy Schema for more info (it’s also present in the SuperMD section of the Zine docs).

In short, this is what it looks like.

Before:

---
.title = "Home",
.date = @date("2018-07-06T00:00:00"),
.description = "",
.author = "Loris Cro",
.layout = "index.shtml",
.alternatives = [{ 
    .name = "rss",
    .layout = "rss.xml", 
    .output = "/index.xml",
}],
---

After:

---
.title = "Home",
.date = .date("2018-07-06T00:00:00"),
.layout = "index.shtml",
.alternatives = [.{ // <-- note the dot!
    .name = "rss",
    .layout = "rss.xml", 
    .output = "/index.xml",
}],
---

For the .author field my recommendation is to hardcode the author name in the template for personal websites where the author is essentially always the same person. The new .authors field accepts a list of strings for cases where your have more complex needs.

In case that you do have more complex author management needs, don’t forget that you can use entries in the .authors field not only as “directly printable” names, but also as keys into page navigation (e.g. each author has a dedicated page) or a .custom field defined somewhere, maybe in $site.custom, for example.

v0.11.3

2026-06-17

This release upgrades to Zig 0.17.0-dev all the way up from 0.15.0. I did not make a release for Zig 0.16.0 because of regressions in translate-c, which have now been fixed.

This release contains relatively few and small user-facing improvements and its main purpose is to isolate potential bugs that come from the Zig upgrade process from upcoming changes to Zine.

In the next version of Zine I will update Ziggy (the .smd frontmatter language) to a new version that changes some of the syntax, requiring you to do some work before you can use it.

Bugfixes & Breaking Changes

  • Only emit id attributes from SuperMD when it has a set value. (#198, thanks GasInfinity!).
  • Fixed a bug where an empty index.smd (i.e. no content, no frontmatter) would be partially picked up by Zine, creating a buggy section, which would then crash Zine under certain circumstances.
  • Fixed a bug where toggling a .draft field from false to true would cause the Zine dev server to crash.

New features

  • In SuperMD, you can now specify audio embeds, like so: [](<$audio.asset('music.wav')>) (#195, thanks PowerUser64!).
  • In SuperHTML, added .minus() to integers (#188, thanks acc1729!).
  • Releases now include a FreeBSD tarball (#202, thanks dmarker!).
  • Zig build system integration has been updated to Zig 0.17.0-dev (see the Zon file for more information).

v0.11.2

2025-11-21

This release fixes some bugs that were discovered since the last and should not require any change for existing websites.

The next release will merge a new version of Ziggy (the data language used in SuperMD frontmatters) that instead will require you to change your content files, so brace for upcoming impact :^) In exchange though, the Ziggy language has improved a lot and you will finally be able to get great code intelligence, which will help you get your frontmatters right more easily. More on that in the next release.

Fixes:

  • Fixed rendering of links to alternatives when absolute paths are involved.
  • zine init template files were created back before superhtml had good autoformatting support, resulting in a bunch of code moving around when you saved the file for the first time (using a new build of superhtml w/ more refined autoformatting behavior). Now the template files embedded in Zine have been formatted, giving you a better out-of-the-box experience.
  • Linking to the site homepage from SuperMD was impossible because of a bug and because of a missing feature. You can now use either vanilla syntax ([home](/)) or explicit Scripty syntax ($link.site(), just added in this release).
  • Zig build system integration: the serve step now correctly uses a prebuilt zine if you specify the path option, previously it would ignore the setting and default to building Zine from scratch always.

v0.11.1

2025-07-26

Another round of bug fixes, with this release all known bugs have been solved!

  • The release and serve commands dealt with some asset paths in an inconsistent manner, resulting in the situation where an asset path would work with only one of them at a time, depending on whether it was absolute or relative. Now all paths are normalized before being used.
  • An absolute install path given to a static asset would be interpreted as a absolute path by release, resulting in an attempt to install assets outside of the output directory which is, generally speaking, not optimal. We now normalize paths to ensure we don’t try to override your /etc/shadow (or ~/.ssh/id_rsa) by mistake :^)
  • Fixed some invalid memory accesses when deiniting a website build. These bugs shouldn’t have impacted the user experience and were in fact caught by CI.

v0.11.0

2025-07-24

This release brings minor backwards compatibility breakage and a few good bug fixes.

Breaking change

Zine will now refuse to output a website release to a non-empty directory. This solves the problem of users being surprised that old files are somehow still “being rendered” when they’re actually not, and this also seems a better choice than defaulting to emtpying the output dir, as that could have catastrophic consequences.

You can use --force (or -f) to ignore the presence of other files.

Related to this change --install has been renamed to --output since Zine now refuses to “install” releases.

Bugfixes

  • Added missing check to nextPage?, prevPage?, hasNext, hasPrev when the target page is the root index. The Zine section model defines the index page of a section to not be part of the section, consequently the root index cannot have any sibling. A proper error will be reported now.
  • Ziggy parsing code failed to report syntax errors in string escapes, resulting in the confusing situation where an error is being reported but the list of diagnostics is empty. This is now reported correctly.
  • SuperMD would mistake ![](https://foo.com/myphoto.jpg) (i.e. remote url in vanilla image syntax) for a local asset. This has now been fixed.

v0.10.4

2025-07-16

This release upgrades Zine to Zig 0.15.0-dev (post Writergate) and fixes two silly bugs about build assets:

  • The Zine live server will now correctly serve build assets (#162, thanks llyrical-island!).

  • Up until now Zine failed to install all build assets when making a site release. Turns out that we were missing the required code entirely, whoops!

As an upgrade suggestion, if you have a website that uses build.zig and you don’t want to upgrade your own code to Zig 0.15.0-dev (post Writergate), then consider using in your build.zig .zine = .{ .path = null } (it’s a field of zine.Options), which will allow you to use a system-provided build of Zine.

Of course that won’t solve potential CI issues, but you will at least be able to try it out locally. If you’re in that situation, do try to upgrade your code it’s going to be worth it, especially in preparation for the upcoming async I/O redesign coming soon to Zig!

v0.10.3

2025-07-07

After hiatus of a few months, I’m resuming development of Zine. This release contains bug fixes and minor improvements.

This new Zine development season will focus on two main new big features:

  • SuperHTML snippets, so the ability to create reusable components
  • LSP support for Scripty

New changes:

  • Zine development server:

    • Added ipv6 support (#146, thanks Spiffyk!).
    • Fixed a crash when trying to some paths with a missing trailing slash.
  • SuperHTML:

    • Added str() to Int to make it possible to use them in formatting (#140, thanks knutwalker!).

v0.10.2

2025-04-22

  • SuperHTML:
    • Fixed a crash when using $page.alternative(), in the refactoting to standalone I forgot to wire back some state management, resulting in a reliable crash when using that function. Sorry! Now it’s fixed though.
    • DateTime: introduced the ability to add and subtract time durations to a DateTime.
  • SuperMD:
    • Fixed a crash when providing a URL to the root path in some directives.
  • Zine Development Server:
    • The hot reload script injected to your pages now has a defer attribute which helps simplify local performance testing of your website.
    • Fixed websocket disconnections when the client sent long headers, particularly felt on Windows ((#139) thanks Scott!).
  • Added nix flake ((#143) thanks sreehax!)

v0.10.1

2025-04-10

  • SuperMD:

    • Added support for $image.size()
      (specify image size manually)
    • Added support for fragments in Markdown vanilla link syntax. Now [](/my/page/#my-ref) will correctly translate to $link.page('my/page').ref('my-ref')
  • SuperHTML:

  • Bugfixes:

    • Fixed one memory corruption bug that could cause Zine to crash on startup (fc0b117).
    • Fixed some path manipulation inconsistencies on Windows, now CI is green also for the greatest OS ever made.
    • The help menu wrongly suggested to use --output to specify an install path, while the correct string is --install.
    • Zine checks for output path collisions but a bug in how page aliases were handled in the code could cause false positives.

v0.10.0

2025-04-08

Standalone Zine is here!

It’s finally time to release the standalone version of Zine:

  • it’s stupid fast
  • it’s more strict than before
  • it removed almost all limitations (e.g. rendering other pages via scripty)
  • it might have added new bugs as many things were reimplemented, sorry!

Download a prebuilt version to try it out.

WARNING

We have updated the suggested way of deploying to GitHub Pages, and for all GitHub based deployments you will want to check out kristoff-it/setup-zine.

If you’re a new user of Zine, I highly recommend you start by running zine init in an empty folder to get a sample website bootstrapped for you by Zine.

For existing users, here’s the upgrade guide after you have downloaded Zine and put it in PATH:

  1. Create a zine.ziggy file in your website’s root directory. Every option your original build.zig can be ported over except build_assets (but build.zig integration still exists if you need that), here’s an example:

    Site {
       .title = "Zine - Static Site Generator",
       .host_url = "https://zine-ssg.io",
       .content_dir_path = "content",
       .layouts_dir_path = "layouts",
       .assets_dir_path = "assets",
       .static_assets = [
          // omitted for brevity
       ],
    }
    
  2. Run zine to activate the development server and watch the errors that it reports. Zine has become a bit more strict when it comes to paths and it might complain about absolute paths in locations where it previously accepted them. Most errors should be straight forward to fix. The only breaking change that will silently cause issues is that previously aliases and alternatives output paths were always relative to the website root, while now a relative path will place the alias / alternative in a subdir of the current page if the output path provided is relative.

  3. If your website is complex enough, you might want to diff the new version against the old. Assuming you have a build in zig-out/ or a similar location, you can run these commands to get a diff:

    $ zine release
    $ git diff --word-diff=color --no-index zig-out public
    

    The first command will output your website to public/ which then you can diff against an older build of your website. You should not expect the diff to be perfectly empty, as some minor rendering details have changed, but all the changes should be positive once you inspect them. If you see a change that seems wrong, please open an issue with a reproducer. We don’t need the full diff, just the sources to produce the new output and an indication of what is wrong (and why you think the output is wrong).

This release adds a ton of improvements to Zine, here are just a couple highlights for brevity:

  • The official docs have been rewritten to reflect the changes, and most importantly I’ve finally added navigation to the Scripty references. Go take a look, they’re pretty cool in my opinion :^)
  • Added support for math formulas in SuperMD via $mathtex. Unfortunately we depend on JavaScript for runtime rendering, but eventually we will have our own parser to do everything at build time.
  • TreeSitter highlighting now captures more classes for tokens thanks to Willians Faria (#131)

v0.9.0

2025-03-11

NEW ZINE

At some point after the last release I started working on a standalone version of Zine that doesn’t depend on the Zig build system anymore, meaning that you will be able to download a zine executable and run zine serve without even needing to have Zig installed. I will write more on it once the work is done, but you should expect plenty of improvement with no downsides as it will still be optionally possible to integrate with the Zig build system as needed. ETA is a few weeks from now.

THIS MEANS that this is the last big release of Zine in its current form. Unless current users of Zine report regressions, I will focus entirely on the new standalone version.

Let’s start with the BREAKING CHANGES:

  • Zine now depends on Zig 0.14.0, which means that you will also need to change your website’s build.zig.zon. Starting from the next version of Zine this will not be necessary anymore as you will only need a zine.ziggy file to define your website’s configuration. That said, here are the changes you will need to make to your build.zig.zon for this version of Zine after upgrading to Zig 0.14.0:

    1. Change name from a string to a Zig enum, like so: .name = .site. Note that this field is required by Zig but not used by Zine so any valid value is fine.
    2. Run zig build after fixing the name field and watch the compiler report a new error. This error will tell you that you need to add a fingerprint field. What’s important is that it will also suggest a value to give to the new field. Copy it and paste it into your Zon file (right below version is a good place where to put it) like so: .fingerprint = 0xAAAAAAAAAA (where 0xAAAAAAAAAA is the value suggested by Zig).
  • $page.prevPage() and $page.nextPage() have been renamed to $page.prevPage?() and $page.nextPage?() respectively. The policy in Zine is to have all builtin functions that return an Optional to end with a question mark.

Other new features in Zine:

  • Zine is now MIT licensed. It was actually intended to be that from the start but I forgot to add the file. Consider this a reverse rugpull where from proprietary we went to open source. A rug has been pushed under your feet, in a sense.

  • Zine can now render footnotes in SuperMD files! Note that you will need to change your SuperHTML templates to explicitly print footnotes (if present). See $page.footnotes?() for more info. This change was contributed by Brandon in #90.

  • Introduced opt-in automated image size attributes for local images! When enabled, this feature will make Zine add automatically height and width attributes to image assets embedded in SuperMD files. Doing so will mean that the browser will be able to know the size of all images even before downloading them, completely removing the need for reflowing the page, and overall giving the impression of a much faster page load. See this section for more info on how to enable it. This feature was contributed in #88, thank you pfg!

    NOTE

    If you were using main branch Zine, this might be perceived as a breaking change because this feature was enabled by default when first added (but it was never part of a full release so it’s not listed under breaking changes).

  • Added Array.first?() and Array.last?(), which will return the first and last item in an array, respectively, or null if the array is empty. This can be useful to implement advanced prev-next page navigation that crosses site section boundaries, as explained here.

  • Added in SuperMD $link.alternative, which allows linking to an alternative version specified in the target page (by using the alternatives frontmatter field).

  • The Zine dev server now implements debouncing in order to avoid issuing a flurry of rebuilds when an editor splits a file save operation into multiple file system level events. The dev server has a new related CLI flag, run zig build help for more info.

  • New version of flow-syntax by neurocyte brings syntax highlighting support for:

    • Gleam
    • CMake (to make it easier for you to port CMake to build.zig using flow)
    • Astro (not sure what that’s used for, probably nothing interesting)

Here are the bugfixes:

  • Git information parsing would error out when a repo only had one commit in it. This has been fixed in #85, thank you Brandon!

  • The Zig stdlib has a bug that causes the webserver to deadlock on Windows, Zine has a temporary workaround in it but I had left a mistake in the code that was fixed in #89, thank you again Brandon!

  • Added support for query parameters in URLs in Zine’s dev server (#87), thank you Techatrix!

  • Zine now uses a new version of Zeit by rockorarger which fixes a couple minor bugs in date time handling (#91).

  • Links generated for page alternatives would erroneously add a trailing slash, fixed in (#103) by Anthon, thanks!

  • Fixed hot-reloading support in the Zine dev server on Windows in (#116), thanks MiXiPi!

  • Previously, defining a page alias that contained new directories would cause an error when outputting the website. This has been fixed in (#122), thanks deevus!

v0.8.0

2024-10-28

  • Page alternatives now require you to set the name field. You will get a build error if the name is not defined. It would previously default to an empty string, but now it’s mandatory because of the newly added builtins.
  • SuperMD
    • Added some error checking to SuperMD builtins that relate to links, like trying to use $link.ref in combination with $link.url.
    • $link.alternative('rss') to link to a page alternative
  • SuperHTML
    • Fixed documentation for linkRef, which wrongly reported it did not accept any argument.
    • Added $page.alternative('rss') which allows you to obtain an Alternative value by name (instead of looping over all $page.alternatives).
    • Added link() to Alternative so you can now link to RSS feeds like so:
      <ctx alt="$page.alternative('rss')">
        <a href="$ctx.alt.link()" type="$ctx.alt.type">
          RSS feed
        </a>
      </ctx>
      
      (the Frequently Asked Questions section was updated accordingly)

v0.7.2

2024-10-25

  • Windows should now work well. A path-related bug that caused assets to not be detected correctly has now been fixed, and a second bug that caused the webserver to fail to update when a new change happened has also been “fixed” (it’s actually an annoying networking bug unrelated to Zine, so we implemented a workaround). In any case, Windows should now be a first-class citizen for Zine so please consider rawdogging Zine on Windows instead of using Docker/WSL (also report any issue you find!).

  • In the previous release I added by mistake some debug logs that could not be silenced and that caused the webserver to show your the error window even when no error had happened, this is now fixed, sorry!

  • Creating duplicate ids inside of a content file is now properly reported as an error by Zine. Some ids would also not be correctly recorded (for the purpose of deeplinking) before, and that too has been fixed now.

  • New SuperHTML Scripty builtins:

    • $page.linkRef('foo') can be used to deep-link from SuperHTML templates
    • sriHash() can be called on assets to generate a hash for Subresource Integrity, thanks Shardion (#73)!
    • $build.git() can be used to access repo information when in a repository, see the SuperHTML Scripty reference for more information, thanks Sc3l3t0n (#77)!

v0.7.1

2024-10-16

  • When using the simplified website() / multilingualWebsite() interface, you can now pass -Dinclude-drafts flag to enable rendering of content pages that have .draft = true set in their frontmatter. Thank you nihklas (#70).

  • New SuperHTML Scripty builtin functions:

    • $page.hasContentSection: You can now query a page to see if it contains a content section with a given id. Thanks nihklas (#76).
    • $page.subpagesAlphabetic: lists sub pages sorted in alphabetic order. Unblocks rockorager’s recipe website. Recipe websites are good, make more of them.
  • New SuperMD Scripty directive:

    • $text, which can be used to give an id and attributes to a inline piece of text.

v0.7.0

2024-10-11

  • Introduced actual arrays in Zine. Previously we would immediately evaluate Scripty expressions into iterators, which made it awkward to implement bulitin functions such as sort. Zine now has a real concept of Array with dedicated builtins, you can read more in the reference for [any]. Note that arrays don’t yet offer a sorting builtin (figuring out the interface to expose to the user has been left as work for a subsequent release), but they do support other basic stuff like slicing. I’m happy to work with users to implement functions that would unblock them, even if the design of the function will have to be changed in the future.

  • Zine currently has a limitation that prevents it from correctly rendering (calling content() on) pages that have been loaded through Scripty. Over time this limitation has been mitigated, but it’s not fully gone yet. Unfortunately the check that told you that this was the case was not working correctly, and has now been fixed. The most likely situation where you might encounter this problem is when trying to render the content of various pages in an RSS template. Previously it might have worked for simple cases and caused a panic in others, now you will get a consistent error message that tells you that the feature is not available yet.

  • The webserver now properly parses percent encoding in URLs, used whenever files have funky names. Thank you GigaGrunch! (#65)

  • Paths with spaces were not handled correctly when tracking dependencies, causing some failures that might have looked like cache issues, now fixed thanks to brandondyck (#68)

v0.6.3

2024-09-19

  • When iterating content sections with $page.contentSections(), you can now obtain the section’s attached heading by calling heading or heading? on it. This was added mainly to support the usecase of giving titles to RSS entries generated from sections.

v0.6.2

2024-09-17

  • $image.linked() is now implemented and allows you to quickly create an image that links to itself.

v0.6.1-vscode

2024-09-07

  • The VSCode marketplace now has a SuperMD Extension that gives you syntax highlighting for SuperMD files (no LSP for it yet though, sorry!).
  • Similarly, the VSCode SuperHTML Extension would not provide you with syntax highlighting before, but now it does.

v0.6.1

2024-09-07

  • Updated rockorager/zeit, which now includes full support for date formatting strings based on the Go magic date. Previously Zine hardcoded only a very small subset of possible formatting styles. See the reference docs for Date.format for more info.

v0.6.0

2024-09-06

  • Added Page.parentSection() in SuperHTML.

  • The website now has a docs section for editor support.
    Follow those instructions to get syntax highlighting and LSP support in your favorite editor.
    If you have trouble setting up your editor feel free to ask for help in the community.

v0.5.1

2024-09-04

  • Added typst syntax highlighting support.

  • On the topic of RSS feeds, this page now has an RSS feed that you can subscribe to. Every entry in this devlog will become an entry in the RSS feed. If you’re curious to see how this is implemented, clone this repo and see how content/log.smd and layouts/log.xml work together to make it happen (see Get Started for more info).

  • Page in SuperHTML gained the ability to loop over sections defined by a Page.

  • SuperMD Directives can now store data (key-value fields) that can then be accessed programmtically by SuperHTML.

v0.5.0

2024-09-01

  • Added $build.generated which evaluates to the date in which the build is taking place. It doesn’t add a dependency on the current date so a page will only display a newer date if it actually gets regenerated.

    Useful for example to set lastBuildDate in RSS feeds. Note that without caching the date will always be updated, potentially resulting in unwanted behavior.

2024-08-30 (later in the day)

zig fetch --save "git+https://github.com/kristoff-it/zine#v0.4.7"
  • Fix an off by one error that gave the wrong value to Iterator.last

2024-08-30 (later in the day)

zig fetch --save "git+https://github.com/kristoff-it/zine#v0.4.6"
  • Fix a bug caused page-local assets to be installed in the wrong place

2024-08-30 (later in the day)

zig fetch --save "git+https://github.com/kristoff-it/zine#v0.4.5"
  • Fix off-by-one error in some iterators (eg in $page.tags)

2024-08-30 (later in the day)

zig fetch --save "git+https://github.com/kristoff-it/zine#v0.4.4"
  • Introduced support for captions directly in Markdown link / image syntax. See the updated SuperMD docs for more details.
  • Fixed a bug that prevented users from accessing $if in the intended way.
  • Some SuperMD Scripty reference docs were improved.

2024-08-30 (later in the day)

zig fetch --save "git+https://github.com/kristoff-it/zine#v0.4.3"

Fixes a printing bug related to Int values.

2024-08-30

zig fetch --save "git+https://github.com/kristoff-it/zine#v0.4.2"

Fixes a bug that prevented users from accessing $loop in the intended way.

2024-08-29 (later in the day)

zig fetch --save "git+https://github.com/kristoff-it/zine#v0.4.1"

Fixes a silly but annoyng bug where SuperMD links nested inside of styiling elements would not get analyzed.

2024-08-29

zig fetch --save "git+https://github.com/kristoff-it/zine#v0.4.0"

The SuperMD update!

With this release Zine is now at a point where the development experience is representative of what the final product will look like.

Error messages need to be improved and a lot of developer tooling is still missing, so take that into account, but the core authoring process is now mostly there.

Zine is also now featureful enough to begin the porting process for the official Zig website!

Note

This is going to be the last hugely breaking update to Zine in a while, so it’s a good moment to try Zine out if the fear of breaking changes has prevented you from attempting before.

Breaking changes will still keep coming, don’t worry, but for a while they will be almost exclusively minor changes that will require little work on your part.

Relatedly, I will be at SquiggleConf this October to talk about Zine and related work.

Now onto new features and breaking changes:

  • This website has been updated with a lot of new documentation. The docs section will now be able to guide you from getting the first few steps of creating a zine website, all the way to learning more advanced concepts.

  • The Scripty reference page that was very incomplete in the past has been improved heavily. Additionally, now there are two references: one for SuperMD and one for SuperHTML.

  • SuperMD comes with a ton of new features, too many to fully list here, but here are some highlights:

    • A page can now define content sections that can be rendered separately by the layout. This is huge, so check out the SuperMD Basics page for more information.

    • You can now give ids to headings and other elements to enable deep-linking (which will be also checked by Zine!), and you can also give classes for styling purposes.

    • Did you notice the “NOTE” block above? SuperMD has the concept of blocks, which make it super easy to include something like that in your content without needing to rely on inlined HTML.

      So cool

      These blocks can also be nested and styled!

    • Inlined HTML is forbidden now btw, but there’s an escape hatch for when you need to embed stuff like YouTube videos. See the SuperHTML docs page for more info.

    • Unfortunately we don’t have any tooling for SuperMD available just yet, but it’s high up in the list of priorities so stay tuned for updates in the near future.

  • Now that we have SuperMD, the file extension for content files has changed to .smd.
    If you need to update your Zine site and have a lot of markdown files, our friendly neighborhood LLM suggests to use this command to bulk rename all (run it inside your content dir):

    find . -type f -name "*.md" -exec bash -c 'mv "$0" "${0%.md}.smd"' {} \;
    
  • The logic attributes in SuperHTML (if, loop, var) have been renamed:

    • :if, :loop are still the same, they just gained a colon prefix
    • :text and :html replace var. The first escapes HTML, while the second doesn’t
    • inline-loop doesn’t exist anymore because now we have <ctx>
  • Introducing <ctx>: a special tag that allows you to create a phantom element that doesn’t render to anything. This element effectively allows you to implement what inline-loop was doing for you before, and more.

    Another cool feature of <ctx> is that defining attributes on it makes them available as fields under $ctx. See the SuperHTML docs page for more info.

2024-08-03

zig fetch --save "git+https://github.com/kristoff-it/zine#v0.3.0"

The Asset System update!

  • Zine has now an asset system with clearly defined semantics. Previously we kinda implemented informally what other static site generators offered (eg a static asset directory), while now Zine gained it’s own spin on assets

    There’s a new Assets section in the docs, read it to learn how assets now work in Zine. The most notable changes are:

    • Zine can now make use of artifacts generated via the Zig build system!
    • static_dir_path has become assets_dir_path (I also recommend renaming static to assets)
    • Page assets (eg images placed in the content directory next to the page they belong to) now have one extra rule for file placement, see the docs for more info on that (or let the error messages gently guide you)
  • The dev server now by default opens a door port to 1990 if you don’t specify -Dport

  • output_prefix has been renamed to output_path_prefix

  • In MultilingualSite, variants has been renamed to localized variants

  • Scripty has gained a new Asset type and handful of new builtins, including adding the ability to query for the current locale_code in a multilingual website ($site.localeCode())

2024-07-27

zig fetch --save "git+https://github.com/kristoff-it/zine#v0.2.0"

The build system flexibility update (part 1)!

  • Upgrading from v0.1: in your build.zig change try zine.addWebsite to zine.website.

  • addWebsite and addMultilingualWebsite have been renamed to website and multilingualWebsite respectively (and now don’t return an error anymore)

  • To align with std.Build‘s naming convention (where ‘add’ functions create steps but don’t wire them to the default install step), addWebsite and addMultilingualWebsite are now more low-level functions for defining build pipelines that allow you to customize more things:

    • You can have other build steps depend on the website being built
    • You can specify if and how the development webserver is bound to a named step
    • et cetera

    To learn how to use this more fine-grained API, look at the implementation of website and multilingualWebsite.

  • As suspected, in the last release I did not proprely wire the dependency on zig-afl-kit as a lazy dependency and that caused build errors to some. This is now fixed in this release. Sorry!

The next item on the roadmap is to add an asset system to Zine in order to have the static content generation depend on other steps defined in your build script.

2024-07-26

zig fetch --save "git+https://github.com/kristoff-it/zine#v0.1.3"
  • Scripty strings now have eql, contains and endsWith. With a pinch of creativity you can create menus that style differently the current active page:

    <div class="$page.permalink().endsWith('/blog/').then('selected', '')">
      Blog
    </div>
    

    Similarly, when printing out the list of pages in a section, you can filter-out occasional “miscellaneous” pages (as in belonging to the same section but unwanted in the current list):

    <div loop="$page.subpages()">
      <div if="$loop.it.permalink().contains('/blog/')">
        <a href="$loop.it.permalink() var="$loop.it.title"></a>
      </div>
    </div>
    
  • The SuperHTML CLI tool was updated with two new commands:

    • check: checks HTML and SuperHTML template files for syntax errors, useful if you don’t have / want LSP integration (see also fmt in such case).
    • interface: prints out the interface of a given SuperHTML template file, useful to bootstrap a new SuperHTML template that extends another.
  • Zine and some of its dependencies depend on kristoff-it/zig-afl-kit for fuzzing. I tried now to make it a lazy dependency so that users don’t have to download it just to use Zine (as it’s a dev-only dependency), but I haven’t yet been able to fully test if I did so correctly.

2024-07-25

zig fetch --save "git+https://github.com/kristoff-it/zine#v0.1.0"

Zine has finally reached a first tagged release!

A lot has happened in these 4 months, so read this changelog to learn how to upgrade your Zine website.

The first thing that changed is the recommended way of updating your build.zig.zon.

The second is that Zine now tracks the latest stable version of Zig, which is 0.13.0 at the moment of writing.

Now onto new features and breaking changes:

  • The recommended GitHub Actions Workflow files for building Zine websites on Github have changed. The new version uses the mlugg/setup-zig@v1 action that will automatically manage caching for you. It is highly recommended to update your scripts.

    • While you’re at it, change --summary all to --summary new, which will only list the pages that were rebuilt.
  • SuperHTML (Zine’s templating language) dropped Tree Sitter as its HTML parser in favor of a handcrafted implementation that more closely follows the HTML5 spec. This brings us significantly improved error messages and other advantages.

    • It’s highly recommended you install and configure SuperHTML as your language server for both HTML and SuperHTML Templates in order to get in-editor diagnostics and kickass autoformatting. The repo also offers a Tree Sitter grammar for SuperHTML that incudes a few visual improvements for tags and attributes that have semantic meaning.

      SuperHTML also has a VSCode extension.

    • SuperHTML follows the HTML5 spec much more closely and, while writing an HTML parser from scratch for it, I learned that self-closing tags (tags with a final /) are not a thing in HTML5, so now <extend> and <super> have been defined as void elements in SuperHTML and want no final slash nor closing tag.

      Note that SuperHTML will consider an error using self-closing tags in HTML (outside of a <svg> scope).

    • The correct file extension for templates is .shtml. You must rename all your templates to the new file extension otherwise you will get an error from SuperHTML when it sees non-HTML compliant syntax, since <extend> and <super> are recognized as void elements only in SuperHTML template files.

  • Scripty has impoved as well: inside of nested loops, it is now possible to access outer $loop variables by doing $loop.up(). Each call to up() goes up one level.

    • This is thanks to the fact that interrupts were implemented in Scripty, opening the door to features that rely on the ability to pass from the outside values into scripty (up() relies on that since loops are a SuperHTML concept that Scripty is completely unaware of).
  • For syntax highlighting, Zine uses a distribution of Tree Sitter that bundles a lot of grammars and highlighting queries from Flow Control. The dependency has now been updated to a new version that adds support for more languages.

That’s mostly it. If you encounter bugs while updating, please don’t hesitate to open a new issue on GitHub with a link to a reproduction.

2024-03-26

.url = "git+https://github.com/kristoff-it/zine#e33a1d79b09e8532db60347a7ec4bd3413888977",
.hash = "12209f9be74fcc805c0f086e4a81ccca041354448f5b3592e04b6a6d1b4a95da5a26",
  • Added support for multilingual websites. See the corresponding docs page for more info. Because of this change now the AddWebsiteOptions struct is slightly different, here’s how to fix it:
    • Take the contents of site and move them top level, rename base_url to host_url.
  • Related-but-distinct from the above, you can now specify an output prefix for your static site. The feature was added primarily for i18n purposes but can also be used in simple websites to add an arbitrary prefix.
  • The markdown renderer now renders tables!
  • Fixed a crash in the dev server that would trigger when refreshing the page multiple times in quick succession (the crash was related to websockets). There’s still one remaining known bug related to this same problem though.
  • The dev server now works on Windows (thanks Parzival-3141)
  • New Scripty builtins:
    • Strings
      • addPath() similar to suffix but knows when to add a / or not.
      • fmt() replaces occurrences of {} in your strings with the provided string arguments.
    • Maps, refined the get family of functions
      • get(key, fallback) allows to get a key from a map and provide a fallback value
      • get!(key) errors out if the key doesn’t exist
      • get?(key) returns null if the value is missing, to be used in conjunction with if attributes.

2024-03-21

.url = "git+https://github.com/kristoff-it/zine#ecc72eb042af07f5b4690a35a7ca1dd9c6fd5b61",
.hash = "1220610a18236cd32936502bd7e762743e89ef70408638675420e453be41f1e83de4",
  • Changed datetime library to rockorager/zeit.
  • If you put a @date("...") literal in your custom fields, it will be recognized as a date by Zine.
  • A few improvements to bulitins:
    • get?(), get!() and get(): different ways of getting values out of Ziggy maps (i.e. custom fields).
    • then() on booleans now gives you the ability to create if-then-else expressions.
    • gt on integers.
    • lt, eq, gt on dates.

2024-03-20

.url = "git+https://github.com/kristoff-it/zine#d06884ec657abe87ab4f408b5dc3f336a6dcea9b",
.hash = "1220d3bc95a5343918d69d3478f27ebb4abe14613c159737af64cd2185151efd2fa1",
  • Zine now uses Ziggy as the frontmatter language! In the near future Zine will develop tooling for editing ziggy-markdown files. In the meantime consider downloading the Ziggy CLI tool for a smoother editing experience if you plan to use Ziggy directly.
  • Added an initial version of sections to Zine! See the updated documentation section for more information about that. Beware that $site.pages() was removed in favor of the new system.
  • Added a the ability to define alternatives in the frontmatter of a page. Alternatives allow you to specify multiple layouts to apply to the same piece of content. Useful for generating RSS feeds.
  • Added syntax highlighting to layouts: now strings have a syntaxHighlight builtin.
  • Updated Zig version because a bugfix was needed to add syntax highlighting to templates. Now Zine depends on Zig 0.12.0-dev.3381+7057bffc1 and above. Make sure to update your GitHub Actions workflows accordingly.

2024-03-08

.url = "git+https://github.com/kristoff-it/zine.git#4b3efd178cb6ee9af3c864fa980ad0499823aac6",
.hash = "1220f6920dbb9540cc9013bbaa1621d62ef79aabadcbb6f7b9f45e415de815d15404",
  • Added syntax highlighting support via tree-sitter. Most code snippets of this website have now gained syntax highlighting. No themes are provided for now and it’s expected that you define your own CSS from scratch. See hightlight.css from this website for an idea on how to proceed.
  • Updated Zig version because of a recent breaking change related to std.http. Now Zine depends on Zig 0.12.0-dev.3161+377ecc6af and above. Make sure to update your GitHub Actions workflows accordingly.

2024-02-13

2024-02-11

.url = "git+https://github.com/kristoff-it/zine.git#beb5434a04fad660ecf8db8379532dfe5b5e13b0",
.hash = "12203c37cb5fb3931d3b7d1f1dace46cf5329ffe2fb5a8d2ac87dc78630ce7f601a7",
  • Updated Zig version because of a recent breaking change related to std.Options / root.std_options. Now Zine depends on Zig 0.12.0-dev.2701+d18f52197 and above. Consider using marler8997/zigup if you’re not building Zig from source.
  • The Scripty reference documentation was improved slightly: the reference for Page displays which fields have default values and which do not.
  • The dev server is now better at reporting build errors: in the event of a build error the message will be shown in the terminal, as well as being shown on the web page, and the 404 page too will connect to the hot-reloading mechanism in order to show build errors.

2024-02-09 (later in the day)

.url = "git+https://github.com/kristoff-it/zine.git#da7c32c2c253f6b0dbd392006055598feb07410b",
.hash = "1220e6580fdbd0a56a97300bab938f61fe3b5b35fc7755a150db267422cf554ab299",
  • When running the dev server (zig build serve), Zine will now show build error messages inside of the web page itself using the hot reload mechanism. Fix the build error and the error overlay will disappear. Error messages are ugly for now (#16).

2024-02-09

.url = "git+https://github.com/kristoff-it/zine.git#527762348ef104dce601f52bca9f958a511ff11b",
.hash = "122018fb2b0ba1479ae28bacf3839d38da69044b006068fd67b1b7f4425114bec8d1",
  • Zine will now stop erroring out in the presence of empty markdown files. It will instead print a warning and ignore them. Now you can touch a bunch of files and fill them out as you make progress, without losing hot reloading in the meantime.

2024-02-08

.url = "git+https://github.com/kristoff-it/zine.git#eaa23f2d3a80868251302a1b979dbcc7e5b81d3a",
.hash = "1220230f7c6abf655ef9b1ec14161bd1c15e55afd14ceaedfe2e0e9cc2471b1dd0ca",
  • Removed the _index.md vs index.md naming convention. Now it’s always index.md and you can use the skip_subdirs frontmatter property to get the old behavior. See the docs for more information.