15149 Commits
Author SHA1 Message Date
BFlatandClaude Opus 5.5 3048db996d Bump FigCraft downloads to 0.5.1 (#137)
Points the FigCraft download buttons on `/apps/figcraft` at
[v0.5.1](https://github.com/storytold/figcraft/releases/tag/v0.5.1) (was
0.4.0). The release ships the same file set, so only the version
changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:44:23 +07:00
BFlatandClaude Opus 5.5 bcff742052 Add FigCraft to the Crafting Apps pages (#135)
## Summary

Adds FigCraft (interface design) as the 13th Crafting App, after
DesignCraft.

- **Catalog**: new `figcraft` entry in `crafting-apps.ts` (copy adapted
from the repo README, no third-party product names), release `0.4.0`,
accent colour from the app icon.
- **Assets**: icon, share card and the seven README screenshots from
`storytold/figcraft` main, plus a poster frame for the video.
- **Video section**: an optional `demo` on an app entry renders a
section directly under the hero. FigCraft uses it for the live-sessions
collaboration clip (muted, looping, plays in view via `LazyVideo`, which
gains a `poster` prop). Later sections renumber when a demo is present.
- **/apps hub**: coverage line mentions interface design, the title
keeps "One craft." on one line, and the icon row is a grid of two even
rows (7 + 6) from `sm` up.

## Testing

Checked `/apps` and `/apps/figcraft` in `next dev` at desktop width,
dark theme: video plays, section indices run 01–06, no broken images.
`tsc` reports no errors in the touched files. Not checked: production
build, light theme, mobile layout beyond the icon row.

## Not included

The launcher's release-polling list
(`poll_craft_app_releases_thread/config.rs`) does not list FigCraft yet.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:37:13 +07:00
Brandon ThomasandClaude Opus 5.5 854869ba53 Coordinate Craft App release polling across the fleet through Redis (#77)
New releases took up to ~30 min to reach \`GET
/v1/craft_apps/release_info\` (ArtCraft Launcher 0.2.0 needed 17–29
min), pods disagreed with each other, and every pod polled GitHub on its
own, which uses up the unauthenticated quota (60/h per IP, and 304s
count) and risks backing off for up to 4 h.

The pods now share their results through Redis. There's no lock:
- **One Redis hash per app** (\`craftApps:releases:app:<key>\`, 7 d
TTL). It holds the latest release, the ETag and \`fetched_ms\`. Before
asking GitHub about an app, a pod reads its entry. If it's fresher than
\`interval − 30 s\` (a clock-skew margin), the pod takes it from Redis
and doesn't call GitHub.
- **Last write wins, guarded by timestamp.** A Lua script never lets an
entry go back in time. A 304 moves only \`fetched_ms\`, and only while
the stored ETag still matches.
- **A soft per-app claim** (60 s, no owner, it just lapses), so a fleet
that ticks every 30 s rarely fetches the same app twice.
- **Backoff shared by the whole fleet**
(\`craftApps:releases:backoff\`), so a rate limit slows everyone down.
Per-app failure retry is 60 s, doubling.
- **The endpoint is unchanged.** It still serves each pod's in-memory
copy. If Redis is down, a pod falls back to polling on its own and warns
at most every 10 minutes.

**The GitHub token is optional.** With
\`CRAFT_APPS_RELEASE_GITHUB_TOKEN\` set, the default interval is 120 s
(min 60). Without it (unset or blank), calls go out unauthenticated with
no Authorization header, at 1800 s (min 300), and startup and polling
are never blocked. \`CRAFT_APPS_RELEASE_COORDINATION_ENABLED=false\`
switches back to the old per-pod polling.

**Tests:** \`cargo test -p storyteller-web --bin storyteller-web craft\`
(49 pass) and \`cargo test -p redis_schema craft\` (4 pass). The Redis
tests passed against a local redis-server with
\`CRAFT_APPS_TEST_REDIS_URL=… -- --include-ignored --skip live_round\`
(51 pass).

**Follow-ups after review:**
- **The poller has its own Redis pool** (2 connections; connects within
3 s; every read and write, including the pool's `PING` check, times out
after 5 s). Before this, it shared the handlers' pool, which has no read
timeout, so a Redis that stopped answering could leave the poller
holding every connection while handlers waited 30 s for one. A test
against a fake Redis that goes silent after the handshake fails without
the timeouts and passes with them.
- **When Redis won't run scripts**, writes fall back to
`WATCH`/`MULTI`/`EXEC`: read and decode the guarding fields, decide, and
write in a transaction that Redis drops if another pod wrote in between.
This switch only happens when Redis refuses the script itself. Being
unreachable, busy, out of memory or read-only doesn't trigger it. It was
tested against a real Redis user denied `@scripting`.
- **New pods load from Redis at startup**, before the startup jitter, so
they serve the fleet's releases right away without asking GitHub.
- **`Cache-Control: no-store` until every app has been fetched**
(`last_fetched` set), so Cloudflare never caches a cold pod's "not
fetched yet" answer. The response body is unchanged.

**Tests:** `cargo test -p storyteller-web --bin storyteller-web craft --
--include-ignored --skip live_round` against a local Redis 8.6: 63 pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 01:05:06 -04:00
Navaras P 25f38b79b8 Add docker-compose setup for self-hosting the backend (#68)
Closes storytold/artcraft#1301

Adds `build/selfhost/`, a Docker Compose setup that runs
`storyteller-web` with MySQL 8.4, Redis, Elasticsearch and SeaweedFS
(S3-compatible storage).

- A `migrate` service applies `_database/sql/migrations` in order and
records them in `__diesel_schema_migrations`, so reruns only apply new
migrations.
- `storage-init` creates the three buckets the server expects.
- The server image is built from `build/service_cpu.Dockerfile`.
- `.env.example` lists every variable the server requires at startup,
with placeholders for third-party providers.

SeaweedFS is used because MinIO no longer publishes Docker images.

**Tested locally (Docker Desktop on Windows):** image build, all 285
migrations (plus a rerun), `GET /_status` healthy, model listing,
account creation and login, and an image upload stored in the bucket.

**Not included:** background workers, Elasticsearch index setup.
2026-10-11 18:51:50 -04:00
Dylan M. Taylor 2893b6f713 chore: bump github actions to fix Node 20 deprecation warnings (#69)
This PR bumps GitHub Actions to their latest versions and pins them to
strict SHAs, remediating Node 20 deprecation warnings.
2026-10-11 18:48:57 -04:00
Dylan M. Taylor ad1ca86b00 Update launcher version (#85)
Fixes link to point to the new version with fixes and improvements
2026-10-11 18:34:45 -04:00
Dylan M. Taylor 165a4b272c Add WordCraft, SoundCraft, DeckCraft, CADCraft, and GridCraft marketing pages (#79)
## Summary

Adds product definitions and marketing page assets for the five
remaining Crafting Apps (WordCraft, SoundCraft, DeckCraft, CADCraft, and
GridCraft), and bumps all Crafting Apps in `crafting-app-releases.ts` to
their latest published GitHub release versions.

### Changes

- **Data & Definitions (`crafting-apps.ts`,
`crafting-app-releases.ts`)**:
- Added `wordcraft`, `soundcraft`, `deckcraft`, `cadcraft`, and
`gridcraft` to `CraftAppSlug` and `CRAFTING_APPS`.
  - Added feature sets, ledes, pitches, and release entries.
  - Bumped all app releases to latest GitHub releases:
    - `photocraft`: `0.7.0`
    - `vectorcraft`: `0.9.0`
    - `filmcraft`: `0.6.0`
    - `lightcraft`: `0.6.0`
    - `pdfcraft`: `0.6.0`
    - `effectcraft`: `0.8.0`
    - `designcraft`: `0.6.0`
    - `wordcraft`: `0.5.0`
    - `soundcraft`: `0.5.0`
    - `deckcraft`: `0.5.0`
    - `cadcraft`: `0.5.0`
    - `gridcraft`: `0.5.0`
- Extended `COUNT_WORDS` to support twelve apps and updated category
coverage on `/apps`.
- Maintained copy rules: accurately describing categories and workflows
without naming third-party products.
- **Branding & Themes (`globals.css`)**:
- Added theme color identities (`--app`, `--app-ink`) for all five apps
across light and dark themes with full AA contrast compliance.
- **Visual Assets (`public/images/apps/<slug>/`)**:
- Generated 256px WebP icons, 1600x1000 WebP screenshots, and 1200x630
OG share cards for each new app.

### Testing

- `npm run typecheck` (`tsc --noEmit`) passes cleanly.
- `npm run build` generates all 12 app pages statically (44/44 static
pages).
2026-10-11 13:09:48 -04:00
Dylan M. Taylor b45df084b8 feat(website): offer format choices for ArtCraft Launcher on Linux (#83)
### Summary

- **Linux format choices**: Added a split dropdown button to
`LauncherDownloadButton` on Linux, allowing users to choose their
preferred package format directly from the download button (Flatpak,
AppImage, Debian package, and RPM package).
- **Default and ordering**: Flatpak is selected by default for the
primary download action, with AppImage, `.deb`, and `.rpm` available via
the dropdown arrow.
- **Page integration**: Passed complete release download lists to
`LauncherDownloadButton` across Crafting Apps pages so all supported
Linux formats are available.
2026-10-11 13:03:11 -04:00
BFlatandClaude Opus 5.5 28bd2e7ee3 Bump ArtCraft Launcher downloads to 0.2.1 (#81)
Points every launcher download on /apps and the Crafting App pages at
[v0.2.1](https://github.com/storytold/craft-launcher/releases/tag/v0.2.1).
That release fixes macOS installs and updates failing with "couldn't
open the disk image".

This is merged before the release is published. It only goes live when
the site is published on Netlify.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:53:27 +07:00
BFlatandClaude Opus 5.5 02a3de2509 Bump ArtCraft Launcher downloads to 0.2.0 (#76)
Points every launcher download on /apps and the Crafting App pages at
[v0.2.0](https://github.com/storytold/craft-launcher/releases/tag/v0.2.0),
the latest release. Checked: all 20 files the site links exist in v0.2.0
and download (HTTP 200).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 02:40:40 +07:00
DWAA1660 4ae6fe7966 fix(website): update VectorCraft downloads to v0.7.0 (#64)
The VectorCraft app page currently advertises v0.6.0, while GitHub's
latest published release is
[v0.7.0](https://github.com/storytold/vectorcraft/releases/tag/v0.7.0).
Update its shared release configuration so the visible version, release
page, and all 21 asset download links use v0.7.0.

Audited all eight advertised apps against GitHub's latest published
releases on October 9, 2026. ArtCraft (0.41.0), PhotoCraft (0.5.0),
FilmCraft (0.4.0), LightCraft (0.4.0), PDFCraft (0.4.0), EffectCraft
(0.6.0), and DesignCraft (0.4.0) are already current. Both ArtCraft
download configurations match the latest release; the static desktop
landing page delegates to the main download page.

Validation: all 151 configured active download URLs match nonempty
published assets in the latest releases, including all 21 VectorCraft
v0.7.0 assets; `npm run typecheck` passes in
`frontend/apps/artcraft-website-next`; `git diff --check` passes.

The website requires deployment after merging before its public download
links change.
2026-10-11 00:09:26 +07:00
Brandon ThomasandClaude Opus 5.5 f37f82b400 Serve Craft App release info for ArtCraft Launcher (#71)
GET /v1/craft_apps/release_info reports the latest release of every
Craft App (and ArtCraft Launcher itself), so launchers stop polling
GitHub directly. A background thread polls GitHub and keeps a shared
cache.

Polling (threads/poll_craft_app_releases_thread):
- One round at startup (after 0–30 s of jitter, so a fleet deploy is
spread out), then every CRAFT_APPS_RELEASE_POLL_INTERVAL_SECS (30 min
default, 5 min minimum) with ±10% jitter
- One request per app (GET /repos/{repo}/releases/latest) with ETag
revalidation and 1 s between requests
- Rate limits stop the round and wait for GitHub's reset, backing off
2×, 4×, … the interval up to CRAFT_APPS_RELEASE_MAX_BACKOFF_SECS (4 h);
rounds where every request failed retry after 1, 2, 4… min, never more
often, up to the interval
- Per app all-or-nothing: either its release and app_last_fetched are
stored, or nothing about it changes
- CRAFT_APPS_RELEASE_POLLING_ENABLED, CRAFT_APPS_RELEASE_GITHUB_TOKEN
and CRAFT_APPS_RELEASE_APPS (key=Name=org/repo,…) configure it

Cache (state/craft_apps_release_cache): Arc<RwLock<BTreeMap<..>>> of
typed entries. release_info.last_fetched is the oldest app_last_fetched,
absent until every app has been fetched. Apps are listed from the start,
without a release or app_last_fetched until fetched.

The endpoint never fails, sets Cache-Control: public, max-age=60, and
returns empty app_renames, repo_renames and important_notices for now.
API types live in artcraft_api_defs::craft_apps::release_info, with each
asset's OS / arch / package type parsed from its file name. reqwest
moves from a dev-dependency to a dependency of storyteller-web.

Tests: cache freshness, asset-name parsing for every published file
type, untrusted-answer filtering, backoff, and an ignored live round
against GitHub that also writes a sample the launcher's parser is tested
against.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 10:35:45 -04:00
BFlatandClaude Opus 5.5 42b1872760 Bump ArtCraft Launcher downloads to 0.1.2 (#74)
Points every launcher download on /apps and the Crafting App pages at
[v0.1.2](https://github.com/storytold/craft-launcher/releases/tag/v0.1.2):
the Windows installer now lets you pick the install folder and offers a
desktop shortcut. Merged ahead of the release being published; it goes
live only when the site is published on Netlify.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 18:44:31 +07:00
BFlatandClaude Opus 5.5 a93ebd6f84 Bump ArtCraft Launcher downloads to 0.1.1 (#73)
Points every launcher download on /apps and the Crafting App pages at
[v0.1.1](https://github.com/storytold/craft-launcher/releases/tag/v0.1.1),
now the latest release: the restyled launcher, the Windows setup wizard
and the blue app icon.

Checked: all 20 files the site links exist in v0.1.1 and download (HTTP
200).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 18:04:32 +07:00
BFlatandClaude Opus 5.5 b57f9155da Lead Crafting App pages with the Launcher download; shorter download labels (#72)
## What

- **App pages lead with ArtCraft Launcher.** The first download button
at the top of every `/apps/<slug>` page now downloads ArtCraft Launcher
for the visitor's system. It's the same OS-aware button as the `/apps`
hub, with the platform and build underneath. With no detected system it
jumps to `#get-it`. If the launcher ever has no release, the hero falls
back to the app's own download.
- **Shorter labels.**
- The launcher button now reads **Download Launcher** (hub, app heroes
and the Get-it callout).
- The standalone platform buttons read **Standalone for macOS / Windows
/ Linux** instead of "Download PhotoCraft for Windows". The section
heading and each "Other PhotoCraft files" list still name the app.
- **Spacing fix.** The "For Windows · Installer (64-bit)" line under the
launcher button no longer widens its column, which used to push the
buttons beside it away.

The launcher stays at v0.1.0, its latest published release. All 20 links
the site builds for it exist in that release. v0.1.1 is built but still
a draft; bump `CRAFT_LAUNCHER_RELEASE` once it's published.

## Checks

- Ran the site locally (`next dev`) and looked at `/apps/photocraft` and
`/apps` in Edge at desktop and phone widths: one row of buttons, even
gaps, and the launcher link goes to the Windows x64 MSI.
- `npm run typecheck` reports the same 54 errors as `main` without this
change (Playwright and three.js types in `tests/` and
`media-viewer-3d.tsx`); none come from these files.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 18:00:18 +07:00
BFlatandClaude c762161646 Recommend ArtCraft Launcher on the Crafting Apps pages (#70)
## Summary

Pushes ArtCraft Launcher
([storytold/craft-launcher](https://github.com/storytold/craft-launcher))
as the recommended way to install the Crafting Apps. One install covers
every app and keeps them up to date. Each app keeps its own installers.

- **`/apps` header:** the main button is now **Download ArtCraft
Launcher**. It detects the visitor's OS and links straight to the right
installer, with a small line naming the file (e.g. "For macOS · Disk
image (Universal)"). Below the buttons, one line says it's the
recommended option and that each app also has its own installers.
- **`/apps/<slug>` Get-it section:** split into two numbered options.
**Option 1 · Recommended** is the launcher block: the pitch on the left,
and on the right a sunken panel with the version, a full-width download
button and an About link. **Option 2 · Standalone** ("Just <App>?
Download it standalone.") comes next, followed by the app's existing
per-platform installers, unchanged.
- **Fallback:** when the OS can't be detected (phones, tablets), the
launcher buttons go to the launcher's GitHub release page.

## Implementation

- `CRAFT_LAUNCHER_RELEASE` in `crafting-app-releases.ts` holds the
launcher's version (v0.1.0). It reuses the shared asset list minus the
macOS CLI zip, and its files are named `artcraft-launcher-*`. Setting it
to `null` hides every launcher button.
- `src/lib/craft-launcher.ts` holds the name, repo, download list and
release-page URL. `src/components/apps/launcher-callout.tsx` holds the
app-page block.
- `craftReleaseDownloads` now delegates to a new repo-based
`releaseDownloads` helper, which the launcher also uses.
- `LauncherDownloadButton` in `app-downloads.tsx` reuses the existing OS
and CPU detection.
- Download clicks are already tracked as `app_download` (`app_name:
craft-launcher`) by the link classifier in `analytics.ts`.
- Small fix: the platform cards get `min-w-0`, so long filenames no
longer push the page wider than the screen on narrow phones.

## Testing

- `npm run typecheck` and `npm run build` pass.
- All 20 launcher release URLs on the page return 200 on GitHub.
- Checked in Chromium with a macOS user agent: `/apps` and app pages in
light and dark mode, and at 1440px, 390px and 360px. Nothing overflows
the page horizontally.

This needs a deploy of `artcraft-website-next` before getartcraft.com
changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-10 12:56:10 +07:00
Brandon ThomasandClaude Opus 5.5 c489e8c650 Cap MySQL statement cache and connection lifetime in storyteller-web (#65)
## Problem

Production `storyteller-web` and the job workers started returning fast
500s with MySQL error 1461:

```
Can't create more than max_prepared_stmt_count statements (current value: 16382)
```

A customer saw this on `GET /v1/prompt/batch`. Errors rose to ~316/hour
on 2026-10-09 and stopped after a rolling restart at ~17:50 UTC. The
restart only clears the caches, so this will happen again under load.

## Cause

sqlx (0.7.4) caches up to **100** prepared statements per connection by
default, and each cached statement stays open on the server. At current
scale, `storyteller-web` can hold:

| Setting                       | Before   | After   |
|-------------------------------|----------|---------|
| Replicas                      | 7        | 7       |
| `MYSQL_MAX_CONNECTIONS`       | 50       | 50      |
| Statement cache / connection  | 100      | 25      |
| Max statements held           | ~35,000  | ~8,750  |
| Server cap                    | 16,382   | 16,382  |

The ~70 call sites that use `QueryBuilder` (variable-length `IN (...)`
lists) produce many different SQL strings, which fill the caches faster.

## Changes

`crates/service/web/storyteller_web/src/configs/connect_to_database.rs`:

- **`statement_cache_capacity`** defaults to 25, configurable with
`MYSQL_STATEMENT_CACHE_CAPACITY`.
- **`max_lifetime`** defaults to 10 minutes (sqlx's default is 30),
configurable with `MYSQL_MAX_LIFETIME_SECONDS`. A query cancelled
between prepare and close (e.g. the client disconnects) leaves its
statement open on the server until the connection closes. Recycling
connections sooner frees those statements sooner.
- Both values are logged with the existing pool settings at startup.

Job workers are left unchanged: their pools are hardcoded to 2
connections, so each holds at most 200 statements.

## Notes

- If replicas or `MYSQL_MAX_CONNECTIONS` increase, keep `replicas ×
MYSQL_MAX_CONNECTIONS × MYSQL_STATEMENT_CACHE_CAPACITY` well under
16,382.
- Follow-up (not in this PR): use `.persistent(false)` on hot dynamic
`QueryBuilder` queries such as `batch_get_prompts`.

## Testing

- `SQLX_OFFLINE=true cargo check -p storyteller-web` passes.
- After deploy, look for the new values in the `Building MySQL pool:`
startup log line, and confirm no new `max_prepared_stmt_count` errors.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:29:20 -04:00
Brandon Thomas 17373314ae remove old website (#63) 2026-10-09 11:31:28 -04:00
931a8b925c Bump Crafting App downloads to the new releases; add ARM64, Flatpak and FreeBSD (#61)
## What

Moves every `/apps/<app>` page on getartcraft.com (Next.js site) to the
app's latest GitHub release, and adds the builds every release now
ships.

| App | Site | Latest release |
|---|---|---|
| PhotoCraft | 0.3.0 → **0.5.0** | v0.5.0 |
| VectorCraft | 0.5.0 → **0.6.0** | v0.6.0 |
| EffectCraft | 0.4.0 → **0.6.0** | v0.6.0 |
| FilmCraft, LightCraft, PdfCraft, DesignCraft | 0.2.1 → **0.4.0** |
v0.4.0 |

New files in every app's list (all seven releases ship them now):
- **Windows:** Installer (ARM64), Portable (ARM64), previously only
PhotoCraft, VectorCraft and EffectCraft
- **Linux:** Flatpak (x86_64), Flatpak (ARM64)
- **Other:** FreeBSD (x86_64)

`WITH_WINDOWS_ARM64` and `CRAFTING_APPS_LATEST_VERSION` are gone: every
app ships the same file set, and versions differ per app. PdfCraft's
`fileSlug: "printcraft"` override is dropped, since 0.4.0 files are
named `pdfcraft-…`. Only the seven classic apps are on the site; the
newer apps aren't touched.

## Verification
- Every one of the 21 links per app matches a file in that app's
published (non-draft, non-prerelease) release. The only release files
not linked are the two `.AppImage.zsync` update files.
- `npm run typecheck` passes.
- `next dev`: `/apps/photocraft`, `/apps/pdfcraft` and
`/apps/effectcraft` render the new links (Windows ARM64, both Flatpaks,
FreeBSD); no stale version or `printcraft` URLs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Brandon Thomas <shared2@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 14:00:29 -04:00
BFlatandClaude Opus 5.5 0e1991de6d Bump VectorCraft downloads to 0.5.0 (#58)
## What

Moves VectorCraft's `/apps/vectorcraft` downloads from 0.4.0 to
[v0.5.0](https://github.com/storytold/vectorcraft/releases/tag/v0.5.0).
The other Crafting Apps are already on their latest releases:

| App | Site | Latest release |
|---|---|---|
| VectorCraft | 0.4.0 → **0.5.0** | v0.5.0 |
| PhotoCraft | 0.3.0 | v0.3.0 |
| EffectCraft | 0.4.0 | v0.4.0 |
| FilmCraft, LightCraft, PdfCraft, DesignCraft | 0.2.1 | v0.2.1 |

## Verification
- All 19 files the site lists, including the Windows on ARM builds, are
in v0.5.0, and each download URL resolves. The release page returns 200.
- The release also ships a FreeBSD tarball, which the site doesn't list
for any app.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:18:42 +07:00
BFlatandClaude Opus 5.5 d950e57352 Rename PrintCraft to PdfCraft on the Crafting Apps pages (#57)
## What

The `storytold/printcraft` repo is now
[`storytold/pdfcraft`](https://github.com/storytold/pdfcraft), and every
Crafting Apps README already links `getartcraft.com/apps/pdfcraft`,
which didn't exist on the site until now. This renames the app on the
site.

- **Slug and name:** `/apps/pdfcraft`, shown as "PdfCraft" in the nav,
footer, `/apps` hub, sitemap, press kit and JSON-LD. Copy, category and
theme colour are unchanged; only the name changes.
- **Legacy links redirect permanently (308):**
  - `/apps/printcraft` → `/apps/pdfcraft`
- `/images/apps/printcraft/<file>` → `/images/apps/pdfcraft/<file>`, so
share images already cached by social sites keep loading.
- **Images:** moved to `public/images/apps/pdfcraft/`. The four
screenshots and the share image were rebuilt from the README's renamed
`pdfcraft-*.png` renders, which are slightly newer than the site's (they
show the in-app Discord button). The "Full size" and press-kit links had
been pointing at the old `printcraft-*.png` names, which no longer
exist; they now point at the new files.
- **Downloads:** v0.2.1 was published before the rename, so its files
are still called `printcraft-0.2.1-*`. `CraftRelease` gains an optional
`fileSlug` for this, and PdfCraft sets it to `printcraft`. Once a
release ships with `pdfcraft-*` names, removing that one line switches
it over.

## Verification
Rendered locally:
- `/apps/printcraft` and the old image URLs return 308 to their PdfCraft
equivalents. Following the redirect lands on the PdfCraft page with its
theme.
- `/apps/pdfcraft`: the title, canonical URL and `og:image` use
pdfcraft. All 17 download links resolve directly on
`storytold/pdfcraft`, without going through GitHub's redirect.
- The sitemap lists `/apps/pdfcraft`, and `/`, `/apps` and `/press-kit`
show no "PrintCraft". All press-kit links for PdfCraft return 200.
- `tsc` shows no errors in the changed files. The errors that remain are
already on `main`.

## Note
The screenshots still show "PrintCraft" inside the app (the window tab
and the sample PDF), because the PdfCraft repo renamed the image files
without re-rendering them. They'll update once the README screenshots
are re-captured.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 03:56:47 +07:00
BFlatandClaude Opus 5.5 652146ad96 Refresh VectorCraft screenshots and bump Crafting App downloads (#56)
## What

### VectorCraft screenshots
The images were regenerated from the latest renders in VectorCraft's
README
([storytold/vectorcraft@1c3b7b1](https://github.com/storytold/vectorcraft/commit/1c3b7b16)).
The site's previous hero still showed the old DrawCraft UI.

- `hero`, `ribbons`, `bezier` and `sheet` replaced, and a new
`perspective` shot added for the Perspective Grid. They follow the
README's order.
- Captions and alt text updated to match what each new shot shows.
- `og.jpg` share image rebuilt from the new hero, composed the same way
as before: a centred crop with a 144px icon in the bottom-left corner.
- Encoding matches the existing files (1600×1000 WebP q82, JPEG q85), so
the files are about the same size, 50–94 KB each.

### Gallery layout
`AppGallery` used to show the first shot after the hero at full width
and pair the rest. With an even number of shots, that left an empty cell
at the end. Now the first shot only goes full width when the count is
odd. VectorCraft's four shots form a 2×2 grid, like the README's "A look
around" table. All other apps have three shots, so their layout is
unchanged.

### Download versions
| App | Before | After |
|---|---|---|
| PhotoCraft | 0.2.0 | **0.3.0** |
| VectorCraft | 0.3.1 | **0.4.0** |
| EffectCraft | 0.3.1 | **0.4.0** |
| FilmCraft, LightCraft, PrintCraft, DesignCraft | 0.2.1 | 0.2.1 (still
latest) |

These three releases add **Windows on ARM** builds (`windows-arm64.msi`,
`windows-arm64-portable.zip`). They're now listed in the Windows card
for those apps through a `WITH_WINDOWS_ARM64` asset list. The main
Windows button stays on x64, because browsers on Windows on ARM
generally report themselves as x64.

## Verification
- Every expected asset exists in each release.
- Rendered all seven `/apps/<slug>` pages locally and requested every
GitHub download and release link. All of them resolve, and the "Version"
label shows the new versions.
- Checked the VectorCraft gallery in the browser: a 2×2 grid with all
images loading.
- `tsc` shows no errors in the changed files. The errors that remain are
already on `main` (missing local Playwright types and
`media-viewer-3d.tsx`).

## Not in this PR
- **`storytold/printcraft` has been renamed to `storytold/pdfcraft`.**
PrintCraft links still work through GitHub's redirect. Renaming the app
on the site is a separate change.
- Releases also ship FreeBSD tarballs, and PhotoCraft also ships Flatpak
and zsync files. The site doesn't list these.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 03:51:45 +07:00
dafe769cda PageScene: dispose GPU assets on webapp (#55)
Targets PageScene and other 3D viewers, handles uniforms and textures,
amongst other general editor mount improvements

---------

Co-authored-by: heojunfo <kleague3@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 17:58:27 -07:00
BFlatandClaude Opus 5.5 ad9b10ba94 Bump Crafting App downloads to their latest releases (#54)
## What

Updates the `/apps/<slug>` download links to each app's latest GitHub
release:

| App | Before | After |
|---|---|---|
| VectorCraft | 0.2.0 | **0.3.1** |
| EffectCraft | 0.2.0 | **0.3.1** |
| FilmCraft | 0.2.0 | **0.2.1** |
| LightCraft | 0.2.0 | **0.2.1** |
| PrintCraft | 0.2.0 | **0.2.1** |
| DesignCraft | 0.2.0 | **0.2.1** |
| PhotoCraft | 0.2.0 | 0.2.0 (still its latest) |

`CRAFTING_APPS_LATEST_VERSION` becomes `0.2.1`, the most common version.
PhotoCraft, VectorCraft and EffectCraft are pinned with
`craftRelease(...)`, following the file's documented convention.

## Verification

- Compared each release's asset list against `CRAFT_RELEASE_ASSETS`:
every expected file exists.
- Rendered all seven app pages locally and requested every GitHub
download/release link (34 per page): all return 200/302.
- The "Version" label and JSON-LD `softwareVersion` show the new
versions.

## Note

PrintCraft 0.2.1 also ships `printcraft-0.2.1-freebsd-x86_64.tar.gz`,
which the site doesn't list. That's left for a follow-up if we want to
offer it.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 07:38:45 +07:00
BFlatandClaude Opus 5.5 3880127f68 Add Google Analytics to the marketing site (#50)
## What

Adds GA4 to `artcraft-website-next`, covering the whole marketing site.

- `src/components/analytics.tsx` loads gtag.js (`afterInteractive`) and
installs one delegated click listener. It's mounted once in the root
layout.
- `src/lib/analytics.ts` holds the typed event catalogue,
`trackEvent()`, `trackAttrs()` (data attributes that work in server
components) and the link classifier.
- Analytics is **off unless `NEXT_PUBLIC_GA_MEASUREMENT_ID` is set**, so
local dev and deploy previews stay out of the reports.

## Why

The new site had no analytics. This gives us download, signup and
checkout funnels, plus CTA performance by page section.

## Events

GA4 Enhanced Measurement handles page views (App Router history
changes), scrolls, outbound clicks and common file downloads. On top of
that:

| Event | Trigger |
|---|---|
| `app_download` | GitHub release asset clicks (ArtCraft + Crafting
Apps): app, version, platform, file |
| `webapp_click` | Links to app.getartcraft.com: destination path |
| `cta_click` | Other `Button` links (tagged `data-cta`) |
| `app_select` | App cards and icons on `/apps` and sibling cards on app
pages: app name |
| `social_click`, `contact_click` | `SOCIAL_LINKS` hosts, mailto / copy
email |
| `begin_checkout` | Plan selection (`signup`/`subscribe`/`switch`) and
credit packs, with GA4 ecommerce `items` |
| `billing_cadence_change`, `view_credit_packs`, `manage_subscription` |
Pricing actions |
| `prompt_submit`, `video_generation` | H3 promptbox (`logged_in`;
complete/failed/timeout) |
| `sign_up`, `login` | Auth gate success |
| `generate_lead` | Beta form submission (honeypot hits excluded) |
| `video_start`, `share`, `copy_command`, `copy_prompt`,
`media_download`, `press_kit_download` | Video facades, share bars, copy
buttons, media/press downloads |

Every delegated click event also carries `link_location`: nav, footer,
modal, or the enclosing `section[id]`.

## Reviewer notes

- `Button` now renders `data-cta=""` on its anchor variants. There is no
visual change.
- `PricingTable.choosePlan` now branches on the computed `checkoutType`
rather than repeating the `user`/`hasActivePlan` conditions. The
behaviour is unchanged.
- `AuthGateModal` forms now return early on error instead of using
if/else, so the success event goes on the success path.

## Deploy

Set `NEXT_PUBLIC_GA_MEASUREMENT_ID` in Netlify, **Production context
only**, then redeploy. The ID is inlined at build time. This is
documented in `netlify.toml`.

After deploying:
- Mark key events in GA: `app_download`, `begin_checkout`, `sign_up`,
`generate_lead`, `webapp_click`.
- Register custom dimensions (e.g. `link_location`, `app_name`,
`platform`, `destination`, `checkout_type`) so the params show in
reports.

## Testing

Ran the dev server with a dummy measurement ID and checked `dataLayer`
on these pages:
- `/`: nav, footer, CTA, social, mailto and YouTube events.
- `/download`: Windows and macOS installers.
- `/apps/photocraft`: Linux downloads, share, copy command, GitHub link,
and `app_select` on the sibling cards (also checked on the `/apps` icon
row and lineup cards).
- `/pricing`: cadence toggle.
- `/minimax-h3`: logged-out `prompt_submit` and example `video_start`.

Not exercised in the browser: the beta form (submitting would post a
real Google Form entry), signup/login, checkout, the credits modal, the
media page, the press kit, email copy and manage plan.

`tsc` shows no new errors. Existing errors in `media-viewer-3d.tsx` and
the Playwright test files are unchanged. The production build was not
run.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 20:00:50 +07:00
Brandon Thomas 03dea1c660 app downloads 2 (#49) 2026-10-05 06:38:56 -04:00
Brandon Thomas b5272188f7 App downloads (#48) 2026-10-05 06:17:51 -04:00
bflatastic b02f710c76 page scroll fix 2026-10-04 21:15:58 +07:00
BFlat 708de238c5 Add VectorCraft, EffectCraft, DesignCraft and app icons to Craft Apps pages (#47)
## Summary

Updates the Craft Apps pages to cover the org's seven Craft repos and
adds each app's icon.

- **VectorCraft replaces DrawCraft.** The repo was renamed, so the page
moves to `/apps/vectorcraft`. `/apps/drawcraft` permanently redirects
there, because every Craft README's app table still links the old URL.
- **New pages:** `/apps/effectcraft` (motion graphics & VFX) and
`/apps/designcraft` (page layout & publishing), each with copy,
screenshots and a share image.
- **App icons:**
  - A row in the `/apps` header linking to every app.
- On each lineup card and sibling-app card, overlapping the bottom of
the screenshot.
  - Above the wordmark in each app's hero.
- **PhotoCraft installers (`v0.1.1-rc.4`):**
  - A download button in the hero.
- macOS, Windows and Linux buttons, plus an "All builds and checksums"
link.
  - An "Installers ready" badge on its card.
  - `softwareVersion` and `downloadUrl` in its JSON-LD.
- **Share images:** each app's image has its icon in the corner, and the
`/apps` share image is the seven icons in a row.
- **Press kit:** a new "Crafting Apps icons" section, linking to each
repo's 1024px PNG.

## Details
- **Data file:** everything is still driven by
`src/lib/crafting-apps.ts`. An app's `release` field (version plus a
filename suffix per platform) produces its download buttons, so other
apps need one data entry when they ship.
- **Icons:** each repo's 512px render, converted to a 256px WebP with
transparency, about 25 KB each.
- **DesignCraft gallery:** its magazine pages are portrait, so they show
four across instead of in the 16:10 layout.
- **Hub copy:** the app count is worked out from the data ("Seven
apps"). The lineup is four columns wide on large screens, so seven cards
plus the Discord cell fill two even rows.
- **Colors:** EffectCraft and DesignCraft get their icon tile colors as
accents, with AA-contrast text colors in light and dark.
- **Adobe names:** the copy still never names Adobe products.

## Testing
- A clean `npm ci && npm run build` (what Netlify runs) passes with a
clean TypeScript check, and all seven app pages are generated as static
HTML.
- `next start` serves every page and asset with a 200, `/apps/drawcraft`
returns a 308 to `/apps/vectorcraft`, and an unknown slug returns 404.
- Checked visually at 1440px and at a 390px mobile viewport: no broken
images or horizontal overflow, and the mobile icon row wraps four over
three.
- The PhotoCraft download links and every 1024px icon URL in the press
kit resolve.
2026-10-02 23:06:56 +07:00
Brandon Thomas fc78787f30 better website hero text (#46) 2026-10-01 20:22:51 -04:00
BFlat e92253c5b4 Add Craft Apps marketing pages to the website (#45)
## Summary

Marketing pages on getartcraft.com for the five Craft Apps: PhotoCraft,
DrawCraft, FilmCraft, LightCraft and PrintCraft. Everything is driven by
one data file, `src/lib/crafting-apps.ts`.

- **`/apps` hub:** lineup cards for all five apps, shared principles,
and Discord calls to action.
- **`/apps/<slug>`:**
  - Hero screenshot with Discord, GitHub and share links.
  - Six highlights and a screenshot gallery.
- "Get it": a Discord waitlist for installers, next to build-from-source
steps with a copy button.
  - Cards linking to the sibling apps.
- **Nav:** a "Craft Apps" link after a divider following Support, and a
Discord button on desktop.
- **Footer:** a "Craft Apps" column.
- **Sitemap:** entries for the hub and all five app pages.
- **Press kit:** all 20 app screenshots, with full-resolution downloads.

## Branding
- **Colors:** each app has its own accent (`.app-*` in `globals.css`),
AA-contrast in light and dark.
- **Wordmarks:** text only, with "Craft" set in the app's color. These
pages don't use the serif accent font.
- **Share previews:** a 1200×630 image per app plus one for the hub, and
`SoftwareApplication` JSON-LD on each app page.
- **Adobe:** the copy never names Adobe products.

## Notes
- No installers exist yet in any of the five repos, so each page sends
visitors to Discord and to building from source. When an app ships a
release, adding `downloads` to its entry in `crafting-apps.ts` replaces
that block with download buttons.
- Screenshots come from each repo's `docs/images`, show only
public-domain artwork, and are converted to WebP at 1600px.
- **Small refactors:**
- New shared `DiscordButton`, now also used by the community strip and
the press kit.
  - New `CopyButton` in the UI kit.
  - `SITE_URL`/`siteUrl` moved into `lib/links.ts`.
  - New `kicker`/`mediaTag` props on `CampaignHero`.

## Testing
- A clean `npm ci && npm run build`, the same build Netlify runs,
passes: TypeScript is clean, `/apps` is static, and all five app pages
prerender as static HTML.
- `next start` serves all six routes plus their images, and an unknown
slug returns 404.
- Each page's Open Graph and Twitter tags render with the per-app image.
- Checked visually at 1440px and 1024px (the nav fits) and at a 390px
mobile viewport.
2026-10-01 22:56:48 +07:00
Brandon Thomas 847edc81e5 Merge pull request #44 from storytold/bt-no-more-gif-thumbnails
webp instead of gif thumbnails
2026-09-28 01:15:13 -04:00
Brandon Thomas 2e596cc4b3 ffmpeg fallback on mac 2026-09-28 01:07:45 -04:00
Brandon Thomas c3d72fb321 Generate WebP thumbnails for videos instead of Gif (except: MacOS 10.15)
MacOS Catalina 10.15 released October 7, 2019 can't do gifs in
safari/webkit.
2026-09-28 00:52:07 -04:00
Brandon Thomas cd27599c0a Merge pull request #43 from storytold/bt-additional-cors-tests
additional cors tests to prevent regressions
2026-09-27 23:43:50 -04:00
Brandon Thomas 46fd907182 additional cors tests to prevent regressions 2026-09-27 23:43:06 -04:00
Brandon Thomas 5bdc19f573 Merge pull request #42 from storytold/bt-domain-config-2
tests
2026-09-27 22:57:15 -04:00
Brandon Thomas 6b34f23b43 tests 2026-09-27 22:55:36 -04:00
Brandon Thomas ddf151794e Merge pull request #41 from storytold/bt-domain-config
domain config
2026-09-27 22:46:20 -04:00
Brandon Thomas 7a88118349 fold cdn configs in 2026-09-27 22:41:01 -04:00
Brandon Thomas e07bf35cd0 static config for first party domains 2026-09-27 22:40:14 -04:00
Brandon Thomas a1147a98d2 Merge pull request #40 from storytold/bt-desktop-site
add desktop marketing website
2026-09-27 22:12:11 -04:00
Brandon Thomas f67fb7db31 add desktop marketing website 2026-09-27 22:10:27 -04:00
Brandon Thomas 7d4e89ced9 Merge pull request #39 from storytold/bt-query-perf-2
fixes to new endpoint
2026-09-27 22:01:25 -04:00
Brandon Thomas b9ee1bd225 fixes to new endpoint 2026-09-27 22:01:01 -04:00
Brandon Thomas 68886b62fe Merge pull request #37 from storytold/bt-query-perf
more performant list user files endpoint
2026-09-27 20:22:51 -04:00
Brandon Thomas 293a41c7d4 let's actually not call the new endpoint prematurely 2026-09-27 17:35:00 -04:00
Brandon Thomas 35a859529f Merge pull request #38 from storytold/bt-tauri-cors
cors for tauri localhost + tauri desktop
2026-09-27 17:28:25 -04:00
Brandon Thomas 3a93e358cd tests 2026-09-27 17:27:40 -04:00
Brandon Thomas 1cb0b32898 move desktop configs 2026-09-27 17:25:20 -04:00