Skip to content
Draft
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
d35eb35
feat(data): normalize piece media and commerce policies
lowestprime Jul 11, 2026
30c87f6
docs: record sitewide UX and data audit
lowestprime Jul 11, 2026
959a2ca
feat(categories): add visual icon management
lowestprime Jul 11, 2026
8b2a39b
feat(studio): add visual content and media workflows
lowestprime Jul 11, 2026
9e143da
feat(commissions): add proportional 3D planning preview
lowestprime Jul 11, 2026
1743143
fix(commissions): harden 3D camera lifecycle
lowestprime Jul 12, 2026
fdf020d
feat(qa): add deterministic two-mode visual archive
lowestprime Jul 12, 2026
9067229
feat(studio): make inline edits atomic and reversible
lowestprime Jul 12, 2026
43bf434
feat(commissions): secure resumable request workflow
lowestprime Jul 13, 2026
2c92209
feat(media): add transactional batch organization
lowestprime Jul 13, 2026
0758c9a
feat(ui): harden accessibility and media delivery
lowestprime Jul 13, 2026
d54e38f
fix(security): protect private media and mutation origins
lowestprime Jul 14, 2026
fff7f07
fix(ui): correct price, icon, and theme behavior
lowestprime Jul 14, 2026
6a1004c
fix(app): harden drafts and standalone output
lowestprime Jul 14, 2026
3f61d4a
fix(qa): harden visual archive capture and reporting
lowestprime Jul 14, 2026
b95a347
fix(qa): prove isolated visual archive modes
lowestprime Jul 14, 2026
c30a909
feat(ops): add verified paired recovery
lowestprime Jul 14, 2026
132c1d7
fix(qa): bound and isolate visual archives
lowestprime Jul 15, 2026
2bde973
fix(qa): drain media before deep capture teardown
lowestprime Jul 15, 2026
3fbb365
fix(qa): drain media after every archive capture
lowestprime Jul 15, 2026
7e18b9d
perf(qa): wait only for active visual requests
lowestprime Jul 15, 2026
88d9cec
fix(qa): settle protected route redirects
lowestprime Jul 15, 2026
2e9e6d6
fix(commissions): contain mobile progress steps
lowestprime Jul 15, 2026
ca43f5e
fix(portfolio): contain mobile category rail
lowestprime Jul 15, 2026
9940969
fix(a11y): correct portfolio and process headings
lowestprime Jul 15, 2026
76b797a
feat(qa): harden and parallelize visual archives
lowestprime Jul 16, 2026
57c0504
fix(editor): keep inline link edits on page
lowestprime Jul 16, 2026
e06e914
fix(studio): select profile images from media library
lowestprime Jul 18, 2026
88b0acd
refactor(media): replace overlapping collection previews
lowestprime Jul 18, 2026
9b223c8
test(media): enforce non-overlapping responsive collections
lowestprime Jul 18, 2026
a8a247d
feat(audit): gate acceleration on reproducible benchmarks
lowestprime Jul 18, 2026
d7548e2
feat(sidecar): coordinate verified CUDA workloads
lowestprime Jul 18, 2026
b7f5b46
feat(audit): verify production media evidence tiers
lowestprime Jul 18, 2026
8db2c77
fix(audit): require exact NAS archive images
lowestprime Jul 18, 2026
5c20039
fix(audit): preserve compose selectors under sudo
lowestprime Jul 18, 2026
a7acbcf
fix(audit): isolate NAS archive runtime
lowestprime Jul 18, 2026
56605f5
fix(client): support internal HTTP operation IDs
lowestprime Jul 18, 2026
3221fa3
fix(audit): allow isolated HTTP lab authentication
lowestprime Jul 19, 2026
49cfc28
fix(audit): remove nested npm lifecycle recursion
lowestprime Jul 19, 2026
f21a5bf
fix(audit): align snapshot lab runtime ownership
lowestprime Jul 19, 2026
4ed9ccb
fix(audit): disable TTY for NAS runners
lowestprime Jul 19, 2026
8a6f6ba
fix(media): canonicalize legacy media URL paths
lowestprime Jul 19, 2026
3345ef9
fix(media): canonicalize rendered legacy media URLs
lowestprime Jul 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
node_modules
site/node_modules
site/.next
visual-audit/node_modules
visual-audit/dist
.output
.sst
.tanstack
Expand All @@ -17,11 +19,18 @@ site/data/media-ai-cache/
.env
.env.local
.env.*.local
.visual-audit-lab.env
#recycle
@eaDir
**/@eaDir
# Legacy local media placeholders, image cache, and releases
pics/
cache/
releases/
archive/
design/
output/
visual-audits/
visual-audit-lab/
secrets/
wsl_build_nas_deploy_commands.md
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ DATA_ROOT=/app/site/data
STUDIO_PASSWORD=change-this-before-public
SESSION_SECRET=replace-with-a-long-random-secret

# Authenticated visual-audit inventory access.
# Generate independently from STUDIO_PASSWORD and SESSION_SECRET.
VISUAL_AUDIT_TOKEN=
VISUAL_AUDIT_MAX_RECORDS=5000

# Payment infrastructure
STRIPE_SECRET_KEY=
STRIPE_PUBLISHABLE_KEY=
Expand Down
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,13 @@ releases/
cloudflared/*.json
cloudflared/config.yml
wsl_build_nas_deploy_commands.md

# Visual audit
visual-audit/node_modules/
visual-audit/dist/
visual-audit/.auth/
visual-audit/tmp/
visual-audits/
visual-audit-lab/
.visual-audit-lab.env
secrets/
16 changes: 16 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,31 +1,47 @@
FROM node:22-bookworm-slim AS builder

ARG WOODSMITH_BUILD_SHA=unknown

WORKDIR /app/site

ENV NEXT_TELEMETRY_DISABLED=1
ENV WOODSMITH_BUILD_SHA=${WOODSMITH_BUILD_SHA}

COPY site/package.json ./package.json
COPY site/package-lock.json ./package-lock.json

RUN npm ci

COPY site ./

RUN npm run build


FROM node:22-bookworm-slim AS runner

ARG WOODSMITH_BUILD_SHA=unknown

WORKDIR /app/site

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=3002
ENV HOSTNAME=0.0.0.0
ENV WOODSMITH_BUILD_SHA=${WOODSMITH_BUILD_SHA}

RUN groupadd --system --gid 1001 nextjs
RUN useradd --system --uid 1001 --gid 1001 nextjs

COPY --from=builder --chown=nextjs:nextjs /app/site/.next/standalone ./
COPY --from=builder --chown=nextjs:nextjs /app/site/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nextjs /app/site/public ./public

RUN mkdir -p /app/site/data \
&& chown nextjs:nextjs /app/site/data \
&& chmod -R a+rX /app/site/public /app/site/.next/static

USER nextjs

EXPOSE 3002

CMD ["node", "--experimental-sqlite", "server.js"]
21 changes: 20 additions & 1 deletion PLANS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# PLANS.md

## 2026-07-11 Sitewide UX, Data, Commission, And Visual Archive Overhaul

- Status: IN PROGRESS ON FEATURE BRANCH; NOT DEPLOYED
- Branch: `codex/sitewide-studio-ux-commission-overhaul-20260711`

| Slice | Status | Evidence |
|------|--------|----------|
| Additive data model and compatibility | DONE / COMMITTED | `d35eb35` adds the migration ledger, normalized piece media, typed commerce policies, edit/rename history, compatibility synchronization, transactional reference rewriting, and disposable-database tests. |
| Audit and category design | DONE / COMMITTED | `30c87f6` records the evidence audit; `959a2ca` adds managed visual category icons and tests. |
| Structured Studio and public content | DONE / COMMITTED | `8b2a39b` adds structured footer/home services, typed public pricing/inquiry/review behavior, visual media selection, compact Page/Piece/Process editing, media roles, and public build records. |
| Conceptual proportional 3D preview | DONE / COMMITTED | `9e143da` adds route-local R3F templates, view/camera controls, exact dimensions, deterministic SVG/text fallbacks, demand rendering, and estimator tests. |
| Deterministic visual archive | IMPLEMENTED / LOCAL DOCKER VALIDATED; NAS RUNTIME VALIDATION PENDING | The QA slice adds protected bounded inventory, strict live-readonly and isolated snapshot-lab modes, pinned Playwright Docker, route/link reconciliation, required high-resolution profiles, overlapping page/nested-scroll tiles with seam checks, deep UI state capture, restricted/redacted HTML/PDF, checksums, diffs, locks, and retention. Local tests, typecheck, lint, safe standalone build, all Compose validation, a disposable-data smoke archive, a `linux/amd64` image build, image-filesystem inspection, and loopback snapshot-lab application smoke pass. NAS backup/candidate/full archive/rollback evidence remains required before deployment. |
| Typed atomic inline editing | DONE | Added a typed field registry spanning scalar, list, link, relation, media, status, and structural settings fields; trusted-origin enforcement; complete-batch validation; one SQLite transaction; optimistic expected-value conflicts; admin audit records; reset, rollback, keyboard, focus, and one-step Undo behavior. Thirty-two application tests, typecheck, lint, production build, and a disposable authenticated Chromium save/undo/reset/conflict/origin smoke pass. Final-candidate archive evidence remains a release gate. |
| Secure resumable custom requests | DONE / LOCAL DOCKER VALIDATED | Added the ten-step autosaved workflow, verified-account server drafts, idempotent server-authoritative submission, hashed submission/render quotas, private staged attachments with rollback, owner-bound generated previews, opaque project-access cookies, POST lookup without URL email, and exact dimension/category continuity into R3F. Additive schema v5, seed v6, 36 application tests, production build, image safety inspection, and disposable browser/container submission evidence pass. |
| Safe standalone and image build | DONE / LOCAL DOCKER VALIDATED | `safe-build` uses disposable build roots, excludes runtime data from Next tracing, rejects database/backup files, and proves cleanup for child-failure, forbidden-output, and success paths. Docker Desktop Engine 29.6.1 and BuildKit 0.31.1 built/loaded `woodsmith:local-gate`; the image contains no runtime DB, env/key, private evidence, audit output, or production media and starts with an empty data directory. |
| Transactional media organization | DONE / LOCAL BROWSER VALIDATED | Additive schema v6 records batch/item snapshots; selected media can be moved, deterministically renamed, tagged, rated, assigned, and given normalized role/stage/visibility metadata in one compensated operation. Optimistic rollback refuses later edits, filesystem failures reverse earlier moves, legacy and normalized references stay synchronized, and optional cleanup writes unpublished source-linked derivatives only. Forty application tests plus disposable desktop/mobile browser apply/rollback and SQLite `quick_check` evidence pass. |
| Accessibility, responsive UI, and public media performance | DONE / LOCAL BROWSER VALIDATED | Added skip navigation, route-aware current navigation, high-contrast focus treatment, focus-safe auto-hide header behavior, modal focus containment/restoration, keyboard/touch bounded lightbox pan and zoom, announced carousel position, 24px target protection, and hydration-safe persistent themes. Public portfolio/shop/carousel thumbnails now use responsive Next image requests while the full-screen viewer retains the source file; raw media supports ETag/Last-Modified revalidation. Disposable browser QA passed at 1440, 390, and 320px in both themes with zero horizontal overflow, zero unnamed/unlabelled controls, no duplicate IDs/heading skips/missing alt text, and tested base/muted contrast above AA thresholds. |
| Remaining product work | PENDING | Final documentation review, full visual-archive evidence, release, NAS backup/restore, rollback, candidate deployment, and production verification remain active. |

## 2026-07-05 Local-First Media AI, Header, and Persistence Pass

- Status: LIVE DEPLOYED AND VERIFIED ON SYNOLOGY
Expand Down Expand Up @@ -76,7 +95,7 @@ Verification for this pass: `npm run typecheck`, `npm run lint`, and `npm run bu
|----|--------|------------------|
| 1. Public navigation and Process/Shop separation | DONE | Removed Process from seeded and rendered primary navigation, removed the Process/behind-the-scenes block from Shop, preserved the dedicated `/process` archive and legacy Journal redirects, and added seed v5 exact-string cleanup for persisted legacy copy. |
| 2. Buyer email verification | DONE / CONFIGURATION REQUIRED | New accounts receive expiring verification tokens, unverified customer login is rejected, resend accepts an email without an authenticated session, SMTP acceptance is checked for the primary recipient, and UI errors distinguish configuration/authentication/sender/connection failures. Live dispatch still requires valid `SMTP_*` credentials and sender values. |
| 3. Direct visual editing | DONE | Admin pencil controls intercept in capture phase and edit mapped public text/URLs in place without navigation or full-page refresh. Route changes clear stale editor state; structural edits use the explicit full-editor handoff. No raw JSON control is exposed. |
| 3. Direct visual editing | DONE | Admin pencil controls intercept in capture phase and edit mapped public fields in place without navigation or full-page refresh. The typed registry drives the server allowlist; saves are atomic, audited, conflict-aware, origin-protected, and reversible. Route changes clear stale editor state; structural edits use the explicit visual full-editor handoff. No raw JSON control is exposed. |
| 4. Compact media assignment desk | DONE | Replaced the long media editor stack with a bounded three-pane workspace: utility tools, paged responsive thumbnail browser, and one active inspector. Added whole-library search, assignment/type filters, collapsed upload/automation/crop sections, and in-place action state. |
| 5. Media assignment integrity | DONE | Reviewed assignments now remove stale old-piece gallery membership and add new-piece membership. Unreviewed assignments remain private. Refresh/cluster tools run locally without AI credentials; the AI route requires admin authentication. |
| 6. Keyboard media workflow | DONE | J/K navigate visible media, F focuses whole-library search, P focuses piece assignment, U clears it, R toggles review state, S saves, Shift+S saves and advances, A approves and advances, and roving tab stops keep one active thumbnail keyboard-focusable. |
Expand Down
33 changes: 25 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,29 @@ Woodsmith is a self-hosted Next.js application for the Beaman Woodworks company
- Portfolio category filtering managed through editable labels, matching terms, and icon styles in the Woodshop dashboard
- Shop pages with asking-price language, cart totals, coupon handling, tax estimate, pickup/delivery/shipping labels, and Stripe checkout plumbing
- Process writing under the dedicated `/process` archive, with markdown content and optional source-credit links for outside references
- Contact-first custom work requests with attachments, lead-time context, material preferences, project tracking, and an optional live procedural 3D scale preview
- Contact-first custom work requests with attachments, lead-time context, material preferences, project tracking, and an optional route-local React Three Fiber conceptual scale preview
- Optional server-side OpenAI image-model previews for custom work when `OPENAI_API_KEY` and `ENABLE_PUBLIC_AI_RENDERING=true` are configured; generated previews are stored back into `/app/pics`
- Buyer account pages for signup, login, password reset, profile editing, profile images, and account-linked projects
- Private Woodshop dashboard with focused workspace tabs for editing settings, pages, pieces, custom work types, users, media, process notes, projects, orders, reviews, and notifications through structured browser forms
- Admin-only pencil controls that edit mapped public text and links in place, with an explicit full-editor link for structural work
- A compact browser media desk with whole-library and AI-state filters, in-place paging and edits, explicit candidate review, guided local training actions, training-weighted visual ranking, upload/rename/delete safety, persistent assignment, crop/focal controls, and source credit against the writable NAS photo library
- Admin-only pencil controls backed by a typed field registry, atomic audited saves, conflict detection, URL/origin validation, reset/undo controls, and an explicit visual full-editor link for structural work
- A compact browser media desk with whole-library and AI-state filters, in-place paging and edits, explicit candidate review, guided local training actions, transactional selected-item folder/name/assignment/role/stage changes with rollback, upload/rename/delete safety, crop/focal controls, and source credit against the writable NAS photo library
- Email notification queueing, Stripe invoice creation, and EasyPost shipping-label requests when the related environment variables are configured
- Full-size image lightbox support with zoom, pan, arrow navigation, plus `Esc` and close-button exit behavior
- Responsive piece carousels with announced position and full-size lightboxes that trap focus, restore the opener, support bounded keyboard/touch pan and zoom, and close through `Esc`, backdrop click, or the visible X control
- Keyword/metadata search plus visual search across public content and, for admins, private media, visual labels, clusters, unpublished content, and project records. The optional local sidecar supplies shared image/text CLIP vectors; Gemini or OpenAI embeddings remain opt-in alternatives.
- Persistent light/day and black OLED night themes using the local ITC New Rennie Mackintosh font assets
- Persistent light/day and black OLED night themes using the local ITC New Rennie Mackintosh font assets; the cookie-backed initial theme and client store are synchronized without hydration overwriting the saved choice
- WCAG-oriented skip navigation, current-page navigation state, visible high-contrast focus, reduced-motion handling, compact 24px-or-larger targets, and focus clearance for the auto-hiding header
- Responsive optimized thumbnail requests on portfolio, shop, cart, and carousel surfaces; the raw source remains available in the full-screen viewer and direct media responses use ETag/Last-Modified revalidation
- Programmatic Beaman Woodworks favicon and brand mark
- Safe profile administration for renaming accounts, replacing legacy developer emails, and deleting non-current users from the dashboard
- Compact auto-hide navigation that keeps the public and dashboard workspaces usable on narrow and desktop viewports
- A pinned two-mode Playwright visual archive with protected source/database/link inventory, read-only production capture, isolated snapshot-lab states, overlapping high-resolution tiles, HTML/PDF reports, checksums, and baseline comparisons

## 📃 Production Notes

- Persistence uses `node:sqlite`, which emits Node's experimental warning during build and runtime.
- Studio overview reports the active `DATA_ROOT`, SQLite `quick_check`, journal mode, and seed version so rebuild-safe persistence can be verified from the browser. Seed upgrades are non-destructive for existing Studio-edited records.
- `/journal` and `/journal/[slug]` now redirect to Process. New public writing should be published as Process notes.
- The public custom work flow is contact-first and includes a credential-free procedural 3D scale preview. The older SVG renderer remains for stored visualization snapshots. Optional photorealistic preview generation is available only when explicitly configured with a server-side OpenAI key and feature flag.
- The public custom work flow is contact-first and includes a credential-free, dynamically loaded React Three Fiber conceptual proportional preview. A deterministic SVG drawing remains available for fallback, printing, and submitted snapshots. Optional photorealistic preview generation is available only when explicitly configured with a server-side OpenAI key and feature flag.
- The public site exposes admin-only edit controls when an admin is signed in. Mapped text and link fields save in place without a page reload; structural changes remain available through the linked `/studio` editor.
- Scientist Desk remains published without photos until the correct black phenolic resin top, birds-eye maple rails, and white maple legs media are verified.
- New piece records can be created without guessed photos. Media should be assigned only after review in the Woodshop dashboard.
Expand All @@ -50,6 +53,8 @@ Woodsmith is a self-hosted Next.js application for the Beaman Woodworks company
- `design/Beaman_Woodworks_V2_Google_Stitch_Beta/`: Beaman Woodworks 2.0 prototypes audited for layout, theme, and dashboard direction
- `ITC_New_Rennie_Mackintosh_Complete_Family_Pack/`: source font assets for the site typography
- `docker-compose.synology.yml`: Synology runtime model
- `visual-audit/`: pinned Playwright capture, report, comparison, validation, and NAS automation package
- `docs/visual-archive.md`: private live-readonly and snapshot-lab operating guide
- `synology-nas-deploy.md`: deployment and NAS operations guide
- `admin.md`: private Woodshop dashboard manual
- `woodsmith_DeepWiki_Merged_03222026.md`: codebase architecture reference
Expand Down Expand Up @@ -82,6 +87,11 @@ Required for a secure deployment:
- `NEXT_PUBLIC_SITE_URL`
- `DATA_ROOT` (production: `/app/site/data`)

Required only when the private visual archive is enabled:

- `VISUAL_AUDIT_TOKEN` and optional `VISUAL_AUDIT_MAX_RECORDS`
- private ignored secret files prepared by `visual-audit/scripts/prepare-live-secrets.sh`

Local-first media AI configuration:

- `AI_PROVIDER`, `AI_ANALYSIS_PROVIDER`, `AI_EMBEDDING_PROVIDER`, `AI_FALLBACK_PROVIDER`
Expand Down Expand Up @@ -135,6 +145,8 @@ Public:
- `/process/[slug]`
- `/commissions`
- `/commissions/status`
- `/contact`
- `/care-and-warranty`
- `/about`
- `/search`

Expand All @@ -143,15 +155,17 @@ Legacy redirects:
- `/journal`
- `/journal/[slug]`

Buyer account and request access:
Buyer account and request access:

- `/account/signup`
- `/account/login`
- `/account/forgot`
- `/account/reset`
- `/account/profile`
- `/account/projects`
- `/requests/[reference]`
- `/requests/[reference]`

Custom work uses a ten-step, locally autosaved request flow. Verified accounts also receive resumable server drafts. Submission is idempotent, rate-limited by a hashed owner key, recalculates the estimate and lead time on the server, stages allowlisted private image references safely, and redirects without putting buyer email in the URL. Existing projects are opened through the POST lookup at `/commissions/status`, which issues an expiring `HttpOnly` capability cookie.

Private Woodshop:

Expand All @@ -161,6 +175,8 @@ Private Woodshop:
## 🚀 Deployment

The supported deployment target is Synology NAS with Docker Compose and reverse proxy termination. The compose file mounts `/volume1/homes/Cooper/Photos/Dad_Woodworking_09262025` directly to `/app/pics:rw`; do not remount or bind the repo-local `pics/` folder under `docker_ssd`. `MEDIA_ROOT` must be an absolute container path.

`npm run build` uses disposable build-time data and media roots and fails if Next standalone output contains SQLite, WAL/SHM, or backup files. Production state must enter the container only through the writable runtime mounts.

After deploying a build from this branch, the startup migration updates legacy `lowestprime@proton.me` developer references in persisted settings and seeded profile data to `cooperbeaman@proton.me`.

Expand All @@ -169,3 +185,4 @@ Use these docs together:
- `synology-nas-deploy.md`
- `admin.md`
- `woodsmith_DeepWiki_Merged_03222026.md`
- `docs/visual-archive.md`
Loading