diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml index 4fcd2c84..9e135816 100644 --- a/.github/workflows/deploy-pages.yml +++ b/.github/workflows/deploy-pages.yml @@ -4,8 +4,9 @@ on: push: branches: - main - tags: - - 'v*' + workflow_run: + workflows: ['Release Native App'] + types: [completed] workflow_dispatch: permissions: @@ -13,18 +14,72 @@ permissions: pages: write id-token: write +concurrency: + group: github-pages + cancel-in-progress: false + jobs: + release_ready: + if: >- + github.event_name != 'workflow_run' || + (github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.head_repository.full_name == github.repository) + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + ready: ${{ steps.release.outputs.ready }} + version: ${{ steps.release.outputs.version }} + source_ref: ${{ steps.release.outputs.source_ref }} + steps: + - name: Checkout deployment source + uses: actions/checkout@v4 + with: + ref: ${{ github.event.workflow_run.head_sha || github.sha }} + persist-credentials: false + + - name: Wait for a published desktop release + id: release + uses: actions/github-script@v7 + with: + script: | + const fs = require('node:fs'); + const { version } = JSON.parse(fs.readFileSync('public/version.json', 'utf8')); + if (!/^\d+\.\d+\.\d+$/.test(version)) { + throw new Error('Pages requires a stable release version'); + } + core.setOutput('version', version); + core.setOutput('source_ref', context.payload.workflow_run?.head_sha || context.sha); + let latest; + try { + latest = (await github.rest.repos.getLatestRelease(context.repo)).data; + } catch (error) { + if (error.status !== 404) throw error; + } + const ready = Boolean(latest && !latest.draft && !latest.prerelease && latest.tag_name === `v${version}`); + core.setOutput('ready', String(ready)); + if (!ready) { + core.notice(`Skipping Pages: v${version} is not the latest published desktop release. A successful native release will trigger another deployment check.`); + } + build_and_deploy: + needs: release_ready + if: needs.release_ready.outputs.ready == 'true' runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout uses: actions/checkout@v4 + with: + ref: ${{ needs.release_ready.outputs.source_ref }} + persist-credentials: false - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: "22" - cache: "npm" + node-version: '22' + cache: 'npm' - name: Install dependencies run: npm ci @@ -41,8 +96,23 @@ jobs: path: ./dist deploy: - needs: build_and_deploy + needs: [release_ready, build_and_deploy] runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} steps: + - name: Recheck release before publishing the update feed + uses: actions/github-script@v7 + env: + EXPECTED_RELEASE_VERSION: ${{ needs.release_ready.outputs.version }} + with: + script: | + const { data: latest } = await github.rest.repos.getLatestRelease(context.repo); + if (latest.draft || latest.prerelease || latest.tag_name !== `v${process.env.EXPECTED_RELEASE_VERSION}`) { + throw new Error('The latest release changed during the build; refusing to deploy a stale update feed.'); + } + - name: Deploy to GitHub Pages + id: deployment uses: actions/deploy-pages@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index da67f0e0..29ff0ab5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -52,6 +52,12 @@ jobs: node-version: '22' cache: 'npm' + - name: Validate release metadata + env: + KROMACUT_RELEASE_TAG: ${{ github.ref_name }} + KROMACUT_RELEASE_REF: ${{ github.ref }} + run: node scripts/check-release-notes.mjs + - name: Install Rust stable uses: dtolnay/rust-toolchain@stable with: @@ -61,15 +67,21 @@ jobs: if: matrix.platform == 'ubuntu-22.04' run: | sudo apt-get update - sudo apt-get install -y libwebkit2gtk-4.0-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf + sudo apt-get install -y libwebkit2gtk-4.0-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf zsync - name: Install dependencies run: npm ci + - name: Test AppImage release validation + if: matrix.platform == 'ubuntu-22.04' + run: node --no-warnings --experimental-strip-types --test tests/appImageUpdates.test.ts + - name: Build uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # linuxdeploy embeds this and generates the matching .zsync sidecar. + LDAI_UPDATE_INFORMATION: ${{ matrix.platform == 'ubuntu-22.04' && format('gh-releases-zsync|{0}|{1}|latest|Kromacut_*_amd64.AppImage.zsync', github.repository_owner, github.event.repository.name) || '' }} with: tagName: ${{ github.ref_name }} releaseName: 'Kromacut ${{ github.ref_name }}' @@ -80,6 +92,27 @@ jobs: assetNamePattern: ${{ matrix.assetNamePattern }} includeUpdaterJson: ${{ matrix.includeUpdaterJson }} + - name: Generate, validate and upload AppImage delta metadata + if: matrix.platform == 'ubuntu-22.04' + shell: bash + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + [[ "$GITHUB_REF_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9_.-]+)?$ ]] + appimage="src-tauri/target/release/bundle/appimage/Kromacut_${GITHUB_REF_NAME#v}_amd64.AppImage" + # appimagetool can put its sidecar in the working directory instead. + # Generate beside the final artifact with an unambiguous release URL. + zsyncmake -u "https://github.com/$GITHUB_REPOSITORY/releases/download/$GITHUB_REF_NAME/${appimage##*/}" \ + -o "$appimage.zsync" "$appimage" + node scripts/check-appimage-update.mjs \ + src-tauri/target/release/bundle/appimage \ + "$GITHUB_REPOSITORY" "$GITHUB_REF_NAME" + # tauri-action does not upload .zsync files itself. + gh release upload "$GITHUB_REF_NAME" \ + "$appimage.zsync" \ + --repo "$GITHUB_REPOSITORY" --clobber + release: needs: build runs-on: ubuntu-latest @@ -113,7 +146,8 @@ jobs: **Linux:** 1. Download the .AppImage or .deb file 2. For AppImage: chmod +x Kromacut*.AppImage && ./Kromacut*.AppImage - 3. For Debian/Ubuntu: sudo dpkg -i kromacut*.deb`; + 3. For Debian/Ubuntu: sudo dpkg -i kromacut*.deb + 4. AppImages from this release include update information for compatible tools such as AppImageUpdate. The matching .AppImage.zsync asset enables delta downloads; it is not an installer. Older AppImages without update information need a one-time manual download. Kromacut's built-in update notification still opens the release page.`; function getChangelogEntry(changelog, tagName) { const heading = /^##\s+(.+?)\s*$/gm; @@ -151,6 +185,18 @@ jobs: }); const draftRelease = releases.find(r => r.draft && r.tag_name === tagName); if (draftRelease) { + const assets = await github.paginate(github.rest.repos.listReleaseAssets, { + owner: context.repo.owner, + repo: context.repo.repo, + release_id: draftRelease.id, + per_page: 100 + }); + const appImageName = `Kromacut_${tagName.replace(/^v/, '')}_amd64.AppImage`; + for (const name of [appImageName, `${appImageName}.zsync`]) { + if (!assets.some(asset => asset.name === name && asset.state === 'uploaded' && asset.size > 0)) { + throw new Error(`Refusing to publish without AppImage update asset: ${name}`); + } + } await github.rest.repos.updateRelease({ owner: context.repo.owner, repo: context.repo.repo, diff --git a/CHANGELOG.md b/CHANGELOG.md index 2908e32d..bbce21df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,39 @@ All notable changes to Kromacut are documented in this file. +## Unreleased + +## v4.1.0 - 2026-09-26 + +### Upgrade notes + +- **Existing profiles and calibration records remain compatible.** Saved Stack Matrices retain their original dimensions and recipes; the one-layer foundation and grouped layout apply to newly generated matrices. Back up important profiles as usual. +- **Printable-detail cleanup is now more conservative.** The saved omission preference remains enabled if you previously enabled it, but now removes only eligible isolated color specks. Rebuild and check the slicer preview to see the updated result. +- **Language changes do not change print data.** Select a language in Settings or on public pages; artwork, filament profiles, physical dimensions, and generated geometry remain unchanged. +- **Linux delta updates start with this AppImage.** Older AppImages without embedded update information need a one-time manual download. Compatible external update tools can then use the matching `.zsync` asset; the in-app update notice still opens the release page. AnyLinux packaging is not part of this release. + +### Added + +- **Release consistency checks** - Builds validate app, native, lockfile, and update-feed versions together with dated changelog entries and translated release notes. Native release jobs reject mismatched tags and branch dispatches before building. Website deployment waits for the matching published desktop release, then uses the release commit so update notices do not advertise unavailable installers. Regression coverage checks version drift, incomplete metadata, and deployment gating. +- **Linux AppImage delta updates** - Release builds embed stable-channel update information and publish a matching `.AppImage.zsync` asset for compatible tools such as AppImageUpdate. Release checks validate the embedded location, target filename/URL, size, checksum, and sidecar structure before publication. Older AppImages without update information require a one-time manual download; the in-app update notification remains download-page based. Other package formats and Linux compatibility targets are unchanged. +- **Twelve-language interface and guides** - Added i18next language selection using the existing shared Select controls in Settings and on public pages for English, French, German, Italian, Romanian, Spanish, Japanese, Simplified Chinese, Hindi, European Portuguese, Ukrainian, and Bengali. Compact public-page selectors match footer-link styling, with the landing selector aligned within the responsive footer navigation. The choice is saved locally, can follow the system language, and updates without resetting artwork, profiles, print settings, or built geometry. App controls, accessibility labels, errors, print instructions, all fifteen guides and their diagrams, landing pages, Privacy, and Terms have localized resources. Language-prefixed public links preserve stable document anchors and provide translated static HTML, metadata, sitemap entries, and language alternatives. Browser install descriptions keep the same installed app identity, and the desktop update feed carries translated release notes while retaining compatibility with older clients. Locally bundled script-appropriate fonts also travel inside full-size translated SVGs. Coverage checks reject missing resources, altered guide structure, stale diagram measurements, and untranslated UI literals; browser, native, and export regressions check language switching, mobile layouts, desktop save dialogs, and unchanged physical output. Unknown diagnostic text is preserved rather than reinterpreted through generic UI templates. +- **Terms and conditions page** - Added `/terms` with landing-footer navigation, reciprocal Privacy links, canonical metadata, a sitemap entry, and readable static HTML. The terms explain AGPL software rights, artwork and export responsibilities, print checks, local backups, Reddit/Discord help, optional Patreon support, and legally bounded warranty wording. Shared legal-page presentation preserves accessible email reveal/copy controls and mobile layouts, with route and browser regression coverage. +- **Privacy and local-data page** - Added `/privacy` and a landing-page footer link to normal builds, including readable static HTML, page metadata, a sitemap entry, and section navigation. The notice explains local storage, hosting and email providers, international processing, six-month manual correspondence reviews, privacy rights, and complaint routes. Click-to-reveal public email replaces its button and hint with the focused address link and Copy action; the literal address stays out of initial HTML and built JavaScript. Mobile, keyboard, clipboard, static-page, and route checks cover the page. Internal legal review notes stay separate from the public notice; email obfuscation is not bot-proof protection. +- **Locally hosted fonts** - Bundled the existing Oxanium, Source Code Pro, and Source Serif 4 fonts with their licenses so the website and desktop app retain their typography without contacting Google Fonts. +- **Sticky mobile launch button** - The landing page keeps an Open Kromacut action at the bottom of phone-sized screens after the header button scrolls away. It hides again at the top and stays off desktop layouts, the editor, and documentation. Safe-area spacing keeps footer links reachable, with browser coverage for scrolling, resizing, keyboard access, themes, and navigation. +- **Custom 404 page** - Broken links now show a responsive, light/dark error page with a custom layered-color 404 illustration and colored two-arm corner accents floating above the lettering, directly over the build plate's corners, a split desktop layout, compact mobile composition, and clear app, homepage, and documentation recovery links. Unknown documentation pages no longer silently show the first guide. GitHub Pages receives the same design in a standalone, search-index-excluded `404.html` that works without JavaScript. Regression coverage checks direct-link status, recovery links, desktop/mobile layouts, decorative-art accessibility, and in-app documentation navigation. +- **Illustrated feature guides** - Expanded the 3D and 2D documentation with control-by-control explanations, physical layer and color examples, calibration workflows, and full-size vector illustrations. The HD guide includes a real eight-filament wedge photograph, its recorded layer settings, and guidance to read physical patches rather than photo colors. The guides distinguish preview-only controls from image and geometry edits, describe settings that interact, and explain what must match the slicer. Mobile navigation collapses to leave room for reading. Documentation checks cover navigation, image assets, and responsive rendering. + +### Changed + +- **Effective line width in print settings** - Moved the existing control into **3D Print Settings**, retaining its saved value and including it in the section reset and modified-state indicator. Removed the separate reset button and helper description. +- **Adaptive Stack Matrix calibration** - New boards use a maximum color-stack thickness instead of a fixed 3–6-layer recipe depth, with up to 64 regular layers. The foundation is only the effective first print layer, without opacity-based thickening; shorter recipes use backing padding inside the color region to preserve a flat photographic surface. The UI shows foundation + color-region height, total height, and print-layer count, with a reminder that thin backing is not guaranteed opaque. Selection uses compatible completed measurements to seek new color, thickness, and filament-transition coverage, while retaining exploratory and reference patches. Similar recipes are grouped to reduce fragmented same-color toolpaths without claiming fewer swaps or a measured time saving. A material-change budget constrains planning without claiming a slicer time guarantee. Existing boards remain readable and re-export with their original foundation, cell positions, and recipes. Regression coverage checks adaptive selection, grouped layout, one-layer foundations, saved-data compatibility, padded recipe predictions, flat manifold exports, and the browser save workflow. + +### Fixed + +- **Desktop Hiding Distance downloads** - Restored calibration STL/3MF downloads in the desktop app using Save As. Wedge exports show a busy state, report write failures, and retain the previous downloaded plan when saving is cancelled or fails. Regression coverage checks both formats and browser downloads. +- **Over-aggressive printable-detail cleanup** - Width checks now account for pixel boundaries and preserve connected regions with a wide core, avoiding whole-pixel rounding and diagonal false positives. Auto-paint's At-risk/Result preview distinguishes warnings from actual removals. The renamed **Omit isolated color specks** option only replaces colors used exclusively in tiny compact specks enclosed by one wider color; thin linework, connected detail, and ambiguous regions stay intact. Matching, preview, and export share the same cleanup without editing the 2D source. Regression coverage checks detail conservation, saved line-width settings, and browser behavior. This remains an image-only estimate, not a model of physical material layers or slicer toolpaths. + ## v4.0.0 - 2026-09-10 ### Upgrade notes diff --git a/docs/TAURI.md b/docs/TAURI.md index 3ed9410c..3418878c 100644 --- a/docs/TAURI.md +++ b/docs/TAURI.md @@ -5,10 +5,10 @@ Kromacut can be built as a native application for macOS, Windows, and Linux usin ## Prerequisites - Rust - ```bash - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - source "$HOME/.cargo/env" - ``` + ```bash + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + source "$HOME/.cargo/env" + ``` - Node.js ## Development @@ -36,26 +36,46 @@ Main config file: `src-tauri/tauri.conf.json` When releasing a new version: -1. Update `package.json` version. -2. Update `src-tauri/tauri.conf.json` version. -3. Commit and tag the release. - -Example: - -```bash -git add package.json src-tauri/tauri.conf.json -git commit -m "Bump version to vX.Y.Z" -git tag vX.Y.Z -git push origin main -git push origin vX.Y.Z -``` +1. Synchronize the version in `package.json`, both root entries in `package-lock.json`, + `src-tauri/tauri.conf.json`, the package in `src-tauri/Cargo.toml`, the `kromacut` package in + `src-tauri/Cargo.lock`, and `public/version.json`. Do not upgrade dependencies as a side effect. +2. Move the completed `CHANGELOG.md` entries into a dated `vX.Y.Z` section, retaining an empty + `Unreleased` section for future work. Preserve previously published release entries. +3. Update the feed's English summary and every `release_notes_localized` translation together. + Point `download_url` to the exact `/releases/tag/vX.Y.Z` page, not `/releases/latest`. +4. Run `node scripts/check-release-notes.mjs`, lint, the relevant regression tests, and + `npm run test:e2e:i18n:build`. The latter builds and checks localized production routes. +5. Commit the reviewed release-preparation files and merge them to `main` before creating the tag. + Leave private audit notes, local calibration data, and temporary files out of the commit. +6. After release approval, tag that reviewed commit as `vX.Y.Z` and push the tag. The native + workflow validates the tag, manifest/lockfile versions, feed, and changelog before building. + Manual workflow runs must select the version tag, not a branch. The GitHub Actions workflow will automatically build native applications for: + - **macOS**: Apple Silicon (M1/M2/M3) and Intel - **Windows**: x64 NSIS setup installer, plus an offline NSIS setup installer with the WebView2 offline installer embedded - **Linux**: AppImage and .deb package -All artifacts are attached to the GitHub release. +All artifacts are attached to a draft GitHub release. The publication job waits for all native +builds and the AppImage metadata checks before publishing it. Check that every platform completed +successfully and that the published downloads match the version; local web tests alone do not +verify all native installers. + +### Website and update-feed ordering + +Pages deployment checks that `public/version.json` matches the latest published stable desktop +release. Merging a version bump before the installers are ready therefore skips deployment and +leaves the existing site and update feed live. Successful completion of **Release Native App** +triggers another check, then builds Pages from that native workflow's exact source commit. A +final check prevents publishing an obsolete feed if the latest release changed during the build. +Main-branch pushes and manual Pages runs still support web-only updates with the current version. + +The Pages workflow must already exist on the default branch for GitHub's +[`workflow_run` trigger](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#workflow_run) +to work. Merge the workflow changes before tagging the first release that uses this ordering. +If native publication or Pages deployment fails, inspect the failed job before rerunning it; +do not announce the new version by manually bypassing the publication check. ## Distribution Notes @@ -72,3 +92,37 @@ For notarized distribution, configure a Developer ID signing identity in `tauri. The standard Windows installers embed the small WebView2 bootstrapper. The NSIS setup installer also checks for WebView2 `114.0.1823.67` or newer and can update the runtime when internet access is available. The release workflow also publishes a larger `*_offline-setup.exe` for Windows machines that need to install without internet access. **Linux:** AppImage bundles are portable and require no installation. `.deb` packages integrate with the system package manager. + +### AppImage delta updates + +Release AppImages embed a stable GitHub Releases update location for the matching CPU architecture. +Compatible tools such as [AppImageUpdate](https://github.com/AppImageCommunity/AppImageUpdate) +can use the accompanying `.AppImage.zsync` file to reuse unchanged data. Download savings depend +on how much of the packaged application changed; this is not a smaller standalone installer. +An older AppImage without embedded update information needs a one-time manual download of a +supporting release. Kromacut's in-app update notice still opens the release download page; it does +not install updates automatically. Windows installers, macOS bundles, `.deb`, and `.rpm` packages +are unaffected. + +The Linux release job sets `LDAI_UPDATE_INFORMATION` for linuxdeploy's AppImage plugin and uses +`zsyncmake` from the `zsync` package to generate a sidecar beside the final AppImage with its exact +release download URL. This avoids relying on appimagetool's sidecar output directory. +For the current x86-64 build, the official update +string is `gh-releases-zsync|vycdev|Kromacut|latest|Kromacut_*_amd64.AppImage.zsync`. Fork workflows +derive the owner and repository from their own GitHub context. `latest` follows stable releases, +not prereleases. + +Before the sidecar is uploaded to the draft release, `scripts/check-appimage-update.mjs` checks the +runtime's embedded update information, exact release filename, target URL, file size, SHA-1, and +block-checksum table length. The SHA-1 check verifies artifact consistency; it is not a digital +signature. Publication waits for this validation and requires both uploaded assets. The sidecar is +uploaded explicitly because tauri-action's artifact list does not include `.zsync` files. + +To validate a local Linux release build (replace the tag with the built version): + +```bash +node scripts/check-appimage-update.mjs src-tauri/target/release/bundle/appimage vycdev/Kromacut v4.1.0 +``` + +Keep the final AppImage unchanged after generating its sidecar. Rebuild both together if packaging, +embedded metadata, or signing changes. Manual release-workflow runs must target the version tag. diff --git a/docs/UPDATE_CHECKER.md b/docs/UPDATE_CHECKER.md index 2f69115a..210a0f35 100644 --- a/docs/UPDATE_CHECKER.md +++ b/docs/UPDATE_CHECKER.md @@ -15,9 +15,9 @@ The `version.json` file should be hosted at `https://kromacut.com/version.json` ```json { - "version": "2.2.0", - "download_url": "https://github.com/vycdev/Kromacut/releases/latest", - "release_notes": "Bug fixes and performance improvements" + "version": "4.1.0", + "download_url": "https://github.com/vycdev/Kromacut/releases/tag/v4.1.0", + "release_notes": "Bug fixes and performance improvements" } ``` @@ -27,6 +27,24 @@ The `version.json` file should be hosted at `https://kromacut.com/version.json` - `download_url` (optional): Direct link to download the update - `release_notes` (optional): Brief description of what's new +### Localized Release Notes + +Keep `release_notes` in English for compatibility with already-released desktop clients. New +releases also supply `release_notes_localized`, an object mapping every supported language code +from `src/lib/languagePreferences.ts` to a full translation of those same notes. Its `en` entry +must equal `release_notes`. The production example is `public/version.json`. + +Update every translation together when changing the release summary. Preserve upgrade warnings, +backup instructions, and version-specific details; do not substitute a generic update message or +reuse notes from a previous release. Run `node scripts/check-release-notes.mjs` before publishing. +The coverage check rejects missing, blank, or identical English entries for translated languages; +runtime fallback is not translation coverage. + +Both desktop update displays choose the current language at render time, so changing language +does not make another update request. The Rust update response preserves the entire translation +map. An older remote feed without a translation remains readable in its original English, with +an explicit `lang="en"` on the note; no bundled note from another release is substituted. + ## Update Frequency - **On Startup**: Checks for updates when the app launches @@ -37,23 +55,35 @@ The `version.json` file should be hosted at `https://kromacut.com/version.json` The version number is managed in multiple places and should be kept in sync: -1. `package.json` - `version` field -2. `src-tauri/tauri.conf.json` - `version` field -3. `src-tauri/Cargo.toml` - `version` field under `[package]` +1. `package.json` and both application entries in `package-lock.json` +2. `src-tauri/tauri.conf.json` +3. `src-tauri/Cargo.toml` under `[package]` and the `kromacut` entry in `src-tauri/Cargo.lock` +4. `public/version.json`, its version-specific download link, and a dated `CHANGELOG.md` entry -When releasing a new version, update all three files. +`node scripts/check-release-notes.mjs` validates these alongside localized note coverage. It runs +as part of the normal build. In release CI it also requires the exact matching version tag; a +manual dispatch from a branch cannot publish a release with mismatched installers. + +Pages only deploys a feed that names the latest published stable desktop release. A main-branch +version bump waits until the native release workflow finishes and publishes its installers; +that completion triggers deployment from the release commit. See [the release sequence](TAURI.md#website-and-update-feed-ordering). ## Disabling Update Checks -Update checks only run in the Tauri desktop environment. The web version is unaffected. To disable update checks in the desktop app, simply don't include the UpdateChecker component. +Update checks only run in the Tauri desktop environment. The web version is unaffected. In the desktop app, turn off **Settings → Updates → Check for updates on startup** to disable automatic startup and periodic checks. The manual **Check** action remains available. ## Testing -To test the update checker locally: +Run the native update-response tests and the release-metadata/deployment-guard regressions: + +```bash +cargo test --manifest-path src-tauri/Cargo.toml --lib --locked +node --no-warnings --experimental-strip-types --test tests/releaseMetadata.test.ts tests/pagesReleaseGate.test.ts tests/i18n.test.ts +``` -1. Change the version in `public/version.json` to a higher version -2. Build and run the Tauri app: `npm run tauri:dev` -3. The update notification should appear after a few seconds +The guard tests simulate missing, draft, superseded, and successfully published releases without +publishing anything. Keep synthetic version numbers in test fixtures; do not deploy a higher +`version.json` as a way to test an update notification. ## Privacy diff --git a/docs/audits/2d-feature-coverage.md b/docs/audits/2d-feature-coverage.md new file mode 100644 index 00000000..f58adccd --- /dev/null +++ b/docs/audits/2d-feature-coverage.md @@ -0,0 +1,65 @@ +# 2D feature documentation audit + +Scope: current develop UI and source, inspected 2026-09-12. Covers all nontrivial 2D fields, toggles, image actions, and their downstream print effects. Source references identify the behavior used for documentation; illustrations are schematic explanations, not measured print predictions. + +## Coverage inventory + +| Feature and controls | Authoritative source | Documentation | Illustration | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ----------------------------------------------- | +| Choose file; image MIME validation; first file on drop; 2D-only drop; default logo | `src/App.tsx:521`, `src/hooks/useDropzone.ts:13` | `loading-images#choose-a-source` | Context diagram 30 | +| Wheel zoom around pointer; left pan; middle pan while editing; crisp nearest-neighbor display | `src/components/CanvasPreview.tsx:204`, `:479`, `:957` | `loading-images#inspect-without-changing-the-image` | 30 distinguishes view vs data vs physical scale | +| Checkerboard background; Image/Crop pixel-size badge | `src/components/PreviewActions.tsx:426`, `src/components/CanvasPreview.tsx:1304`, `:1406` | `loading-images#inspect-without-changing-the-image` | 31, 35 show alpha checkerboard | +| Crop; rectangle move; corner/edge resize; Save crop; Cancel crop; source-pixel output | `src/components/CanvasPreview.tsx:1021`, `:1185`, `src/App.tsx:978` | `loading-images#crop` | 30 | +| Resize Scale 1–100%, default50; current/after dimensions; Apply; reset; downscale-only; repeated resampling | `src/components/ImageResizePanel.tsx:32`, `src/lib/imageResize.ts:1`, `src/App.tsx:541` | `loading-images#resize-image` | 30 | +| Resize smoothing introduces colors/alpha; distinction from Pixel Size and full opaque physical width | `src/App.tsx:575`, `src/hooks/useSwatches.ts:174` | `loading-images#physical-size-in-3d` | 30 | +| Brush; hard-edge circular footprint; size1–64 imagepx; opaque paint; one changed stroke/historystep | `src/components/PreviewActions.tsx:554`, `src/lib/touchup.ts:41`, `src/components/CanvasPreview.tsx:846` | `loading-images#touch-up-pixels` | 31 | +| Eraser; same brushsize; canonical alpha0 pixels; physical holes/disconnections | `src/components/CanvasPreview.tsx:846`, `src/lib/touchup.ts:96` | `loading-images#touch-up-pixels` | 31 | +| Fill; exact RGBA; four-connected floodfill; one region vs global swatch | `src/components/CanvasPreview.tsx:872`, `src/lib/touchup.ts:138` | `loading-images#touch-up-pixels` | 31, 35 | +| Picker; nontransparent source RGB; switches to Brush | `src/components/CanvasPreview.tsx:866`, `src/App.tsx:872` | `loading-images#touch-up-pixels` | 31 context | +| Tool color picker; imagepalette chips; six-digithex; popover-close commit; session-only toolsettings | `src/components/PreviewActions.tsx:473`, `src/App.tsx:287` | `loading-images#touch-up-pixels` | 31 | +| Text; size6–128px; multilines; wrap; move/resizehandles; livecolor/size; Apply/CtrlEnter/MetaEnter; discard/Escape; click-away/tool-switchcommit; rasterization | `src/components/PreviewActions.tsx:575`, `src/components/CanvasPreview.tsx:521`, `:702`, `:786`, `:885`, `:1330`, `src/lib/touchup.ts:208` | `loading-images#place-text` | 31 | +| Flat background removal through global alpha0 vs local eraser; no automatic AI removal | UI inventory `src/App.tsx:670`, `src/components/PreviewActions.tsx:156`; swatch `src/hooks/useAppHandlers.ts:316` | `loading-images#remove-a-background` | 35 | +| Undo/Redo image commits vs settings; new edit clears redo; session-onlyhistory; Remove image is not new historyentry | `src/hooks/useImageHistory.ts:42`, `:64`, `:85`, `:106`, `src/App.tsx:535` | `loading-images#undo-download-and-clear` | 33 explains live vs committed | +| PNG Download underlying full-resolution pixels; excludes overlays/unbakedadjustments; desktop save vs browserdownload | `src/components/CanvasPreview.tsx:1230`, `src/hooks/useAppHandlers.ts:111`, `src/hooks/saveBlobToFile.ts:33` | `loading-images#undo-download-and-clear` | 33 | +| Exposure ±3stops step.01; Contrast ±100; Highlights ±100; Shadows ±100; Whites ±100; Blacks ±100 | `src/components/sliderDefs.ts:12`, `src/lib/applyAdjustments.ts:144`, `:184`, `:251` | `image-adjustments#tone` | 32 all six explicitly | +| Saturation ±100; Vibrance ±100; Hue ±180deg; Temperature ±100 notKelvin; Tint ±100; Clarity ±100 | `src/components/sliderDefs.ts:68`, `src/lib/applyAdjustments.ts:198`, `:219`, `:275` | `image-adjustments#color-and-local-detail` | 32 all six explicitly | +| Adjustments preview oncommit; individualreset; allreset; Apply bake and zero sliders; Undo after bake | `src/components/AdjustmentsPanel.tsx:83`, `:108`, `:124`, `src/App.tsx:679`, `:687` | `image-adjustments#preview-versus-apply`, `#reset-and-undo-are-different` | 33 | +| Source vs adjusted pipeline for quantize/download/resize/3D vs Dedither; alphaunchanged by adjustment | `src/components/CanvasPreview.tsx:1230`, `:1252`, `src/hooks/useQuantize.ts:131`, `src/components/DeditherPanel.tsx:51`, `src/components/ThreeDView.tsx:866` | `image-adjustments#preview-versus-apply` | 33 | +| Palette Auto vs fixed; NumberofColors2–256 default16; fixedpalette disablescount; Weight2–256 default128; Apply; Reset | `src/components/ControlsPanel.tsx:99`, `:303`, `:336`, `src/App.tsx:739`, `:778` | `reducing-colors#the-two-stage-pipeline` | 34 | +| None postprocessonly (Weightdisabled); Posterize; Median-cut; K-means/randominit; Wu; Octree; intermediatepalette semantics | `src/hooks/useQuantize.ts:174`, `src/lib/algorithms.ts:95`, `:182`, `:326`, `:479`, `:645` | `reducing-colors#choose-an-algorithm` | 34 names methods and stages | +| Final upperlimit not guaranteedexactcount; fixedpalette Labmapping; partialalpha becomes255; alpha0stays0 | `src/hooks/useQuantize.ts:162`, `:208`, `src/lib/algorithms.ts:1232`, `:1362` | `reducing-colors#the-two-stage-pipeline`, `#fixed-and-supplier-palettes` | 34 | +| Supplier palettes unofficial, immutable; hexes not printcalibration; clone tocustom | `src/components/ControlsPanel.tsx:189`, `src/data/supplierFilaments.ts`, `src/hooks/usePaletteManager.ts:138` | `reducing-colors#fixed-and-supplier-palettes` | 34 fixedpalette branch | +| Palette create/name; edit; add; picker/hex#RGB/#RRGGBB; optionalcolorname; enable/disable; removerow; Save/Cancel; onevalidenabledrequired | `src/components/PaletteManager.tsx:46`, `:109`, `:279`, `:392`, `:437` | `reducing-colors#custom-palettes` | 34 enabled-color consequence | +| Clone built-in/custom; import .kpal summary; export; delete; localpersistence; enabled/totalcount; disabled/name preservation | `src/hooks/usePaletteManager.ts:25`, `:138`, `:183`, `:203`, `:221` | `reducing-colors#custom-palettes` | 34 fixedpalette consequence | +| Image colors count excludingtransparent; hex/alpha/pixelcounttooltip; bounded list; alpha-identicalmatch; picker;6digitretainscurrentalpha;8digitalpha | `src/hooks/useSwatches.ts:27`, `:123`, `src/components/SwatchesPanel.tsx:63`, `:115`, `:190`, `:267` | `reducing-colors#image-colors` | 35 | +| Swatch Apply global exactRGB/alpha; transparentbucket replacement; alpha0 deletion; Delete requantizes remainingpalette with activealgorithm; Close/Escape | `src/hooks/useAppHandlers.ts:296`, `:316`, `src/components/SwatchesPanel.tsx:132` | `reducing-colors#image-colors` | 35 | +| Dedither exact8neighborRGBA; Weight1–9 default4; Passes1–10 default1; resets; Apply; sequentialpasses; randomties; alpha/silhouettechange | `src/components/DeditherPanel.tsx:30`, `:75`, `:103`, `:132`, `:156`, `:248` | `dedithering-cleanup` allsections | 36 | +| Dedither distinct from quantize, photonoisereduction, nozzle simulation, and 3D heightdither | Sourcealgorithmabove and `src/lib/printableFeatures.ts` | `dedithering-cleanup#effect-on-the-print`, `#dedither-versus-height-dithering` | 36 and crosslink3D | + +## Important corrections made + +- Download, quantization, resize, and 3D do not automatically consume the live adjustment preview. Bake first. +- Six-digit swatch hex input preserves the current alpha; explicit eight-digit alpha or the picker controls opacity. +- Swatch Delete is requantization, not erasure. It can change other colors because the selected algorithm still runs. +- Weight is an intermediate palette budget, not generic grouping strength. +- Number of Colors is an upper limit and cannot restore earlier discarded colors. +- Resize resamples and may introduce new colors/alpha; changing XY scale alone does not reduce workload. +- Remove image is not a reliably undoable new clear-state history entry. +- Dedither can precede or follow quantization depending on exact source patterns. It is not restricted to post-quantization. +- Dedither Weight9 has only8neighbors, alpha participates, and tied alternatives are random. + +## Owned deliverables + +- Rewritten `src/docs/loading-images.md` +- New `src/docs/image-adjustments.md` (order35) +- Rewritten `src/docs/reducing-colors.md` +- Rewritten `src/docs/dedithering-cleanup.md` +- Seven accessible SVG assets `30_crop_resize_scale.svg` through `36_dedither_neighbors.svg`, each 960×540, explanatory title/description and 18pxminimum labels. + +## Local verification + +- Rendered all seven SVGs in headless Chromium with the installed Playwright dependency. +- Inspected contact sheet and fixed a label/image overlap and one panel-overflow label. +- Checked text bounding boxes against each viewBox and containing panel: no remaining overflow in the seven SVGs. +- Local inspection outputs: `tmp/docs-2d-illustrations.png`, `tmp/30_crop_resize_scale.png` through `tmp/36_dedither_neighbors.png`; helper `tmp/qa-docs-2d.cjs`. These are temporary review artifacts, not published docs assets. +- Root owns cross-page navigation, docs rendering/integration tests, build, and final full-page visual QA. diff --git a/docs/audits/3d-feature-coverage.md b/docs/audits/3d-feature-coverage.md new file mode 100644 index 00000000..11e4977f --- /dev/null +++ b/docs/audits/3d-feature-coverage.md @@ -0,0 +1,115 @@ +# 3D Feature Documentation Audit + +Audited on develop, 2026-09-12. Source code, not previous prose, is authoritative. This is an authoring inventory, not a user-facing guide or a claim that physical print predictions are verified. Calibration-dialog controls are inventoried separately. Root-owned export/global documentation and tests complete the cross-page checks. + +## Source Review And Corrections + +- `App.tsx:274,969-977` wires toolbar Undo/Redo to `useImageHistory`. `src/hooks/useImageHistory.ts:3,53,74` confirms image snapshots only. Old docs incorrectly described 3D-settings undo; the new guide explicitly excludes it. +- `src/hooks/useColorSlicing.ts:54-81,144-217` proves layer-height edits reset manual thicknesses, first-layer edits reset the first run, and order changes resnap first/later runs. Old prose omitted these consequential effects. +- `src/components/ThreeDControls.tsx:346` excludes saved profile appearance evidence while the working set is dirty. `FilamentRow.tsx:108-128,241-248` clears HD calibration on manual HD/TD conversion/wand; `src/lib/filamentUpdates.ts:9-31` preserves but deactivates calibration on color edits and can reactivate on revert. New docs distinguish these behaviors. +- `src/lib/autoPaint.ts:3175-3205` uses effective-filament dark-to-light luminance sorting when Enhanced matching is off, not row order. Newly documented. +- Independent review verified the second half of standard matching in `src/components/ThreeDView.tsx:1451-1503`: normalized source-image luminance maps between foundation and stack top, then snaps to the print grid. Equal-brightness hues can share a height. The Standard Matching section now distinguishes this from enhanced color-aware assignment, not just enhanced order search. +- Independent review verified `src/lib/autoPaint.ts:2673` returns an already-discrete palette height and `ThreeDView.tsx:1168-1201` reuses those cached target mappings. Height dithering only redistributes fractional-height error where it is present; many mapped regions may remain unchanged. The guide and diagram16 now explicitly present a conditional mechanism rather than a guaranteed visible before/after. +- `ThreeDControls.tsx:366-401` computes Flat Paint slab depth separately from normal appearance-stack height, including a carrier in the default layout. New docs do not call Max Height a carrier-inclusive slab cap. +- `ThreeDControls.tsx:417-447` uses the built snapshot for print instructions; `src/hooks/useAppHandlers.ts:128-182,187-280` exports the built mesh. Old docs underexplained how unapplied sidebar settings differ from built geometry. +- `AutoPaintTab.tsx:1673-1768` contains the entirely undocumented Suggest next filament workflow, including hypothetical HD and three independent metrics. New guide includes it. + +## Common Build, Dimensions, Manual Controls + +| Feature or control | Verified source and conditions | Documentation and visual coverage | +| ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| Build 3D Model | `ThreeDControls.tsx:452-574`: explicit apply; Auto-paint blocked while computing, when error exists, or when simulation/result/slice-grid mapping absent. Old preview can remain. | `3d-mode` introduction and Layer Preview; export workflow; root diagram 40. | +| Computing percentage / Waiting / Failed | `ThreeDControls.tsx:557-586`; `AutoPaintTab.tsx:939-978`. Printable-detail analysis precedes optimization; current computation shown; no manual fallback. | `auto-paint#enhanced-color-matching`; core last section. | +| Performance warning, Build Anyway, cancel | `useBuildWarning.ts:4-6,124-174`: >64 generated runs, >2.5M image pixels, >32 Flat Paint layers, or Flat Paint with dithering. Confirmation applies pending state; cancel does not build. | Root `generating-exporting-output`; `flat-paint#cost-and-detail-tradeoffs`. | +| Pixel Size XY | `PrintSettingsCard.tsx:112-115,151-185`: valid 0.01-10 mm/pixel; text draft permits comma decimal; valid values commit while typing, bounded on blur. | `3d-mode#pixel-size-is-not-nozzle-size`; diagram 10. | +| Model dimensions badge | `ThreeDControls.tsx:366-416`: nontransparent bounds times pixel size; no Auto-paint estimate until current slice data ready; flat depth distinct. | Core XY section; Flat Paint carrier caveat; diagram 10. | +| Layer Height | `PrintSettingsCard.tsx:116-119,194-220`: 0.01-10 mm; `useColorSlicing.ts:54-64` resets heights; later reconciliation makes first minimum valid. | Core layer-boundary section; diagram 11; root 41. | +| First Layer Height | `PrintSettingsCard.tsx:120-128,225-253`: 0-10 input range, physical minimum reconciled against regular height. `useColorSlicing.ts:67-81` resets first color. | Core layer-boundary section and manual example; diagram 11. | +| Smooth Meshing | `PrintSettingsCard.tsx:256-272`; `ThreeDControls.tsx:209-221`: flat active forces effective false; enabling smooth disables flat. `ThreeDView.tsx:1737,1919` selects actual smooth versus greedy geometry. | `3d-mode#smooth-meshing`; diagram 12; Flat Paint comparison. | +| Print settings Reset / disabled state / dirty indicator | `PrintSettingsCard.tsx:133-147`; `ThreeDControls.tsx:296-306`; `printSettingsStorage.ts:7-25`: defaults .1/.12/.2/off, no-op reset disabled when all default; manual thicknesses reset preserving order. | Core settings table and manual reset distinction. | +| Manual / Auto-paint tabs | `ThreeDControls.tsx:615-627`: separate modes; retained tab content; Flat Paint effective only Auto-paint. | Core intro and links to dedicated guides. | +| Manual row drag | `ThreeDColorRow.tsx:45-55`; `useColorSlicing.ts:185-217`: physical sequence, first-row minimum/resnap. | `3d-mode#reorder-colors`; diagram 11. | +| Manual thickness slider | `ThreeDColorRow.tsx:28-35,69-80`: local preview while dragging, commit on release; step regular layer, max 10; first min max(regular,first). | Core adjust/reset section and cumulative example; diagram 11. | +| Thickness readout | `ThreeDColorRow.tsx:85-87`: row thickness, two decimals. `ThreeDView.tsx:1804-1811,1886-1900`: cumulative stack and earlier-layer support. | Core manual explanation; diagram 11 labeled run thickness versus top height. | +| Manual reset | `useColorSlicing.ts:144-162,227-246`: dark-to-light luminance order plus minima; disabled when already matches. | Core manual reset section. | +| Manual >64 colors | `ThreeDControls.tsx:751-763`; `PrintInstructions.tsx:166-172`: rows/instructions replaced with reduce-color warning, Copy disabled. | Core Manual and root export guide. | +| Transparent regions | `useColorSlicing.ts:20-22`; `ThreeDView.tsx:1839-1845`: alpha zero skipped, disconnected opaque regions not automatically connected. | Core manual final paragraph and source-image/XY sections. | + +## Filaments And Profiles + +| Control | Source / availability / side effect | Documentation | +| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- | +| Add Filament | `AutoPaintTab.tsx:848-860`; `useFilaments.ts:14-25`: adds gray with estimated HD. No implicit printer-slot assignment. | `auto-paint#filament-inputs`. | +| Color swatch, picker, Hex | `FilamentRow.tsx:144-163,87-95`; `filamentUpdates.ts:9-31`: debounced optical color edit; calibration inactive if mismatch, measured scalar restores on matching revert. | Inputs and calibration-edit paragraph; established frontlit diagram 06 linked through calibration. | +| Name / blank / Enter | `FilamentRow.tsx:97-105,168-176`: trim on blur, Enter blurs, blank uses autogenerated name. | Inputs table. | +| HD numeric / bounds | `FilamentRow.tsx:19-20,108-116,178-194`: .01-2, invalid returns previous, clears calibration when committed. | Inputs table and side-effect warning. | +| Convert TD popover / value / Convert / Enter | `FilamentRow.tsx:119-129,199-236`: positive conventional value, x.1 rounded .01 clamped .01-2, clears calibration. | Inputs table. | +| Wand | `FilamentRow.tsx:239-252`: estimate from current color, clears calibration. | Inputs table and warning. | +| Calibration badge and RGB tooltip | `FilamentRow.tsx:42-60,254-271`: only active measured color counts as calibrated; otherwise Estimate. | Inputs table and confidence distinctions. | +| Remove filament | `FilamentRow.tsx:274-283`; `useFilaments.ts:36-38`. | Inputs table. | +| Calibrate | `AutoPaintTab.tsx:862-873`: appears with at least one filament; opens dialog. | Inputs table; calibration-workflows owns detailed tabs. | +| Profile dropdown, Templates | `AutoPaintTab.tsx:588-655`; supplied template IDs read-only. | `auto-paint#filament-profiles` and Templates. | +| Save current | `AutoPaintTab.tsx:660-678`: disabled without active editable dirty profile. | Profiles action table. | +| Save new popover / name / Enter / Save | `AutoPaintTab.tsx:680-712`: nonempty trimmed name required. | Profiles table. | +| Rename popover / name / Enter / Rename | `AutoPaintTab.tsx:716-758`: active non-template only, nonempty name. | Profiles table. | +| Import file | `AutoPaintTab.tsx:761-780`: .kfil/.kapp/.json/.csv/.tsv. `useProfileManager.ts` owns duplicate/storage policy. | Profiles table; root `settings-and-controls` retains import specifics. | +| Export file / disabled empty | `AutoPaintTab.tsx:782-791`: disabled without filaments; file save and dirty-evidence behavior delegated to profile manager. | Profiles table; root import/export page. | +| Delete profile | `AutoPaintTab.tsx:797-810`: selected editable profile only; no template deletion. | Profiles table. | +| Unsaved profile indicator and evidence | `AutoPaintTab.tsx:570-582`; `ThreeDControls.tsx:346-349`: dirty profile appearance omitted from optimizer. | Profiles paragraph explicitly separates individual HD calibration. | + +## Auto-paint Controls And Diagnostics + +| Control | Source and conditions | Documentation / visual | +| ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| Standard baseline | `autoPaint.ts:3175-3205`: dark-to-light effective luminance. `ThreeDView.tsx:1451-1503`: normalized source luminance maps to heights and snaps; equal-brightness hues may share heights. | `auto-paint#standard-matching`, including distinction from enhanced color-aware assignment. | +| Max Height field / Auto / actual height | `AutoPaintTab.tsx:876-939`: nonempty >0 draft, clamp .5-20 blur, Auto clears; cap lower than auto shows compressed warning. `autoPaint.ts:3225-3249` floor valid grid and reject insufficient foundation. | `auto-paint#max-height`; diagram 15. | +| Effective line width / Reset | `AutoPaintTab.tsx:982-1026`: .1-2, .01 increment, blur/Enter commit; Reset .42. | Printable detail; diagrams 10 and 13. | +| Omit at-risk colors | `AutoPaintTab.tsx:1028-1040`; `usePrintableFeatureSimulation.ts`: supplied to preanalysis and conceal snapshot for build. | Printable detail; diagram 13. | +| Open preview / Close | `PrintableFeaturePreview.tsx:87-123,206-212`: enlarged modal, affected counts. | Printable detail. | +| At risk / Printable views | `PrintableFeaturePreview.tsx:32-69,138-165,181-204`: amber takeover and pink unsupported; latter retained if no replacement; actual passed pixels versus risk only. | Printable detail; diagram 13. | +| Enhanced matching | `AutoPaintTab.tsx:1053-1064`; `ThreeDControls.tsx:187-193`: turning off resets separation and dithering. | Enhanced matching. | +| Total repeat limit | `AutoPaintTab.tsx:1068-1110`: enhanced-only; Off/2/4/6/8/12 extra appearances shared globally. | Repeat section; diagram 14. | +| Preserve color separation | `AutoPaintTab.tsx:1114-1128`; `ThreeDControls.tsx:195-200`: enhanced-only, turning on disables dither. | Separation section; diagram 14. | +| Unique-match limit | `AutoPaintTab.tsx:1133-1170`: visible only enhanced + separation, .1 step, normalized1-100, default6, invalid draft resets. | Separation section; diagram 14. | +| Require unique every color | `AutoPaintTab.tsx:1172-1193`: visible under separation; default true; false permits unmatched source-color merging. | Separation section; diagram 14. | +| Height dithering | `AutoPaintTab.tsx:1197-1211`; `ThreeDControls.tsx:202-207`: enhanced-only, disables separation. `ThreeDView.tsx:1269-1426` block-aware spatial heights, rounded line-width/pixel-size blocks and special edge handling. `autoPaint.ts:2673` and `ThreeDView.tsx:1168-1201` can supply already-discrete cached heights, leaving no fractional-height error to redistribute. | Height Dithering explicitly notes possibly unchanged regions; diagram16 illustrates the mechanism only when fractional heights exist. | +| Flat Paint toggle / face-up child | `AutoPaintTab.tsx:1217-1254`: independent of enhanced; child disabled without flat. `ThreeDControls.tsx:209-221` forces effective smoothing off. | Dedicated Flat Paint guide; diagram17. | +| Algorithm | `AutoPaintTab.tsx:1263-1310`: enhanced-only, Fast/Balanced/Thorough/Deep/Exact base order; exact hint notes subset permutations. | Optimizer section; diagram18 includes ordered search tiers. | +| Region priority | `AutoPaintTab.tsx:1312-1340`; `useAutoPaintWorker.ts:222-249`: weights target counts based on spatial source-color statistics, enhanced UI only. | Optimizer section; diagram18 same image with three weight overlays. | +| Transition detail | `AutoPaintTab.tsx:1343-1374`: enhanced UI only; .8/.9/.95. `autoPaint.ts:489-574`: opacity endpoint capped or earlier perceptual convergence. | Optimizer table; diagram18 potential additional choices;15 compression. | +| Seed | `AutoPaintTab.tsx:1378-1413`: enhanced-only; blank automatic; integer parse, invalid resets; blur/Enter commit. | Optimizer table. | +| Transition count/bar / ranges / compressed badges | `AutoPaintTab.tsx:1421-1533`: result-only and >0zones; real run color, physical start/end/delta, hover ideal compression. | Result table; diagram15. | +| Appearance model and prediction counts | `AutoPaintTab.tsx:101-258`: model level, physical training/local/matrix counts, weighted average/minimum mapping confidence, exact/interpolated/fitted/simulated breakdown. | Result table; detailed evidence model delegated calibration guide. | +| Result Confidence + factors | `AutoPaintTab.tsx:1535-1577`: calibration, coverage, compression plus overall percentage. | Explicitly not measured accuracy. | +| Optimizer metadata | `AutoPaintTab.tsx:1578-1668`: algorithm, score or Partial palette, iterations/cache, exact/best-found, no-removable-run. | Result table and optimizer section. | +| Suggest next filament / Finding / error / none | `AutoPaintTab.tsx:1673-1768`: requires a result and source targets; disabled during request; resets on image/filament edits (`503`). | Complete new Suggest Next Filament section. | +| Suggestion hex, Est ΔE, HD, Captures, Isolation | `AutoPaintTab.tsx:1689-1738`: approximate blend-aware improvement, borrowed nearest HD, percent affected pixels,0-1 gap distinction. | Separate field-by-field table; explicitly hypothetical and not a store result. | +| Add to filaments suggestion | `AutoPaintTab.tsx:1740-1760`: generated suggestion name, add row with estimated HD, clear prior suggestion. | Suggestion section warns to own/measure real filament before print. | + +## Preview And Export Boundary + +| Feature | Source / constraint | Coverage | +| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------ | +| Orbit, pan, zoom | `useThreeScene.ts:62-64`: standard OrbitControls, damping. | Core preview intro. | +| Render mode menu and current state | `PreviewActions.tsx:184-232`: four choices, closes on selection. `ThreeDView.tsx:482-510` modifies presentation only. | Core preview table. | +| Simulated / physical | `PreviewActions.tsx:235-261`: only built Auto-paint; remembered; mesh metadata separates from export. | Core preview table; 19 preview-only diagram. | +| Orthographic / perspective | `PreviewActions.tsx:264-278`; `useThreeScene.ts:117-149`: preserves camera transform and target. | Core preview table. | +| Undo / Redo disabled history | `PreviewActions.tsx:280-299`; `App.tsx:274,969-977`: shared image history; disabled with absent history or active crop. | Core table corrects old inaccurate 3D-settings claim. | +| Layer lower/upper handles | `ThreeDView.tsx:571-614,689-695,2300-2449`: hide outside range, snap boundaries, full export unaffected. | Core layer section; diagram19. | +| Segment hovers / range labels / Flat Paint plain track | `ThreeDView.tsx:616-671,2342-2354`; no one-material-per-layer sequence for flat. | Core and Flat Paint guide. | +| Download STL/3MF and disabled build/export state | `PreviewActions.tsx:374-418`; flat hidesSTL. `useAppHandlers.ts:128-280` generated files from built mesh, save progress. | Root export guide; Flat Paint export steps. | +| Print instructions and Copy | `PrintInstructions.tsx:31-215`; `useSwapPlan.ts:37-192`; uses built settings (`ThreeDControls.tsx:417-447`). | Core snapshots warning, root export detailed layer/Z distinctions. | + +## Files Created Or Reworked + +- `src/docs/3d-mode.md`: retains all legacy section heading anchors with summaries and destination links; core/manual/preview now detailed. +- `src/docs/auto-paint.md`: full non-calibration field/control guide. +- `src/docs/flat-paint.md`: both physical layouts, object assignment, orientation and cost. +- `src/assets/diagrams/10_physical_size.svg` through `19_preview_only.svg`: ten local SVG schematics, including weighting/search/transition diagram18. Alt text, title/desc, explicit schematic captions, consistent colors and >=18px labels. + +## Validation Recorded + +- `npm run test:docs`: passed all3 tests after initial nine diagrams and calibration links corrected. Repeat after diagram18 and other agents' final edits. +- Root visually checked the final diagrams, including the explicit columns/arrows in diagram11, the conditional mechanism in diagram16, and weighting/transition choices in diagram18. Integrated verification is recorded in `feature-documentation.md`. +- No runtime implementation edits, commits, pushes, or user-file changes in this scope. +- Whole-goal completion requires root verification of 2D/calibration/global controls and public/static rendered documentation, not just this source inventory. diff --git a/docs/audits/calibration-feature-coverage.md b/docs/audits/calibration-feature-coverage.md new file mode 100644 index 00000000..6d8130de --- /dev/null +++ b/docs/audits/calibration-feature-coverage.md @@ -0,0 +1,119 @@ +# Calibration And Profile Documentation Coverage + +Audited against the working tree on 2026-09-12. Scope: filament rows and named profiles, the three Calibrate tabs, appearance-evidence compatibility, and confidence interpretation. Other agents audit the surrounding 3D and 2D features. This inventory is evidence for documentation completeness, not proof of physical printing accuracy. + +Primary practical guide: `src/docs/calibration-workflows.md` (slug `calibration-workflows`, order 65). Deep model reference: `src/docs/calibration-theory.md`. Diagram references below are filenames under `src/assets/diagrams/`. + +## Filament Rows And Profile Ownership + +| Feature / action | Authoritative implementation | Practical documentation | Illustration / reason for text | +| ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | +| Swatch picker / Hex | `FilamentRow.tsx:141`; `filamentUpdates.ts:10`; `calibration.ts:366` | Prepare And Protect Your Filament Profile: row table | 06 shows the physical nominal-color/base relationship. Color edits deactivate rather than silently reuse a mismatched wedge record. | +| Editable name | `FilamentRow.tsx:97` | Row table; named-profile dirty-state guidance | Text: identity label only; no optical diagram needed. | +| Manual HD and limits 0.01–2 mm | `FilamentRow.tsx:19`; `FilamentRow.tsx:108` | Row table | 06: greater thickness reduces show-through; states manual entry clears calibration. | +| Conventional TD conversion | `FilamentRow.tsx:119` | Row table; link to file-format guide | 06 distinguishes input scales; multiplying by 0.1 is a Kromacut conversion, not an independent measurement. | +| Wand estimate | `FilamentRow.tsx:240` | Row table | 06 plus explicit estimate/replacement warning. | +| Estimate / measured badge, RGB HD tooltip | `FilamentRow.tsx:42`; `FilamentRow.tsx:255` | Row table and Read Confidence Without Overclaiming Accuracy | 20 separates opacity evidence from recipe evidence. | +| Add / remove filament | `AutoPaintTab.tsx:836`; `AutoPaintTab.tsx:853`; `FilamentRow.tsx:272` | Row table | 23 explains why changing a material set can change empirical compatibility. | +| Select saved profile | `useProfileManager.ts:155` | Profile-toolbar list | Text: replaces the working list, so save edits first. | +| Save selected / overwrite | `useProfileManager.ts:121`; `profileManager.ts:314` | Profile-toolbar list and dirty-state paragraph | 23 for compatibility. Records generally retained, not falsely documented as erased by every overwrite. | +| Save as new, name field | `useProfileManager.ts:95`; `profileManager.ts:279` | Profile-toolbar list | Copies filament rows including wedge calibration, not old appearance history. | +| Rename, name field | `useProfileManager.ts:134` | Profile-toolbar list | Text: label change only. | +| Import/export, dirty export, desktop cancellation | `useProfileManager.ts:195`; `profileManager.ts:292`; `profileManager.ts:667` | Profile-toolbar list and dirty-state paragraph; links to Settings And Controls | Actual current output is `.kfil`, despite older AGENTS wording. Dirty export creates a separate evidence-free appearance profile. | +| Delete profile | `useProfileManager.ts:171` | Profile-toolbar list | Text: backup warning; saved profile and evidence removed. | +| Templates / read-only actions | `AutoPaintTab.tsx:625`; `useProfileManager.ts:157` | Profile introduction, read-only copy guidance | Text: estimated HD, not measured supplier data. | +| Unsaved changes and storage failures | `useProfileManager.ts:84`; `useProfileManager.ts:44`; `StackMatrixCalibrationPanel.tsx:1864`; `PaletteProofPanel.tsx:1147` | Save-before-tracking instructions and matrix storage warning | 23 shows evidence tied to a compatible configuration. | + +## Hiding Distance Wizard + +| Feature / action | Authoritative implementation | Practical documentation | Illustration | +| ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | ---------------------------------------------------------------------------------------- | +| Tab selection / How calibration works | `FilamentCalibrationDialog.tsx:753`; `FilamentCalibrationDialog.tsx:1454` | Guide introduction and theory links | 20 tool comparison. | +| Individual selection, Select all / Deselect all | `FilamentCalibrationDialog.tsx:469`; `FilamentCalibrationDialog.tsx:777` | Hiding Distance step 1 | 20 wedge path; simple selection explained in text. | +| Quick | `FilamentCalibrationDialog.tsx:251`; `FilamentCalibrationDialog.tsx:337`; `FilamentCalibrationDialog.tsx:873` | Hiding Distance step 1 | 06/08 plus explicit one-base/scalar interpretation. | +| Accurate, bases, recommendation reset, max 3 bases | `FilamentCalibrationDialog.tsx:129`; `FilamentCalibrationDialog.tsx:251`; `FilamentCalibrationDialog.tsx:337`; `FilamentCalibrationDialog.tsx:884` | Hiding Distance step 1 | 08 substrate contrast; no claim that 2–3 reads directly measure three spectral channels. | +| Layer height 0.04–0.40 | `FilamentCalibrationDialog.tsx:359`; `FilamentCalibrationDialog.tsx:975` | Wedge print-control table | 07 side view, each rise is one layer. | +| Max layers 4–40 | `FilamentCalibrationDialog.tsx:127`; `FilamentCalibrationDialog.tsx:369`; `FilamentCalibrationDialog.tsx:994` | Wedge print-control table | 07 last patch/Max label. | +| STL / 3MF format | `FilamentCalibrationDialog.tsx:484`; `FilamentCalibrationDialog.tsx:1015` | Wedge print-control table | 07 cross-section over common base; full instructions in prose. | +| Download, copied plan, layer/Z swap instructions | `FilamentCalibrationDialog.tsx:484`; `FilamentCalibrationDialog.tsx:1042`; `generateCalibrationPrint.ts:98` | Wedge step 2 | 07, numbered patches explicitly differ from absolute printer-layer numbers. | +| Next / Back / Close | `FilamentCalibrationDialog.tsx:414`; `FilamentCalibrationDialog.tsx:453`; `FilamentCalibrationDialog.tsx:1094` | Wedge steps and close/reset paragraph | Text: navigation and unsaved-state consequences. | +| Match read | `FilamentCalibrationDialog.tsx:550`; `FilamentCalibrationDialog.tsx:1208` | Wedge step 3 | 07 rail comparison and 08 JND crossing. | +| Optional Merge read and warning | `FilamentCalibrationDialog.tsx:696`; `FilamentCalibrationDialog.tsx:1225`; `calibration.ts:1040` | Wedge step 3 | 07 explicitly distinguishes adjacent-patch comparison from rail comparison. | +| Predicted swatches, reference, HD/channel diagnostic display | `FilamentCalibrationDialog.tsx:1250` | Wedge step 3 | 06/08; outputs not new inputs. | +| Confidence, boundary reads, fitting diagnostics | `calibration.ts:1026`; `calibration.ts:1040`; `FilamentCalibrationDialog.tsx:1290` | Wedge step 3 and confidence section | 08 qualitative curve, no numerical data claim. | +| Session JND pending/save gate | `FilamentCalibrationDialog.tsx:598`; `FilamentCalibrationDialog.tsx:644` | Close/reset and session-fit paragraph | Text; detailed math preserved in theory. | +| Partial entry, Not entered, Won't save, ready/skipped count | `FilamentCalibrationDialog.tsx:542`; `FilamentCalibrationDialog.tsx:664`; `FilamentCalibrationDialog.tsx:683` | Wedge step 3 | Text makes per-filament all-chosen-base requirement explicit. | +| Save replacement semantics | `FilamentCalibrationDialog.tsx:688`; `FilamentCalibrationDialog.tsx:725` | Wedge save paragraph and profile backup guidance | 20 downstream effects. | + +## Palette Proof + +| Feature / action | Authoritative implementation | Practical documentation | Illustration | +| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------- | +| Prerequisite: computed stack, not artwork mesh | `AutoPaintTab.tsx:1784`; `autoPaint.ts:1101`; `PaletteProofPanel.tsx:215` | First proof paragraph | 09 prefix concept. | +| Targets, default 8, max 10 | `paletteProof.ts:16`; `PaletteProofPanel.tsx:925` | Choose Targets And Candidates table | 09 target rows. | +| Candidates 2–5 / available prefixes | `paletteProof.ts:18`; `paletteProof.ts:647`; `PaletteProofPanel.tsx:960` | Choose Targets And Candidates table | 09 candidate columns/physical stopping heights. | +| Choose from image, image clicks and keyboard color toggles | `PaletteProofPanel.tsx:615`; `PaletteProofPanel.tsx:1037` | Target table and selected-region paragraph | 21 explains resulting evidence choices. | +| Original image / Fitted-achievable | `PaletteProofPanel.tsx:553` | Target table | Explicit processed-image versus current prediction distinction; no new fit or physical guarantee. | +| Total proof targets / clear / use smart / use chosen+smart / Back | `PaletteProofPanel.tsx:531`; `PaletteProofPanel.tsx:588`; `PaletteProofPanel.tsx:624` | Target table | Text covers filling unselected slots and dropping surplus priorities. | +| Proof map, IDs, F foundation | `PaletteProofPanel.tsx:1050`; `paletteProof.ts:688` | Print And Identify The Coupon | 09 accessible two-row schematic. | +| Download, saved identity, locking, orientation, exact re-download | `PaletteProofPanel.tsx:308`; `PaletteProofPanel.tsx:821`; `paletteProof.ts:675`; `paletteProofExport.ts:128` | Print And Identify; retain original 3MF | 09 foundation/candidate physical relationship. | +| Results, ties, quality selectors, None | `PaletteProofPanel.tsx:100`; `PaletteProofPanel.tsx:1189`; `PaletteProofPanel.tsx:1366` | Record What You Actually See table | 21 different judgments and next-round branches. | +| Progress, Complete results, Edit results | `PaletteProofPanel.tsx:1170`; `PaletteProofPanel.tsx:1372` | Results paragraph | 21 follow-up after completion. | +| Saved records / target-set grouping | `PaletteProofPanel.tsx:660`; `PaletteProofPanel.tsx:887` | Results paragraph | Text; grouping is not separate physical behavior. | +| Continue targets / same image and process / None exploration | `PaletteProofPanel.tsx:772`; `PaletteProofPanel.tsx:1390`; `paletteProof.ts:402` | Follow-up action list | 21. | +| New targets / history priority | `PaletteProofPanel.tsx:1410`; `paletteProof.ts:245` | Follow-up action list | 21. | +| Reduced candidates / exhausted targets | `PaletteProofPanel.tsx:992`; `paletteProof.ts:667` | Follow-up action list | 21 footer; no unrelated filler promises. | +| Delete proof and confirmation | `PaletteProofPanel.tsx:845`; `PaletteProofPanel.tsx:859` | Follow-up action list | Text: explicit removal of evidence. | +| Global fit gates vs local evidence | `appearanceModel.ts:74`; `appearanceModel.ts:1180`; `calibration-theory.md` | End of proof workflow, confidence section, deep theory | 20 and 21 distinguish evidence strength. | + +## Stack Matrix + +| Feature / action | Authoritative implementation | Practical documentation | Illustration | +| ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | +| Saved dropdown / New matrix / Back to saved | `StackMatrixCalibrationPanel.tsx:1307`; `StackMatrixCalibrationPanel.tsx:1850` | Plan intro and closing action paragraph | Text: record ownership/navigation. | +| Filaments, 2–8, profile order | `StackMatrixCalibrationPanel.tsx:451`; `StackMatrixCalibrationPanel.tsx:458`; `StackMatrixCalibrationPanel.tsx:1877` | Plan controls | 20 grid path. | +| Recipe layers 3–6 | `StackMatrixCalibrationPanel.tsx:1913`; `stackMatrixCalibration.ts:277` | Plan controls / N^L explanation | 23 shows fixed layer counts and physical thickness. | +| Maximum cells exact choices / board footprint | `StackMatrixCalibrationPanel.tsx:116`; `StackMatrixCalibrationPanel.tsx:439`; `stackMatrixCalibration.ts:32` | Plan controls / 170 mm example | 22 grid and marker border. | +| Opaque backing, lightest selected default | `StackMatrixCalibrationPanel.tsx:430`; `StackMatrixCalibrationPanel.tsx:1951` | Plan controls | 23 backing/recipe separation. | +| New-plan regular + first LH live source; saved plan immutable | `FilamentCalibrationDialog.tsx:1396`; `StackMatrixCalibrationPanel.tsx:484`; `StackMatrixCalibrationPanel.tsx:1987` | Plan intro and download paragraph | 23 warns against reinterpretation of old measurements. | +| Exhaustive vs HD-selected gamut / swap estimate | `stackMatrixCalibration.ts:253`; `stackMatrixCalibration.ts:277`; `StackMatrixCalibrationPanel.tsx:2006` | N^L and summary guidance | 20/23 plus prose limitations. | +| Create/download progress / Save As / cancellation / storage errors | `StackMatrixCalibrationPanel.tsx:470`; `StackMatrixCalibrationPanel.tsx:517`; `StackMatrixCalibrationPanel.tsx:2021` | Plan download and persistence paragraphs | Text: no plan inserted merely by cancelling export. | +| Saved Download 3MF / Calibrated indicator | `StackMatrixCalibrationPanel.tsx:1374` | Plan immutable-record guidance | Text clarifies measured-record status, not universal validity. | +| Choose photo / drop zone / image decode | `StackMatrixCalibrationPanel.tsx:653`; `StackMatrixCalibrationPanel.tsx:1396`; `StackMatrixCalibrationPanel.tsx:1485` | Load And Align A Photo | 22. | +| Printed corner orientation key | `StackMatrixCalibrationPanel.tsx:118`; `StackMatrixCalibrationPanel.tsx:1503` | Alignment table | 22 uses 1 TL, 2 TR, 3 BR, 4 BL. | +| Rotate both directions / rerun detection | `StackMatrixCalibrationPanel.tsx:714`; `StackMatrixCalibrationPanel.tsx:1418` | Alignment table | 22 orientation diagram. | +| Zoom ±, 100% reset, scrolling, fit-percent meaning | `StackMatrixCalibrationPanel.tsx:1292`; `StackMatrixCalibrationPanel.tsx:1442` | Alignment table | 22 enlarged-corner illustration. | +| Four handle placement, loupe, drag | `StackMatrixCalibrationPanel.tsx:1141`; `StackMatrixCalibrationPanel.tsx:1551` | Alignment table / placement paragraph | 22 directly distinguishes cell center from physical corner. | +| Show template grid | `StackMatrixCalibrationPanel.tsx:1699` | Alignment table | 22 complete projected grid. | +| Detect again / Reset | `StackMatrixCalibrationPanel.tsx:1206`; `StackMatrixCalibrationPanel.tsx:1210` | Alignment table | Text says resetting alignment is not deleting calibration. | +| Auto detection status / explicit manual or low-confidence review | `StackMatrixCalibrationPanel.tsx:422`; `StackMatrixCalibrationPanel.tsx:1660`; `StackMatrixCalibrationPanel.tsx:1715` | Alignment table | 22 review footer. | +| Perspective-corrected preview | `StackMatrixCalibrationPanel.tsx:1733` | Check-square-cells paragraph | 22 supports exact grid geometry. | +| Reference marker correction off/on | `StackMatrixCalibrationPanel.tsx:1751`; `stackMatrixCalibration.ts:466`; `stackMatrixCalibration.ts:502` | Decide How To Sample | Explicit gains from predicted reference recipes, not independent lighting measurement; cannot repair shadows/glare. | +| Extracted LUT preview / hover RGB | `StackMatrixCalibrationPanel.tsx:1768`; `stackMatrixCalibration.ts:431` | Decide How To Sample | 20 sample-grid motif, practical effect described in prose. | +| Save / Replace / alignment gate | `StackMatrixCalibrationPanel.tsx:1230`; `StackMatrixCalibrationPanel.tsx:1811`; `stackMatrixCalibration.ts:390` | Decide How To Sample | 22 approval gate and 23 physical scope. | +| Photo metadata vs original image persistence | `stackMatrixCalibration.ts:403`; `appearanceProfile.ts:1333` | Source-photo-retention paragraph | Text: profile saves measured colors, not original photo bytes. | +| Delete and combined older/newer evidence | `StackMatrixCalibrationPanel.tsx:1268`; `appearanceProfile.ts:632`; `appearanceModel.ts:195` | Final matrix paragraph | 23 compatibility; no claim that newer sparse boards erase older evidence. | + +## Evidence Scope And Confidence + +| Requirement | Source evidence | Documentation / illustration | +| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| Ordered ID/color/HD/calibration fingerprint, names excluded | `appearanceProfile.ts:594`; `profileManager.ts:360` | Compatibility table; distinction between dirty UI state and optical fingerprint. | +| Proof local/global process: profile+LH+firstLH+transitionOpacity | `appearanceModel.ts:154` | Compatibility table. | +| Exact proof anchors omit transitionOpacity equality | `appearanceModel.ts:163` | Compatibility table with realizable suffix requirement. | +| Matrix eligibility: complete, not explicitly unverified, compatible profile, regular LH | `appearanceModel.ts:171`; `appearanceModel.ts:195` | Compatibility table and 23. Does not falsely add unconditional first-LH equality. | +| Matrix backing equivalence, limited continuation, local coverage | `appearanceModel.ts:1446`; `appearanceModel.ts:2995`; existing `calibration-theory.md` Prediction Uncertainty | Practical warning with deep theory link. | +| HD scalar remains a thickness model at new LH, not physical validation | `calibration.ts:858`; `appearanceModel.ts:171` | 23 and 0.08→0.04 example. | +| Reference correction is predicted-corner RGB gain, not illumination measurement | `stackMatrixCalibration.ts:492` | On/off consequences paragraph. | +| Estimate vs measured/interpolated/fitted/simulated and confidence separation | `AutoPaintTab.tsx:183`; `calibration.ts:1331`; `types/appearance.ts` | Read Confidence Without Overclaiming Accuracy; avoids treating Result Confidence as physical match percentage. | +| Wedge aging and interval uncertainty | `calibration.ts:1026`; `calibration.ts:1331` | Confidence section and existing deep theory retained. | + +## Gaps Closed And Verification + +- Prior 3D documentation described these workflows in long, dense paragraphs. The new guide makes every audited input/action independently findable in short steps, tables, and outcome warnings. +- Added four purpose-specific diagrams: `20_calibration_choices.svg`, `21_proof_rounds.svg`, `22_matrix_alignment.svg`, and `23_calibration_scope.svg`. +- Replaced the hard-to-read original diagrams `06_frontlit_hiding_distance.svg`, `07_calibration_wedge.svg`, `08_opacity_solve.svg`, and `09_palette_proof.svg` with readable, accessible same-style schematics. The old proof cross-section visually resembled independently colored columns; the replacement now uses the same physical material along each horizontal layer. +- All eight use 960×540 viewBoxes, 18 px minimum labels, 26 px titles, explicit title/description accessibility, and a disclaimer where colors or graphs are schematic. +- Rendered all eight with headless Playwright to `tmp/docs-calibration-qa/`. Checked every SVG text bounding box against the full viewport and inspected all eight raster previews. No text overflow was found. The opacity graph's threshold annotation and numeric patch spacing were refined after visual review. +- Confirmed that Palette Proof consumes `autoPaintResult.finalStack`, so documentation does not require building a separate artwork mesh first. +- No app algorithms, profile files, printer settings, or user evidence were changed. Root agent owns integrated docs registration, navigation, changelog, links, build, and rendered in-app verification. diff --git a/docs/audits/feature-documentation.md b/docs/audits/feature-documentation.md new file mode 100644 index 00000000..be14ae70 --- /dev/null +++ b/docs/audits/feature-documentation.md @@ -0,0 +1,72 @@ +# Illustrated documentation coverage audit + +Scope: nontrivial 3D and 2D features on `develop`, with 3D first. The implementation and its handlers are the authority, not older prose or screenshots. This inventory is for maintaining the guide; user-facing pages live in `src/docs`. + +## Coverage inventories + +- [3D controls](3d-feature-coverage.md): print dimensions, layer snapping, manual stacks, filaments, Auto-paint, geometry options, confidence, inspection. +- [Calibration](calibration-feature-coverage.md): HD wedge, Palette Proof, Stack Matrix, profile ownership, compatibility and evidence. +- [2D controls](2d-feature-coverage.md): loading, cropping, resizing, touch-up, adjustments, quantization, palettes, swatches and dedithering. + +## Cross-workflow and export controls + +| Feature / control | Authoritative implementation | User-facing coverage | Illustration / demonstration | +| ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------ | +| Build, progress, Build Anyway / cancel | `src/components/ThreeDControls.tsx`, `src/App.tsx`, build handler | `3d-mode`, `generating-exporting-output` | `40_build_export_snapshot.svg` | +| Built snapshot vs current settings; instructions attached to build | `ThreeDControls.tsx` builtState instruction fields; `useAppHandlers.ts` last mesh export | `generating-exporting-output` | `40_build_export_snapshot.svg` | +| PNG download vs live adjustments | `useAppHandlers.ts` onDownloadImage; `CanvasPreview.tsx` exportImageBlob | `loading-images`, `image-adjustments`, `faq` | 2D pipeline illustration | +| STL / 3MF and physical materials | `useAppHandlers.ts`, `exportStl.ts`, `export3mf.ts` | `generating-exporting-output`, `flat-paint` | Built stack and flat orientation diagrams | +| Start color, swaps, Copy, no-swaps / too-many-colors state | `PrintInstructions.tsx`, `useSwapPlan.ts` | `generating-exporting-output`, `troubleshooting` | `41_swap_layers.svg` | +| New-layer number vs boundary Z vs top Z | `useSwapPlan.ts` Auto-paint and Manual formulas | `generating-exporting-output#print-instructions` | Exact 0.10 / 0.04 mm example in `41_swap_layers.svg` | +| Layer Preview, shaded/color/physical choices not export edits | Preview controls and last mesh export | `3d-mode`, `generating-exporting-output` | Preview diagram plus `40_build_export_snapshot.svg` | +| Desktop Save As, browser download, cancellation | `src/hooks/saveBlobToFile.ts` as called by export handlers | `generating-exporting-output`, `settings-and-controls`, calibration guide | Control table and written consequence; no invented geometry effect | +| Collapse, reset, splitter, 2D/3D navigation | `CollapsibleCard.tsx`, `App.tsx`, `PrintSettingsCard.tsx` | `settings-and-controls`, relevant control guides | Relevant before/after diagrams; state vs geometry explained | +| Shared Undo/Redo image history, not printer-setting history | `App.tsx` useImageHistory and preview callbacks | `3d-mode`, `loading-images`, `settings-and-controls` | Explicit scope and rebuild warning | +| Remembered settings vs profile backups | `printSettingsStorage.ts`, profile manager, Auto-paint storage | `settings-and-controls`, `calibration-workflows` | Evidence context diagram | +| Experimental multi-plate has no print effect | `Header.tsx` experimental toggle and `App.tsx` animation-only branch | `settings-and-controls#experimental-multi-plate-mode` | Explicitly documented as no geometry effect | +| Theme / resources / update / diagnostics | `Header.tsx` | `settings-and-controls` | Readable control reference, no claimed print effect | + +## Accuracy corrections identified by the code audit + +- Algorithm Weight controls an intermediate quantization palette size, not generic effect strength. +- Source and adjusted preview are distinct; bake adjustments before workflows that consume the source. +- Deleting a swatch remaps the palette; erasure/zero alpha removes pixels. +- Resizing pixels can simplify geometry; changing mm/pixel alone cannot reduce mesh complexity. +- The 3D toolbar's Undo/Redo callbacks operate on image history. +- Preview trimming and display styles do not change the exported mesh. +- Auto-paint output being calculated is separate from building that output. +- A Max Height cap on appearance stacks is not an unconditional guarantee about the carrier-inclusive Flat Paint slab. +- Changing regular layer height affects empirical calibration eligibility. Matrix backing transfer and first-layer behavior must not be reduced to a blanket all-settings-equal rule. +- Confidence summaries, exact search labels and color-accurate rendering do not certify measured physical accuracy. +- Standard Auto-paint maps normalized image luminance to height; equal-brightness hues can share a height. Enhanced mapping considers image color. +- Height dithering redistributes fractional-height error when present. Already-discrete mappings may remain unchanged, so the illustration is explicitly conditional. +- Profile duplicate detection includes appearance evidence. Same-ID files without usable evidence do not erase an existing profile's measurements. +- HD is a frontlit model parameter; 10% modeled show-through at one HD is not an unconditional visual match threshold. +- A diagnostic trace records a new Auto-paint calculation, not a mesh build from an already-computed result. + +## Verification gates + +Completion requires source/control coverage review, actual rendered illustration inspection, docs navigation and image integrity tests, production build/static pages, responsive browser checks and lint. Passing structural checks alone is not evidence that prose or illustrations describe the implementation correctly. + +- `npm run test:docs`: metadata, sidebar membership, cross-page anchors, local image existence and accessibility metadata. +- `node scripts/verify-doc-illustrations.mjs`: readable SVG labels, bounds, native-size renders and contact sheets in `test-results/docs-illustrations` for human/agent visual inspection. +- `npm run build`: TypeScript, bundled app and statically generated docs with image-file verification. +- `npm run test:e2e:docs`: every documentation route at desktop and mobile widths, loaded image counts, full-size links, direct anchor and keyboard navigation, mobile navigation collapse, and the static pages without JavaScript. +- `npm run lint`: project lint gate. + +## Completion evidence, 2026-09-12 + +| Requirement | Current evidence | +| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Inventory nontrivial 3D features first | `3d-feature-coverage.md` and `calibration-feature-coverage.md` map the current controls, handler side effects, availability, output meaning, guide sections, and diagrams. Source cross-review corrected standard mapping, dithering, profile imports, and calibration scope. | +| Inventory and document nontrivial 2D features | `2d-feature-coverage.md` maps loading, pixel tools, all twelve adjustments, quantizers, palettes, alpha editing, dedithering, and their downstream print effects. | +| Complete missing practical guides in `src/docs` | Four new guides: Auto-paint Controls, Flat Paint, Calibration Workflows, and Image Adjustments. Core 3D and 2D pages were rewritten; overview, quick start, export, settings, troubleshooting, and FAQ agree with them. The collection has 15 pages. | +| Explain effects, dependencies, and limits | Tables and examples distinguish source edits, physical geometry, optical prediction, display-only controls, reset/persistence consequences, and slicer alignment. Existing 3D section anchors still resolve after the split. | +| Provide useful, readable illustrations | 27 local SVG schematics: 23 new and four redesigned calibration figures. Every SVG has accessible title/description metadata, at least 18px text at native size, and no text outside its viewBox. All four contact sheets and focused native/in-page renders were visually reviewed; spacing and misleading diagrams were corrected. | +| Make the guides usable in the app and on the web | 3D-first sidebar groups, full-size illustration links, and collapsible mobile navigation. Desktop and mobile page screenshots were inspected, including the final mobile layout and the Flat Paint/height-dithering figures. | +| Verify current implementation | `npm run test:docs`: 3/3 pass. `npm run lint`: pass. `npm run test:e2e:docs`: 5/5 pass, including its fresh production build and static-page verification. `node scripts/verify-doc-illustrations.mjs`: all 27 pass. `git diff --check`: pass. | +| Stay on develop and preserve unrelated work | Branch is `develop`. Existing candidate-print assets and unrelated `tmp` contents were not removed. No commit or push was performed. | + +Browser coverage visits every guide at 1440px and 390px, loads every documented image, checks layout bounds and full-size links, follows an anchor and keyboard navigation, exercises both mobile navigation panels, and visits every generated static page with JavaScript disabled. The production build retains the existing large-chunk warning; it completes successfully. + +The source/control inventories are semantic evidence; structural tests do not independently prove the physical explanations. No printer settings, optical algorithms, or model/export geometry are changed by this documentation work. Generated QA captures are under ignored `test-results`, not published assets. diff --git a/docs/i18n.md b/docs/i18n.md new file mode 100644 index 00000000..45ef8c49 --- /dev/null +++ b/docs/i18n.md @@ -0,0 +1,39 @@ +# Localization maintenance + +Kromacut uses i18next and react-i18next. English is the source language. Supported translations are French, German, Italian, Romanian, Spanish, Japanese, Simplified Chinese, Hindi, European Portuguese (`pt-PT`), Ukrainian, and Bengali. Do not substitute Brazilian Portuguese or duplicate US/UK English. + +## Content boundaries + +- `src/locales/en/*.json` contains source interface, runtime-message, public-page, documentation-metadata, and diagram text. +- English Privacy and Terms remain in `src/data/privacyNotice.json` and `termsNotice.json`; translated notices live in the corresponding locale directories. Preserve their facts, section IDs, URLs, and paragraph structure. +- Each locale has complete Markdown guides in `src/locales//docs`. Translate the full guide, not a summary. Keep filenames, `slug`, `order`, heading depth/order, links, assets, code examples, list items, and table cells. Titles and descriptions must match that locale's `docsMeta.json`. +- Original document anchors are kept across languages so existing and shared deep links stay valid. +- User-entered names, original artwork titles, vendor product names, filenames/extensions, IDs, numeric model data, and diagnostic/protocol fields are not translation text. Never rewrite stored profiles, matrices, worker messages, or geometry when switching language. +- New UI code should use `t` or `Trans` directly. `translateRuntimeMessage` is a compatibility boundary for existing catalogued worker/status/error strings, applied at display time. Do not add general DOM translation or run translation inside optimizer/meshing hot paths. + +English fallback keeps the app usable if a resource fails; it does **not** count as translated coverage. Native-language review is still valuable: structural checks cannot establish linguistic accuracy. + +## Adding or changing text + +1. Update the English message and every translated catalog. Preserve interpolation placeholders and rich-text tags; use plural forms required by `Intl.PluralRules` for each locale. +2. If workflow behavior changes, update the English guide and all full translated guides as well. +3. Run `npm run check:i18n`, `npm run test:i18n`, and the relevant app tests. Use `npm run test:e2e:i18n` for live switching, persistence, local fonts, accessibility, and model/export invariance. +4. Run `npm run test:e2e:i18n:build` to build and inspect production public routes, static translated guides/legal pages without JavaScript, localized 404 recovery, and documentation under the desktop content security policy. Inspect native save dialogs as applicable. Do not treat matching key counts as sufficient review. + +`npm run build` checks all catalogs, guides, and diagram measurements before generating every translated public route. Missing translations fail the build rather than shipping English under a translated URL. + +The browser install manifest uses the selected language's app description. Its `id`, scope, and `/app` start URL stay identical across languages, so changing language does not create a separate installed workspace. Vite serves these manifests during development and emits them for production. + +## Diagrams + +All app-authored SVG text, titles, and descriptions must have `data-i18n` keys. The source SVG preserves English artwork; translated resources replace text safely. A template marked `data-i18n-rich="label"` permits only an inert `