Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ on:
description: Also build the Intel macOS app and dmg (x86_64-apple-darwin)
type: boolean
default: false
release:
description: Publish a GitHub release v<package.json version> from main with every bundle (tick intel too)
type: boolean
default: false

jobs:
test:
Expand Down Expand Up @@ -118,3 +122,39 @@ jobs:
src-tauri/target/x86_64-apple-darwin/release/bundle/**
if-no-files-found: error
retention-days: 3

# Run on demand from main (Actions, "Run workflow", tick release and intel): tags
# v<package.json version> on this commit and publishes the bundles, a SHA256SUMS.txt
# and that version's CHANGELOG.md section as a GitHub release.
release:
name: release
if: >-
!cancelled() && github.event_name == 'workflow_dispatch' && inputs.release &&
github.ref == 'refs/heads/main' && needs.desktop.result == 'success' &&
(needs.desktop-intel.result == 'success' || needs.desktop-intel.result == 'skipped')
needs: [desktop, desktop-intel]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
path: bundles
- name: Collect the installers
shell: bash
run: |
mkdir out
find bundles -type f \( -name '*.dmg' -o -name '*-setup.exe' -o -name '*.msi' -o -name '*.deb' -o -name '*.AppImage' \) -exec cp {} out/ \;
(cd out && sha256sum * > SHA256SUMS.txt)
ls -la out
cat out/SHA256SUMS.txt
- name: Publish
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
version=$(node -p "require('./package.json').version")
awk -v v="## $version" '$0 ~ "^"v {on=1; next} on && /^## / {exit} on' CHANGELOG.md > notes.md
test -s notes.md || { echo "CHANGELOG.md has no section for $version"; exit 1; }
gh release create "v$version" out/* --target "$GITHUB_SHA" --title "Folio $version" --notes-file notes.md
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Changelog

## 0.3.0 (2026-10-20)

The first public release. Folio is now open source under the MIT license.

- Bundles opencode 2.0.32 (was 2.0.28), with the six release archives pinned by SHA-256. The 2.0.28 to 2.0.32 API change is additive; a live run against 2.0.32 passed.
- Chapters are named by opencode from what you asked. Folio used to create sessions titled "Untitled", which stopped opencode from naming them.
- A drop cap opens a chapter only when the paragraph starts with a letter or digit and runs at least as many lines as the cap is tall.
- Footnote marks read as marks: runs collapse to "3–7", and the numbers no longer stretch the line they sit on.
- Your words in ink are set ragged right, even when the theme justifies text.
- Questions from opencode show their Markdown (italics, code) instead of the raw asterisks.
- A code-mode script reads "Ran a script." in its footnote.
- Reject in flight: rejecting a plate while opencode is still finishing the turn no longer loses the plate when its id changes.
- The drop cap is whole. It hung below its one-line box and was cut off, so an "I" could read as a "T".
- Reject on a plate whose turn opencode hasn't named yet says to try again in a moment, instead of reading as final.

### Install

Download from the assets below. The builds are not code-signed yet.

- **macOS 12 or later** (`Folio_0.3.0_aarch64.dmg` for Apple silicon, `Folio_0.3.0_x64.dmg` for Intel): drag Folio to Applications and open it. When macOS says it can't check the app, open System Settings → Privacy & Security and click **Open Anyway**. If macOS says Folio is damaged, run `xattr -dr com.apple.quarantine /Applications/Folio.app` in Terminal and open it again.
- **Windows 10 or later** (`Folio_0.3.0_x64-setup.exe` or `Folio_0.3.0_x64_en-US.msi`): run the installer. If SmartScreen stops it, click **More info**, then **Run anyway**.
- **Linux x86_64** (`Folio_0.3.0_amd64.deb` or `Folio_0.3.0_amd64.AppImage`): `sudo apt install ./Folio_0.3.0_amd64.deb`, or make the AppImage executable and run it.

`SHA256SUMS.txt` lists a digest for every file.

## 0.2.0 (2026-10-14)

The first complete build: the book, chapters, footnotes, plates with Accept, Revise, Reject and Fold back, margin notes, ask before the press writes, questions as forms, six themes, the bundled press (opencode 2.0.28), and desktop bundles for macOS, Windows and Linux.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

Folio is an open-source desktop reading room for [opencode](https://opencode.ai), by [Basalt](https://hellobasalt.dev). A repository is a book. Each opencode session is a chapter. You write on the page.

Website and downloads: [folio.hellobasalt.dev](https://folio.hellobasalt.dev/). Every release is on the [releases page](https://github.com/dpdev69/folio/releases), with install steps in [CHANGELOG.md](CHANGELOG.md).

The shell is Tauri 2. The pages are React, TypeScript, and Vite. opencode is not forked: Folio runs `opencode serve` as a separate process, which this document calls the press, and speaks its HTTP API. opencode is MIT licensed; the notice is in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).

## What the first launch does
Expand Down Expand Up @@ -184,6 +186,12 @@ The CI macOS job passes the certificate, identity, and Apple ID variables throug
codesign -dv --verbose=2 src-tauri/target/aarch64-apple-darwin/release/bundle/macos/Folio.app
```

## Website

`site/` is the static site at [folio.hellobasalt.dev](https://folio.hellobasalt.dev/): plain HTML and CSS with no build step, served by Cloudflare Pages from `main` (output directory `site`). `site/_headers` sets the security headers. The screenshots in `site/assets/` are taken from the real app.

## CI

`.github/workflows/build.yml` runs the unit tests and the web build, then fetches the bundled press and runs `tauri build` on Linux (deb, AppImage), macOS (app, dmg), and Windows (nsis, msi).

To publish a release, bump the version in `package.json`, `src-tauri/tauri.conf.json` and `src-tauri/Cargo.toml`, add its section to `CHANGELOG.md`, merge to `main`, then run the workflow from `main` with **release** (and **intel**) ticked. The `release` job tags `v<version>` on that commit and attaches the dmg, the Windows installers, the deb, the AppImage and `SHA256SUMS.txt`, with the changelog section as the notes.
2 changes: 2 additions & 0 deletions docs/e2e.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ The scratch repository was `greeter` (`src/greet.ts`, `src/shout.ts`, `src/index

Found in this run and left for the interface pass: the drop cap in row 41; the toggle in the menu leaves the menu open (row 39); and "This plate cannot be set back on its own." for a plate whose turn is not named yet (row 38) reads as final, though Reject works a moment later. Not tested here: the packaged window itself (rows 17, 18 and 21 to 22 cover it on 2.0.28), the margin note, the question form and a window resize.

After this run: the drop cap of row 41 is whole (`.line.with-drop` shows its overflow and clips only its words; checked in the browser, cap 145–187 px over its 143–172 px line), and the refusal of row 38 now reads "The press is still setting this plate. Reject it again in a moment." while the plate's turn is still `local-…`. The menu staying open after the ask toggle is kept: it shows the new state.

## Notes

- Defect fixes routed into this run: footnote paths from the book's root (`3ad63c6`), room at the page foot
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "folio",
"private": true,
"license": "MIT",
"version": "0.2.0",
"version": "0.3.0",
"description": "Folio is a book-skinned desktop client for the opencode server.",
"type": "module",
"opencode": {
Expand Down
99 changes: 99 additions & 0 deletions site/404.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Page not found · Folio</title>
<meta name="description" content="This page is not on folio.hellobasalt.dev.">
<meta name="color-scheme" content="light dark">
<meta name="theme-color" content="#ebe4d8" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#141210" media="(prefers-color-scheme: dark)">
<meta name="robots" content="noindex">
<link rel="icon" type="image/png" href="/assets/favicon.png">
<link rel="apple-touch-icon" href="/assets/icon-256.png">
<link rel="stylesheet" href="/assets/site.css?v=1">
</head>
<body>
<a class="skip" href="#main">Skip to content</a>
<svg class="sprite" width="0" height="0" aria-hidden="true" focusable="false">
<defs>
<symbol id="i-download" viewBox="0 0 24 24"><path d="M12 4v11M7 10.5l5 5 5-5M5 20h14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></symbol>
<symbol id="i-github" viewBox="0 0 24 24"><path fill="currentColor" d="M12 2C6.48 2 2 6.58 2 12.23c0 4.52 2.87 8.35 6.84 9.7.5.1.68-.22.68-.49l-.01-1.7c-2.78.62-3.37-1.37-3.37-1.37-.46-1.18-1.11-1.5-1.11-1.5-.91-.64.07-.62.07-.62 1 .07 1.53 1.06 1.53 1.06.9 1.57 2.35 1.12 2.92.85.09-.67.35-1.12.64-1.38-2.22-.26-4.56-1.14-4.56-5.07 0-1.12.39-2.04 1.03-2.76-.1-.26-.45-1.3.1-2.71 0 0 .84-.28 2.75 1.05a9.4 9.4 0 0 1 5 0c1.91-1.33 2.75-1.05 2.75-1.05.55 1.41.2 2.45.1 2.71.64.72 1.03 1.64 1.03 2.76 0 3.94-2.34 4.81-4.57 5.06.36.32.68.94.68 1.9l-.01 2.81c0 .27.18.6.69.49A10.14 10.14 0 0 0 22 12.23C22 6.58 17.52 2 12 2Z"/></symbol>
</defs>
</svg>

<header class="nav">
<div class="wrap nav-inner">
<div class="brand-lockup">
<a class="brand" href="/" aria-label="Folio home">
<img src="/assets/icon-256.png" alt="" width="28" height="28">
<span>Folio</span>
</a>
<a class="by-basalt" href="https://hellobasalt.dev/">by Basalt</a>
</div>
<nav class="nav-links" aria-label="Sections">
<a href="/#how">How it reads</a>
<a href="/#catch">The catch</a>
<a href="/#faq">FAQ</a>
</nav>
<div class="nav-end">
<a class="nav-gh" href="https://github.com/dpdev69/folio" aria-label="GitHub"><svg aria-hidden="true"><use href="#i-github"/></svg><span>GitHub</span></a>
<a class="btn btn-primary btn-sm" href="/#download">Download</a>
</div>
</div>
</header>

<main id="main" class="doc">
<div class="wrap doc-wrap center">
<p class="eyebrow">404</p>
<h1>This page isn't in the book.</h1>
<p class="lede-doc soft">The link may be old, or the page was never set.</p>
<p class="back"><a class="btn btn-primary btn-lg" href="/">Back to Folio</a></p>
</div>
</main>

<footer class="footer">
<div class="wrap">
<div class="footer-grid">
<div class="footer-brand">
<a class="brand" href="/"><img src="/assets/icon-256.png" alt="" width="24" height="24" loading="lazy"><span>Folio</span></a>
<p>A desktop reading room for opencode: sessions as chapters, commands as footnotes, edits as plates. A Basalt app.</p>
</div>
<nav aria-labelledby="f-product">
<p class="f-title" id="f-product">Project</p>
<a href="/#download">Download</a>
<a href="https://github.com/dpdev69/folio">GitHub</a>
<a href="https://github.com/dpdev69/folio/releases">Releases</a>
<a href="https://github.com/dpdev69/folio/issues">Report a bug</a>
</nav>
<nav aria-labelledby="f-basalt">
<p class="f-title" id="f-basalt">More from Basalt</p>
<a href="https://hellobasalt.dev/">Basalt</a>
<a href="https://upright.hellobasalt.dev/">Upright, free mouse app</a>
<a href="https://lull.hellobasalt.dev/">Lull, coming soon</a>
<a href="https://gocmeden.hellobasalt.dev/">Göçmeden, remote jobs</a>
<a href="https://cosmicpost.lol/">Cosmic Post, space photo keepsakes</a>
<a href="https://kacpapel.app/">Kaç Papel?, price game</a>
<a href="https://github.com/dpdev69/claude-fight-hud">Fight HUD for Claude Code</a>
<a href="https://byewall.hellobasalt.dev/">ByeWall, find archived pages</a>
<a href="https://kahve.hellobasalt.dev/">KahveDrop, coffee finder</a>
<a href="https://outcrop.hellobasalt.dev/">Outcrop, SEO for software makers</a>
<a href="https://x.com/hellobasalt">X @hellobasalt</a>
<a href="https://www.youtube.com/@hellobasalt">YouTube @hellobasalt</a>
</nav>
<nav aria-labelledby="f-help">
<p class="f-title" id="f-help">Help</p>
<a href="mailto:support@hellobasalt.dev">support@hellobasalt.dev</a>
<a href="/#faq">FAQ</a>
<a href="/privacy">Privacy</a>
<a href="/terms">Terms</a>
</nav>
</div>
<div class="footer-base">
<p>© 2026 Folio contributors · MIT license · A <a href="https://hellobasalt.dev/">Basalt</a> app</p>
<p>opencode is an open-source project by its own authors. Folio is an independent app and is not affiliated with or endorsed by them.</p>
</div>
</div>
</footer>
</body>
</html>
16 changes: 16 additions & 0 deletions site/_headers
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
Content-Security-Policy: default-src 'self'; img-src 'self' data:; style-src 'self'; font-src 'self'; script-src 'self' https://static.cloudflareinsights.com; connect-src 'self' https://cloudflareinsights.com; base-uri 'self'; form-action 'none'; frame-ancestors 'none'
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()

/assets/*
Cache-Control: public, max-age=86400

/assets/fonts/*
Cache-Control: public, max-age=31536000, immutable

https://:project.pages.dev/*
X-Robots-Tag: noindex
Binary file added site/assets/card-actions.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added site/assets/card-contents.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added site/assets/card-ink.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added site/assets/card-notes.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added site/assets/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
93 changes: 93 additions & 0 deletions site/assets/fonts/OFL-Caveat.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
Copyright 2014 The Caveat Project Authors (https://github.com/googlefonts/caveat)

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL


-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Loading
Loading