- /api/upload-image endpoint saves images and returns URL
- Paste images from clipboard directly into the editor
- Drag and drop images onto the editor
- Toolbar image button opens file picker for manual upload
- Uploaded images inserted as  Markdown at cursor position
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reusable MarkdownEditor component using EasyMDE:
- Toolbar: bold, italic, headings, quotes, lists, links, images,
tables, code blocks, preview, side-by-side, fullscreen
- Dark mode support with custom CSS overrides
- Value syncs via bind:value for form submission
- Applied to both wiki new and edit pages
- Replaces the plain textarea + manual preview toggle
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reusable TagInput component:
- Tags appear as blue chips instantly on comma/Enter press
- Remove tags by clicking X on the chip or Backspace when empty
- Autocomplete dropdown filters existing tags as you type
- All existing tags shown as clickable suggestions below
- Hidden input submits comma-separated value
- Applied to wiki new and edit pages
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents package-lock.json conflicts by resetting local changes
before pulling. Uses npm ci (clean install from lockfile) instead
of npm ci --production=false.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- wiki_categories, wiki_pages, wiki_tags, wiki_page_tags tables
- /wiki overview with category-grouped pages, tag sidebar, search
- /wiki/new create page with Markdown editor and live preview
- /wiki/[slug] view page with rendered Markdown, tags, edit/delete
- /wiki/[slug]/edit with pre-populated editor
- Tag input with existing tag suggestions (click to add)
- Category management (add/delete) in sidebar
- Tailwind Typography plugin for prose styling
- marked package for Markdown rendering
- Sidebar nav item with book icon
Run db:push on server to create wiki tables.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Caddy sets X-Forwarded-Proto: https on all routes, making SvelteKit
think the request is HTTPS. The session cookie got the Secure flag,
but the browser on http://100.81.174.129 won't send Secure cookies
over plain HTTP. Now checks the actual Origin header to determine
if the connection is truly HTTPS.
Tor works because .onion is treated as a secure context by Tor Browser.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SvelteKit's built-in CSRF only allows one origin, breaking access via
NetBird/Yggdrasil/Tor IPs. Now:
- Disabled checkOrigin in svelte.config.js
- Custom CSRF in hooks.server.ts checks Origin against ALLOWED_ORIGINS
- ALLOWED_ORIGINS env var: comma-separated list of trusted origins
- Caddy no longer needs to rewrite Host/Origin headers
- Each access method (public domain, NetBird IP, Yggdrasil, Tor onion)
just needs its URL added to ALLOWED_ORIGINS
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SvelteKit checks the browser's Origin header, not just Host or
X-Forwarded-Proto. Rewrite Origin to https://collection.newedge.house
so CSRF passes on all non-public routes (NetBird, Yggdrasil, Tor).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SvelteKit expects ORIGIN=https://... so all routes must send
X-Forwarded-Proto: https regardless of actual connection scheme.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rewritten for the actual architecture: separate upstream Caddy handles
TLS for public domain, LXC Caddy only does HTTP. Added NetBird
interface binding, explicit per-interface blocks, upstream Caddy
config snippet.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Caddyfile config with shared proxy snippet that sets Host header for
CSRF compatibility. Handles:
- Public domain with auto HTTPS (Let's Encrypt)
- LAN/internal on port 80
- Tor hidden service via localhost:8880
- Yggdrasil IPv6 on port 80
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ID and barcode on same row (flex horizontal, ID left, barcode right)
- 1mm gap between title and details section
- Detail text bumped to 6-6.5pt (from 5-5.5pt)
- Details grouped in their own block for consistent spacing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Barcode rendered without embedded text (includetext: false)
- ID rendered as separate HTML text at 9pt bold with letter-spacing
- All label text left-aligned instead of centred
- Barcode height reduced to 5mm to fit
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The custom @page margins conflicted with browser minimum margins,
causing overflow. Now @page margin is 0, barcode height 8mm→6mm,
scale 3→2, text size 12→10 so content fits within 29mm even with
browser minimum margins selected.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Set @page margins to top:0 right:0.5mm bottom:0 left:2mm matching
the working configuration found for DK-11209 labels. Updated setup
hint text on all print pages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All print labels (device, component, batch) now show centred text
with a Code 128 barcode at the bottom instead of a QR code on the
left. Layout uses vertical flex column for clean centring.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- feature_requests table with title, description, status, votes, createdBy
- Submit new requests with title and description
- Upvote button sorted by most votes
- Status dropdown: open, planned, in-progress, done, declined
- Color-coded status badges
- Delete button per request
- Sidebar nav item with lightbulb icon
Run db:push on the server to create the new table.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- All print labels (device, component, batch) now use JetBrains Mono
- Device labels show voltage and frequency below serial number
- Batch print labels include voltage/frequency from server data
- Font loaded from Google Fonts on print pages only
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- /batch-print page with device table, checkboxes, search, category filter
- Select all / individual selection with highlighted rows
- "Print N Labels" button opens popup with all selected labels
- /print/batch renders one DK-11209 label per device with page breaks
- Auto-print on popup open, last label avoids trailing blank page
- Sidebar nav item added
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- QR code on left encodes the short ID (not URL)
- Text centred on right: title, brand/model, serial, large bold ID
- ID displayed at 11pt bold with letter-spacing for readability
- Removed barcode dependency from print pages
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Reduced padding (1.5mm → 1mm), barcode height (7mm → 6mm)
- Tighter line heights, combined serial/voltage on one line
- Barcode text size 14 → 12 to reduce image height
- Vertical flex layout instead of horizontal (no QR = more space)
- Should now fit within 29mm height without overflow
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Component cards now display the first uploaded image as a thumbnail,
matching the device list card design. Falls back to a placeholder
icon when no image is uploaded.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reusable ImageLightbox component with dark overlay, close on
click/Escape, zoom-in cursor on thumbnails. Applied to both
device and component detail pages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Click chevron or checklist title to expand/collapse
- Completed checklists (100%) auto-collapse on page load
- Completed title shown in green with checkmark icon
- Progress bar and count always visible when collapsed
- Items and add-item form hidden when collapsed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New reusable components:
- ImageUpload.svelte: drag-and-drop / click / paste zone with file
icon, preview of selected filename, caption field, 50MB limit
- DocumentUpload.svelte: same pattern for documents with description
field instead of caption
Applied to both device and component detail pages, replacing the old
inline file input forms. Cleaner look matching modern upload UIs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shows number of non-done todos (todo + in_progress) next to the
Todos nav item. Hides when count is zero.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Image upload/delete on component detail page (same pattern as devices)
- Images section with grid, hover-to-delete, 50MB client-side limit
- Sidebar component count now excludes disabled components by joining
componentInstances with components and filtering disabled=false
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- disabled boolean on components table (default false)
- Component list filters out disabled components
- Component detail returns 404 for disabled
- Delete button with confirmation on component detail page
- Installation form filters out instances of disabled components
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Components are now split into two concepts:
- Component (type definition): title, componentType, brand, partNumber,
specs, notes, default condition/firmware/location
- Component Instance (individual physical unit): serialNumber, condition,
firmwareVersion, notes, currentDeviceId, locationId
Key changes:
- New component_instances table with per-unit tracking
- Component list shows cards with total/installed/available counts
- Component detail page shows all instances with inline edit
- Add instances: bulk (quantity) or one at a time
- Each instance has install/remove/edit/delete actions
- Installation log now references instances, not components
- Device detail shows installed instances with instance numbers
- Dashboard and sidebar counts use instance totals
- Location pages show instances, not component types
- Labels show component type info (serial is per-instance)
IMPORTANT: Run db:push on the server after deploy to create the new
tables and columns. Existing component data will need manual migration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Added BODY_SIZE_LIMIT=52428800 (50MB) to .env.example
- handleError in hooks catches body size exceeded and returns friendly message
- Client-side file size check on image upload input (alerts before submit)
- adapter-node uses BODY_SIZE_LIMIT env var (default was 512KB)
To fix: add BODY_SIZE_LIMIT=52428800 to .env on the server and restart.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
In production with adapter-node, the relative path 'static/uploads'
doesn't resolve correctly. Now uses UPLOAD_DIR env variable (absolute
path) for both saving and serving uploaded files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replaced all 'B4L Repair' references with 'My Collection'
- Added favicon.png next to the title in the sidebar
- Added favicon.png on the login page above the title
- Updated all page <title> tags
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- /checklists now shows templates in a responsive grid (cards with
title, description, item count, last updated)
- Clicking a card opens /checklists/[id] dedicated edit page
- "New Template" creates and redirects to the edit page
- Edit page has: rename title/description, add/edit/delete/reorder
items, delete template (redirects back to grid)
- Breadcrumb navigation back to grid
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Templates page (/checklists):
- Edit template name/description inline (pencil icon)
- Edit item text and unit inline (pencil icon on hover)
- Move items up/down with arrow buttons
- Reorder swaps sort order values in the database
Device checklists:
- Rename checklist title inline (pencil icon)
- Edit item text and unit inline (pencil icon on hover)
- Move items up/down with arrow buttons
- All item types (checkbox and input) support editing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
adapter-node only serves pre-built static assets, not files uploaded
at runtime. Added /uploads/[...path] catch-all route that reads from
static/uploads/ with proper MIME types and cache headers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Added HEIF, GIF, AVIF, BMP, TIFF to allowed image types
- All uploads converted to JPEG via sharp (fixes HEIC/HEIF from iPhones)
- Upload action wrapped in try/catch, errors shown in UI
- Error banner displayed above image section on failure
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- /locations/[id] shows all devices and stored components at a location
- Breadcrumb navigation with parent location
- Sub-location chips for navigating to children
- Location names are clickable links everywhere: location list page,
device detail sidebar, component detail current location
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Device/component detail pages show "Parent › Child" for locations
- Device list cards show full location path
- Location edit form now includes a Parent selector to move locations
between parents or make them top-level
- Prevents setting a location as its own parent
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LocationPicker component shows parent locations first. Once a parent
is selected, a second dropdown appears with its children. If the
parent has no children, it's selected directly. Used in device
create/edit, component create/edit, and installation log forms.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pencil icon on each location opens inline form to edit name and
description. Saves via rename action, cancel to discard.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Settings page shows account info and a change password form.
Validates current password, minimum 8 chars, confirmation match.
Added Settings link in sidebar.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two SSH keys needed:
- DEPLOY_KEY: CI runner → LXC server (SSH access)
- REPO_DEPLOY_KEY: LXC server → Gitea repo (git pull access)
Workflow writes the repo deploy key to ~/.ssh on the server and
configures SSH to use it for git.b4l.co.th. Handles first deploy
(clone) and subsequent deploys (pull) automatically.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>