Skip to content
grayumPublic

About

Local-first cycling cue sheets for a stem or top tube. A portable static web app with light/dark/system themes and a lime folded-tape identity. All editing, imports, storage and printing happen in the browser.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

StemTape

A little tape. A clear plan.

Free hosted app / live demo — hosted on GitHub Pages behind Cloudflare; ride plans stay in your browser. Docker/VPS self-hosting remains available.

The canonical URL is https://stemtape.cc/; www redirects to it. See the completed hosting migration and operational settings.

Local-first cycling cue sheets for a stem or top tube. A portable static web app with light/dark/system themes and a lime folded-tape identity. All editing, imports, storage and printing happen in the browser.

Start on your laptop

Python 3 and a modern browser are sufficient. No npm install or build is needed to use the app.

python3 scripts/serve.py

Open http://127.0.0.1:8080. Use this local server rather than opening index.html as a file: ES modules require HTTP. The development server binds to loopback only and is not intended for public hosting.

Included in StemTape

  • Saved events, duplication/deletion and project-owner presets.
  • Relative last-save status with honest failure/recovery feedback (shipped in v0.4.0).
  • Deployment hardening and Cloudflare/Caddy guidance, including the proxy-only capability fix (shipped in v0.4.0). Edge settings are separate from repository fixes.
  • Nutrition by distance or elapsed h:mm, with separate cue lists; route and custom presets.
  • Metric defaults and explicit mile/inch overrides; canonical distance storage.
  • Local Lucide outline icon picker plus pasted emojis/text.
  • Mouse/touch drag handles and keyboard/arrow-button reordering; custom typed columns, visibility, width, alignment and emphasis.
  • Live white print preview, physical dimensions, padding, font, spacing, rotation and headers.
  • Overflow warnings; printing blocked when content does not fit.
  • A4/Letter printing and browser Save as PDF; copies, cut marks and calibration scale.
  • CSV file import/export and tab-separated spreadsheet paste. JSON full backup/restore with confirmation.
  • Remembered bike dimensions, local personal presets, storage failure/recovery handling.
  • Transparent SVG logos, symbol files, banners and SVG/ICO favicon assets.
  • Hardened Docker recipe and local / standalone VPS / existing Traefik examples.

Custom Unicode emojis use the user's system font. The outlined symbols combine Lucide with original StemTape symbols, including the approved Bar, Bottle, Gel and Cobbles replacements, not Font Awesome. Lucide 1.8.0 and DejaVu font notices are bundled in dist/assets/. No CDN assets or analytics.

v0.9.0 changes (review candidate)

  • Open a cue's icon picker to choose Default / monochrome, Blue, Orange, Green, Red or Purple. Colour belongs to the row, survives icon changes/None, and affects primary icon artwork only. Both Bidon rows can have different colours.
  • Layout & print settings → Appearance → Grid: None, Row lines (existing default), or Full grid. Visible Note/custom columns participate; footer stays outside.
  • Colours and grid are undoable and retained in complete JSON, duplicates, personal presets and their recovery revisions. Old plans remain monochrome with row lines.
  • Coloured CSVs add versioned metadata requiring v0.9.0; monochrome CSVs stay in the previous format. CSV omits grid settings. Older apps lose new styling on JSON save.
  • Same selected icon colour on screen, preview and print; audited interior fills and dark outlines retain gaps/details. Feed zone keeps coloured utensil strokes. Default Banana is monochrome like every cue. Shared 0.8 mm cell insets keep artwork/text away from grid borders within configured widths. Browser/printer settings and greyscale printers can affect colour. Verify an actual-size physical print before use.

See design, compatibility and review checklist. Final user-reported manual preview acceptance: 2026-10-19, including upright Bar, Gel and Bottle/Bidon at 0°. Physical printing remains unverified and is not a release gate at the user's request; production verification is not claimed.

v0.8.0 changes (review candidate)

  • New preset names and renames must be unique after trimming and ignoring case, including built-in names. Existing duplicate-name backups remain usable and entries have numbered personal-preset labels.
  • Event names identify ride plans, with a separate case-insensitive namespace from preset names. New/duplicated/imported plans get checked unique names; legacy duplicate-name backups remain intact with numbered display labels.
  • Save as new preset associates the plan with a personal copy. Update preset confirms its identity and preserves its name while capturing the event name, both cue lists, columns, units and plan print settings. Rename preset is separate and never renames existing events. Undo/Redo name conflicts keep history intact; free the name on the other plan, then retry.
  • Recover one previous preset version from its Update dialog. Atomic failures preserve both versions; preset actions remain outside plan Undo/Redo.
  • Tour of Flanders is a supplied 19-cue distance example with visible Location, not a verified current official route. Default strip dimensions remain unchanged.
  • Complete JSON retains preset identities, associations and recovery. CSV does not; older apps can lose new metadata. Back up before rollback.

See design, compatibility and manual review. The user reports final manual preview acceptance on 2026-10-18, including the final button layout and Tour of Flanders preset. Physical printing and exhaustive device/accessibility testing are not inferred. Deployment remains separate.

v0.7.0 changes (review candidate)

  • Undo/Redo for completed edits to the active plan, including row/column ordering, Note visibility, name, units and print settings. Position edits and their automatic sort are one action; continuous field edits finish on blur.
  • Separate session-only histories: up to 20 actions per plan within an 8 MiB shared snapshot budget. Reload clears history; JSON/CSV and localStorage never contain it. Large plans may retain fewer actions.
  • Native text undo remains available in fields. Outside fields/dialogs, use Cmd/Ctrl+Z and Cmd/Ctrl+Shift+Z (also Ctrl+Y on non-Mac).
  • Invalid drafts must be corrected or explicitly cancelled first. Storage failures retain valid restored changes in memory with honest Not saved status.

See scope, design and review checklist. The user confirms v0.6.2 is deployed and working and reports passing v0.7.0 manual preview testing and final acceptance, including Undo/Redo placement, on 2026-10-18. Physical printing, screen-reader and exhaustive device coverage are not inferred.

v0.6.2 changes (review candidate)

  • Rotate Bidon/Bottle artwork 45° clockwise everywhere, retaining key bottle.
  • Clarify Scale 100% / Actual size. printing guidance and ruler calibration in Help.
  • Right-align Privacy & help at the bottom of the preview frame; keep the hosted-service privacy explanation.
  • Preserve plans, imports/exports, sorting, save protections and physical sheet dimensions.

The user reports the final local preview passed on 2026-10-18, including the right-aligned Help button. Physical printing and exhaustive device coverage are not inferred.

See v0.6.2 review and local Docker checklist for validation and remaining release gates.

v0.6.1 changes

  • Responsive Import cards with equal widths and aligned actions; stack on mobile.
  • Understated secondary action buttons with visible borders, tinted backgrounds and lime keyboard focus in both themes.
  • Existing Layout & print settings grouped under Sheet size, Appearance, Columns, Footer and Printing. Open the native disclosure to edit dimensions or font. Footer text stays visible below its toggle, disabled when off and retained for later use.
  • Presentation-only patch: plans, backups, imports, save protections and physical print geometry are unchanged. See changelog, validation and upgrade/rollback.

v0.6.0 changes

  • Compact Columns list under Layout & print settings: drag with mouse/touch, use Move left/right, or use arrow keys/Home/End on a handle. Distance/time stays fixed first; data follows column IDs. Boundary buttons are disabled and movement restores focus and announces position.
  • Optional Show note column, off by default. Initially insert Note before Cue/Fuel; rename it to Location, reorder it, or hide it without deleting its text. Column details uses the same labels, widths and cells as other custom columns. Existing columns named Note are not reclassified.
  • CSV exports visible data columns in display order, plus the required position/icon fields. Complete JSON retains hidden content, both modes, column identities, presets and layout. Existing width shares and physical sheet dimensions stay unchanged; adding a column can cause overflow and block printing.

The new Note starts with a relative width share of 25; other shares are preserved. Review design and manual checks, validation and upgrade/rollback. The owner confirms v0.6.0 is live and working; this is user-reported deployment status, not an independently executed production check.

v0.5.0 changes

  • Four approved local SVG replacements: smiling unwrapped Bar, cycling Bottle, diagonal blank Gel sachet and perspective Cobbles/Pavé. Existing icon keys and saved cues keep their identity.
  • Optional per-plan printed footer under Layout & print settings, off by default. Plain text/emojis stay inside the chosen sheet dimensions; overflow blocks printing. Turning it off preserves the text; JSON includes it, cue CSV stays unchanged.
  • Lime expand/collapse arrow on Layout & print settings, retaining native keyboard operation.

Footer text uses the plan font on one centered line, with a maximum of 80 UTF-16 units (most emojis use two). The actual measured line must also fit. Review compatibility and manual checks, validation and upgrade/rollback. The previously deferred column features are now prepared for v0.6.0 following the owner’s live v0.5.0 confirmation; see the roadmap.

v0.4.1 changes

  • Publish the owner-authorized security.txt with maintained expiry and an exact hidden-path exception.
  • Keep the relative save status; hover, focus or tap it for the localized saved date/time and explicit timezone. No permanent ISO line.
  • Grey out distance units in Time mode, retaining the preference and formatting of additional distance columns. Sheet dimensions stay editable.
  • Use Fuel in newly created nutrition templates, in both modes. Existing/imported labels remain unchanged because v1 stores no reliable marker distinguishing an untouched default from a chosen label.

Remaining Cloudflare cipher/key-exchange findings are accepted deployment limitations for now. No perfect Internet.nl score, security audit or production check is claimed. See upgrade/rollback instructions.

Data and privacy

The hosted service uses GitHub Pages and Cloudflare, which process normal hosting requests and connection metadata. Cloudflare Network Error Logging was disabled; this does not eliminate all provider logs or data processing. StemTape adds no application analytics or tracking scripts. Plans live in your current browser's localStorage, not the server. Different domains, profiles and browsers have separate data. Clearing site data removes plans. Export JSON backups regularly; CSV preserves cue data, not full layout/types. Remembered bike dimensions are included in JSON. Optional last-save timing metadata shares the same atomic state record and byte budget; exact-limit plans preserve their data and explicitly retain time only for the current tab when metadata cannot fit. Old data with no timestamp keeps an unknown save time. See save-status behavior. Saved state, complete JSON backups and imported files share a 2,000,000-byte UTF-8 limit. State includes all events, both cue modes, presets and preferences; changes exceeding the limit are rejected without truncation. Existing oversized or unreadable storage remains protected with an original-data recovery export. Imports also allow at most 200 rows per mode and 8 columns. No GPX parsing, accounts, automatic nutrition advice or route detection.

CSV example:

distance_km,icon,Fuel
20,banana,Banana
40,bottle,Drink

Time example:

time,icon,Fuel
0:30,bar,Bar
1:00,gel,Gel

The Softdrink symbol retains CSV/JSON key can.

Use point decimal separators in CSV numbers. A comma in text must be quoted. Supported icon keys: banana, bottle, bar, gel, smile, mountain, feed, flag, coffee, warning, cobbles, ricecake, can, neutral, litter (or empty). Unknown icons are rejected. Export includes the active mode, cumulative position values, icons and visible data columns in display order. Hidden content requires JSON backup. The position column stays in the editor and CSV even when hidden in print. If no other data column is visible, a position/icon-only CSV import creates an empty Fuel column; it does not recover omitted hidden data. Custom distance columns export explicit stemtape:column: JSON metadata in their CSV headers, preserving labels and distance units. v0.6.0 extends that tuple with an optional validated cue/note role to preserve identity when columns move or are renamed; marked exports require v0.6.0 for import. Older CSV files remain supported. CSV still does not preserve all custom types, exact text, precision, hidden content or layout; use JSON for complete backups. Literal labels ending in [km] or [mi] remain text unless explicitly marked; legacy CSV suffixes are not inferred. Use JSON for lossless backups.

Cue ordering

Editing the primary distance or time sorts cues when you leave the field or press Enter. Manual reordering then persists until the next valid position edit (or Sort). Equal positions retain their relative order. Text and symbol edits do not sort.

Print workflow

Measure the usable surface, not the nominal stem length. Set width/length, adjust text and print at Scale 100% / Actual size. Fit to page can change physical dimensions; disable browser headers/footers. Save as PDF from that dialog if wanted. Measure the printed 50 mm calibration line before fitting the strip. Each copy gets its own page. Actual printer scaling and readability still require a physical test. The app never silently shrinks fonts; default is 11 pt, minimum 8 pt.

Optional Docker development and self-hosting

The current hosted service uses GitHub Pages behind Cloudflare. The following Docker, Traefik and VPS examples are optional self-hosting paths. The user confirmed on 2026-10-17 that the former production VPS is turned off. Rollback requires starting it and verifying the app, TLS and configuration before switching traffic. Deletion of the VPS, disks, snapshots and backups is unconfirmed.

Copy the examples once into your private configuration files:

cp docker-compose.example.yaml docker-compose.yaml
cp .env.example .env
docker compose -f docker-compose.yaml config --quiet
docker compose -f docker-compose.yaml up --build -d

The default is local testing at http://127.0.0.1:8080. The .env uses an official, versioned Alpine Nginx tag for convenience. Resolve and scan an immutable digest before a production build. The application image, domain, network and resolver references are all parameterised.

For an existing Traefik proxy, remove the local ports section and uncomment all three marked sections in your private docker-compose.yaml: the service's traefik network entry, the labels block, and the external network definition. Configure STEMTAPE_DOMAIN, TRAEFIK_NETWORK and TRAEFIK_CERTRESOLVER in .env to match your installation. The proxy should already handle HTTP-to-HTTPS redirection. No Caddy configuration is needed.

An independent Caddy/VPS example is in deploy/docker-compose.vps.example.yaml, with deploy/.env.vps.example. See deployment instructions for the exact command and certificate-directory setup.

For upgrades, preserve your existing private .env and docker-compose.yaml. See UPGRADE.md. No private files are included in this archive.

Checks

Node 22+ for the dependency-free data tests:

npm test
npm run check

Browser tests (development-only dependency):

npm install --no-save --package-lock=false playwright@1.62.1
npx playwright install chromium
# Keep the local server running in another terminal:
npm run test:browser

This creates screenshots and an actual PDF in ignored artifacts/. Review all screenshots and verify PDF dimensions; passing assertions alone is not visual QA.

sh scripts/container-smoke.sh stemtape:0.9.0

v0.6.1 validation: the complete native Chromium suite against the local Docker app, real-browser screenshot/200% zoom review, 69 Node tests, syntax and container checks passed. The owner also reports a passing manual Docker-preview check on 2026-10-09. Physical printing, screen-reader/device testing and production verification are not inferred; remaining gates are recorded in current validation.

Prior v0.4.1 validation: see current evidence and remaining gates and separate security/application review groups. v0.4.0 was merged to main; that does not establish unperformed production checks.

Prior v0.3.0 validation: Main CI run 36886880381 passed on 2026-10-01: 30 data tests, app/core syntax checks, Chromium browser regressions with screenshot/PDF generation, basic tracked-file checks and the container smoke test. User-reported manual and actual-size-print checks are recorded separately. Production proxy checks, full secret/image scans, broader visual/device checks and large-active-plan performance profiling remain outstanding; this is not a security audit or production-deployment verification. See the validation record for coverage and limitations.

Repository workflow

Unzip into a fresh development directory, review, then initialise Git and create your chosen private GitHub repository. Keep .env, actual deployment overrides and runtime/ outside version control. CI validates changes; the separate Pages workflow deploys only when manually dispatched on main, after validation. See the configured Pages destination guard before another deployment. Public release needs a chosen project license, domain/trademark checks and release gates. First-party code/brand licensing remains for the owner to choose; bundled third-party assets retain their own notices.

Credits

StemTape is developed and maintained by Graham van der Wielen.

Created with assistance from ChatGPT & Codex (OpenAI).

About

Local-first cycling cue sheets for a stem or top tube. A portable static web app with light/dark/system themes and a lime folded-tape identity. All editing, imports, storage and printing happen in the browser.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages