diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index 450e57347..77c030386 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -27,16 +27,15 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 300 steps: - - uses: actions/checkout@v4 - # The Japanese font tests run against craft-fonts (an optional build input; they skip without it). + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Check out craft-fonts - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: repository: storytold/craft-fonts ref: abb83316d96aa59c1cf64784289e378fe9fa5695 path: craft-fonts - name: Build and test in a FreeBSD VM - uses: vmactions/freebsd-vm@c46abacb49f09938ca4e1702d15d836285d694cc # v1 + uses: vmactions/freebsd-vm@c46abacb49f09938ca4e1702d15d836285d694cc # v1.5.9 with: release: "14.3" usesh: true diff --git a/.github/workflows/packaging-lint.yml b/.github/workflows/packaging-lint.yml index 603764095..59c0a1ceb 100644 --- a/.github/workflows/packaging-lint.yml +++ b/.github/workflows/packaging-lint.yml @@ -23,7 +23,7 @@ jobs: env: ACTIONLINT_VERSION: 1.7.12 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install tools run: | sudo apt-get update @@ -54,4 +54,15 @@ jobs: desktop-file-validate packaging/linux/ai.storyteller.designcraft.desktop sed -e 's/@VERSION@/0.0.0/' -e 's/@DATE@/2026-01-01/' packaging/linux/ai.storyteller.designcraft.metainfo.xml.in >"$RUNNER_TEMP/ai.storyteller.designcraft.metainfo.xml" appstreamcli validate --no-net --explain "$RUNNER_TEMP/ai.storyteller.designcraft.metainfo.xml" - python3 -c "import yaml; m = yaml.safe_load(open('packaging/linux/flatpak/ai.storyteller.designcraft.yml')); assert m['id'] == 'ai.storyteller.designcraft' and m['command'] == 'designcraft'" + python3 - <<'EOF' + import yaml + src = yaml.safe_load(open("packaging/linux/flatpak/ai.storyteller.designcraft.yml")) + bundle = yaml.safe_load(open("packaging/linux/flatpak/ai.storyteller.designcraft.bundle.yml")) + for m in (src, bundle): + assert m["id"] == "ai.storyteller.designcraft" and m["command"] == "designcraft", m["id"] + for key in ("runtime", "runtime-version", "sdk", "finish-args"): + assert src[key] == bundle[key], f"Flatpak manifests disagree on {key}: {src[key]} vs {bundle[key]}" + print("Flatpak manifests ok") + EOF + - name: FreeBSD package layout (dry run) + run: bash packaging/freebsd/package.sh --dry-run diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5cd3f09ed..634552c49 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,9 +1,12 @@ # Release pipeline: every push to `release` builds signed installers for every platform and # creates or updates the draft GitHub Release "DesignCraft v". See docs/releasing.md. # -# All jobs run in the `release` environment (restricted to the `release` branch), which holds -# the signing secrets. Every secret is optional: missing ones produce unsigned artifacts and a -# warning, never a failed build. +# The signing jobs (macOS, Windows) and the draft-release job run in the `release` environment +# (restricted to the `release` branch), which holds the signing secrets. Every secret is +# optional: missing ones produce unsigned artifacts and a warning, never a failed build. The jobs +# that sign nothing (version, Linux, Flatpak, FreeBSD, web) get no environment and no secrets, +# so a `workflow_dispatch` on another branch dry-runs them; the signing jobs are refused there, +# which skips the draft release. name: Release on: @@ -35,13 +38,13 @@ jobs: version: name: version runs-on: ubuntu-latest - environment: release + # No `environment: release`: no secrets needed, and branch dry runs need this job. outputs: version: ${{ steps.v.outputs.version }} date: ${{ steps.v.outputs.date }} prerelease: ${{ steps.v.outputs.prerelease }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - id: v name: Resolve version run: | @@ -69,18 +72,17 @@ jobs: DESIGNCRAFT_VERSION: ${{ needs.version.outputs.version }} DESIGNCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }} steps: - - uses: actions/checkout@v4 - # Optional font input (docs: storytold/craft-fonts docs/integration.md); releases always embed it. + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Check out craft-fonts - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: repository: storytold/craft-fonts ref: abb83316d96aa59c1cf64784289e378fe9fa5695 path: craft-fonts - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable with: targets: aarch64-apple-darwin, x86_64-apple-darwin - - uses: Swatinem/rust-cache@v2 + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 with: key: release-macos-universal - name: Apply version override @@ -100,7 +102,7 @@ jobs: APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} run: packaging/macos/package.sh --arch universal - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: release-macos path: dist/release/* @@ -128,18 +130,17 @@ jobs: DESIGNCRAFT_VERSION: ${{ needs.version.outputs.version }} DESIGNCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }} steps: - - uses: actions/checkout@v4 - # Optional font input (docs: storytold/craft-fonts docs/integration.md); releases always embed it. + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Check out craft-fonts - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: repository: storytold/craft-fonts ref: abb83316d96aa59c1cf64784289e378fe9fa5695 path: craft-fonts - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable with: targets: ${{ matrix.target }} - - uses: Swatinem/rust-cache@v2 + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 with: key: release-windows-${{ matrix.arch }} - name: Apply version override @@ -163,7 +164,7 @@ jobs: AZURE_SIGNING_ACCOUNT: ${{ secrets.AZURE_SIGNING_ACCOUNT }} AZURE_CERT_PROFILE: ${{ secrets.AZURE_CERT_PROFILE }} run: ./packaging/windows/package.ps1 -Arch ${{ matrix.arch }} - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: release-windows-${{ matrix.arch }} path: dist/release/* @@ -175,7 +176,7 @@ jobs: # The oldest GitHub-hosted image, so the binaries only need glibc >= 2.35 (Ubuntu 22.04+, # Debian 12+, Fedora 36+, RHEL 10). See docs/releasing.md. runs-on: ${{ matrix.runner }} - environment: release + # No `environment: release`: this job signs nothing, so it gets no release secrets. strategy: fail-fast: false matrix: @@ -187,10 +188,9 @@ jobs: DESIGNCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }} NFPM_VERSION: 2.47.0 steps: - - uses: actions/checkout@v4 - # Optional font input (docs: storytold/craft-fonts docs/integration.md); releases always embed it. + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Check out craft-fonts - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: repository: storytold/craft-fonts ref: abb83316d96aa59c1cf64784289e378fe9fa5695 @@ -200,12 +200,12 @@ jobs: sudo apt-get update sudo apt-get install -y --no-install-recommends \ libxkbcommon-dev libwayland-dev libx11-dev libxrandr-dev libxi-dev libgl1-mesa-dev \ - desktop-file-utils appstream python3-yaml + desktop-file-utils appstream python3-yaml zsync curl -fsSL -o "$RUNNER_TEMP/nfpm.deb" \ "https://github.com/goreleaser/nfpm/releases/download/v${NFPM_VERSION}/nfpm_${NFPM_VERSION}_${{ matrix.deb }}.deb" sudo dpkg -i "$RUNNER_TEMP/nfpm.deb" - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 + - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 with: key: release-linux-${{ matrix.arch }} - name: Apply version override @@ -225,35 +225,127 @@ jobs: for appimage in dist/release/*.AppImage; do chmod +x "$appimage" APPIMAGE_EXTRACT_AND_RUN=1 "$appimage" --version + # Update information is embedded, and the .zsync for delta updates sits beside it. + "$appimage" --appimage-updateinformation | grep -F 'gh-releases-zsync|' + test -s "$appimage.zsync" done - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: release-linux-${{ matrix.arch }} path: dist/release/* if-no-files-found: error + flatpak: + name: Flatpak ${{ matrix.arch }} + needs: [version, linux] + # Repackages the Linux job's tarball (the same binaries as the AppImage/deb/rpm); no Rust + # build here. 24.04 for a newer flatpak-builder (1.4) than 22.04 ships. + runs-on: ${{ matrix.runner }} + # No `environment: release`: this job signs nothing, so it gets no release secrets. + strategy: + fail-fast: false + matrix: + include: + - { arch: x86_64, runner: ubuntu-24.04 } + - { arch: aarch64, runner: ubuntu-24.04-arm } + env: + DESIGNCRAFT_VERSION: ${{ needs.version.outputs.version }} + DESIGNCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 + with: + name: release-linux-${{ matrix.arch }} + path: dist/linux + - name: Install flatpak-builder + run: | + sudo apt-get update + # librsvg2-common: the SVG pixbuf loader. flatpak-builder runs the host's + # `appstreamcli compose`, which renders the scalable icon and fails without it. + sudo apt-get install -y --no-install-recommends flatpak flatpak-builder librsvg2-common + # Ubuntu 24.04's AppArmor blocks the unprivileged user namespaces bubblewrap needs. + sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 + - name: Build the Flatpak bundle + run: packaging/linux/flatpak-bundle.sh "dist/linux/designcraft-$DESIGNCRAFT_VERSION-linux-${{ matrix.arch }}.tar.gz" + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: release-flatpak-${{ matrix.arch }} + path: dist/release/*.flatpak + if-no-files-found: error + + freebsd: + name: FreeBSD x86_64 + needs: version + # GitHub has no FreeBSD runners: build in a FreeBSD 14.3 VM, the same image and packages as + # .github/workflows/freebsd.yml. + runs-on: ubuntu-latest + timeout-minutes: 180 + # No `environment: release`: this job signs nothing, so it gets no release secrets. + env: + DESIGNCRAFT_VERSION: ${{ needs.version.outputs.version }} + DESIGNCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Check out craft-fonts + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: storytold/craft-fonts + ref: abb83316d96aa59c1cf64784289e378fe9fa5695 + path: craft-fonts + - name: Build and package in a FreeBSD VM (tar.gz) + # vmactions/freebsd-vm v1.5.9, pinned by commit (it runs our build). + uses: vmactions/freebsd-vm@c46abacb49f09938ca4e1702d15d836285d694cc # v1.5.9 + with: + release: "14.3" + usesh: true + cpu: 4 + mem: 12288 + # Passed into the VM by name; never templated into the script below. + envs: "DESIGNCRAFT_VERSION DESIGNCRAFT_BUILD_DATE DESIGNCRAFT_BUILD_SHA INPUT_VERSION" + # The FreeBSD CI packages, plus bash for the packaging script. + prepare: | + pkg install -y bash git curl pkgconf cmake python3 libxkbcommon wayland libX11 libXcursor libXrandr libXi libxcb mesa-libs vulkan-loader gtk3 fontconfig freetype2 alsa-lib + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable + run: | + set -e + . "$HOME/.cargo/env" + git config --global --add safe.directory "$PWD" + # Build outside the checkout so only dist/ is copied back out of the VM (copying the + # target dir back fails on large builds). 4 jobs: more runs the VM out of memory. + export CARGO_TARGET_DIR=/var/tmp/designcraft-target CARGO_BUILD_JOBS=4 + if [ -n "$INPUT_VERSION" ]; then cargo xtask version set "$DESIGNCRAFT_VERSION"; fi + # craft-fonts was checked out next to the sources and synced into the VM with them. + export CRAFT_FONTS_DIR="$PWD/craft-fonts" CRAFT_FONTS_REQUIRED=1 + bash packaging/freebsd/package.sh + tar -tzf dist/release/designcraft-*-freebsd-x86_64.tar.gz \ + | grep -E '/bin/designcraft$|/bin/designcraft-cli$|/share/applications/ai\.storyteller\.designcraft\.desktop$|/share/doc/designcraft/LICENSE-MIT$' + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: release-freebsd-x86_64 + path: dist/release/* + if-no-files-found: error + web: name: Web (wasm) needs: version runs-on: ubuntu-latest - environment: release + # No `environment: release`: this job signs nothing, so it gets no release secrets. env: DESIGNCRAFT_VERSION: ${{ needs.version.outputs.version }} DESIGNCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }} TRUNK_VERSION: 0.21.14 steps: - - uses: actions/checkout@v4 - # Optional font input (docs: storytold/craft-fonts docs/integration.md); releases always embed it. + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Check out craft-fonts - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: repository: storytold/craft-fonts ref: abb83316d96aa59c1cf64784289e378fe9fa5695 path: craft-fonts - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable with: targets: wasm32-unknown-unknown - - uses: Swatinem/rust-cache@v2 + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 with: key: release-web - name: Install trunk @@ -269,7 +361,7 @@ jobs: CRAFT_FONTS_DIR: ${{ github.workspace }}/craft-fonts CRAFT_FONTS_REQUIRED: "1" run: packaging/web/package.sh - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: release-web path: dist/release/* @@ -277,7 +369,7 @@ jobs: release: name: Draft GitHub Release - needs: [version, macos, windows, linux, web] + needs: [version, macos, windows, linux, flatpak, freebsd, web] runs-on: ubuntu-latest environment: release permissions: @@ -288,7 +380,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: pattern: release-* path: artifacts diff --git a/.github/workflows/windows-arm64.yml b/.github/workflows/windows-arm64.yml index f2c54375d..b568d4e78 100644 --- a/.github/workflows/windows-arm64.yml +++ b/.github/workflows/windows-arm64.yml @@ -36,11 +36,11 @@ jobs: runs-on: windows-latest timeout-minutes: 90 steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@89b12181fb390509a0842a86cc55eeb8eb928c1d # stable + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable with: targets: aarch64-pc-windows-msvc - - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2 + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 with: key: windows-arm64-package - name: Install WiX @@ -51,7 +51,7 @@ jobs: env: DIST: ${{ github.workspace }}\dist\arm64 run: ./packaging/windows/package.ps1 -Arch arm64 - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: designcraft-windows-arm64 path: dist/arm64/* @@ -64,7 +64,7 @@ jobs: runs-on: windows-11-arm timeout-minutes: 30 steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: designcraft-windows-arm64 path: dist diff --git a/AGENTS.md b/AGENTS.md index 3a2bcb59a..bedbfb6e6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # DesignCraft — instructions for agents -DesignCraft is a clean-room, open-source, Rust-native page-layout application targeting Adobe InDesign parity — and superiority (speed, openness, agent control). It runs natively on macOS, Windows and Linux, and on the web via WASM. Siblings with the same conventions: `../drawcraft` (Illustrator-class), `../photocraft` (Photoshop), `../pdfcraft` (Acrobat), `../filmcraft` (Premiere), `../lightcraft` (Lightroom). +DesignCraft is a clean-room, open-source, Rust-native page-layout application targeting Adobe InDesign parity — and superiority (speed, openness, agent control). It runs natively on macOS, Windows and Linux, and on the web via WASM. Siblings with the same conventions: `../vectorcraft` (Illustrator-class), `../photocraft` (Photoshop), `../pdfcraft` (Acrobat), `../filmcraft` (Premiere), `../lightcraft` (Lightroom). ## Start every session here 1. Read `plan/STATUS.md` (current milestone, next task), then the task in `plan/execution-plan.md` and the relevant `plan/architecture.md` section. Behaviour reference: `plan/indesign/*.md` (`11-observed-ui.md` holds measured observations of the running app). diff --git a/ASSETS.md b/ASSETS.md index cfe1f807d..66479bd7b 100644 --- a/ASSETS.md +++ b/ASSETS.md @@ -8,6 +8,10 @@ Generated-in-code art is original and has no file to list: the UI icon set (`cra | Asset | Author | Source | Licence | Notes | |---|---|---|---|---| +| `crates/ui-egui/src/i18n/uk.rs` (332 common phrases) | VectorCraft contributors | https://github.com/storytold/vectorcraft/blob/01f329aa99fa0c9517311c7b6cf7233ffc207742/crates/ui-egui/src/i18n/uk.tsv | MIT OR Apache-2.0 | Adapted for DesignCraft context; remaining Ukrainian phrases are original or credited below | +| `crates/ui-egui/src/i18n/uk.rs` (68 common phrases) | PhotoCraft contributors | https://github.com/storytold/photocraft/blob/ed057bdc6fd89a858c5e0ea4eab158a2a7052dde/crates/ui-egui/src/i18n/uk.tsv | MIT OR Apache-2.0 | Adapted interface vocabulary | +| `crates/ui-egui/src/i18n/uk.rs` (37 common phrases) | WordCraft contributors | https://github.com/storytold/wordcraft/blob/7fb7b013591e2593fbd038f90bbc535be318e5a3/crates/ui-egui/src/i18n/uk.tsv | MIT OR Apache-2.0 | Adapted interface vocabulary | +| `crates/ui-egui/src/i18n/uk.rs` (15 common phrases) | DeckCraft contributors | https://github.com/storytold/deckcraft/blob/3a45c614a409349fc13d449f35bd98f3321f0df5/crates/ui-egui/src/i18n/uk.tsv | MIT OR Apache-2.0 | Adapted interface vocabulary; validation dictionaries are external and are not bundled | | `assets/hyphenation/en-us.dic` | Grady Ward (Moby Hyphenator II word list); converted by DesignCraft contributors | https://www.gutenberg.org/ebooks/3204 (file `https://www.gutenberg.org/files/3204/files/mhyph.txt`, SHA-256 `eeb30474c86b8af3469035ec1a0913e35905325ca885290db9dfed9881e230ac`) | Public Domain ("Public Domain material by grant from the author, January, 2001"; Project Gutenberg: "Public domain in the USA") | ~165k words with break points, front-coded + deflate; regenerate with `cargo run --release -p designcraft-compose --example hyphgen -- mhyph.txt assets/hyphenation` | | `assets/hyphenation/en-us.pat` | DesignCraft contributors (generated) | trained from `en-us.dic`'s source list by `crates/compose/src/hyphen/patgen.rs` (`examples/hyphgen.rs`) | Public Domain (CC0-1.0) | Our own Liang hyphenation patterns (no TeX/hyph-utf8 patterns used) | | `assets/fonts/Inter-Medium.ttf` | Rasmus Andersson / The Inter Project Authors | https://github.com/rsms/inter | OFL-1.1 (`assets/fonts/OFL-Inter.txt`) | Open-source typeface | @@ -60,3 +64,5 @@ Generated-in-code art is original and has no file to list: the UI icon set (`cra | `docs/brand/artcraft-mark.svg`, `docs/brand/artcraft-mark.png` | ArtCraft / Storyteller | ArtCraft brand kit (project owner) | ArtCraft trademark, see docs/brand/LICENSE-brand.txt (not open source) | The mark; its outline is drawn in code by `crates/ui-egui/src/about.rs` (About splash, start screen) | | `docs/brand/artcraft-mark-black.svg`, `docs/brand/artcraft-mark-black.png` | ArtCraft / Storyteller | ArtCraft brand kit (project owner) | ArtCraft trademark, see docs/brand/LICENSE-brand.txt (not open source) | Mark for light backgrounds | | `docs/brand/LICENSE-brand.txt` | (licence text) | ArtCraft Team | — | Terms for the ArtCraft Marks in `docs/brand/` | +| `packaging/macos/dmg/background.svg` | @XusBadia | original work for this repository, derived from the DesignCraft app icon (`assets/app-icon/designcraft.svg`, referenced via ``, not copied); text is outlined Inter / JetBrains Mono glyph paths | MIT OR Apache-2.0 (`LICENSE-MIT`, `LICENSE-APACHE`) | DesignCraft macOS DMG window background for the Finder window; `packaging/macos/dmg/generate.py` renders it to `background.tiff` (2026-10-08) | +| `packaging/macos/dmg/background.tiff` | @XusBadia | Rendered from `packaging/macos/dmg/background.svg` by `packaging/macos/dmg/generate.py` | MIT OR Apache-2.0 (`LICENSE-MIT`, `LICENSE-APACHE`) | 1x + 2x HiDPI TIFF for the macOS DMG window; includes the app icon (see its row) and LittleCMS's built-in sRGB profile ("No copyright, use freely") | diff --git a/CLAUDE.md b/CLAUDE.md index 7d0673992..dfe78adb6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,6 @@ # DesignCraft — instructions for agents -DesignCraft is a clean-room, open-source, Rust-native page-layout application targeting Adobe InDesign parity — and superiority (speed, openness, agent control). It runs natively on macOS, Windows and Linux, and on the web via WASM. Siblings with the same conventions: `../drawcraft` (Illustrator-class), `../photocraft` (Photoshop), `../pdfcraft` (Acrobat), `../filmcraft` (Premiere), `../lightcraft` (Lightroom). +DesignCraft is a clean-room, open-source, Rust-native page-layout application targeting Adobe InDesign parity — and superiority (speed, openness, agent control). It runs natively on macOS, Windows and Linux, and on the web via WASM. Siblings with the same conventions: `../vectorcraft` (Illustrator-class), `../photocraft` (Photoshop), `../pdfcraft` (Acrobat), `../filmcraft` (Premiere), `../lightcraft` (Lightroom). ## Start every session here 1. Read `plan/STATUS.md` (current milestone, next task), then the task in `plan/execution-plan.md` and the relevant `plan/architecture.md` section. Behaviour reference: `plan/indesign/*.md` (`11-observed-ui.md` holds measured observations of the running app). diff --git a/Cargo.lock b/Cargo.lock index 76d00d16e..6473b84f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -635,6 +635,12 @@ dependencies = [ "wayland-client", ] +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.5.1" @@ -855,7 +861,7 @@ dependencies = [ [[package]] name = "designcraft" -version = "0.3.0" +version = "0.5.0" dependencies = [ "designcraft-engine", "designcraft-ui-egui", @@ -871,7 +877,7 @@ dependencies = [ [[package]] name = "designcraft-cli" -version = "0.3.0" +version = "0.5.0" dependencies = [ "designcraft-color", "designcraft-compose", @@ -887,7 +893,7 @@ dependencies = [ [[package]] name = "designcraft-color" -version = "0.3.0" +version = "0.5.0" dependencies = [ "kurbo", "moxcms", @@ -899,7 +905,7 @@ dependencies = [ [[package]] name = "designcraft-compose" -version = "0.3.0" +version = "0.5.0" dependencies = [ "designcraft-color", "designcraft-doc", @@ -915,7 +921,7 @@ dependencies = [ [[package]] name = "designcraft-doc" -version = "0.3.0" +version = "0.5.0" dependencies = [ "designcraft-color", "designcraft-geom", @@ -927,7 +933,7 @@ dependencies = [ [[package]] name = "designcraft-engine" -version = "0.3.0" +version = "0.5.0" dependencies = [ "designcraft-color", "designcraft-compose", @@ -952,12 +958,13 @@ dependencies = [ "serde", "serde_json", "thiserror 2.0.21", + "wasm-bindgen-test", "zip", ] [[package]] name = "designcraft-epub" -version = "0.3.0" +version = "0.5.0" dependencies = [ "designcraft-color", "designcraft-doc", @@ -966,7 +973,7 @@ dependencies = [ [[package]] name = "designcraft-fonts" -version = "0.3.0" +version = "0.5.0" dependencies = [ "harfrust 0.13.3", "kurbo", @@ -978,7 +985,7 @@ dependencies = [ [[package]] name = "designcraft-format" -version = "0.3.0" +version = "0.5.0" dependencies = [ "designcraft-doc", "serde", @@ -989,7 +996,7 @@ dependencies = [ [[package]] name = "designcraft-geom" -version = "0.3.0" +version = "0.5.0" dependencies = [ "flo_curves", "kurbo", @@ -1002,7 +1009,7 @@ dependencies = [ [[package]] name = "designcraft-idml" -version = "0.3.0" +version = "0.5.0" dependencies = [ "designcraft-color", "designcraft-doc", @@ -1014,7 +1021,7 @@ dependencies = [ [[package]] name = "designcraft-images" -version = "0.3.0" +version = "0.5.0" dependencies = [ "designcraft-color", "designcraft-fonts", @@ -1025,7 +1032,7 @@ dependencies = [ [[package]] name = "designcraft-mcp" -version = "0.3.0" +version = "0.5.0" dependencies = [ "designcraft-engine", "designcraft-geom", @@ -1038,7 +1045,7 @@ dependencies = [ [[package]] name = "designcraft-pdf" -version = "0.3.0" +version = "0.5.0" dependencies = [ "designcraft-color", "designcraft-compose", @@ -1060,7 +1067,7 @@ dependencies = [ [[package]] name = "designcraft-render" -version = "0.3.0" +version = "0.5.0" dependencies = [ "designcraft-color", "designcraft-compose", @@ -1078,7 +1085,7 @@ dependencies = [ [[package]] name = "designcraft-textimport" -version = "0.3.0" +version = "0.5.0" dependencies = [ "designcraft-doc", "quick-xml 0.38.4", @@ -1087,7 +1094,7 @@ dependencies = [ [[package]] name = "designcraft-tools" -version = "0.3.0" +version = "0.5.0" dependencies = [ "designcraft-color", "designcraft-compose", @@ -1099,7 +1106,7 @@ dependencies = [ [[package]] name = "designcraft-ui-egui" -version = "0.3.0" +version = "0.5.0" dependencies = [ "designcraft-color", "designcraft-compose", @@ -1123,12 +1130,14 @@ dependencies = [ [[package]] name = "designcraft-web" -version = "0.3.0" +version = "0.5.0" dependencies = [ "designcraft-engine", + "designcraft-format", "designcraft-ui-egui", "eframe", "egui", + "form_urlencoded", "js-sys", "log", "rfd", @@ -2550,6 +2559,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "minicov" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4869b6a491569605d66d3952bcdf03df789e5b536e5f0cf7758a7f08a55ae24d" +dependencies = [ + "cc", + "walkdir", +] + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -2673,6 +2692,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -3022,6 +3050,12 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + [[package]] name = "open" version = "5.4.4" @@ -4746,6 +4780,45 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-bindgen-test" +version = "0.3.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae7499dfd45780a0a91d7ee6bb9ac51970a4479a41a89da443fdda5a39547d42" +dependencies = [ + "async-trait", + "cast", + "js-sys", + "libm", + "minicov", + "nu-ansi-term", + "num-traits", + "oorandom", + "serde", + "serde_json", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", + "wasm-bindgen-test-shared", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b84b5ac638bfb168196a1a461fcc8f46a294a18b1b6be52133b4e0db122cc9f" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "wasm-bindgen-test-shared" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f692aa943ccd88363733b77063f32cfed5bc6cbea8e6e8b251b302f881606fe" + [[package]] name = "wayland-backend" version = "0.3.17" @@ -5562,7 +5635,7 @@ checksum = "9440ea3e5aeabb0ac63af70daf835274065238cdd0cec83418f417eae38bacee" [[package]] name = "xtask" -version = "0.3.0" +version = "0.5.0" dependencies = [ "serde_json", ] diff --git a/Cargo.toml b/Cargo.toml index 1e2ae948e..af62549eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ members = ["crates/*", "apps/*", "xtask"] default-members = ["crates/*", "apps/designcraft", "apps/designcraft-cli", "xtask"] [workspace.package] -version = "0.3.0" +version = "0.5.0" edition = "2024" license = "MIT OR Apache-2.0" rust-version = "1.90" diff --git a/README.md b/README.md index ff321c21a..6884a15e5 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ Quick start · Web · Architecture · + Downloads · The Crafting Apps · License and credits

@@ -86,7 +87,8 @@ by its own renderer. Try it yourself with `File → New → Sample Document`, or Line breaks are identical on screen and in PDF. - **Fast.** Multithreaded SIMD rendering (vello_cpu), copy-on-write documents with O(1) undo snapshots, and cached composition. -- **Open.** A documented native format, IDML import and export, PNG export, and PDF on the roadmap. +- **Open.** A documented native format, IDML import and export, and PNG, PDF (PDF/X-4, PDF/A-2b) + and EPUB export. No subscription, no licence server, no telemetry. - **Agent-native.** Every menu item, tool gesture, panel control and dialog can be driven over a JSON control channel and an **MCP server**, so Claude and other agents can lay out and edit @@ -121,6 +123,23 @@ include it. To drive a running app, send JSON lines to `127.0.0.1:7979`. The protocol is described in [`docs/control-protocol.md`](docs/control-protocol.md). +### Logs + +The desktop app writes its `log` records to standard error and to `logs/designcraft.log` in the +settings directory, beside `ui.json` and `prefs.json` (Linux `~/.config/designcraft/logs/`, or +`$XDG_CONFIG_HOME/designcraft/logs/`; macOS `~/Library/Application Support/DesignCraft/logs/`; +Windows `%APPDATA%\DesignCraft\logs\`). A start launched from a desktop menu or the Dock has no +terminal, so this file is what to attach to a bug report: the crash guard's panic report and a +failed crash-recovery save land there. Each launch moves the previous log to `designcraft.1.log` +(and that one to `designcraft.2.log`), so the log of a run that crashed survives the next start. +The file stops growing at 16 MiB. `--version` writes no file. + +| Variable | Effect | +|---|---| +| `RUST_LOG` | Log levels for standard error and the log file. Default: `info` for DesignCraft's own crates, `warn` for everything else. env_logger-style directives replace that, e.g. `RUST_LOG=debug`, `RUST_LOG=warn,designcraft_render=trace` or `RUST_LOG=info,wgpu_core=warn`; a directive ending in `*` covers every target starting with it (`designcraft*=debug`). | + +The logger is `apps/designcraft/src/logging.rs`; the web build logs to the browser console instead. + ### Web ```sh @@ -154,6 +173,51 @@ egui frontend is a separate crate, so the UI can be swapped without touching the - **Bundled assets:** every one is listed with its licence in [`ASSETS.md`](ASSETS.md) - **App icon and colour:** a calico cat in a polka-dot scarf on DesignCraft green `#7bb51c`; see [`assets/app-icon/`](assets/app-icon/README.md) +## Downloads + +**New to DesignCraft?** Download it from the [DesignCraft page on getartcraft.com](https://getartcraft.com/apps/designcraft). That's the easiest way to install it. + +**Want a specific build or format?** On GitHub, the [latest release](https://github.com/storytold/designcraft/releases/latest) has every build listed below, and [all releases](https://github.com/storytold/designcraft/releases) has earlier versions and their notes. `` in the file names is the version number, and `SHA256SUMS.txt` lists a checksum for every file. + +### Windows + +| Build | Installer | Portable | +|---|---|---| +| x64 (64-bit Intel/AMD) | `designcraft--windows-x64.msi` | `designcraft--windows-x64-portable.zip` | +| arm64 (Snapdragon and other ARM PCs) | `designcraft--windows-arm64.msi` | `designcraft--windows-arm64-portable.zip` | +| x86 (32-bit) | `designcraft--windows-x86.msi` | `designcraft--windows-x86-portable.zip` | + +Installers and executables are code-signed. + +### macOS + +| Build | File | Notes | +|---|---|---| +| App, universal (Apple silicon + Intel) | `designcraft--macos-universal.dmg` | Signed and notarized | +| Command-line tool, universal | `designcraft-cli--macos-universal.zip` | Signed and notarized | + +### Linux + +| Format | x86_64 | aarch64 (ARM64) | Notes | +|---|---|---|---| +| AppImage | `designcraft--linux-x86_64.AppImage` | `designcraft--linux-aarch64.AppImage` | Runs anywhere; updates itself with [AppImageUpdate](https://github.com/AppImageCommunity/AppImageUpdate) (`.zsync` files) | +| Flatpak | `designcraft--linux-x86_64.flatpak` | `designcraft--linux-aarch64.flatpak` | Sandboxed; `flatpak install --user ` | +| Debian/Ubuntu | `designcraft--linux-x86_64.deb` | `designcraft--linux-aarch64.deb` | | +| Fedora/RHEL/openSUSE | `designcraft--linux-x86_64.rpm` | `designcraft--linux-aarch64.rpm` | | +| Tarball | `designcraft--linux-x86_64.tar.gz` | `designcraft--linux-aarch64.tar.gz` | Unpack anywhere | + +### FreeBSD + +| Build | File | +|---|---| +| x86_64 | `designcraft--freebsd-x86_64.tar.gz` | + +### Web (WebAssembly) + +| Build | File | Notes | +|---|---|---| +| Static site | `designcraft-web-.zip` | Runs in a modern browser; host it on any static server | + ## The Crafting Apps DesignCraft is one of the **Crafting Apps**: free, open-source creative tools from the diff --git a/ROADMAP.md b/ROADMAP.md index aa4c17396..bb517b5a3 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,5 +1,7 @@ # DesignCraft roadmap +- FID1: PDF export normalizes mixed emitted gradient color spaces to RGB with an explicit warning; homogeneous device-space gradients retain their components. Linear/radial, midpoint, archival and print policies have synthetic regression coverage. + - AR1.1: Kashida justification tests verify space fallback without Tatweel fonts using an isolated font database, while retaining elongation assertions when fonts support it. - AR1: Arabic IDML controls now retain character direction, kashida switches, diacritic offsets/forms, paragraph policy values and independent story/table directions. Shaping preserves joining context and script/language selection; paragraph-level bidi, contextual digit conversion, mark-safe elongation, RTL columns/tables and physical column insertion have regression coverage. Vendor-specific justification and diacritic presets remain explicit preflight warnings; see docs/arabic-typography.md. @@ -12,6 +14,7 @@ DesignCraft aims at full Adobe InDesign parity — and to be better: faster, ope **Working today** - Document model: facing/non-facing spreads, parent pages with page-number markers, sections, layers, frames (text / graphic / unassigned), threaded stories, paragraph/character/object styles with based-on, swatches (process/spot/tints/gradients), corner options, text wrap, drop shadow, opacity/blend. - Text engine: Knuth–Plass paragraph composer and single-line composer, dictionary hyphenation (public-domain Moby list + our own trained Liang patterns), justification with word/letter/glyph-scaling ranges, keep options (keep with next, keep lines together, widows/orphans), balance ragged lines, optical margin alignment, hyphenation zone/limit, columns, threading across frames and spreads, tabs, bullets/numbering, rules, shading, baseline grid, first-baseline options, vertical justification, wrap exclusions, overset detection, caret/hit testing. +- FID2: IDML underline/strikethrough Text Color resets survive style inheritance and roundtrip. Raster and PDF paint underlines behind glyphs, retain strikethrough above, and center rules on their offsets; synthetic overlap tests cover both backends. - Rendering: vello_cpu (SIMD, multithreaded) — pages, items, images with mip levels, gradients, composed text. - UI (egui): application bar + menus, Control panel (object and text modes), Tools panel with flyouts, document tabs, rulers, pasteboard, guides (margins, columns, bleed, baseline grid), frame edges, selection handles, text ports and threads, Properties / Pages / Layers dock, Swatches, Styles, Character, Paragraph, Stroke, Text Wrap panels, New Document and Preferences dialogs, Quick Apply (⌘Return), 4 brightness themes. - Pen tool, Direct Selection anchor/handle editing, rotate from corners, Rotate/Scale/Shear tools, Eyedropper, snapping & smart guides, Align/Distribute, Find/Change (text + GREP), Story Editor, Paragraph Style Options, Effects panel, hidden characters, native macOS menu bar, measured InDesign 2026 look (Medium Dark): Properties sections per selection state, 21 pt spinner fields with arithmetic, Contextual Task Bar, inverse (black) text selection, Pages panel with drag-and-drop reorder / parent apply, Layers panel with per-object rows. @@ -88,11 +91,12 @@ DesignCraft aims at full Adobe InDesign parity — and to be better: faster, ope - Edit ▸ Menus (`window.hideMenuItem`): hide menu items; menus with hidden items end in Show All Menu Items. - View ▸ Separations Preview (`view.separations`): one process plate as ink density, or areas over an ink limit in red (a GCR preview of the rendered view; spot plates and the flattener preview aren't there). - Separations Preview plates now come from each object's own colour (CMYK as authored; RGB and images through the colour settings, images via a cached 17³ table), so pure black shows only on the black plate. -- Language (Character panel ▸ Language, `type.char {language}`): text in other languages isn't hyphenated or spell-checked with the English dictionaries, typographer's quotes follow the language (German „…“, French and Russian «…», Swiss, Spanish, Dutch, Nordic, Japanese and Traditional Chinese 「…」, Simplified Chinese “…”), the font's localized forms (OpenType `locl`: Turkish i, Bulgarian Cyrillic, Japanese / Chinese / Korean glyph variants) follow it too, and so does the fallback for CJK characters the font lacks (with Draw Missing Glyphs from Fallback Fonts on): Japanese, Simplified Chinese, Traditional Chinese and Korean each try their own installed system fonts (Japanese starts with the craft-fonts Japanese faces, Mincho first, when built with them; Hangul, kana and Bopomofo pick their language's fonts whatever the language). Language names from IDML keep their spelling; locale codes as InDesign writes them for some languages (`de_DE_2006`, `nl_NL_2005`; any `ll`, `ll_CC`, `ll-CC`) are their language and region for all of the above, and Chinese is also recognised in its common spellings ("Simplified Chinese", "Chinese (Traditional)", `zh_CN`, `zh-Hant`). Hyphenation dictionaries for other languages aren't bundled yet. +- Language (Character panel ▸ Language, Properties ▸ Character, `type.char {language}`): text in other languages isn't hyphenated or spell-checked with the English dictionaries, typographer's quotes follow the language (German „…“, French and Russian «…», Swiss, Spanish, Dutch, Nordic, Japanese and Traditional Chinese 「…」, Simplified Chinese “…”), the font's localized forms (OpenType `locl`: Turkish i, Bulgarian Cyrillic, Japanese / Chinese / Korean glyph variants) follow it too, and so does the fallback for CJK characters the font lacks (with Draw Missing Glyphs from Fallback Fonts on): Japanese, Simplified Chinese, Traditional Chinese and Korean each try their own installed system fonts (Japanese starts with the craft-fonts Japanese faces, Mincho first, when built with them; Hangul, kana and Bopomofo pick their language's fonts whatever the language). Language names from IDML keep their spelling; locale codes as InDesign writes them for some languages (`de_DE_2006`, `nl_NL_2005`; any `ll`, `ll_CC`, `ll-CC`) are their language and region for all of the above, and Chinese is also recognised in its common spellings ("Simplified Chinese", "Chinese (Traditional)", `zh_CN`, `zh-Hant`). Hyphenation dictionaries for other languages aren't bundled yet. - Books (`book.new/open/add/remove/list/styleSource/paginate/syncStyles/exportPdf`, Book panel): `.dcbook` files, continuous page numbering, style sync from a style source, one merged PDF. - Track Changes (`changes.track/list/acceptAll/rejectAll`, Type ▸ Track Changes): typing is marked added (highlighted on screen), deletions are kept but hidden from layout and exports until accepted or rejected. Per-change accept/reject and authors aren't there yet. - Track Changes: per-change Accept / Reject (`changes.accept`, `changes.reject`) and a Track Changes panel listing every change. - Move table rows and columns (`table.moveRow`, `table.moveColumn`, Table panel buttons; with whole rows or columns selected, drag them with the Type tool — `table.dropCells`). +- Distribute Rows Evenly (`table.distributeRows`, Table menu and panel): selected rows receive equal fixed heights preserving their composed total height, with undo/redo. Row-spanning merges and rows outside the composed frames are rejected; shrinking tall cells can overset their text. - Object States (`states.create/show/rename/release/list`, Object States panel): multi-state objects show one state on screen and in PDF. - Buttons (`button.set/clear/list`, Buttons and Forms panel): go to page / next / previous / first / last / URL as PDF link annotations. Form fields too (`form.set/clear/list`: text fields, check boxes, combo and list boxes, signature fields; required, multiline, defaults) exported as AcroForm widgets through an incremental update (not in PDF/X). - Power Zoom (Hand tool: press and hold still for half a second, or Alt-press): zoom out to the spread, aim the red view rectangle, release to zoom back in there. @@ -103,7 +107,7 @@ DesignCraft aims at full Adobe InDesign parity — and to be better: faster, ope - Transparency flattener (`file.exportPdf {flatten: high|medium|low|ppi}`, Edit ▸ Transparency Flattener Presets): each object involving transparency becomes an opaque image of its area over what's beneath it, so everything else stays vector (spreads with transparent parent items are rasterised whole). - Video and sound (place .mp4/.mov/.webm/.mp3/.wav…, `media.options/get`, Media panel): media frames show a poster or placeholder on screen, in print and in PDF, and export as HTML5 `