feat: Helios Booth 2026 — Lit web component voting booth#1
Open
benadida-agent wants to merge 33 commits into
Open
feat: Helios Booth 2026 — Lit web component voting booth#1benadida-agent wants to merge 33 commits into
benadida-agent wants to merge 33 commits into
Conversation
Four-phase implementation plan for rebuilding the voting booth with Lit web components, TypeScript, and Vite (booth2026). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy crypto libraries from the legacy booth to the new Lit-based booth2026: - Copy jscrypto directory with all crypto libraries (jsbn, sjcl, bigint, elgamal, helios, sha1, sha2) - Copy underscore-min.js utility library - Add TypeScript type declarations for crypto globals (BigInt, ElGamal, HELIOS, etc.) - Types enable type-safe crypto operations in the voting booth Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create src/main.ts as the entry point that imports booth-app component - Create src/booth-app.ts with BoothApp LitElement component that: - Manages voting booth screen state (loading, election, question, review, submit, audit) - Holds voter state including election data, answers, and encrypted ballot - Implements election loading from server with metadata support - Provides screen navigation and exit functionality - Renders election info screen with start button - Includes placeholder screens for Phase 2-3 work - Update crypto/types.ts to remove global declarations (use window access instead) - Adjust tsconfig.json to disable unused locals/params checking for Phase 1 shell This provides the main application shell for the voting booth with state management ready for Phase 2 (voting flow) and Phase 3 (submission/audit). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Load jscrypto libraries and underscore before the app to ensure crypto globals are available when booth-app initializes. Scripts are loaded synchronously in dependency order before the module script that imports booth-app.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix crypto script paths from root-absolute (/lib/jscrypto/) to relative (../lib/jscrypto/) to work correctly when served at /booth2026/ in production - Add typed declare global block in crypto/types.ts for non-conflicting globals (HELIOS, b64_sha256, ElGamal, Random, UTILS, sjcl) with proper type narrowing - Update booth-app.ts to use declared typed globals directly instead of (window as any) casting - Move inline styles to CSS classes (.start-button-container and .help-email) in booth-app.ts static styles - Export BoothScreen type for child component usage in later phases Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…avigation Implement Task 2 of Phase 2 - integrate question screen component into booth app: - Add imports for question-screen component and event types - Implement handleAnswerChange to track answer selections with immutable updates - Implement validateCurrentQuestion to enforce minimum answer requirements - Implement handleNavigation to handle previous/next/review navigation with validation - Add goToQuestion method for editing answers from review screen - Add renderQuestionScreen method to render question with proper answer orderings - Update startVoting to show review button immediately for single-question elections - Update renderCurrentScreen to use renderQuestionScreen instead of placeholder Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Important Fix 1: Remove redundant checkbox accessibility attributes from li element. Native input type="checkbox" handles the semantics; role="checkbox", aria-checked, and aria-disabled on the li create duplicate announcements for screen readers. - Important Fix 2: Change warning-box color from var(--color-success, #28a745) to var(--color-text-secondary, #666) for proper semantic coloring of constraint messages. - Minor Fix 1: Skip validation in handleNavigation() for 'previous' direction to allow users to navigate back without meeting minimum answer requirements. - Minor Fix 2: Use explicit undefined comparisons in getConstraintText() to properly handle cases where min=0 or max=0. Verification: - TypeScript: npx tsc --noEmit ✓ - Build: npm run build ✓ - Tests: uv run python manage.py test -v 0 → 198 tests pass ✓ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Task 7 implementation: - Added screen imports for review, submit, audit, and encrypting screens - Updated BoothScreen type to include 'encrypting' state - Added encryption-related state properties: worker, encryptionProgress, answerTimestamps, dirty tracking, encryptedBallot, auditTrail, rawElectionJson - Implemented worker initialization and async encryption handling - Updated loadElection to store rawElectionJson and initialize worker - Updated handleAnswerChange to mark questions as dirty - Updated handleNavigation to trigger sealBallot on review - Added handleReviewNavigation, prepareForCast, and auditBallot handlers - Added handleAuditNavigation, resetAndReencrypt, and postAuditedBallot methods - Added handleBallotSubmit for submission flow - Updated renderCurrentScreen with cases for all new screens - Updated getProgressStep to include encrypting screen - Updated beforeunload handler to warn on encrypting screen - Added getPrettyChoices and worker encryption helper methods - Fixed EncryptedVote interface to support includeRandomness parameter and optional clearPlaintexts method Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Critical: Add worker.onerror handler to catch encryption failures and set error state - Important: Fix worker instantiation to use import.meta.url instead of root-absolute path - Important: Fix encrypting.gif and loading.gif paths to use import.meta.url for production - Important: Remove dead hidden form from review-screen (submission is in submit-screen) - Minor: Replace `as any` with `Record<string, unknown>` in hasClearPlaintexts type guard - Minor: Make auditExpanded @State() reactive and remove manual requestUpdate() - Minor: Move inline styles to CSS classes in review-screen, audit-screen, submit-screen All TypeScript compilation passes, build succeeds, and all 198 Django tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…gement Implements Task 1 from Phase 4 accessibility enhancements: - Add skip link element with proper styling for keyboard navigation - Add ARIA landmarks: header with role="banner", nav with aria-label - Add focus management via focusMainContent() method for screen transitions - Call focusMainContent() in sealBallot(), prepareForCast(), and auditBallot() - Update render() to use proper semantic HTML structure - Add aria-live="assertive" to error alert for screen reader announcements - getProgressStep() method already exists and works correctly The main element now has id="main-content" and tabindex="-1" for focus management. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement Task 2 from Phase 4 with: - isInitializing state property for tracking booth initialization - renderErrorScreen() method with recovery options (Reload, Return to Election) - Error screen CSS styles (.error-screen, .error-message, .error-actions) - Improved initializeBooth() with error classification (network, crypto, generic) - New waitForCryptoWithTimeout() method with 10-second timeout - Updated renderCurrentScreen() loading case with initialization message - Updated election case to show error screen when initialization fails - Enhanced loading display with role="status" and aria-live="polite" - loading-detail style for secondary messaging Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update vite.config.ts with production build options: - Add sourcemaps for debugging - Configure terser minification - Add dev server proxy for Django API calls - Set publicDir to 'public' - Use hashed filenames for assets - Update index.html with metadata and production paths: - Add SEO meta tags (description, robots noindex) - Add noscript fallback for JavaScript requirement - Use relative paths for crypto scripts and styles - Add preload hints for critical assets - Create public/ directory and move GIF files: - Move encrypting.gif to public/ - Move loading.gif to public/ - Update references in encrypting-screen.ts and review-screen.ts - Add @vite-ignore comments to suppress build warnings - Install terser as dev dependency for minification - Verified production build succeeds with single bundle Build output: - dist/assets/booth.[hash].js (61KB) - dist/assets/booth.[hash].css (3KB) - Source maps generated for debugging - Hashed filenames for cache busting Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add documentation comment to the booth2026 URL route explaining how to switch from serving source files in development to serving built dist files in production deployment.
…uild Move encrypting.gif and loading.gif from root heliosbooth2026/ to public/ directory so they are properly included in the production build output. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Critical (C1): Fix aria-current attribute binding on progress steps
- Changed from string interpolation (which Lit ignores) to proper conditional
attribute binding using aria-current=${condition ? 'step' : nothing}
- Imported 'nothing' from 'lit' for proper undefined handling
Important (I1): Add focusMainContent() to startVoting() and goToQuestion()
- Both screen transition methods now call focusMainContent() like other
transitions (sealBallot, prepareForCast, auditBallot) for consistent
focus management and accessibility
Important (I2): Remove redundant role="main" from main element
- The <main> element has implicit role="main", explicit attribute was
redundant and violates HTML best practices
Minor (M1): Consolidate duplicate .error and .error-message styles
- Both classes had identical background, border, color, padding, and
border-radius CSS. Now consolidated with shared rule and context-
specific margin handling for .error-screen .error-message
Minor (M2): Remove useless preload link from index.html
- The <link rel="preload"> before the actual stylesheet provided zero
benefit and cluttered the HTML
All issues verified:
- TypeScript compiles without errors (npx tsc --noEmit)
- Production build succeeds (npm run build)
- No regressions introduced
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- C1: Fix crypto script paths in index.html - change ../lib/ to lib/ to resolve correctly when served from /booth2026/ - I1: Re-enable noUnusedLocals and noUnusedParameters in tsconfig.json and fix unused cryptoReady state - I2: Clarify production deployment comment in urls.py - serve full heliosbooth2026/ directory (not just dist/) because crypto libs in lib/ are needed - I3: Fix choices[index]?.length < question.max comparison - use nullish coalescing (?? 0) to avoid undefined comparison - M1: Change encryptedBallot type from unknown to EncryptedVote | null and remove unnecessary type casts - M2: Merge duplicate import statements from crypto/types into single import All issues fixed: ✓ TypeScript compilation passes with strict unused checking ✓ npm run build succeeds ✓ Django test suite passes (198/198 tests) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… dist on build - Update Django URL to serve from heliosbooth2026/dist/ instead of source - Add explicit route for /booth2026/ to serve index.html (directory index) - Update npm build script to copy lib/ into dist/ for crypto library access Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Heroku runs `heroku-postbuild` which installs dependencies and builds the heliosbooth2026 Lit app into dist/. Requires adding the Node.js buildpack before the Python buildpack: heroku buildpacks:add --index 1 heroku/nodejs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Heroku prunes devDependencies before running heroku-postbuild, so tsc and vite aren't available. Move them to dependencies so they're installed when the build script runs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix ballot submission broken by Shadow DOM: switch submit-screen to light DOM rendering via createRenderRoot() so the <form> can POST natively. Add single ballot verifier (verify-screen component, worker, verify.html entry point) and fix audit screen verifier link. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
heliosbooth2026/directory implementing a complete voting booth using Lit 3.3.x + TypeScript 5.x + Vite 6.xquestion-screen,review-screen,submit-screen,audit-screen,encrypting-screen) orchestrated by centralbooth-appstate holder/booth2026/serves the new booth alongside the existingheliosbooth/Architecture
booth-appholds all state, child components receive data via properties and emit events viaCustomEventwithbubbles: true, composed: trueencryption-worker.js) handles ballot encryption in background thread using existing jscrypto libraries<script>tags (same as existing booth), TypeScript declarations insrc/crypto/types.tsTest Plan
noUnusedLocals,noUnusedParameters)/booth2026/?election_url=<url>, complete full voting flow🤖 Generated with Claude Code