Changelog

All notable changes to the Network Patch Mapper project will be documented in this file.

The format is based on Keep a Changelog,
and this project adheres to Semantic Versioning.

[Unreleased]

[2026-09-03]

Fixed

  • MDX build compatibility — Kept the @astrojs/mdx 8.0.0 upgrade on the current Astro dependency set and added a Vite alias for Astro 7.3.0's missing astro/_internal/logger export, while still using the compatible bundled @astrojs/markdown-satteri version needed to build .mdx pages.

[2026-09-01]

Changed

  • Dependencies — Updated all pnpm dependencies including better-auth (1.6.26 → 1.7.1), @awesome.me/webawesome-pro (3.11.0 → 3.12.0), nanostores (1.4.2 → 1.5.2), wrangler (4.120.0 → 4.125.0), astro (7.2.0 → 7.2.4), and various other patch and minor updates.

Added

  • DB migration 0005_account_issuer.sql — Adds the issuer column to the account table required by Better Auth 1.7. Backfills all existing credential (email/password) accounts with issuer = 'local:credential'. Better Auth 1.7 scopes account identity by a trusted issuer field; without this migration, existing users would be unable to sign in.

[2026-08-25]

Changed

  • 2FA code input — Replaced plain <input> elements in TwoFactorVerifyModal (both TOTP and backup codes) and TwoFactorSetupModal (step 4 verify code) with the <wa-otp-input> Web Awesome component. Backup codes use format="#####-#####" with type="alphanumeric" for a segmented entry experience. Also fixed backup code verification to call twoFactor.verifyBackupCode instead of verifyTotp.

Fixed

  • 2FA modal layout — Backup code <wa-otp-input> now uses size="xs" with explicit --segment-size: 2em and --segment-gap: 0.2em CSS variables, and ::part(segments) sets overflow: visible to prevent the last segment from being clipped.
  • 2FA OTP input style — Added wa-otp-input CSS ::part() styling to global.css so the OTP segments match the site's wa-input appearance: consistent borders, backgrounds, rounded corners, focus rings, and dark mode variants.
  • 2FA verify button — Fixed OTP input state tracking by using stable ref callbacks with wa-complete, wa-clear, and input events, ensuring the Verify button reliably enables once all segments are filled.
  • Backup code verification — Removed case="upper" from the backup code OTP input (Better Auth generates mixed-case codes). Also reinserts the dash separator before submission since wa-otp-input strips separators from its value property.

[2026-07-20]

Fixed

  • CSP inline script hash — Added new Astro 7.1.1-generated inline script hash (sha256-Ya0pUYrC7nM5Cn/056TyVuEiz6dFGrzmkWzgON0pF0U=) to the script-src directive in src/utils/csp.ts to resolve a Content-Security-Policy violation introduced by the Astro 7.0.7 → 7.1.1 update.

[2026-07-13]

Fixed

  • 2FA sign-in 500 error — Added failedVerificationCount and lockedUntil columns to the twoFactor database table (migrations/0004_twofactor_lockout.sql). Better Auth's twoFactor plugin now uses these columns for account lockout protection (tracking consecutive failed TOTP attempts). Without them, every correct TOTP code during sign-in resulted in a 500 error because Better Auth tried to UPDATE twoFactor SET failedVerificationCount = 0 after successful verification. Also updated migrations/0000_full_schema.sql for new installations.

  • CSP inline script hashes — Added two new Astro-generated inline script hashes (sha256-gV+0WW9V47UqmHoPAWTBo5ix/7APsHNqp+G5zHlvj9w= and sha256-JAnFZ+le3YgxnIcqb+wLBW0bqsS57bLsa2bmAoVFQgs=) to the script-src directive in src/utils/csp.ts to resolve Content-Security-Policy violations that were blocking scripts on the sign-in page.

[2026-06-23]

Changed

  • Dependencies — Upgraded core framework and tooling packages in package.json, including Astro 7 (astro, @astrojs/cloudflare, @astrojs/mdx, @astrojs/react) and related dependency updates (@cloudflare/workers-types, @xyflow/react, @typescript-eslint/eslint-plugin, @typescript-eslint/parser, globals).

  • Documentation tech versions — Refreshed README.md, agents.md, and .github/copilot-instructions.md to match the current stack and tooling versions, including Astro 7, Better Auth 1.6, Web Awesome Pro 3.9, React Flow 12.11, and the current ESLint plugin versions.

[2026-06-11]

Fixed

  • CI workflow pnpm auth and action versions — Updated .github/workflows/copilot-setup-steps.yml, .github/workflows/eslint-check.yml, .github/workflows/format-check.yml, .github/workflows/release.yml, and .github/workflows/commit-lint.yml to avoid pnpm 10.34.2 repository .npmrc variable expansion breakage by setting WEBAWESOME_NPM_TOKEN at job level and writing WebAwesome auth to ~/.npmrc before pnpm/action-setup. Also corrected invalid action versions (actions/checkout@v6/actions/setup-node@v6 → @v4) and standardized setup order to checkout → auth config → pnpm setup (v4, version 10) → node setup → install.

[2026-05-26]

Fixed

  • CSP inline script hash — Added Astro-generated inline script hash sha256-SaCkFfPruIdTXT8/97JArQmGxiJAL2o4bBDvSgJ5y3Q= to script-src in src/utils/csp.ts to resolve runtime Content-Security-Policy violations after dependency updates. Also updated tests/security/xss.test.ts hash assertions to include the latest Astro runtime hashes.

[2026-05-01]

Changed

  • Web Awesome size values updated — Migrated all WA component size attributes from the deprecated small/medium/large values to the new s/m/l shorthand introduced in Web Awesome 3.6. Updated wa-button, wa-input, wa-number-input, wa-textarea, wa-switch, wa-color-picker, and wa-select TypeScript type definitions in src/types/web-awesome.d.ts to include the full set of sizes (xs, s, m, l, xl) alongside the deprecated aliases for backwards compatibility.

[2026-04-27]

Fixed

  • CSP script-src hash for Astro 6.1.9 — Added missing inline script hash sha256-BrDhGE1lwa85arfXcrBxSo+n37uVSX5CAROXnIM6Q+g= to the script-src directive in src/utils/csp.ts. The hash was introduced by the Astro 6.1.9 dependency update and was blocking a framework-generated inline script at runtime.

[2026-04-21]

Fixed

  • ESLint react-hooks/immutability errors — eslint-plugin-react-hooks v7.1.1 introduced a new react-hooks/immutability rule that flags functions accessed in hooks before they are declared. Moved loadUsers in AdminPanel.tsx and handleTouchEnd in both Cabinet.tsx and CabinetList.tsx to be declared before the useEffect hooks that reference them.

[2026-04-15]

Fixed

  • 2FA TOTP verification 500 error — Added the missing verified column to the twoFactor table across all migrations. Better Auth's twoFactor plugin requires this column to track whether a user has completed the TOTP setup step. Without it, every correct TOTP code during sign-in triggered a failed UPDATE twoFactor SET verified = true, causing a 500 with a blank response body (shown in the UI as "Invalid verification code"). Incorrect codes still returned the expected 401 "Invalid code" error from Better Auth. Three changes were made: new migration 0003_twofactor_verified.sql (ALTER TABLE … ADD COLUMN) for existing databases, and the CREATE TABLE "twoFactor" statement updated in both 0000_full_schema.sql and 0002_admin_and_2fa.sql for new installations. The column defaults to 1 (true) to keep existing 2FA-enabled users verified.

[2026-04-07]

Added

  • FontAwesome icons hosted locally — SVG files are now served from public/assets/fontawesome/svgs/ and loaded via Web Awesome's setIconPath() API, eliminating the dependency on the FontAwesome CDN (ka-f.fontawesome.com, ka-p.fontawesome.com).

Changed

  • CSP — Removed ka-f.fontawesome.com and ka-p.fontawesome.com from style-src, img-src, font-src, and connect-src directives in src/utils/csp.ts since FontAwesome icons are now self-hosted.
  • CSP dev mode — CSP is now disabled in Vite dev mode (import.meta.env.DEV) in both src/middleware.ts and src/layouts/Layout.astro. Vite's HMR injects inline scripts without nonces that are blocked by the strict CSP, making the dev server unusable. CSP continues to be enforced in production builds.
  • Web Awesome — Updated to version 3.5.0.

Fixed

  • React hydration mismatch — MainContent now defers lineVisibility and viewMode store values until after mount. Both stores read from localStorage synchronously on the client, causing attribute mismatches (e.g., variant="primary" vs "neutral" on the lines-visibility button, or flow view vs devices view being rendered) between the server render and the first client render. Matches the mounted-guard pattern already in use by Header and NetworkFlowVisualization.
  • React hydration mismatch (Web Awesome elements) — Added suppressHydrationWarning to all <wa-icon> and <wa-button> elements in .tsx components. Web Awesome custom elements add default DOM attributes (aria-hidden, library, rotate, style, appearance, size) via their connectedCallback when they register, which races with React 18's concurrent hydration scheduler and causes mismatch warnings. Using suppressHydrationWarning tells React to skip attribute-level reconciliation on these elements; Web Awesome manages those attributes independently through its own Lit-based reactivity.

[2026-04-02]

Fixed

  • CSP inline script hash — Added new Astro-generated inline script hash (sha256-0I5yW+89TVLK5fxhuiluTUWRJwOp6LyBLVWznCz2+o0=) to the script-src CSP directive in src/utils/csp.ts, resolving Content-Security-Policy violations for a newly emitted Astro inline script.
  • picomatch ReDoS vulnerabilities — Updated the pnpm picomatch override from >=2.3.2 to >=4.0.4 to force all transitive resolutions (both 2.x and 4.x dependents) to the patched 4.0.4 release, resolving CVE-2026-33671 (High) and CVE-2026-33672 (Medium). picomatch 4.x is API-compatible with 2.x; the build and all tests continue to pass with only 4.0.4 in the dependency tree.

[2026-03-26]

Changed

  • SiteSelector - Migrated site creation from manual keydown + setTimeout workaround to native allow-create attribute and wa-create event on <wa-combobox> (requires Web Awesome Pro 3.4.0+)

Fixed

  • SiteSelector - Combobox input text is now cleared when the selected site is deleted, preventing stale site name from remaining in the input field

[2026-03-16]

Fixed

  • CSP inline script hash — Added new Astro-generated inline script hash (sha256-HpArP2A4Z5FQRezY979kSGc2G+bnDQ8kqb67BHTEJtU=) to the script-src CSP directive in src/utils/csp.ts, resolving Content-Security-Policy violations and the resulting React hydration error (#418) that appeared after the latest dependency update.
  • /api/data 500 resilience — Replaced the module-level Response constant in src/pages/api/data/index.ts with a factory function (dbNotConfigured()) so each request gets a fresh Response object, avoiding potential body-stream locking issues in the Cloudflare Workers workerd runtime when the DB binding is unavailable.
  • loadDataFromServer error handling — Refactored src/stores/authStore.ts to check response.ok before attempting to parse the JSON body. Error responses now use a nested try/catch so an empty or non-JSON body (e.g. a bare 500 from a Worker crash) produces a clear "Server error 500" message instead of a confusing SyntaxError.
  • Cloudflare Turnstile hydration mismatch (login & contact pages) — The SSR stub for @marsidev/react-turnstile returns null, but the real component renders a <div> on its very first client render. React 19's stricter hydration detected this server/client mismatch (error #418) on both the /login and /contact pages, corrupting the React component tree just as Turnstile was initialising and causing Turnstile error 600010. Turnstile's 8-second auto-retry then produced the "eventually succeeds after waiting" behaviour. Fixed in AuthForm.tsx (added mounted state) and ContactForm.tsx (reused existing isMounted state and removed the "Loading…" early-return) so that the Turnstile widget is only rendered after React hydration has completed — the first client render now matches the SSR output and error #418 is eliminated. Also added setError('') to handleTurnstileSuccess in both components so any error message set during a failed auto-retry is cleared when the challenge eventually succeeds.
  • Turnstile transient error UX (login & contact pages) — After the hydration fix, Cloudflare's challenge can still transiently fail (emitting DOMException: An exception was thrown from api.js in the browser console, corresponding to error 600010). The old handleTurnstileError called setError('Verification failed. Please try again.'), which showed a misleading permanent-looking red banner while Turnstile was already silently auto-retrying every 8 s, then the banner disappeared on its own when the retry succeeded — confusing UX. handleTurnstileError in both AuthForm.tsx and ContactForm.tsx now only resets turnstileToken to null (keeping the submit button disabled as expected feedback). The Turnstile widget renders its own retry/error visual state; no separate form-level error banner is needed for transient challenge failures. The Feature Policy: Skipping unsupported feature name "cross-origin-isolated" console warning is emitted by Cloudflare's own api.js and is not actionable from application code.
  • Turnstile PAT challenge fallback delay (login & contact pages) — On Firefox and Chrome, Cloudflare Turnstile attempts a Private Access Token (PAT) challenge on load. Because those browsers don't implement Apple's PAT attestation protocol, the challenge immediately returns HTTP 401. Turnstile then waits for retryInterval milliseconds before falling back to the visual/interactive challenge. The default retryInterval is 8000 ms, so the widget appeared to hang for up to 8 seconds — perceived by users as "intermittent" functioning. Reduced retryInterval to 2000 ms in both AuthForm.tsx and ContactForm.tsx so the fallback to the visual challenge is nearly immediate. Also added consistent theme: 'auto' to ContactForm.tsx's Turnstile widget (it previously passed no options at all).
  • Turnstile iframe feature permissions (login & contact pages) — Added a Permissions-Policy HTTP response header in src/middleware.ts that explicitly allows the Cloudflare challenge iframe to use autoplay and keyboard-map browser features. Without this, strict Firefox configurations (Enhanced Tracking Protection enabled) can silently block those features for cross-origin iframes, causing intermittent challenge failures. The cross-origin-isolated feature name seen in the browser console is a COOP/COEP concept, not a Permissions-Policy directive, and is intentionally excluded.

Changed

  • astro.config.mjs comment — Updated the explanation for why security.csp (Astro v6's stable CSP API, formerly experimental.csp in v5) is not used. The root cause is that Astro's build pipeline always hashes ISLAND_STYLES into style-src when a renderer (React) is present; per the CSP spec this makes browsers ignore 'unsafe-inline', which is required for React and Web Awesome inline style="" attributes. The manual CSP implementation in src/utils/csp.ts remains the correct approach.

[2026-03-12]

Added

  • pnpm deploy script — Added deploy npm script (astro build && wrangler deploy --config dist/server/wrangler.json) to package.json so deployment uses the adapter-generated Wrangler config (which includes the correct main entrypoint). This replaces the manual pnpm build && pnpm exec wrangler deploy workflow and avoids the missing-entrypoint issue when running wrangler with the root wrangler.jsonc.

Changed

  • commitlint: Added "Changes before error encountered" to the ignored commit message list alongside "Initial plan", allowing coding agents to use this as a checkpoint commit message when they encounter errors mid-work.
  • Docs: Updated README.md, agents.md, and .github/copilot-instructions.md to reflect the current Astro 6.x tech stack, the removal of Astro.locals.runtime.env in favour of import { env } from "cloudflare:workers", the updated pnpm preview command, Web Awesome Pro v3.3.1, and the presence of Vitest for tests.

Fixed

  • Unhandled rejection in waitUntil email send — Wrapped emailPromise passed to waitUntil with .catch() in src/pages/api/contact.ts so rejections from background email sends are caught and logged rather than becoming unhandled promise rejections.
  • Build failure after @astrojs/cloudflare v13 upgrade — Removed the "main" field from wrangler.jsonc. @astrojs/cloudflare v13 uses @cloudflare/vite-plugin internally, which validates that the main field points to an existing file before building; dist/_worker.js/index.js is a build artifact that doesn't exist yet. In v13 the adapter manages the entrypoint automatically.
  • Changelog page empty after @astrojs/cloudflare v13 upgrade — Changed src/pages/changelog.astro to use Vite's ?raw import query instead of readFileSync. The v13 adapter prerenders via a workerd-based environment where process.cwd() returns /, not the project root, causing the file read to fail.
  • wrangler dev entrypoint after @astrojs/cloudflare v13 upgrade — Updated the preview script to use the generated dist/server/wrangler.json (wrangler dev --config dist/server/wrangler.json) and updated assets.directory in wrangler.jsonc to ./dist/client to match the v13 adapter output structure.
  • Astro.locals.runtime.env removed in @astrojs/cloudflare v13 — Migrated all usages of the removed Astro.locals.runtime.env API to import { env } from "cloudflare:workers" as required by Cloudflare adapter v13. Updated src/middleware.ts, src/pages/api/auth/[...all].ts, src/pages/api/data/index.ts, src/pages/api/send-password-reset-email.ts, src/pages/api/contact.ts. Also updated src/env.d.ts to extend Cloudflare.Env for correct typing and removed the generic parameter from Runtime (no longer accepted in v13).
  • [object Object] on login and contact pages after v13 upgrade — import { env } from "cloudflare:workers" does not resolve correctly in .astro page frontmatter (only in .ts modules). The PUBLIC_TURNSTILE_SITE_KEY is now read in the middleware (which is a .ts module where cloudflare:workers works) and forwarded to pages via Astro.locals.turnstileSiteKey, matching the same pattern used for nonce and csp.
  • Dev server 500 / [object Object] on login and contact after v13 upgrade — import { env } from 'cloudflare:workers' in src/middleware.ts caused Vite's SSR dep optimizer to pre-bundle astro/virtual-modules/middleware.js and produce a cached file that no longer existed on disk, resulting in a 500 on every route in pnpm dev. Fixed by adding cloudflare:workers to vite.optimizeDeps.exclude in astro.config.mjs so Vite treats it as a virtual module rather than bundling it.
  • "Invalid hook call" React errors in dev server after v13 upgrade — Static import { Resend } from 'resend' in src/pages/api/contact.ts (and transitively via email.ts) caused Vite's SSR dep optimizer to discover resend lazily during the first request, triggering a module-runner reload that briefly created two React copies. Fixed by making createResendClient in src/lib/email.ts use a dynamic import('resend') so resend is never part of the static SSR module graph.
  • waitUntil TypeError after @astrojs/cloudflare v13 upgrade — Fixed TypeError: Cannot read properties of undefined (reading 'waitUntil') in the auth and contact API endpoints. In v13, locals.runtime.cfContext is no longer populated at runtime; the execution context's waitUntil must now be imported directly via import { waitUntil } from "cloudflare:workers". Updated src/pages/api/auth/[...all].ts and src/pages/api/contact.ts.
  • [object Object] rendering on SSR pages in workerd runtime — Middleware was constructing new Response(response.body, { headers: newHeaders }) to inject CSP header, but in workerd, response.body on streaming SSR responses returns a locked ReadableStream, causing workerd to stringify it to [object Object]. Fixed by using context.response.headers.set() to set the CSP header before calling next() instead of creating a new Response object. Added conditional check for context.response existence to handle both SSR requests (where it exists) and prerendering (where it doesn't). This follows the documented Astro pattern for setting response headers in middleware.
  • [object Object] on login and contact pages in Cloudflare Workers (workerd) — @marsidev/react-turnstile (used only on login and contact pages) was bundled into the SSR worker chunk (index_CPUld9CQ.mjs) by Astro 6's Cloudflare adapter, which bundles all imports into a single worker file including browser-only packages. The package references document, window, and MutationObserver in function bodies that workerd evaluates at module-init time, crashing the worker for those routes. Fixed by adding @marsidev/react-turnstile to the ssrBrowserOnlyStub Vite plugin in astro.config.mjs, which replaces the package with a no-op Turnstile component (return null) during SSR — matching the existing pattern for Web Awesome components. The real Turnstile widget continues to load from the client bundle after hydration.
  • [object Object] body on all SSR pages in workerd (nodejs_process_v2) — The nodejs_compat compatibility flag combined with a compatibility date of 2025-11-26 activates the Node.js process v2 shim in workerd, which breaks SSR response body streaming and causes the entire page body to be serialized as [object Object]. Fixed by adding disable_nodejs_process_v2 to compatibility_flags in wrangler.jsonc, reverting to the v1 process shim that does not exhibit this behavior.

[2026-03-11]

Added

  • CSP hash regression tests — Added a Vitest security test (tests/security/xss.test.ts) that asserts every expected Astro 6 inline script hash is present in the script-src CSP directive, so future Astro upgrades will surface CSP breakage at test time rather than silently in production.

Fixed

  • CSP inline script hashes — Added new Astro 6 inline script hashes (globalThis.process initialiser and Astro islands runtime) to the script-src CSP directive in src/utils/csp.ts, resolving Content-Security-Policy violations that blocked page functionality after the Astro 5 → 6 upgrade.
  • Cloudflare Workers deploy crash — Removed static import of spinner.js from Layout.astro's <script> block and consolidated the SSR stub into a single ssrBrowserOnlyStub Vite plugin in astro.config.mjs that stubs three categories of SSR-incompatible imports: (1) all @awesome.me/webawesome-pro/dist/components imports (custom elements that extend HTMLElement, which crashes Workers on startup); (2) @react-email/render, an optional peer dependency of resend@6.9.3 that is not installed — Wrangler's bundler tries to resolve this dynamic import at bundle time and fails with Could not resolve "@react-email/render"; (3) sharp, which is used at build time only for image compilation but calls detect-libc → process.report.getReport() at module initialization — an API not implemented in the Cloudflare Workers unenv shim — crashing the Worker on startup. Client builds are completely unaffected by any of these stubs.
  • Added spinner.js dynamic import to LoadingSpinner.tsx, ProfileForm.tsx, and AdminPanel.tsx, which all render <wa-spinner> but previously relied on the now-removed global spinner import in Layout.astro.

Changed

  • Astro 5 → 6 upgrade — Upgraded astro from ^5.x to ^6.0.1 and updated all Astro integrations to their compatible major versions:
    • @astrojs/react → ^5.0.0
    • @astrojs/cloudflare → ^12.6.13
    • @astrojs/mdx → ^4.3.14
    • @astrojs/sitemap → ^3.7.1
  • Dependency updates — Bumped supporting dependencies for compatibility and security:
    • @cloudflare/workers-types → ^4.20260310.1
    • @typescript-eslint/eslint-plugin and @typescript-eslint/parser → ^8.57.0
    • lint-staged → ^16.3.3
  • Added .wrangler/** to ESLint ignore list to prevent linting of Wrangler's temporary build artifact files generated during pnpm preview.

[2026-03-06]

Added

  • Analytics Page - New /analytics page with rich data visualisations for authenticated users
    • Auth-gated page following the same pattern as profile.astro and admin.astro
    • AnalyticsDashboard component reads from Nanostores and respects site filtering
    • Six chart sub-components using Web Awesome Pro chart elements (auto light/dark themed)
    • Port Status doughnut, Connection Types pie, Cabinet Capacity polar area
    • Port Utilisation bar chart and Connections Over Time line chart (last 30 days)
    • Cabinet Health Radar chart shown for networks with 5+ cabinets
    • Empty-state message with icon when no network data is present
    • Analytics link added to user dropdown in UserMenu.tsx
    • /analytics/ excluded from the sitemap
    • Added TypeScript JSX declarations for all new Web Awesome chart components

Fixed

  • Analytics Site Filter - Fixed site filter dropdown not updating chart data when selecting a site
    • Changed event handler to use e.currentTarget instead of e.target to correctly access the wa-select element's value
    • The change event's target may refer to internal elements within the web component, while currentTarget always refers to the element with the event listener

[2026-03-05]

Changed

  • Toast Notifications - Migrated from custom toast implementation to Web Awesome Pro's wa-toast component
    • Replaced custom Toast.tsx component with new ToastContainer.tsx using wa-toast element
    • Maintains same functionality with success, error, and info variants
    • Improved accessibility with Web Awesome's built-in toast patterns
    • Added custom styling for toast components in global.css following project's styling conventions
    • Fixed SSR hydration issues by loading component in useEffect with customElements.whenDefined()
    • Added wa-toast and wa-toast-item type declarations to web-awesome.d.ts
    • Added wa-toast and wa-toast-item to isolated components section in global.css
    • Implemented guard to verify element is upgraded before calling create() method

Fixed

  • Duplicate Toast Notifications - Fixed issue where "Data synced from cloud" toast appeared twice when returning to the tab
    • Added useRef guard in DataInitializer component to ensure data initialization only runs once per component lifecycle
    • Prevents duplicate sync toasts caused by Better Auth's useSession hook refetching session data when tab regains focus

[2026-03-04]

Added

  • agents.md - Added root-level agent instructions file for Claude coding agent, mirroring the project context already provided in .github/copilot-instructions.md

[2026-03-02]

Changed

  • Better Auth 1.5.0 Native D1 Support - Migrated from custom @nichoth/kysely-d1 adapter to Better Auth's built-in native Cloudflare D1 support
    • Removed @nichoth/kysely-d1 and kysely dependencies (no longer required)
    • Pass the D1 binding directly to Better Auth (database: db) — no custom adapter setup needed
    • Better Auth now handles D1 query execution, batch operations, and introspection natively

Security

  • Dependabot Vulnerability Fixes - Resolved 8 security vulnerabilities via pnpm.overrides:
    • hono upgraded from 4.11.4 to override range ^4.11.7 (currently resolved to 4.12.3 in pnpm-lock.yaml; resolves CVE-2026-24398, CVE-2026-24472, CVE-2026-24473, CVE-2026-24771 — IP spoofing, cache deception, arbitrary key read, and XSS in error boundary)
    • ajv (used by @commitlint/config-validator) upgraded from 8.17.1 to override range ^8.18.0 (see pnpm-lock.yaml for the exact resolved version; resolves CVE-2025-69873 — ReDoS via $data reference)
    • minimatch upgraded from 3.1.2 to override range ^3.1.4 (currently resolved to 3.1.5 in pnpm-lock.yaml; resolves 3 ReDoS CVEs — combinatorial backtracking via GLOBSTAR segments, nested *() extglobs, and repeated wildcards)

Fixed

  • ESLint accessibility warnings — Resolved all remaining lint warnings:
    • Changed resize handle from role="separator" to role="slider" (interactive role with ARIA value attributes), eliminating the need for the previous jsx-a11y/no-noninteractive-tabindex suppression comment and resolving the underlying accessibility lint warning
    • Added role="region" and aria-label to the cabinet sidebar container for proper landmark semantics
    • Added role="list" to the device grid container so role="listitem" children have the required parent context
    • Removed unused _error variable from catch clause in src/utils/export.ts

[2026-02-25]

Fixed

  • External Device Port Count - External connections now correctly count as 1 used port (source side only) instead of 2, fixing the available ports stat
  • Site-Filtered Stats - The statistics bar at the top of the page now filters counts based on the currently selected site
    • Cabinet, device, connection, and available port counts reflect only the items belonging to the selected site
    • When no site is selected, all items are counted as before

[2026-02-23]

Added

  • Site Filtering in Flow View - The React Flow visualization now filters cabinets and connections based on the selected site
    • When a site is selected in the sidebar, only cabinets belonging to that site are shown in the flow diagram
    • Connections are automatically filtered to only display edges between visible devices
    • External connections are hidden when their internal device belongs to a different site
    • Selecting no site (clearing the selector) shows all cabinets and connections as before
    • Assigning a site to a group now cascades the siteId to all descendant groups and all cabinets within those groups
    • Moving a cabinet into a group automatically assigns the group's site to that cabinet
  • Multi-site/Project Management - Organize cabinets and groups by sites or projects
    • Site selector using wa-combobox component with allow-custom-value attribute
    • Create new sites by typing custom values directly in the combobox
    • Filter cabinets and groups by selected site in the sidebar
    • Automatic site assignment when creating new cabinets and groups
    • Sites data included in export/import and cloud sync
    • CSS styling for wa-combobox component following the established ::part() pattern
    • Ability to move cabinets and groups between sites via add and edit modals
    • Site selector added to EditGroupModal and EditCabinetModal
    • Delete site functionality with ConfirmModal component
  • Comprehensive Input Sanitization - All user text inputs sanitized using escapeHtml() to prevent XSS attacks
    • Site Management - Site names with length validation (max 100 chars) and duplicate prevention
      • SiteSelector - Site name input with case-insensitive duplicate detection
    • Cabinet/Group Modals - Names and descriptions sanitized in Add/Edit modals
      • AddCabinetModal, EditCabinetModal - Cabinet name and description fields
      • AddGroupModal, EditGroupModal - Group name and description fields
    • Device Management - Device names and port labels sanitized
      • AddDeviceModal - Device name field during creation
      • Device component - Inline editing of device names and port labels
    • Connection Management - External device names, port labels, and connection labels sanitized
      • CreateConnectionModal - All text fields in create and edit flows
    • Admin Panel - Ban reason textarea sanitized before storage
    • Authentication - User names sanitized during signup and profile updates
      • AuthForm - Name field during user registration
      • ProfileForm - Name field during profile updates
      • Note: Passwords are NOT sanitized (hashed server-side instead)
      • Note: Numeric inputs (port counts, ban hours) are validated for range, not sanitized
    • Security Tests - Comprehensive test suite in tests/security/component-input-sanitization.test.ts (46 tests)
      • Tests all components including sites for proper input sanitization
      • XSS payload neutralization (script injection, event handlers, DOM-based XSS)
      • Real-world attack scenarios (reflected XSS, stored XSS, polyglot payloads)
      • Edge cases (empty strings, special characters, unicode preservation)
      • Site-specific validation (length limits, duplicate detection)
      • Context-specific XSS prevention (React rendering, localStorage, server transmission)
      • Total security test coverage: 190 tests across 6 test suites - all passing ✅

Fixed

  • Cabinet not cleared on site switch - Selecting a different site now clears the currently displayed cabinet so stale content from the previous site is no longer shown

  • Recursion in Delete Group - Replaced the recursive collectChildIds function in deleteGroup() with an iterative BFS traversal (same pattern as updateGroup), guarded by a visited set to prevent infinite loops from cyclic data in corrupted stores

  • Infinite loop in isDescendant - Added a visited set to the while loop in isDescendant() so cyclic parentGroupId data cannot cause a browser-freeze infinite loop

  • Stack overflow in renderGroup - Added an ancestors set to CabinetList's recursive renderGroup() function; if a group ID is already being rendered in the current path (due to cyclic data), the branch returns null instead of recursing infinitely

  • React Hydration Mismatch - Resolved hydration errors with Web Awesome components

    • Added suppressHydrationWarning to MainContent container div
    • Web Awesome components add default attributes during client-side hydration
    • Suppressing warnings for expected Web Awesome component behavior in SSR
  • Tailwind CSS Spacing - Added wa-combobox to Preflight isolation list to prevent dropdown spacing issues

  • CSP Violations - Added missing script hashes to Content Security Policy

    • Added two inline script hashes that were causing CSP errors
    • All inline scripts now properly authorized via hash allowlisting
  • Site Selector UI - Improved consistency and layout

    • Changed "Site / Project" header to "Site" to match "Cabinets" header style
    • Fixed combobox and delete button being cut off
    • Moved delete button to header row instead of inline with combobox
    • Delete button now appears to the right of "Site" heading when a site is selected
    • Site deletion now uses ConfirmModal component instead of browser confirm() dialog for consistency
    • Site deletion modal positioning - Fixed modal rendering within sidebar by using React portal to render at document body level, ensuring it overlays the entire page like other modals in the application
    • Layout shift prevention - Delete button now always reserves space in the layout using invisible class when not needed, preventing text misalignment and layout shifts when button appears

[2026-02-12]

Added

  • File Upload Security Tests (45 tests) - Comprehensive validation of JSON import functionality

    • Valid data structure validation (3 tests)
    • Invalid JSON format detection (4 tests)
    • Cabinet structure validation (6 tests)
    • Device validation with type checking (2 tests)
    • Port validation with status enum checking (3 tests)
    • Connection validation with type enum checking (3 tests)
    • XSS and SQL injection payload handling (3 tests)
    • JSON bomb protection for large/nested structures (3 tests)
    • Group validation with optional fields (4 tests)
    • Type safety enforcement for all fields (3 tests)
    • Error handling and validation failures (3 tests)
    • Security test coverage for JSON import validation
  • Comprehensive security testing suite now with 144 tests across 5 domains

    • SQL Injection Tests (12 tests): Validates parameterized queries protect against 24 attack vectors
    • XSS Prevention Tests (29 tests): Validates HTML escaping using production function, CSP with production generator, React protection, URL sanitization
    • Authorization Tests (25 tests): Validates authentication, user data isolation, RBAC, session management
    • Input Validation Tests (33 tests): Validates email/string/type validation, rate limiting concepts, resource protection
    • File Upload Tests (45 tests): Validates JSON structure, type safety, XSS/SQL injection handling, large file support
    • Security test coverage expanded across all critical domains
    • Added test scripts: test, test:watch, test:ui, test:security
    • Updated SECURITY_ASSESSMENT.md documenting comprehensive findings
    • Test results: 144/144 tests passing
  • Vitest testing framework

    • Configured in vitest.config.ts with proper path aliases
    • Test coverage support with v8 provider
    • Tests located in tests/security/ directory
  • Security test suites:

    • tests/security/sql-injection.test.ts - SQL injection vulnerability testing
    • tests/security/xss.test.ts - Cross-site scripting prevention testing
    • tests/security/authorization.test.ts - Authentication and access control testing
    • tests/security/input-validation.test.ts - Input validation and rate limiting testing
    • tests/security/file-upload.test.ts - File upload security testing

[2026-02-06]

Changed

  • Replaced <wa-input type="number"> with proper <wa-number-input> Web Awesome component

    • AddDeviceModal.tsx: Replaced port count input with <wa-number-input>
      • Changed string attributes (min="1", max="96") to numeric props (min={1}, max={96})
      • Added step={1} for proper number input behavior
      • Removed type="number" attribute (not needed with dedicated component)
    • AdminPanel.tsx: Replaced custom ban hours input with <wa-number-input>
      • Changed string attributes to numeric props for min and step
      • Improved accessibility and UX with native number input controls
    • Device.tsx: Replaced edit port count input with <wa-number-input>
      • Refactored modal to use shared Modal component from @components/Modal
      • Improved modal structure and accessibility
      • Changed string attributes to numeric props
    • src/styles/global.css: Added comprehensive wa-number-input styling with ::part() selectors
      • Light mode styling for ::part(base) and ::part(input)
      • Dark mode variants with .dark prefix
      • Focus states matching wa-input for consistency
      • Added reference implementation comments documenting the styling pattern
      • Added component to Tailwind CSS reset exclusion list
    • src/types/web-awesome.d.ts: Added TypeScript definitions for <wa-number-input> component
      • Complete prop types including min, max, step, and number-specific attributes
      • Event handler types for React synthetic events and Web Awesome custom events
  • Enhanced Web Awesome component styling documentation

    • Added comprehensive "Web Awesome Component Styling Rules" section (~160 lines) to .github/copilot-instructions.md
    • Documented critical rules: ALWAYS use CSS ::part() selectors, NEVER use component attributes like appearance or filled
    • Added component parts reference (base, input, button, etc.) for common Web Awesome components
    • Provided step-by-step workflow for styling new Web Awesome components
    • Included debugging checklist for styling issues
    • Added detailed comment blocks in src/styles/global.css explaining ::part() pattern
    • Documented why ::part() is required (Shadow DOM encapsulation)
    • Listed common parts for form components (base, input, textarea, etc.)
    • Marked wa-input and wa-number-input as reference implementations for proper styling pattern

[2026-02-05]

Changed

  • Updated copilot instructions to reference Web Awesome LLM resources
    • Added documentation for node_modules/@awesome.me/webawesome-pro/dist/llms.txt
    • Added documentation for node_modules/@awesome.me/webawesome-pro/dist/skills/webawesome/ (agent skills and reference guides)
    • Added comprehensive Web Awesome UI Components section with usage examples and component categories
    • Improved guidance for working with Web Awesome components in Astro and React

[2026-01-22]

Added

  • ESLint analysis report documenting all 90 warnings across 23 files
    • Categorized warnings by difficulty: Easy (24), Medium (49), Moderate to Complex (17)
    • Grouped by type: Accessibility (66), TypeScript (17), React (7)
    • Provided fix strategies and priority recommendations
    • Estimated effort: 7-12 hours total to resolve all warnings
    • Created ESLINT_ANALYSIS.md with comprehensive overview
  • Reusable Modal component for consistent modal behavior across the application
    • Centralized keyboard event handling (Escape key to close)
    • Centralized click-outside-to-close functionality
    • Configurable options for special cases (e.g., 2FA modals)
    • Proper ARIA attributes for accessibility

Fixed

  • ESLint warnings reduced from 90 to 4 through systematic fixes

    • Removed 13 unused variables and imports across 8 files
      • CookieConsent.tsx: Removed 6 unused error variables in catch blocks
      • Device.tsx: Removed unused addConnection import
      • NetworkFlowVisualization.tsx: Removed unused Connection type import
      • Toast.tsx: Removed unused useEffect import
      • contact.ts: Removed unused RequestWithWaitUntil interface
      • networkStore.ts: Removed unused isAuthenticated import
      • auth.ts: Renamed unused otp parameter to _otp following convention
      • export.ts: Renamed unused error parameter to _error following convention
    • Fixed 7 unescaped apostrophes with HTML entities (&apos;)
      • AuthForm.tsx: Fixed password reset message
      • ProfileForm.tsx: Fixed 3 instances in email and 2FA descriptions
      • TwoFactorSetupModal.tsx: Fixed 2 instances in setup instructions
    • Replaced 6 explicit any types with proper TypeScript types
      • ConnectionLines.tsx: Changed debounce function args to unknown[] for better reusability
      • env.d.ts: Replaced 4 any types with Record<string, unknown> for PostHog config
      • auth.ts: Fixed plugins array type to use correct union syntax: Array<ReturnType<typeof admin> | ReturnType<typeof twoFactor> | ReturnType<typeof captcha>>
      • export.ts: Changed validateImportData to use type guard pattern with unknown
    • Fixed 30 modal backdrop accessibility warnings by creating reusable Modal component
      • All 9 modal components refactored to use shared Modal component
      • Added proper keyboard event handlers (Escape key) with configurable behavior
      • Added proper ARIA roles and attributes (role="dialog", aria-modal, aria-labelledby)
      • Modal backdrop made non-interactive (removed incorrect role="button" and tabIndex)
      • Removed redundant keyboard handler from backdrop
      • Modals: AddCabinetModal, AddDeviceModal, AddGroupModal, ConfirmModal, CreateConnectionModal, EditCabinetModal, EditGroupModal, TwoFactorSetupModal, TwoFactorVerifyModal
    • ConfirmModal prevents accidental closes during loading operations
      • Added closeOnClickOutside={!isLoading} and closeOnEscape={!isLoading} to prevent cancellation of in-progress operations
    • Fixed 13 interactive element accessibility warnings by adding keyboard support
      • UserMenu: Added keyboard handler (Escape/Enter/Space) to backdrop
      • CabinetList: Added keyboard support to drag handles and mobile overlay
      • Device: Added keyboard support to editable name, port count, and port labels
      • Device: Changed h3 and p elements to divs to allow interactive roles
      • Cabinet: Added role="listitem" to draggable device containers
      • Modal: Added ESLint suppressions for intentional backdrop behavior
    • Fixed 7 label association warnings in CreateConnectionModal
      • Changed <label> elements to <div> elements for display-only fields
      • Proper semantic HTML usage: labels only used with actual form controls
      • Improved accessibility by using appropriate HTML elements
    • Kept autofocus behavior on 2FA input fields (suppressed 2 ESLint warnings with justification)
      • TwoFactorSetupModal and TwoFactorVerifyModal: Input fields auto-focus for better UX
      • Users expect to immediately type their 2FA code without clicking
      • Intentional UX decision to reduce friction during authentication
      • Added ESLint disable comments with clear justification
    • Fixed React Hook dependency warnings (2 warnings)
      • Cabinet.tsx and CabinetList.tsx: Added ESLint suppressions for touch event handlers
      • Touch handlers intentionally stable to avoid re-registering event listeners
      • Prevents performance issues from frequent listener registration/deregistration
    • Suppressed 6 intentional accessibility patterns with clear justifications
      • Cabinet.tsx: Touch handlers on list items for mobile drag-and-drop
      • CabinetList.tsx: Drag-and-drop root target and keyboard-accessible resize handle
      • Device.tsx: Modal backdrop click handler with keyboard alternative
      • Modal.tsx: Backdrop click-to-close with Escape key handling in useEffect
      • All suppressions include detailed comments explaining intentional design decisions
    • Fixed modal z-index stacking issues (modals appearing behind other elements)
      • All modals now use React's createPortal to render at document.body level
      • Added SSR safety checks (typeof document !== 'undefined')
      • Fixed 8 modals across Device, Cabinet, and CabinetList components
      • Modals now properly appear above all page content with z-index 9999
      • Prevents modals from being trapped in parent component stacking contexts
  • Modal close button now uses Web Awesome wa-button component with appearance="plain" for consistency

  • Connection type buttons in CreateConnectionModal now display cursor pointer and change border color on hover to better indicate interactivity

Changed

  • Removed connection labels from device/cabinet view to reduce confusion
    • Connection labels removed from ConnectionLines.tsx SVG overlay
    • Labels remain in flow view (NetworkFlowVisualization.tsx) where context is clearer
    • Device view now shows only colored connection lines for cleaner visualization
  • Moved charset declaration out of the AstroSEO section to the start of the head in layout.astro

[2026-01-21]

Fixed

  • Prevented /api/data API calls for unauthenticated users to eliminate 401 console errors
    • DataInitializer now checks authentication state before attempting to load data from server
    • Users who are not logged in no longer trigger unnecessary API requests
    • Fixed issue where page content (main content, footer, dev warning banner) would not load for unauthenticated users
    • DataInitializer now properly sets isInitializing to false for unauthenticated users

[2026-01-20]

Changed

  • Aligned loading UI across profile and admin panel pages to match main page
    • ProfileForm now displays full-screen loading overlay with spinner and "Loading..." message
    • AdminPanel now displays full-screen loading overlay with spinner and "Loading..." message
    • Improved consistency of loading experience across the application

Fixed

  • CSP violations on local dev server - Added missing script hashes for Astro dev mode inline scripts to allow them to execute without CSP errors

[2026-01-19]

Added

  • Automated versioning and release management with semantic-release
    • Installed semantic-release v23 with GitHub and changelog plugins
    • Configured to analyze commits and automatically bump versions
    • Generates release notes and GitHub releases
    • Updates CHANGELOG.md automatically
    • Commits version bumps with chore(release): X.Y.Z [skip ci] format
  • Commit message validation with commitlint
    • Installed commitlint v19 with conventional config
    • Enforces Conventional Commits format on all commits
    • Pre-commit hook validates commit messages via Husky
    • CI workflow validates all PR commits
    • Supports types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
    • Configured header-max-length to 200 characters (increased from default 100)
  • Commitizen integration for interactive commit creation
    • Added pnpm commit script for guided commit message creation
    • Uses cz-conventional-changelog adapter
    • Helps developers write properly formatted commits
  • GitHub Actions workflows for release automation
    • commit-lint.yml - Validates PR commits against conventional format with WEBAWESOME_NPM_TOKEN
    • release.yml - Runs semantic-release on main branch pushes with WEBAWESOME_NPM_TOKEN
    • Updated trivy-scan.yml to track commit-lint workflow status in CI/CD report
  • CONTRIBUTING.md guide with comprehensive contribution guidelines
    • Development workflow and branch naming conventions
    • Commit message format with detailed examples (200 character subject limit)
    • Code quality standards and tooling
    • Dependency management with Depfu configuration prefix
    • Troubleshooting section for commit lint failures with fix instructions
    • Pull request process and review requirements
    • Automated release process explanation
  • Updated copilot-instructions.md with commit conventions section
    • Conventional Commits format reference
    • Commit types and their semantic versioning impact
    • Scope guidelines for better commit organization
    • Interactive helper usage instructions
    • Automated release workflow explanation

Fixed

  • Added WEBAWESOME_NPM_TOKEN environment variable to commit-lint and release workflows to fix 404 errors when installing private dependencies

  • Semantic-release workflow conflicts with update-changelog workflow

    • Removed @semantic-release/changelog plugin from .releaserc.json to prevent CHANGELOG.md conflicts
    • Removed @semantic-release/npm plugin from .releaserc.json (not needed - no npm publishing)
    • Removed @semantic-release/git plugin from .releaserc.json to prevent repository file modifications
    • Removed @semantic-release/changelog and @semantic-release/git packages from devDependencies
    • Simplified .releaserc.json to only create GitHub releases with version tags
    • Eliminates "branch behind" errors caused by race conditions between workflows
    • Updated CONTRIBUTING.md to reflect that semantic-release no longer updates CHANGELOG.md
  • Race condition between changelog and release workflows causing semantic-release to fail with "local branch main is behind" error

    • Added git pull step in release workflow to synchronize with changelog commits before running semantic-release
    • Ensures release workflow operates on the latest commit even if changelog workflow commits while release is running

Changed

  • Disabled commitlint subject-case rule to allow capital letters in commit messages

    • Developers can now use proper capitalization for acronyms (API, SQL, HTTP, etc.)
    • Proper nouns and technical terms (GitHub, TypeScript, React) are now allowed
    • Updated CONTRIBUTING.md with flexible capitalization guidelines
    • Maintains conventional commit format while allowing natural language
    • Added exception for "Initial plan" commit message pattern
  • Updated @commitlint/cli from ^19.8.1 to ^20.3.1

  • Updated @commitlint/config-conventional from ^19.8.1 to ^20.3.1

  • Updated semantic-release from ^23.1.1 to ^25.0.2

  • Updated Node.js requirement from v22 to v24 (LTS) across all CI workflows

  • Updated documentation to reflect Node.js v24 (LTS) requirement

[2026-01-16]

Added

  • Integrated ESLint with Astro plugin support for improved code quality and consistency
    • Installed ESLint v9 with flat config format
    • Added TypeScript ESLint plugin for TypeScript-specific linting rules
    • Added React plugins (eslint-plugin-react, eslint-plugin-react-hooks) with React 19 compatibility
    • Added JSX accessibility plugin (eslint-plugin-jsx-a11y) for accessibility best practices
    • Added eslint-config-prettier to ensure ESLint and Prettier work together without conflicts
    • Configured global variables for browser and Node.js environments
    • Added lint and lint:fix npm scripts
    • Updated lint-staged to run ESLint before Prettier on staged files
    • Added ESLint GitHub Actions workflow for automated CI/CD checks on pull requests
  • Added Trivy security scanning workflow for automated vulnerability detection
    • Scans for vulnerabilities in dependencies, secrets, and misconfigurations
    • Runs on pull requests and main branch pushes
    • Posts consolidated CI/CD status report as PR comment showing Format Check, ESLint Check, and Trivy scan results
    • Includes detailed vulnerability listings with CVE IDs, package names, and descriptions
    • Comment automatically updates when results change
    • Fails build on CRITICAL and HIGH severity issues

[2026-01-15]

Added

  • Implemented Content Security Policy (CSP) using meta tags for primary enforcement, with Cloudflare Workers middleware setting equivalent HTTP headers as a fallback
    • Uses nonce-based protection for inline scripts (dark mode toggle, PostHog analytics)
    • Allows 'unsafe-inline' for styles to support React component inline style attributes
    • Properly configured for all external resources (PostHog, Cloudflare Turnstile, Web Awesome)
    • Includes CSP directives and security headers (via both meta tags and HTTP headers): object-src 'none', base-uri 'self', form-action 'self', frame-ancestors 'none', upgrade-insecure-requests

Changed

  • Migrated from Astro's experimental CSP to middleware-based CSP for better control
    • Astro's CSP automatically generates hashes for <style> tags, which causes browsers to ignore 'unsafe-inline' per CSP specification
    • Middleware-based CSP allows proper handling of inline style attributes used by React components
  • Updated Layout.astro to use nonce for inline scripts
  • Updated posthog.astro to accept and use nonce prop

Removed

  • Removed experimental Content Security Policy (CSP) configuration from astro.config.mjs

[2026-01-13]

Added

  • Enabled Astro's experimental Content Security Policy (CSP) feature to enhance security against XSS and code injection attacks
  • Configured CSP directives for external resources including PostHog analytics and Cloudflare Turnstile
  • Added SHA-256 hashes for inline scripts (dark mode toggle and PostHog tracking) to allow them under CSP
  • Added style-src 'unsafe-inline' directive to support React component inline styles

Fixed

  • Fixed CSP blocking Web Awesome icons by adding ka-f.fontawesome.com and ka-p.fontawesome.com to both img-src and connect-src directives (icons are loaded via XHR/fetch)
  • Fixed CSP blocking PostHog analytics scripts by adding both https://ph.network-patch-mapper.com and https://ph-assets.i.posthog.com to script-src resources
  • Fixed CSP blocking Cloudflare Turnstile by adding https://challenges.cloudflare.com to script-src resources

[2026-01-12]

Added

  • Added loading spinner (wa-spinner) displayed during app initialization to prevent layout shifts and improve user experience

  • Created LoadingSpinner component that shows a centered spinner with "Loading..." text while data is being initialized

  • Added isInitializing state atom to networkStore to track data initialization status

  • Added support for patch panel internal connections - patch panels can now connect ports to other ports on the same device (e.g., Port 5 to Port 6 on Patch 1)

  • Patch panel ports can now be connected to any port on the same device (except the same port) via the connection modal

Changed

  • Updated initializeData function to properly set loading state before and after data initialization

  • Modified main index page to include the LoadingSpinner component

  • Updated wa-spinner component to use font-size for sizing instead of incorrect size attribute (follows Web Awesome documentation)

  • Increased loading text size from text-sm to text-lg for better readability

  • Changed spinner color customization to use CSS custom properties --indicator-color and --track-color instead of Tailwind text color classes (follows Web Awesome documentation)

  • Moved spinner component import to Layout.astro head to ensure it loads immediately when the page loads, improving visibility during initialization

  • Updated existing wa-spinner components in UserMenu, AdminPanel, and ProfileForm to remove deprecated size attribute and use font-size classes instead

  • Updated all spinner components (LoadingSpinner, AdminPanel, ProfileForm) to use CSS variables (var(--color-primary-600) and color-mix()) for colors, ensuring consistency across the application

  • Updated connection modal to include the source device in available devices when creating internal connections from a patch panel

  • Modified port selection logic to exclude the source port when connecting to the same device (prevents a port from connecting to itself)

  • Switches continue to be restricted from connecting to ports on the same device (unchanged behavior)

  • All port connections now use the connection modal for consistency

Fixed

  • Removed incorrect size attribute from wa-spinner TypeScript definitions - spinner sizing is controlled via font-size as per Web Awesome documentation

[2026-01-05]

Added

  • Created centralized uiStore for managing UI preferences like sidebar width

Changed

  • Sidebar width changes are now persisted immediately to localStorage and synced to cloud (with 1-second debounce) for authenticated users

Removed

  • Removed legacy standalone sidebarWidth localStorage key in favor of centralized NetworkData storage

Fixed

  • Fixed sidebar width not persisting across page refreshes for both authenticated and non-authenticated users
  • Sidebar width is now properly synced to the D1 database for authenticated users
  • Resolved React rendering issue where inline width style wasn't being applied to the sidebar DOM element
  • Fixed sidebar width responsiveness on mobile devices - sidebar now uses a fixed 256px width on mobile (< 768px) to prevent overflow and usability issues, while maintaining variable width on desktop

[2025-12-29]

Fixed

  • Fixed password manager autofill issue where credentials autofilled by password managers (like Bitwarden) were not being read correctly, causing login failures
  • Forms now read values directly from DOM elements at submission time to handle password manager autofill that doesn't trigger input events
  • Applied fix to AuthForm, ResetPasswordForm, ProfileForm, and ContactForm components
  • Added autocomplete, name attributes to form inputs to help password managers properly identify and autofill email/username and password fields
    • Email fields now use autocomplete="username" to help password managers recognize them
    • Password fields use autocomplete="current-password" for login and autocomplete="new-password" for signup/password change
    • All form inputs now have appropriate name attributes

[2025-12-24]

Fixed

  • Port, view toggle and label edit buttons now have pointer cursor to make it easier to recognise them as buttons

[2025-12-23]

Changed

  • Updated device action buttons (edit, rename, delete) to use Web Awesome wa-button components with appearance="plain" for consistent styling
  • Device action buttons now use button-no-hover attribute to prevent background color changes on hover
  • Added custom CSS styling for device buttons with proper hover states and dark mode support
  • Device buttons now styled consistently with cabinet, group, and admin panel buttons
  • Reduced CSS duplication by consolidating repeated styles:
    • Consolidated button sizing (24x24px for cabinet/group/device buttons, 40x40px for admin buttons) into shared rules
    • Consolidated button variant base styles (border-radius, padding, font-weight, transition, border) for brand/danger/neutral variants
    • Consolidated header button hover states (dark mode toggle, user menu, profile settings)
    • Consolidated form control label styling across inputs, textareas, and selects
  • Updated admin panel action buttons to use Web Awesome icon buttons with appearance="plain" for consistent styling
  • Admin action buttons (change role, ban/unban, delete) now styled consistently with header, user menu, and cabinet/group buttons
  • Added custom CSS styling for admin action buttons with proper hover states and dark mode support
  • Replaced all Font Awesome icons with Web Awesome wa-icon components throughout the application
  • Removed Font Awesome CDN script from Layout component
  • Updated icon names to use Web Awesome icon names (e.g., fa-times → xmark, fa-arrow-left → arrow-left)
  • Updated README.md to remove Font Awesome from tech stack
  • Updated header buttons to use wa-button in dark mode and user menu
  • Updated cookie consent banner button text from "Continue without cookies"/"Accept cookies" to "Necessary cookies only"/"Accept All cookies"
  • Enhanced cookie consent banner message to explain that cookies are used for essential site functionality including authentication when creating or logging into an account

Fixed

  • Changelog workflow now properly merges duplicate subsection headers (e.g., ### Changed) when multiple PRs are merged on the same day, preventing duplicate sections in the changelog

[2025-12-22]

Added

  • Group assignment dropdown to AddCabinetModal allowing cabinets to be added to existing groups
  • Input length validation with maxlength attributes across multiple modal text inputs and textareas
  • Touch-based drag and drop support for mobile devices
    • Added touch event handlers for device reordering within cabinets
    • Added touch event handlers for cabinet drag and drop in sidebar
    • Added touch event handlers for group drag and drop in sidebar
    • Created touchDrag.ts utility module with reusable touch drag functions
  • Group selection dropdown in Edit Cabinet modal
    • Added ability to move cabinets to different groups when editing
    • Added "None" option to move cabinets to root level

Changed

  • Refactored CabinetList component to use Web Awesome wa-button, wa-icon, and wa-button-group components instead of standard HTML button elements
  • Updated cabinet and group action buttons (edit, delete, add) to use Web Awesome components with custom styling
  • Improved button attribute naming for group action buttons (button-edit-group, button-delete-group)
  • Updated dependencies: @cloudflare/workers-types to 4.20241218.0 and wrangler to 3.100.0
  • Updated group selection implementation in Edit Cabinet modal to match Add Cabinet modal pattern
    • Made wa-select uncontrolled by removing value prop to allow Web Awesome to manage its own state
    • Read group value directly from element ref at submit time instead of using React state
    • Changed clearable attribute to with-clear for proper Web Awesome syntax
    • Improved visual indication of nested groups with '├─' prefix
  • Updated .github/copilot-instructions.md to document Prettier formatting configuration, EditorConfig settings, Husky/lint-staged pre-commit hooks, and CI workflows introduced in PR #118
  • Improved port click behavior in Device component: clicking a different port on the same device now opens the connection modal instead of showing an error toast

Fixed

  • Fixed z-index issue for mobile sidebar
  • Fixed mobile logo size
  • Mobile drag and drop functionality for devices, cabinets, and groups
    • Devices can now be reordered within cabinets on touchscreen devices
    • Cabinets can now be moved between groups on touchscreen devices
    • Groups can now be reorganized on touchscreen devices
    • Added global touchend/touchcancel listeners to handle edge cases when touch ends outside draggable elements
  • Page scrolling during mobile drag operations
    • Added preventDefault() in touch start handlers to prevent scrolling when dragging
    • Added touch-action: none CSS to grip handle elements for complete scroll prevention
    • Prevents browser's default scroll behavior from interfering with drag gestures
  • Group selection in Edit Cabinet modal now properly updates cabinet's group assignment
    • Always initializes select element value, even for cabinets without a group
    • Removed duplicate toast message (updateCabinet already shows one)
  • Delete device confirmation modal z-index stacking
    • Wrapped ConfirmModal with createPortal to render at document body level
    • Modal now appears above sidebar and stats bar instead of being hidden beneath them
  • Code quality improvements
    • Removed redundant pointer-events setting in touchDrag utility
    • Fixed edge case where ghost elements could remain on screen if touch ended outside draggable area

Removed

  • Removed obsolete "Cannot connect ports on the same device" error toast that was replaced by connection modal validation

[2025-12-19]

Added

  • Comprehensive SEO metadata including canonical URLs, charset, and image alt text for better search engine optimization
  • Twitter Card metadata with card type, title, description, image, and image alt text for enhanced social media sharing
  • Open Graph metadata with proper URL, siteName, locale, and image alt text for better social media previews
  • Description, title, and image props to Layout component for per-page SEO customization
  • Keywords meta tag with relevant terms for improved search engine discoverability
  • Author and robots meta tags for better search engine crawling
  • Theme color meta tags that adapt to light and dark mode preferences

Changed

  • Improved default meta description to be more descriptive and keyword-rich
  • Enhanced Open Graph configuration with required URL field and image alt text
  • Added siteName property to Open Graph metadata for better brand recognition
  • Updated all wa-button components to use the loading attribute instead of custom loading text with spinner icons for consistent button behavior
  • Replaced Font Awesome spinner icons (fa-spinner) with Web Awesome wa-spinner component in the UserMenu component
  • Standardized loading states across all forms (AuthForm, ContactForm, ResetPasswordForm, ProfileForm, TwoFactorSetupModal, TwoFactorVerifyModal, ConfirmModal)
  • Refactored WebAwesome button custom styling to use opt-in data-custom-style attribute instead of global styling, allowing native WebAwesome button appearances (like appearance="plain", appearance="outlined", appearance="filled") to work correctly alongside custom-styled buttons
  • Updated variant-specific button styles (brand, danger, neutral) to also require data-custom-style attribute, ensuring complete opt-in behavior for all custom styling
  • Changed custom button variant from primary to brand to align with WebAwesome's native variant naming conventions

Fixed

  • Removed duplicate charset meta tag that was redundant with SEO component
  • Header and stats bar scroll behavior on main page. Stats bar is now sticky and header collapses/reappears on scroll. Header remains sticky on MDX and profile pages.
  • User menu dropdown now displays correctly above the stats bar. Fixed z-index stacking context issues by adding proper positioning and using correct Tailwind v4 arbitrary value syntax.
  • Corrected TypeScript type definitions for wa-button to include only valid WebAwesome appearance values: "accent", "filled", "outlined", "filled-outlined", and "plain"
  • Fixed invalid variant="text" usage in ProfileForm to use variant="neutral" for native WebAwesome styling
  • Fixed ConfirmModal variant logic to use 'brand' instead of 'primary'

[2025-12-18]

Added

  • New logo as an SVG in the header, changes colours between dark and light themes
  • Network Patch Mapper logo to all email templates for consistent branding
  • Logo appears in verification emails, password reset emails, email change confirmations, and contact form notifications

Changed

  • Updated brand color from #2563eb to #2b65e8 across the application
  • Updated primary button color in email templates to use new brand color
  • Updated favicon.svg to use new brand color
  • Updated CSS theme variables and Web Awesome component styling with new brand color

Fixed

  • Port labels now update correctly when edited. Fixed event listener attachment timing issue in Device component.
  • CSS files are now named based on their source (e.g., global.[hash].css) instead of being named after the first page that imports them (e.g., privacy-policy.[hash].css)

[2025-12-17]

Added

  • Remember me switch on login page using wa-switch component

Changed

  • wa-switch on the contact page to use span inside the switch
  • bump packages to newer versions
  • Moved to @awesome.me/webawesome-pro@3.1.0 in place of @awesome.me/webawesome@3.1.0

[2025-12-16]

Changed

  • Updated web-awesome.d.ts with complete type declarations for all Web Awesome components used in the application
  • Added wa-tooltip component declarations with all placement and trigger options
  • Added wa-callout component declarations for future use (variant, open, closable, duration, event handlers)
  • Added wa-select and wa-option component declarations for future use with complete property and event support
  • Added wa-dialog component declarations for future use (label, open, without-header, light-dismiss, contained, event handlers)
  • Enhanced wa-icon with family and variant properties for Font Awesome icon customization
  • Added React synthetic event handlers (onInput, onChange, onBlur, onKeyDown, etc.) to wa-input and wa-textarea for better React integration
  • Added with-caret property to wa-button for dropdown support
  • Added slot attribute to wa-icon for proper slotted content support

Fixed

  • Admin panel search box now works correctly by using React's onInput synthetic events instead of manual addEventListener

[2025-12-15]

Added

  • Changelog link to Footer component for easy access to version history
  • Privacy Policy link to cookie banner
  • Header logo and title now clickable to navigate to homepage
  • Footer component with consistent styling across all pages
  • CHANGELOG.md file with complete project history
  • Changelog page at /changelog displaying the CHANGELOG.md content
  • Copilot instructions updated to require changelog maintenance
  • Mobile sidebar toggle with circular arrow button for opening/closing sidebar on small screens
  • Footer hide/show feature with circular buttons and localStorage persistence of state
  • SidebarToggle component for mobile navigation control
  • Migrated all buttons, inputs, and avatar components to Web Awesome library
  • Integrated Web Awesome components across the entire application
  • Web Awesome type definitions and CSS styling for custom component parts

Changed

  • Footer component simplified by removing hide/show toggle functionality and unused state
  • MDXLayout now uses Footer component instead of manual footer markup
  • Removed version field from package.json
  • Updated Privacy Policy with login information
  • Import paths in index.astro updated to use path aliases for consistency
  • Two-factor authentication now handled via modals instead of separate pages
  • Mobile layout fixes for full-width main content on small screens (sidebar uses fixed positioning)
  • Responsive toolbar buttons: icon-only display on mobile devices (< 640px), full text on larger screens
  • Device grid breakpoint adjustments for improved mobile screen support (3→4→6→8 columns)
  • Container changed from h-screen to min-h-screen for flexible content height
  • Reduced padding on mobile: 12px vs 24px on desktop for more visible content
  • Toolbar gaps and spacing reduced for compact mobile view
  • Footer show button positioned on bottom-right
  • Sidebar completely removed from document flow on mobile, overlays content when opened
  • Added min-h-0 to flex child to enable proper scrolling behavior
  • Web Awesome button loading attribute replaces manual spinner rendering
  • Web Awesome inputs use label slots pattern (<span slot="label">)
  • Event handling for Web Awesome components uses refs with addEventListener
  • Password inputs now use wa-input with password-toggle attribute
  • Improved email sending on Cloudflare Workers using ExecutionContext.waitUntil
  • Better Auth email callbacks now properly handle async operations with waitUntil

Fixed

  • Email input class styling in ProfileForm (removed background color classes)
  • Minlength attributes on inputs (add proper maxlength and pattern for backup codes)
  • TwoFactor components reverted to use HTML inputs except password input in setup modal
  • TwoFactorVerifyModal input validation with without-spin-buttons, pattern validation, and minlength/maxlength
  • TwoFactorVerifyModal to use type="number" for authenticator codes, type="text" for backup codes
  • TwoFactorVerifyModal properly filters digits and reads value at submission
  • TwoFactorVerifyModal digit filtering and enabled verify button
  • Web Awesome components now use proper closing tags instead of self-closing tags
  • Password input event handling in TwoFactorSetupModal by re-attaching listeners on step changes
  • Icon slot imports and wa-icon usage in UserMenu

Removed

  • Two-factor authentication pages (two-factor-setup.astro, two-factor.astro) - replaced with modal components
  • Footer hide/show toggle functionality with circular buttons and localStorage persistence
  • Unused state variables and event handlers from Footer component
  • package-lock.json deleted (project uses pnpm exclusively)

[2025-12-12]

Added

  • Blue color (#3b82f6) added to color picker swatches
  • Contact form with Resend email integration and Turnstile CAPTCHA
  • Contact page with form component and API endpoint
  • wa-switch component for terms and conditions agreement
  • Cookie consent component with proper EU-compliant styling
  • Terms & Conditions and Privacy Policy pages with MDX support
  • Better Auth email functionality with Resend integration
  • Environment variable requirements for email functionality (FROM_EMAIL, CONTACT_EMAIL)
  • Web Awesome components migration across 21 of 22 components
  • MDX layout with proper prose styling for documentation pages

Changed

  • Cookie consent buttons now use neutral variant for EU compliance
  • Button variants properly mapped (primary, neutral, danger) in Web Awesome migration
  • ProfileForm updated to use Web Awesome components as the final migrated component
  • All password fields include password-toggle attribute for UX improvement
  • Forgot password button re-enabled after waitUntil implementation fix
  • ContactForm to use Web Awesome components (wa-input, wa-textarea, wa-button)
  • ContactForm uses Web Awesome label slots and fixed event handling with refs
  • Minlength attributes now use strings for web standards compliance
  • Complete migration: ProfileForm to use Web Awesome components - ALL components now migrated
  • TwoFactorSetupModal to use Web Awesome with loading attribute for spinners
  • TwoFactorSetup to use Web Awesome inputs and buttons
  • Device.tsx to use Web Awesome inputs and buttons with icons
  • TwoFactorVerifyModal to use Web Awesome input and buttons
  • AdminPanel to use Web Awesome inputs and textarea
  • CreateConnectionModal to use Web Awesome inputs and buttons with icons
  • All buttons, inputs, and avatar to Web Awesome components
  • Group modal components to use Web Awesome
  • TwoFactorVerify and cabinet modal components to use Web Awesome
  • ConfirmModal, ResetPasswordForm, and CookieConsent to use Web Awesome components
  • UserMenu and AuthForm to use Web Awesome components
  • MainContent buttons updated with Web Awesome components

Fixed

  • CSS isolation for Web Awesome components from Tailwind Preflight resets (changed all property to revert-layer in global styles)
  • Icon slot naming from 'prefix' to 'start' as per Web Awesome specs
  • Label spacing and use wa-icon in button prefix slots (Cabinet, AddDeviceModal)
  • Web Awesome styling issues: avatar shape, textarea background, duplicate asterisks
  • Duplicate CSS rules for dark mode buttons removed
  • Close button appearance and improved password input event handling in TwoFactorSetupModal
  • Password input by reading value directly from element at form submission
  • Password input in ProfileForm and added password-toggle to all password fields
  • Type safety improvements for waitUntil implementation
  • Event listener patterns for Web Awesome components in React
  • Form submission patterns to read Web Awesome input values directly from refs
  • 404 error by adding prerender=false to contact API route
  • Label styling in ContactForm component (removed unnecessary block and margin-bottom classes)
  • ContactForm validation, error handling, and UI improvements
  • Accessibility improvements and require FROM_EMAIL config
  • ContactForm asterisks from labels and added CONTACT_EMAIL env var

Security

  • HTML content in emails properly escaped using escapeHtml helper to prevent XSS
  • Turnstile CAPTCHA integration for contact form spam prevention
  • Security policy (SECURITY.md) updated with new reporting methods

[2025-12-11]

Added

  • wa-tooltip component for port hover interactions with fade-in transitions
  • 2FA modal components (TwoFactorSetupModal, TwoFactorVerifyModal) replacing separate pages
  • QR code generation using react-qr-code package for 2FA setup
  • 2FA backup codes display in setup flow
  • Accessibility attributes (role, aria-label) to modal components
  • Named constants for setTimeout delays in 2FA flows

Changed

  • 2FA modals prevent ESC key and click-outside closing during critical flows
  • TwoFactorSetup page updated with react-qr-code package

Fixed

  • QR code scanning in 2FA setup modal (disabled ESC/click-outside closing)
  • QR code import corrected to use default import pattern (replaced Web Awesome QR code with react-qr-code package)
  • Delays added to state transitions for better UX
  • Extracted delay constant and removed unused import

[2025-12-10]

Added

  • Sitemap integration with @astrojs/sitemap for improved SEO
  • Open Graph meta tags and default OG image (network-patch-mapper-og.jpg)
  • MIT License (LICENSE.md)
  • SECURITY.md policy document
  • robots.txt for search engine crawling
  • Unified Header.tsx component with back button support across all pages
  • Simple header component for account and MDX pages
  • Legal document pages (terms.mdx, privacy-policy.mdx) with proper styling
  • Custom swatches to color picker in CreateConnectionModal
  • htmlFor attributes to all form labels for better accessibility
  • Slot-based label styling for wa-color-picker to match other form labels

Changed

  • Page titles updated for legal documents to include "Network Patch Mapper" branding
  • Header component refactored with sticky positioning and scroll visibility detection
  • Sitemap filtering using regex to exclude authentication and admin pages
  • MDX layout footer now includes links to Privacy Policy and Terms & Conditions
  • Sitemap filter refactored with regex (simplified from multiple explicit URL checks)
  • Vulnerability reporting method updated in SECURITY.md
  • Web Awesome link updated to point to official website

Fixed

  • Scroll visibility logic in header component (added aria-label to back button)
  • TypeScript syntax errors in Header component
  • Form label associations (htmlFor attributes) for accessibility
  • MDX page titles now render correctly from frontmatter (MDX title prop by accessing frontmatter)
  • Web Awesome components to use proper closing tags instead of self-closing tags

Documentation

  • README updated with Web Awesome and React Flow information
  • Setup documentation consolidated with combined migration file approach
  • Dependencies updated in package.json (astro-seo, @cloudflare/workers-types, resend)
  • package-lock.json removed in favor of pnpm-lock.yaml
  • Cleaned up trailing whitespaces and addressed code review feedback

Note to contributors: When making changes, please update this changelog according to the Keep a Changelog format. Group changes by type (Added, Changed, Deprecated, Removed, Fixed, Security) and be specific about what changed.