Skip to content

Conversation

@ahaasco
Copy link

@ahaasco ahaasco commented Feb 2, 2026

Summary

  • Add QR Code Generator plugin with customizable styling options
  • Custom settings page with file upload for default logo
  • Fix SVG preview for eye color and shape customization
  • Fix responsive preview sizing (280x280)
  • Reduce logo size to 5% area coverage (~22% width)
  • Encode short URL in QR codes for redirect tracking
  • Pre-provision short codes for new QR codes
  • Add redirect-management as plugin dependency

Features

  • Generate QR codes with custom colors (foreground/background)
  • Shape customization (corner and dot shapes)
  • Logo embedding with automatic error correction adjustment
  • SVG and PNG export
  • Redirect tracking with scan analytics
  • HTMX-powered real-time preview

Test plan

  • Create new QR code with default settings
  • Customize colors and verify preview updates
  • Customize shapes (corner, dot) and verify preview
  • Upload logo and verify it appears in preview
  • Save QR code and verify redirect URL works
  • Export as SVG and PNG
  • Check plugin settings page with file upload

🤖 Generated with Claude Code

Andrew Haas and others added 30 commits January 30, 2026 08:46
- STACK.md - Technologies and dependencies
- ARCHITECTURE.md - System design and patterns
- STRUCTURE.md - Directory layout
- CONVENTIONS.md - Code style and patterns
- TESTING.md - Test structure
- INTEGRATIONS.md - External services
- STACK.md - Technologies and dependencies
- ARCHITECTURE.md - System design and patterns
- STRUCTURE.md - Directory layout
- CONVENTIONS.md - Code style and patterns
- TESTING.md - Test structure
- INTEGRATIONS.md - External services
- CONCERNS.md - Technical debt and issues
Fix HTMX form action attribute that was causing POST requests to malformed URLs like:
POST /admin/redirects/%22/admin/redirects%22

The issue was caused by nested template literals that included extra quotes around
the formAction variable, resulting in literal quote characters being embedded in the URL.

Changed from:
  ${isEdit ? `hx-put="${formAction}"` : `hx-post="${formAction}"`}

To:
  hx-${isEdit ? 'put' : 'post'}="${formAction}"

This ensures the HTTP method is dynamically set without introducing extra quotes
in the URL path.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add comprehensive console logging to POST and PUT handlers in redirect admin routes
to help diagnose validation failures. Logs include:
- Parsed form body
- Constructed input object
- Service validation results
- Error messages when returning 400 status

This will help identify why valid form submissions are getting rejected with 400 Bad Request.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…I consistency

Replace custom layout functions in redirect templates with the shared
renderAdminLayoutCatalyst template from @sonicjs-cms/core/templates.

Benefits:
- Eliminates code duplication (removed ~100 lines of duplicate layout code)
- Ensures consistent UI/UX across all admin pages
- Automatically includes standard admin features (mobile sidebar, user dropdown,
  migration banner, notifications, etc.)
- Easier to maintain - layout updates apply to all admin pages
- Preserves custom dialog backdrop styling via inline styles

The redirect admin pages now match the standard SonicJS admin design system
and will automatically benefit from future admin layout enhancements.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added "Redirects" menu item to both admin layout templates (Catalyst and v2).
Menu item appears between "Users" and "Plugins" in the sidebar, pointing to /admin/redirects.

Issue: PluginBuilder.addMenuItem() was being called correctly by the plugin,
but SonicJS core doesn't yet have a dynamic menu system that collects menu items
from plugins. As a workaround, manually added the menu item to the hardcoded
baseMenuItems arrays in both layout templates.

This enables users to access the redirect management UI from the admin sidebar.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add manifest.json with plugin metadata and permissions
- Define TypeScript interfaces for QR codes
- Include ErrorCorrectionLevel type and all CRUD interfaces
- Add color-validator for hex color validation and normalization
- Add url-validator for QR destination URL validation
- Add contrast-checker for WCAG contrast ratio calculation
- All utilities tested and working correctly
- Add plugin entry point with PluginBuilder
- Register lifecycle hooks (placeholder implementations)
- Add metadata for plugin registration
- Prepared for service registration in Plan 02
- Add qrcode-svg@^1.1.0 to dependencies
- Add @types/qrcode-svg@^1.1.5 to devDependencies
- Pure JavaScript QR library with no Canvas dependencies
- Compatible with Cloudflare Workers edge runtime

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Implement QRService class with CRUD operations (create, getById, list, update, delete)
- Add generate() method using qrcode-svg with 4-module quiet zone
- Integrate validation utilities (color, URL, contrast checking)
- Support all error correction levels (L, M, Q, H)
- Return both SVG string and data URL from generate()
- Create qr_codes table migration with soft delete support
- Add plugin settings management
- Add lifecycle methods (install, activate, deactivate, uninstall)
- 620 lines of comprehensive service implementation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Import QRService from services/qr.service
- Register service with builder.addService as singleton
- Update lifecycle hooks to use QRService methods
- Install hook creates QRService and calls install()
- Activate hook creates QRService and calls activate()
- Deactivate hook calls deactivate() and cleans up
- Uninstall hook calls uninstall() and cleans up
- Configure hook calls saveSettings()
- Export QRService and types for external use

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Install linkedom for SVG DOM manipulation
- Create svg-shapes.ts with path generators for all shape variants
- Export CornerShape (square, rounded, dots, extra-rounded)
- Export DotShape (square, rounded, dots, diamond)
- Export getPathGenerator helper function
- Add SvgCustomizer class for post-processing qrcode-svg output
- Implement eye region detection (7x7 at three corners)
- Support cornerShape, dotShape, and eyeColor options
- Use linkedom for SVG DOM parsing
- Handle 4-module quiet zone padding in position calculations
- Export customize() and generateCustomizedSvg() methods
- Add CornerShape type (square, rounded, dots, extra-rounded)
- Add DotShape type (square, rounded, dots, diamond)
- Add cornerShape, dotShape, eyeColor to QRCode interface
- Add shape fields to CreateQRCodeInput and UpdateQRCodeInput
- Add shape options to QRCodeGenerateOptions
- Add default shape settings to QRGeneratorSettings
- LogoEmbedder class with embed() method for SVG composition
- Enforces 25% max logo coverage (safe for scannability)
- Adds white padding (10% default) behind logo for visibility
- Preserves logo aspect ratio in positioning
- Helper methods: calculateAspectRatio, isValidLogoDataUrl, estimateLogoSize
- Convenience embedLogo function exported

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Migration 002_add_logo_and_shapes.sql:
- logo_url, logo_aspect_ratio, error_correction_before_logo columns
- corner_shape, dot_shape, eye_color columns
- Updates plugin version to 1.1.0

Types updates:
- QRCode interface: logoUrl, logoAspectRatio, errorCorrectionBeforeLogo
- CreateQRCodeInput: logoUrl, logoAspectRatio
- UpdateQRCodeInput: logoUrl, logoAspectRatio
- QRCodeGenerateOptions: logoUrl, logoAspectRatio

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ervice

QRService updates:
- Import and use SvgCustomizer for shape customization
- Import and use LogoEmbedder for logo embedding
- generate() uses join:false when shapes/eyeColor needed
- generate() forces Level H when logo is present (STYLE-04)
- create() stores error_correction_before_logo when logo added
- update() restores error correction when logo removed
- mapRowToQRCode includes all Phase 2 fields
- Updated SQL queries for Phase 2 columns

Requirements implemented:
- STYLE-03: Logo embedding via LogoEmbedder
- STYLE-04: Automatic Level H with logo, restoration on removal
- STYLE-05: Corner shape customization
- STYLE-06: Dot shape customization
- STYLE-07: Eye color customization

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add @cf-wasm/resvg for edge-compatible SVG-to-PNG conversion
- Create PngExporter class with export() method
- Support DPI options: 72 (web), 150 (screen), 300 (print)
- Support transparent background option
- Include size estimation and warning utilities
- Export convenience exportPng function
- Add DpiOption type (72 | 150 | 300)
- Add ExportFormat type ('svg' | 'png')
- Add PngExportOptions interface with dpi and transparent options
- Add PngExportResult interface with buffer, dimensions, size
- Update QRCodeGenerateOptions with exportFormat and pngOptions
- Update QRCodeGenerateResult with optional png field
- Import PngExporter and PNG export types
- Add private pngExporter instance
- Add generatePng() for direct PNG generation with DPI options
- Add generateForRecordAsPng() for PNG from stored QR code
- Add estimatePngSize() for size warnings in UI
- Support transparent background option
- Size warning for files over 5MB
- Add RedirectIntegration class for QR-redirect module communication
- getSourcePath() builds /qr/{shortCode} redirect paths
- getScanCount() queries redirect_analytics for scan tracking
- redirectExists() validates short code collision checking
- invalidateCache() wraps redirect cache invalidation
- Update create() to atomically insert QR code + redirect via D1 batch()
- Generate unique short code and create redirect with /qr/{code} path
- Update update() to atomically update redirect when destination changes
- Update delete() to atomically soft-delete QR code and redirect
- Add short_code to all SELECT queries and mapRowToQRCode
- Invalidate redirect cache after all redirect modifications
- Add RedirectIntegration instance for facade methods
- Create qr-redirect.ts with GET /qr/:code handler
- Return 302 redirect for active QR codes
- Return 410 Gone with branded expired page for deleted/inactive codes
- Register route in plugin index.ts with public access

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add optional scanCount field to QRCode interface
- Update list() to JOIN with redirects and redirect_analytics
- Update getById() to include scan count from analytics
- Map scan_count column to scanCount in mapRowToQRCode()

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tasks completed: 2/2
- QR redirect route handler with expired page
- Scan count integration in QR listing

SUMMARY: .planning/phases/03-redirect-integration/03-03-SUMMARY.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create routes/admin.ts with GET / handler for list page
- Add placeholder routes for /new, /:id/edit, POST /, PUT /:id
- Implement DELETE /:id handler for QR code deletion
- Register admin routes at /admin/qr-codes in index.ts
- Add QR Codes menu item with order 86 in admin sidebar

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create qr-list.template.ts with QRListPageData interface
- Render table with Preview, Name, URL, Scans, Created, Actions columns
- Add HTMX search input with debounced filtering (300ms delay)
- Add scan count badges with color coding (gray/blue/green/purple)
- Add relative time formatting for created dates
- Add delete confirmation dialog with scan count warning
- Add pagination controls for multi-page results
- Add empty state with contextual messaging

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create qr-preview.template.ts with renderQRPreview function
- Display QR code SVG in centered container with shadow
- Show short URL with copy-friendly code styling
- Add download buttons for SVG and PNG formats
- Include DPI selector for existing QR codes (72/150/300)
- Add renderQRPreviewLoading and renderQRPreviewError helpers
- Support exactOptionalPropertyTypes with explicit undefined types

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- GET /new renders create form with default settings and initial preview
- GET /:id/edit renders edit form with QR code data and current preview
- POST /preview returns preview partial HTML for HTMX updates
- Import renderQRFormPage and renderQRPreview from templates
ahaasco and others added 3 commits February 1, 2026 21:06
- POST / creates QR code via QRService.create() and redirects to list
- PUT /:id updates QR code via QRService.update() and redirects to list
- DELETE /:id soft-deletes QR code via QRService.delete() and returns JSON
- GET /:id/preview returns SVG thumbnail for list view
- GET /:id/download/png returns PNG file with specified DPI
- All routes have proper error handling and logging
Added QR Codes navigation item to both admin-layout-catalyst
and admin-layout-v2 templates, positioned after Redirects.
- Add custom settings page with file upload for default logo
- Fix SVG preview for eye color and shape customization
- Fix responsive preview sizing (280x280)
- Reduce logo size to 5% area coverage (~22% width)
- Encode short URL in QR codes for redirect tracking
- Pre-provision short codes for new QR codes
- Add redirect-management as plugin dependency
- Update plugin author to ahaas

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Pass defaultSettings to form template when creating new QR codes
- Form values now properly initialize from plugin settings
- Default logo from settings is pre-populated in the form
- Fix settings form to use proper <form> element for FormData
- Preview endpoint uses plugin settings for fallback defaults

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant