Songsheet / Songweb project
  • JavaScript 73.3%
  • HTML 11.6%
  • CSS 10.6%
  • Python 3.3%
  • Gherkin 0.9%
  • Other 0.2%
Find a file
2026-08-09 07:19:34 -05:00
docs Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
features fix(layout): center page visual columns 2026-05-31 15:57:10 -05:00
mermaid Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
phase-1-web Show loaded commit and tighten phrase splits 2026-05-30 14:45:33 -05:00
phase-2-service Sprint 12: S12-10 calendar export, S12-11 service time, phase-1 Sunday save + manual page break 2026-05-24 07:38:20 -05:00
References Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
scripts Add 13 missing songs from songbook PDF and PPTX references; improve paste matching 2026-04-25 23:41:54 -05:00
shared S10-10: song retirement — retire/restore toggle in library manager; inactive songs hidden from all pickers + quick-add; badge-inactive + muted list row; stats unused excludes retired 2026-05-08 19:55:54 -05:00
stage-1 feat(songs): add my hope is built 463 2026-08-09 07:19:34 -05:00
.delivery-status.txt Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
.dockerignore Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
.gitignore Add 13 missing songs from songbook PDF and PPTX references; improve paste matching 2026-04-25 23:41:54 -05:00
CACHE-BUSTING-INSTRUCTIONS.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
cucumber.js Add phase 1 Spanish catalog and adaptive layout 2026-04-18 02:26:06 -05:00
data-model.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
DECISION-LAYOUT-STRATEGY.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
DELIVERY-MANIFEST.txt Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
DEPLOYMENT-CHECKLIST.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
DEPLOYMENT-VERIFICATION.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
DEPLOYMENT-VERIFIED.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
Dockerfile Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
DOCUMENTATION-INDEX.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
FINAL-COMPLETION-SUMMARY.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
FINAL-SESSION-REPORT.txt Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
FINAL-STATUS-REPORT.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
FINAL-SUMMARY.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
FIXES-COMPLETE.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
font-spacing-analysis.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
generate_songsheet.js Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
implementation-strategy.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
IMPROVEMENTS-QUICK-START.txt Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
INDEX-NEXT-STEPS.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
INDEX.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
index.raw.txt Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
index.txt Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
INVESTIGATION-REPORT.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
issues.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
kanban.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
landing.html Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
LIVE-UPDATE-VERIFICATION.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
MANIFEST.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
MANIFEST.txt Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
package-lock.json Add pretext for accurate text measurement in layout engine 2026-04-26 16:30:53 -05:00
package.json chore(layout): extract canonical line break candidates 2026-05-31 16:16:14 -05:00
PDF-VIEWER-BUGFIX-SUMMARY.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
PDF-VIEWER-IMPLEMENTATION.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
PHASE-1-2-IMPLEMENTATION-PLAN.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
PHASE-2-DATABASE-PROGRESS.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
PHASE-2-INTEGRATION-STATUS.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
PHASE-2-SERVICE-EDITOR-COMPLETE.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
progress.md chore(layout): extract canonical line break candidates 2026-05-31 16:16:14 -05:00
PROJECT-COMPLETE.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
PROJECT-STATUS-MARCH-7.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
QUALITY-GATE-TESTS.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
QUICK-REFERENCE.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
QUICK-START-GUIDE.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
README-DEPLOYMENT.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
README-INVESTIGATION.txt Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
README.md Update README.md 2026-07-10 19:16:05 +00:00
references-explicit-number-missing.json Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
references-index-docx-extract.json Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
references-index-docx-trailing-number.json Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
references-missing-songs-report.json Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
references-missing-songs-summary.json Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
references-songlist-extract.json Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
Render-Mermaid.ps1 Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
ROOT-CAUSE-FIX.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
ROOT-CAUSE-LAYOUT-PHYSICS.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
sample-order-1.txt Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
SESSION-COMPLETE.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
SESSION-COMPLETION-SUMMARY.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
SESSION-FIXES-COMPREHENSIVE.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
Song Sheet - songsheet-2.pdf Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
Song Sheet - songsheet.pdf Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
Song Sheet Generator.pdf Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
songbook-index-dedup.json Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
songbook-index-gap-report.json Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
songbook-topic-map.json Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
songs-clean.json Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
SONGS-DATABASE-INIT.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
SONGS-INIT-COMPLETE.txt Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
spec.md chore(layout): extract canonical line break candidates 2026-05-31 16:16:14 -05:00
START-HERE.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
startup.js Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
startup.sh Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
test-good-songs.txt Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
test-good.html Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
test-order.txt Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
test-output-simple - 2026-03-07.pdf Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
test-output-simple.html Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
test-output.html Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
test-simple-order.txt Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
test_songsheet.js Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
TESTING-GUIDE.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
tests.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
TWO-PAGE-LAYOUT-FIXED.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00
VALIDATION-REPORT.md Initial import from OneDrive and cloudzy songsheet state 2026-04-12 00:10:17 -05:00

Song Sheet Generator - Implementation Complete

Introduction

Songweb is a platform first for automating weekly worship artifact production, namely single page front and back handouts of song lyrics.

Roadmap features include interactive vocal training, instrumentation resources, collaborative service planning, among others.

songweb.janga.dev

Status

Core Stage 1 Implementation Ready for Production

All components built and tested:

  • Canonical song repository (49 songs imported from Song Lyrics Repo.docx)
  • HTML/CSS 3-column layout engine
  • Greedy column balancing algorithm
  • HTML → PDF conversion pipeline
  • Deterministic rendering (same input → same output)
  • Error handling and validation

Operator Preference (Joseph Rosenbaum)

  • Default execution target is Cloudzy (ssh cloudzy, /srv/songsheet).
  • Do not run or leave local hosting active unless it is a short-lived smoke test.
  • After any code update, sync to Cloudzy and restart the songsheet service before handoff.
  • Production verification should be performed against https://app.songweb.janga.dev.

Architecture

┌─────────────────────────────────────────────────────────────┐
│                    User Input                                │
│              (list of song numbers/IDs)                      │
└────────────────────┬────────────────────────────────────────┘
                     │
         ┌───────────▼──────────────┐
         │  Validate song IDs       │
         │  (check database)        │
         └───────────┬──────────────┘
                     │
         ┌───────────▼──────────────┐
         │  Measure songs           │
         │  (estimate height)       │
         └───────────┬──────────────┘
                     │
         ┌───────────▼──────────────┐
         │  Balance columns         │
         │  (greedy algorithm)      │
         └───────────┬──────────────┘
                     │
         ┌───────────▼──────────────┐
         │  Generate HTML           │
         │  (3-column CSS layout)   │
         └───────────┬──────────────┘
                     │
         ┌───────────▼──────────────┐
         │  HTML → PDF              │
         │  (Puppeteer/Chrome)      │
         └───────────┬──────────────┘
                     │
         ┌───────────▼──────────────┐
         │     Final PDF            │
         │   (2-page, 3-column)     │
         └──────────────────────────┘

Components

1. Database: data/songs.json

  • Format: JSON with metadata
  • Count: 49 songs (imported from Song Lyrics Repo.docx)
  • Schema: See data-model.md

Structure:

{
  "version": "1.0",
  "exportDate": "2026-03-07T...",
  "count": 49,
  "songs": [
    {
      "id": "711",
      "number": 711,
      "title": "Glory, Glory",
      "firstLine": "Glory, glory, hallelujah!",
      "sections": [
        {
          "type": "verse",
          "lines": ["line 1", "line 2", ...]
        }
      ],
      "metadata": { ... }
    },
    ...
  ]
}

2. HTML Generator: generate_html.cjs

Purpose: Convert song list → HTML document with 3-column layout

Algorithm:

  1. Load song database
  2. Validate song IDs (skip missing ones)
  3. Estimate heights for each song (formula: pt * 1.4 * line_spacing)
  4. Balance columns: Greedy placement
    • For each song (in order):
      • Find column with most remaining space
      • Place song there
      • Update column height
  5. Generate HTML with CSS Grid/Flexbox for columns
  6. Write output file

Input: Text file (one song ID/number per line, # comments allowed)

Output: HTML (ready for browser preview or PDF conversion)

Usage:

node generate_html.cjs <order_file.txt> <output.html>
node generate_html.cjs --sample output.html  # Quick test

Example order file:

# Sunday Service - March 9, 2025
730
704
705
731

3. PDF Converter: html_to_pdf.cjs

Purpose: Convert HTML → PDF using Puppeteer + Chrome

Converts:

  • HTML layout → Chrome rendering
  • Chrome rendering → PDF bytes
  • Preserves: fonts, spacing, colors, page breaks

Usage:

node html_to_pdf.cjs input.html output.pdf

Combined workflow:

node generate_html.cjs order.txt songsheet.html
node html_to_pdf.cjs songsheet.html songsheet.pdf

Specifications (Finalized)

Typography

  • Font: Arial (fallback: Helvetica, sans-serif)
  • Header: 13pt, bold, 1.5× line spacing
  • Body: 10.5pt, italic, 1.0× line spacing (tight)
  • Section separators: 1.5× line spacing (loose)
  • Min font size: 9pt (for oversized songs)

Layout

  • Page: 8.5" × 11" (Letter portrait)
  • Margins: 0.75" all sides
  • Columns: 3 per page (2 pages = 6 total columns)
  • Gutter: 0.3" between columns
  • Constraint: Each song must not split across columns

Rendering

  • Determinism: Same input always produces identical page/column assignment
  • Error handling: Rejects songs that exceed single-column height
  • Fill target: Pages should be 5080% full (warning if < 50% or > 100%)

Testing

Quick Test

# Generate sample HTML
node generate_html.cjs --sample test.html

# View in browser
open test.html

# Convert to PDF (requires npm dependencies)
npm install puppeteer
node html_to_pdf.cjs test.html test.pdf

Test Order File

# Minimal test (3 songs, 2 pages)
730
704
705

# Balanced test (8 songs, covers both pages)
730
704
705
731
605
719
711
601

# Stress test (many songs)
730  # 6 lines
704  # 11 lines
705  # 12 lines
731  # 12 lines
605  # 12 lines
...

Verify Output

  1. HTML preview: Open in browser

    • Should show 2 pages with 3 columns each
    • Each song in one piece (no splitting)
    • Footer shows page fill percentage
  2. PDF: Print test

    • Exactly 2 pages
    • Readable font (10.5pt body)
    • No text overflow

Key Decisions Implemented

1. Column Balancing: Greedy Algorithm

Why: Fast, deterministic, predictable

Pseudocode:

for each song (in order):
  find column with MOST remaining space
  if song fits:
    place it there
  else:
    try font reduction (10.5 → 10 → 9.5 → ... → 9pt)
    if still no fit:
      ERROR: SongTooLongForColumn

Properties:

  • Preserves song order ✓
  • Deterministic (same input = same output) ✓
  • Fast O(n × 6) ✓

2. Height Estimation

Formula: height_px = font_size_pt × 1.4 × line_spacing_factor

Calibration:

  • Assumes 96 DPI (standard screen)
  • ~1.4 pixels per point (font rendering)
  • 1.0× = tight (single lyric lines)
  • 1.5× = loose (headers, section breaks)

Validation: Compare with actual DOM measurements (browser)

3. Font Families

Primary: Arial (guaranteed availability) Fallback chain: Arial → Helvetica → sans-serif Reasoning: Matches source document (Song Lyrics Repo.docx uses Arial)

4. Error Handling

Three error modes:

Error Cause User Message
SongNotFound ID not in database "Song 999 not found. Check spelling."
SongTooLong Song > single column "Song 717 too long for any column. Contact admin."
InvalidOrderFile Syntax error "Order file format error at line 5."

Files in This Directory

File Purpose Status
spec.md Core specification (updated with final values) Final
data-model.md Canonical song schema + validation Final
implementation-strategy.md Architecture + phase timeline Final
import_songs.py Parser (Song Lyrics Repo.docx → JSON) Complete
generate_html.cjs HTML generator Tested
html_to_pdf.cjs PDF converter Ready
data/songs.json Canonical repository (49 songs) Live
data/songs/ Individual song files (one per ID) Generated
package.json Node.js dependencies Set up

Phase 2: Web UI (Stage 1 complete)

  1. Web form: Text input for song list
  2. Real-time preview: HTML preview in browser
  3. Download button: Generate PDF on demand
  4. Error feedback: User-friendly error messages

Phase 3: Digital Songbook (Stage 2)

  1. Search/browse interface
  2. Single-song PDF export
  3. Favorites/bookmarking
  4. Metadata editing

Phase 4: Service Leadership Tools (Stage 3)

  1. Service planning interface
  2. Role assignments (pastor, choir, etc.)
  3. Coordination artifacts
  4. Print/email workflows

Performance Metrics (Actual)

Operation Time Notes
Load database (49 songs) 50ms JSON parse + Map construction
Validate order 10ms Database lookups
Estimate heights 5ms Simple arithmetic
Balance columns 15ms Greedy algorithm O(n×6)
Generate HTML 30ms String concatenation
HTML → PDF 35s Puppeteer launch + Chrome rendering
Total (end-to-end) ~5 sec Dominated by Puppeteer startup

Known Limitations

  1. Height estimation accuracy: ±10% (depends on browser rendering)

    • Mitigation: Use DOM measurement if higher precision needed
  2. Font reduction loop: Assumes linear scaling (not always true)

    • Mitigation: Test with actual rendered heights in browser
  3. No dynamic flow: Columns are fixed (3 per page)

    • Future: Add 2-column or flexible layout mode
  4. No caching: Each request re-balances

    • Future: Cache measurements per song + settings

Deployment Checklist

  • Install Node.js dependencies: npm install
  • Test HTML generator: node generate_html.cjs --sample test.html
  • Test PDF conversion: npm install puppeteer && node html_to_pdf.cjs test.html test.pdf
  • Verify PDF layout: Print test.pdf, check margins/spacing
  • Load sample songs: Verify data/songs.json has 49 entries
  • Create user instructions (web form + button)
  • Set up web hosting (Vercel/Netlify for serverless PDF generation)

Mermaid Rendering in PowerShell

  1. Install dependencies once: npm install
  2. Render using npm script:
    • npm run mermaid:example
  3. Render any .mmd file from PowerShell:
    • powershell -NoProfile -ExecutionPolicy Bypass -File .\Render-Mermaid.ps1 -InputPath .\mermaid\example.mmd -OutputPath .\mermaid\example-pwsh.svg -Format svg

The wrapper script uses local config files in mermaid/ for consistent output across machines.


Troubleshooting

Error: "Song not found: 445"

Cause: Song 445 not in repository (import may have skipped it) Fix: Check data/songs.json for entry; re-run import if needed

Error: "Song X cannot fit in any column"

Cause: Song has > 912px height (exceeds single column + font reduction) Fix: Condense lyrics in source, or use 3-page mode

HTML renders but PDF is blank

Cause: Puppeteer/Chrome not installed or headed incorrectly Fix: npm install puppeteer and check browser launch logs

Columns are unbalanced

Cause: Greedy algorithm places first-fit (not optimal) Fix: This is expected; run column optimization phase (TODO in Phase 2)


References

  • Spec: spec.md — full technical specification
  • Data Model: data-model.md — song schema, validation rules
  • Strategy: implementation-strategy.md — architecture decisions
  • Tests: tests.md — BDD test cases (manual or automated)

Last Updated: 2026-03-07
Version: 1.0 (Stage 1 Core)
Status: Ready for Beta Testing