- Add --format pdf flag to calendar PDF generation route
- Upgrade Typst from v0.11.1 to v0.13.0 in Dockerfile
- Typst v0.11.1 doesn't support --format flag with stdin/stdout
- Fixes "could not infer output format" error in production
The calendar generation scripts were in scripts/ directory which wasn't
included in tsconfig.server.json, so they weren't being compiled during
build. This required tsx in production just to import .tsx files at runtime.
Changes:
- Moved generateCalendarComposite.tsx and generateCalendarAbacus.tsx to src/utils/calendar/
- Removed CLI interface code from these files (they're now pure utility modules)
- Updated imports in API routes to use @/utils/calendar/...
- Moved tsx back to devDependencies where it belongs
- Removed scripts/ copy from Dockerfile (no longer needed)
Now these files are compiled to JavaScript during the build process and
don't require tsx at runtime. This fixes the architecture issue properly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changes:
- Refactored scripts/generateCalendarAbacus.tsx to export generateAbacusSVG function
- Refactored scripts/generateCalendarComposite.tsx to export generateCalendarComposite function
- Updated API route to import and call functions directly instead of spawning subprocesses
- Removed tsx from production Dockerfile (not needed - Next.js transpiles at build time)
- Kept scripts and abacus-react package copies in Dockerfile (needed for imports)
This eliminates the need for tsx at runtime and improves performance by avoiding subprocess overhead.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Copy scripts directory to production container (needed for generateCalendarComposite.tsx)
- Copy abacus-react package (scripts import from @soroban/abacus-react/static)
- Install tsx globally to fix npm EACCES /nonexistent error in production
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
CRITICAL REGRESSION: Production container crash-looping with:
"Error: libc.musl-x86_64.so.1: cannot open shared object file"
Root cause identified:
Commit dafdfdd2 (3D printing support) changed runner stage from:
FROM node:18-alpine → FROM node:18-slim
to add OpenSCAD (not available in Alpine repos).
However, the deps stage was NOT updated and remained Alpine-based,
causing a binary incompatibility:
- deps stage (Alpine/musl) compiles better-sqlite3 for musl libc
- runner stage (Debian/glibc) cannot run musl-compiled binaries
Solution:
Changed deps stage from node:18-alpine to node:18-slim to match
runner, ensuring better-sqlite3 is compiled for the correct target.
Both stages must now use Debian because OpenSCAD is required for
3D printing functionality and is not available in Alpine.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The BOSL2 repository has no release tags, causing Docker build to fail
when trying to clone --branch v2.0.0. Removed the branch flag to clone
the default branch instead.
Error:
fatal: Remote branch v2.0.0 not found in upstream origin
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Reverted selective copying of package subdirectories because:
- packages/core has no "server" directory (it's "src")
- packages/templates has .typ files in root, not subdirectory
Still maintains key optimization: production-only node_modules via deps stage.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed Docker build failure caused by copying non-existent /typst subdirectory.
The .typ template files are in the root of packages/templates/, not in a subdirectory.
Changed from:
COPY /app/packages/templates/typst (does not exist)
To:
COPY /app/packages/templates/*.typ (actual files)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The styled-system directory containing generated Panda CSS was being
created during build but not copied to the production image, causing
all Panda CSS classes to be undefined at runtime.
This fix copies the generated styled-system directory from the builder
stage to the production image, ensuring styles.css is available.
Fixes missing CSS definitions for classes like fs_xl, font_bold, text_blue.400
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The Python flashcard generator requires qpdf for PDF processing.
Without it, the script exits with code 1 even though it prints
a warning saying it will skip linearization.
Added qpdf to Alpine packages to fix PDF generation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Typst was failing with "input file not found" error because the templates
directory containing flashcards-input.typ was missing from the Docker image.
Added COPY command to include packages/templates in the production image.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit fixes two critical production issues:
1. **Flashcard generator dependencies** - Added all required dependencies to Dockerfile:
- Typst for PDF generation
- Python pip and setuptools
- Python packages (pyyaml, Pillow, imagehash)
- packages/core directory with generate.py script
2. **Deployment info modal** - Fixed git commit hash display on production:
- Modified generate-build-info.js to accept env vars as fallback when .git is unavailable
- Updated Dockerfile to accept GIT_* build arguments
- Updated GitHub Actions workflow to pass git information during Docker build
The deployment info modal (Ctrl+Shift+I) will now show the correct commit hash,
branch, and build time on production, matching the behavior on dev.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Python 3.11+ prevents global pip installs by default. Since this is a
controlled Docker environment, use --break-system-packages to allow installing
the flashcard generation dependencies.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The pip3 command was not available, causing the Docker build to fail when
trying to install Python dependencies from requirements.txt. Added py3-pip
to the Alpine package installation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The Python scripts in packages/core require pyyaml, pillow, imagehash, and
other dependencies to generate flashcards. Install these from requirements.txt
during Docker build.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The flashcard generator was failing in production because the packages/core
directory (which contains the Python scripts for PDF generation) wasn't being
copied to the production Docker image. The SorobanGenerator class needs these
scripts at runtime to generate flashcards.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The flashcard generator requires both Python and Typst to work.
Added typst to the runtime dependencies in the Dockerfile.
This fixes the issue where flashcards work in dev but fail in production.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem:**
- Dockerfile was trying to copy socket-server.js from root (no longer exists)
- Also copying entire src/ directory instead of compiled dist/
**Solution:**
- Changed to copy dist/ directory which contains all compiled server code
- Removed separate socket-server.js copy (now in dist/)
- Removed src/ copy (runtime only needs compiled code)
**Files changed:**
- Line 50-51: Replaced socket-server.js and src/ copies with single dist/ copy
**Tested:**
- Docker build completes successfully ✓
- Container starts and Socket.IO initializes ✓
- Server responds on port 3000 ✓
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove standalone output references, copy .next directly
- Add compiled server files (server.js, socket-server.js, src/)
- Include drizzle migrations folder for database setup
- Create data directory for SQLite database
- Keep Python/g++/make in runtime for better-sqlite3
- Set correct working directory to /app/apps/web
- Add NODE_ENV=production environment variable
This enables proper production deployment with database migrations
running on container startup using pure Node.js.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Next.js expects static files at /.next/static and public at /public
when running from /app, not at /apps/web/.next/static.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The Next.js standalone build creates symlinks in node_modules that point
to ../../../node_modules/.pnpm. When the working directory is /app, these
resolve to /node_modules/.pnpm. Fixed by copying node_modules to /node_modules
instead of /app/node_modules.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The Next.js standalone build outputs server.js to /app/server.js, not
/app/apps/web/server.js. This was causing the container to crash on
startup with MODULE_NOT_FOUND errors, resulting in 404s for abaci.one.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
.npmrc no longer exists after reverting to default pnpm mode.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The pnpm lockfile was generated with hoisted mode, so Docker must
also use hoisted mode to match the module resolution paths.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Docker builds should use default pnpm isolated mode, not hoisted mode
which causes tsup module resolution failures.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Upgrade Dockerfile from pnpm 8.0.0 to 9.15.4 for lockfile compatibility
- Add "types": [] to abacus-react tsconfig to prevent implicit @types includes
- Fixes Docker build lockfile incompatibility
- Fixes TypeScript error looking for @types/minimatch
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add python3, py3-setuptools, make, g++ to Alpine base image
- Required for better-sqlite3 native module compilation in Docker build
- Fixes 'ModuleNotFoundError: No module named distutils' error
- Restore @soroban/core and @soroban/client dependencies in apps/web
- Correct workspace configuration to include all existing packages
- Disable problematic example-server.ts to fix TypeScript build
- Update Dockerfile to copy all required package.json files
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix pnpm-workspace.yaml to use packages/* pattern
- Remove references to non-existent @soroban/core and @soroban/client packages
- Update Dockerfile to only copy existing package.json files
- Clean up dependencies after workspace changes
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>