LumaSync-Site
releasev1.1.25Marketing site, docs, and blog for LumaSync — the tray-first open-source ambilight + Philips Hue desktop app.
README Snapshot
LumaSync-Site
Marketing site, docs, and blog for LumaSync — the tray-first open-source ambilight + Philips Hue desktop app.
- Site: https://lumasync.app
- App repo: https://github.com/voyvodka/LumaSync
Stack
- Astro 6 + MDX content collections (
docs,compare,legal,blog) - Pagefind for offline in-browser search; Umami for cookie-free analytics
- Cloudflare Pages (direct-upload via
wrangler) + Cloudflare DNS - IBM Plex Sans / Mono (self-hosted WOFF2, preloaded)
- No secrets bundled; the site never talks to the desktop app
Develop
Requires Node 22.12+ and pnpm (pinned via packageManager in package.json).
pnpm install
pnpm dev # http://localhost:4321
pnpm lint # prettier --check
pnpm check # astro check (type-check + content schema)
pnpm build # astro build + pagefind index
Deploy
Every push to main triggers .github/workflows/deploy.yml, which lint- and type-checks, builds, and ships dist/ to Cloudflare Pages.
Required repo secrets for CI: CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID.
License
- Code (Astro components, layouts, CI, styling) — MIT
- Content (MDX under
src/content/, docs, comparisons, blog) — CC BY 4.0
See /license for the public-facing summary and LICENSE for the full text.
Contributing
Issues and PRs welcome — see CONTRIBUTING.md for scope, local preview, and commit conventions. For app-level bugs (firmware, USB pipeline, Hue streaming), file against the LumaSync app repo instead.
Security
Report vulnerabilities through GitHub's Private Vulnerability Reporting — see SECURITY.md or /.well-known/security.txt.
Changelog
Changelog — lumasync-site
This is the changelog for the marketing/docs site at lumasync.app. The LumaSync app's own release notes live in the app repo and surface on /changelog — site versions track independently from app versions.
The site follows Semantic Versioning at its own cadence; bumping the LumaSync app submodule does not require bumping the site version.
[1.1.25] — 2026-06-15
Security
- esbuild advisory cleared (GHSA-gv7w-rqvm-qjhr, GHSA-g7r4-m6w7-qqqr): a pnpm
overridesentry now pinsesbuildto>=0.28.1, resolving the high- and low-severity advisories that reached the build transitively through@tailwindcss/vite > vite > esbuild. The dependency-audit CI gate (pnpm audit --prod --audit-level=high) is green again. yamladvisory cleared (GHSA-48c2-rrv3-qjmp): a pnpmoverridesentry forcesyamlto>=2.8.3, replacing the vulnerable 2.7.1 pulled in transitively via@astrojs/check's language-server chain. With this,pnpm auditreports no known vulnerabilities at any severity, production or development.- External-link hardening completed site-wide:
rel="noopener noreferrer"now covers the remaining outbound links on the home, download, and community pages (Hue developer portal, and the GitHub repo / releases / Code of Conduct / Contributing links), finishing the rollout begun in 1.1.24. The links open in the same tab (notarget="_blank"site-wide), so this is defense-in-depth plus Referer-header suppression.
Accessibility
- Disabled community forum links show their tooltip on hover:
.forums a.disabledswapspointer-events: noneforcursor: default, so the nativetitle("Pending community growth") surfaces on hover for the inert Discord card. The links already render nohrefand guard:hover/:activestyling with:not(.disabled), so there is no interactivity regression.
Dependencies
astro6.4.4 → 6.4.7: routine upstream patch (manifest + lockfile only), bringing theaddAttributeinvalid-attribute-name hardening and prerendered-error-page origin validation from the 6.4.5–6.4.7 patch line. A full lockfile refresh also picked up the latest in-range patches across the tree.
[1.1.24] — 2026-06-09
Security
- CSP
img-srcno longer allows arbitrary HTTPS origins: the Content-Security-Policy permitted images from anyhttps:origin, but a repo-wide scan confirmed the site loads no external images at all — every image is served first-party or inlined as adata:URI. The directive is nowimg-src 'self' data:, so an injected or compromised markup path can no longer exfiltrate data via attacker-controlled image loads. - External outbound links carry
rel="noopener noreferrer": the GitHub links inCompareCTA.astro, the Footer's outbound column links, and the repo/license links on/changelog/and/license/now set both hints. The links open in the same tab (notarget="_blank"exists site-wide), so this is defense-in-depth rather than an active tabnabbing fix, plus it stops leaking the Referer header to the destination.
[1.1.23] — 2026-06-08
Build
- Declared Node engine floor aligned to the real requirement:
package.jsonengines.nodetightened>=22.0.0→>=22.12.0, and the README "Develop" note now reads "Node 22.12+", matching the actual floor imposed by Astro 6 and@astrojs/mdx6 (both declarenode >=22.12.0). No runtime or build-output change —.nvmrcalready resolves a compliant Node 22.x, so CI was unaffected; this just makes the declared range honest.
[1.1.22] — 2026-06-08
Accessibility
- Keyboard focus rings on call-to-action buttons: the primary and secondary CTAs in
CompareCTA.astroand on the homepage now render a visible:focus-visibleoutline (2px--focus-ring, 2px offset), so keyboard and switch-device users can see which button holds focus. Mouse users are unaffected —:focus-visibleonly triggers for keyboard-style focus. - Compare-listing grid cards respond to keyboard focus and press: cards in the
/compare/index grid gain the same:focus-visibleoutline plus a subtlescale(0.96)active-press transform, gated behindprefers-reduced-motion, matching the interaction feedback already present on the homepage compare cards.
Dependencies
- Minor/patch group bump (4 updates):
astro6.4.2 → 6.4.4,marked18.0.4 → 18.0.5,dompurify3.4.7 → 3.4.8, andisomorphic-dompurify3.15.0 → 3.16.0 — routine upstream patches, lockfile + manifest only, no source change. @astrojs/mdx5 → 6: major bump of the MDX integration (5.0.6 → 6.0.2). Astro 6.4 satisfies the newastro: ^6.4.0peer range; the build, type-check, and Lighthouse-CI gates all pass with MDX-rendered pages unchanged.
[1.1.21] — 2026-06-01
Accessibility
- Disabled Discord card explains itself on hover: the "Coming soon" Discord card on
/community/— inert until the active-user count crosses its threshold — now carries a nativetitletooltip ("Pending community growth"), mirroring the disabled download-card treatment from v1.1.19 so hovering users understand why the card is non-interactive. The card was already kept out of the keyboard/click path viaaria-disabledand an undefinedhref.
Dependencies
- Minor/patch group bump (4 updates):
astro6.3.7 → 6.4.2,@astrojs/sitemap3.7.2 → 3.7.3,dompurify3.4.5 → 3.4.7, andisomorphic-dompurify3.14.0 → 3.15.0. The Astro bump is a minor release; the rest are routine upstream patches — lockfile + manifest only, no source change.
[1.1.20] — 2026-05-29
Security
- Response-header hardening: removed the deprecated
block-all-mixed-contentdirective from the Content-Security-Policy (theupgrade-insecure-requestsdirective, already present, supersedes it), and added aPermissions-Policythat denies camera, microphone, geolocation, payment, USB, the motion sensors, and the Topics API. The static site calls no powerful browser APIs, so this is attack-surface reduction with no functional change.
Structured Data
- Organization logo dimensions corrected: the logo
ImageObjectdeclared512x128, butbrand/logotype-light.svghas an intrinsic320x80viewBox. Aligned the declared dimensions (same 4:1 ratio, both above Google's 112px minimum) so strict validators don't flag a mismatch against the asset.
[1.1.19] — 2026-05-29
Bug Fixes
- Dead links in
llms.txtand slashless structured-data URLs: thellms.txtindex linkedllms-full.txt/and.well-known/security.txt/with trailing slashes, which 404 because those are static assets rather than HTML routes — an agent following the index to the full-text corpus or the security contact hit a dead end. Both now point at the slashless forms, and the Telemetry entry resolves to/docs/reference/telemetry/instead of the docs index. Separately, the docs leaf ([...slug]) and docs group ([group]/index) pages emitted their breadcrumb-leaf, TechArticle/HowTo, and CollectionPage schema URLs without a trailing slash, so the structured-data entity URLs 308-redirected instead of resolving directly — they now carry the slash to match thetrailingSlash:'always'canonical, as do thellms-full.txtSource:pointers. /docs/and/compare/hub OG images 404'd: both hub pages derive anog:imageURL (/og/docs.png,/og/compare.png), but the OG generation route had no matching key, so social and crawler unfurls of those two pages rendered without a card. Added both keys.SoftwareApplicationdeclared one@idwith two download URLs: the homepage and/download/both emit the app'sSoftwareApplicationnode under the same@idbut with divergentdownloadUrlvalues, asking consumers to merge conflicting nodes. Both now resolve to the stable/download/canonical.humans.txtnamed the wrong host: the credits file listed a stale deploy target; corrected to Cloudflare Pages.
Content
- Hue Sync comparison repositioned:
/compare/hue-sync/claimed Signify had discontinued the Hue Sync desktop app for PC/Mac, but official Philips release notes show that app is still actively maintained — only the separate Hue Sync mobile app was retired. The page now positions LumaSync as a free, open-source alternative to the desktop app (keeping the migration guidance), and the unresolved editorial placeholder was removed.
Accessibility
- Muted text now meets WCAG AA contrast:
--text-mutedwas#6b7280, below the 4.5:1 normal-text threshold on the dark surfaces (blockquotes, footer tagline, download meta). Lightened to#8b919cwhile keeping the muted feel. - Disabled download cards explain themselves on hover: unavailable-platform cards now carry a native
titletooltip ("No release available for this platform yet"), so hovering users understand why the card is inert — the cards were already out of the keyboard/click path from earlier releases.
Performance
- Long-lived caching for
/media/: hero and screenshot images were served with the 4-hour must-revalidate default; added a 30-dayCache-Controlblock so the LCP hero stops paying a conditional round-trip on repeat visits.
Discoverability
- Markdown entry-point
Linkrelation: the RFC 8288Link:header advertisingllms.txt/llms-full.txtnow usesrel="alternate"; type="text/markdown"— the relation header-only agents key off for markdown discovery — instead ofdescribedby/text/plain.
Build
- CI gates on production CVEs: added a
pnpm audit --prod --audit-level=highstep so a published vulnerability introduced into the shipped dependency tree fails the build. The existing license audit checks compliance only, not vulnerabilities.
[1.1.18] — 2026-05-29
Security
- Sanitized GitHub API fetch errors on
/download/:src/pages/download.astropreviously surfaced the raw caught error's.messagedirectly into the page'sfetchErrorstate, which could leak upstream API structure, internal request details, or network specifics into the rendered UI. The raw error is now logged server-side viaconsole.errorfor diagnostics, while the user-facing state is pinned to a generic'upstream API unavailable'string. No behavioral change for the success path; failed release fetches now degrade with an opaque message instead of an implementation-revealing one.
Accessibility
- Search-hint
<kbd>glyphs hidden from the accessible name inSearch.astro: the modal's "Type to search. ↑↓ to navigate, ↵ to open." hint rendered raw arrow and enter symbols that screen readers announced as literal characters. The visual<kbd>clusters now carryaria-hidden="true"and are paired withsr-onlytext equivalents ("Up and down arrows", "Enter") so assistive technology reads meaningful labels while sighted users keep the compact symbol hint. The same treatment is applied to both the static markup and therenderEmpty()JS template that repaints the hint on an empty query, plus the modal Close button's<kbd>Esc</kbd>is nowaria-hidden(thearia-keyshortcuts="Escape"already conveys the shortcut semantically).
Interaction
- Press-state feedback on search results: added a
:activetransform: scale(0.98)to.search-result, gated behind@media (prefers-reduced-motion: no-preference)so it respects motion preferences. Gives keyboard and pointer users tactile confirmation when activating a result without affecting reduced-motion sessions.
[1.1.17] — 2026-05-27
Security
- CSP hardening —
https:wildcard removed fromscript-srcandconnect-src:public/_headerspreviously shippedscript-src 'self' 'unsafe-inline' https:andconnect-src 'self' https:, which let any HTTPS origin execute scripts and accept connections — neutralizi
Releases
- v1.1.25Open on GitHub
Security
- esbuild advisory cleared (GHSA-gv7w-rqvm-qjhr, GHSA-g7r4-m6w7-qqqr): pnpm override pins
esbuildto>=0.28.1; thepnpm audit --prod --audit-level=highCI gate is green again. - yaml advisory cleared (GHSA-48c2-rrv3-qjmp): pnpm override forces
yamlto>=2.8.3(was 2.7.1 via@astrojs/check).pnpm auditnow reports no known vulnerabilities at any severity, prod or dev. - External-link hardening completed site-wide:
rel="noopener noreferrer"extended to the remaining outbound links on the home, download, and community pages, finishing the rollout begun in 1.1.24.
Accessibility
- Disabled community forum links now show their native
titletooltip on hover (.forums a.disabled:pointer-events: none→cursor: default), with no interactivity regression.
Dependencies
astro6.4.4 → 6.4.7 (manifest + lockfile), plus a full lockfile refresh for the latest in-range patches across the tree.
- esbuild advisory cleared (GHSA-gv7w-rqvm-qjhr, GHSA-g7r4-m6w7-qqqr): pnpm override pins
- v1.1.24Open on GitHub
Security
- CSP
img-srctightened to'self' data:— removed thehttps:scheme wildcard; the site loads no external images (verified by repo-wide scan), so arbitrary-origin image loads are no longer permitted. rel="noopener noreferrer"on external outbound links — GitHub links inCompareCTA, Footer outbound links, and repo/license links on/changelog/and/license/now carry both hints; defense-in-depth (notarget="_blank"exists site-wide) plus Referer-leak prevention.
Full details in CHANGELOG.md.
- CSP
- v1.1.23Open on GitHub
Build
- Declared Node engine floor aligned to the real requirement:
package.jsonengines.nodetightened>=22.0.0→>=22.12.0, and the README "Develop" note now reads "Node 22.12+", matching the floor imposed by Astro 6 and@astrojs/mdx6 (both declarenode >=22.12.0). - No runtime or build-output change —
.nvmrcalready resolves a compliant Node 22.x, so CI was unaffected; this only makes the declared range honest. No redeploy needed.
- Declared Node engine floor aligned to the real requirement:
- v1.1.22Open on GitHub
Accessibility
- Keyboard focus rings (
:focus-visible, 2px--focus-ring) on the primary/secondary CTAs inCompareCTA.astroand on the homepage. - Compare-listing (
/compare/) grid cards gain the same focus ring plus aprefers-reduced-motion-gatedscale(0.96)active-press transform.
Dependencies
- Minor/patch group:
astro6.4.2 → 6.4.4,marked18.0.4 → 18.0.5,dompurify3.4.7 → 3.4.8,isomorphic-dompurify3.15.0 → 3.16.0. @astrojs/mdx5.0.6 → 6.0.2 (major) — Astro 6.4 satisfies the newastro: ^6.4.0peer range; build, type-check, and Lighthouse-CI gates green.
- Keyboard focus rings (
- v1.1.21Open on GitHub
Accessibility
- Disabled Discord card explains itself on hover — the "Coming soon" Discord card on
/community/now carries a nativetitletooltip ("Pending community growth"), mirroring the disabled download-card treatment so hovering users see why the card is inert. Already kept out of the keyboard/click path viaaria-disabledand an undefinedhref.
Dependencies
- Minor/patch group bump (4 updates) —
astro6.3.7 → 6.4.2,@astrojs/sitemap3.7.2 → 3.7.3,dompurify3.4.5 → 3.4.7,isomorphic-dompurify3.14.0 → 3.15.0. Astro is a minor release; the rest are routine upstream patches — lockfile + manifest only, no source change.
- Disabled Discord card explains itself on hover — the "Coming soon" Discord card on
- v1.1.20Open on GitHub
Security
- Response-header hardening — removed the deprecated
block-all-mixed-contentdirective from the CSP (upgrade-insecure-requestssupersedes it) and added aPermissions-Policydenying camera, microphone, geolocation, payment, USB, the motion sensors, and the Topics API. No functional change — the static site calls no powerful browser APIs.
Structured Data
- Organization logo dimensions corrected — the logo
ImageObjectnow declares320x80to match the SVG's intrinsic viewBox (was512x128), so strict validators don't flag a dimension mismatch.
- Response-header hardening — removed the deprecated
- v1.1.19Open on GitHub
Bug Fixes
- Dead links + slashless structured-data URLs —
llms.txtlinkedllms-full.txt/and.well-known/security.txt/(both 404 with a trailing slash) and the docs leaf/group schema URLs (breadcrumb, TechArticle/HowTo, CollectionPage) 308-redirected; all now resolve directly and the Telemetry link points at/docs/reference/telemetry/. /docs/and/compare/hub OG images 404'd — added the missing OG route keys so social/crawler unfurls render a card.SoftwareApplicationshared one@idwith twodownloadUrls — homepage and/download/now both resolve to the stable/download/canonical.humans.txt— corrected the deploy host to Cloudflare Pages.
Content
- Hue Sync comparison repositioned — corrected the inaccurate "discontinued" claim (the Hue Sync desktop app is still maintained; only the mobile app was retired) and reframed LumaSync as a free, open-source alternative, keeping the migration guidance.
Accessibility
- Muted text now meets WCAG AA contrast —
--text-mutedlightened from#6b7280to#8b919c. - Disabled download cards carry a native
titletooltip explaining why they're inert.
Performance
- 30-day caching for
/media/— the LCP hero and screenshots no longer pay the 4h must-revalidate round-trip.
Discoverability
Linkheader advertisingllms.txt/llms-full.txtnow usesrel="alternate"; type="text/markdown".
Build
- CI gates on production CVEs via
pnpm audit --prod --audit-level=high.
- Dead links + slashless structured-data URLs —
- v1.1.18Open on GitHub
Security
- Sanitized GitHub API fetch errors on
/download/— raw error messages are no longer surfaced to the UI; failures are logged server-side and degrade with a genericupstream API unavailablestring, avoiding leakage of upstream API structure or network details.
Accessibility
- Search-hint
<kbd>glyphs hidden from the accessible name — the modal hint's arrow/enter symbols now carryaria-hidden="true"paired withsr-onlytext equivalents ("Up and down arrows", "Enter"), applied to both the static markup and the empty-query repaint; the Close button'sEsckbd is alsoaria-hidden(covered byaria-keyshortcuts).
Interaction
- Press-state feedback on search results — added a
:activescale(0.98)to search results, gated behindprefers-reduced-motion: no-preference.
- Sanitized GitHub API fetch errors on
- v1.1.17Open on GitHub
Security
- CSP hardening —
https:wildcard removed fromscript-srcandconnect-srcinpublic/_headers. Both directives are now pinned to'self'. The site serves no third-party scripts at runtime, so this is a no-op for legitimate traffic and narrows the XSS / data-exfiltration attack surface.
Accessibility
aria-keyshortcuts+ dialog-popup semantics on search triggers: header search button and 404-page.search-ctanow exposearia-keyshortcuts="Control+K Meta+K",aria-haspopup="dialog", andaria-controls="search-dialog", with their visual<kbd>⌘</kbd><kbd>K</kbd>wrappers hidden from AT viaaria-hidden="true". The search modal's Close button also gainsaria-keyshortcuts="Escape". 404 CTA additionally gets an explicitaria-label="Search".- Focus-visible outline on landing-page compare cards — closes the last landing-page interactive surface that lacked a keyboard outline, matching the focus pattern used across the rest of the site.
Dependencies
- Minor/patch group bump (4 updates):
astro6.3.3 → 6.3.7,marked18.0.3 → 18.0.4,dompurify3.4.4 → 3.4.5,isomorphic-dompurify3.13.0 → 3.14.0.
- CSP hardening —
- v1.1.16Open on GitHub
Bug Fixes
- Stale search queries no longer overwrite fresher results in
Search.astro: a monotonicqueryIdis now captured per input event and re-checked after everyawait(loadPagefind,pf.search,data()), so a superseded query bails before touching the DOM. Clearing the input also invalidates any in-flight query.
Accessibility
- Disabled download cards on
/download/now drop theirhrefentirely, removing unavailable-platform cards from the keyboard tab order and disabling Enter-key activation. Mirrors the community-forum-card fix from v1.1.15.
Dependencies
- Minor/patch group:
astro6.3.1 → 6.3.3,@astrojs/mdx5.0.4 → 5.0.6,dompurify3.4.2 → 3.4.4,isomorphic-dompurify3.12.0 → 3.13.0. pnpm/action-setup6.0.6 → 6.0.8 (CI + deploy).cloudflare/wrangler-action3.15.0 → 4.0.0 (deploy) — non-breaking,wranglerVersion: '4'already pinned; stale pin comment corrected to# v4.0.0.
- Stale search queries no longer overwrite fresher results in
- v1.1.15Open on GitHub
Accessibility
- Disabled forum cards on
/community/now drop theirhrefentirely: the "Coming soon" anchor previously rendered withhref="#"+aria-disabled="true"+pointer-events: none, but the dummy#href left the element in the keyboard tab sequence and pressing Enter would scroll the page to the top. The href is now conditionally omitted, so the browser treats the<a>as a non-interactive placeholder and removes it from the focus order.aria-disabledis still set for assistive tech.
UX
- Focus-visible outline + tactile click feedback on
/community/forum cards: the same anchors render an explicit:focus-visibleoutline usingvar(--focus-ring)with a 2px offset, and the:activestate scales to0.96wrapped inprefers-reduced-motion: no-preference. Disabled cards are excluded via:not(.disabled). Closes the last navigation surface lacking tactile / keyboard parity with the rest of the site.
Performance
- Manual indexed loops in
pickAsseton/download/: replaced the inner.find()inside the matchers loop with a manual indexedforloop, eliminating per-asset closure allocation when resolving GitHub release assets at build time. Build-time-only — runtime user behavior is unchanged.
- Disabled forum cards on
- v1.1.14Open on GitHub
Dependencies
devaluebumped 5.8.0 → 5.8.1: transitive dep via Astro, used to serialize server-rendered state into the client hydration bundle. Upstream patch forces sparse arrays to allocate sparsely (sveltejs/devalue@206ca67), a defensive fix for a memory-blowup vector when hydration payloads contain sparse-array structures. Lockfile-only — no source change in this repo.
- v1.1.13Open on GitHub
Security
- Single-quote escape in Pagefind result rendering (
Search.astro):escapeHtmlwas neutralizing&,<,>, and"before injecting Pagefind result excerpts into the DOM but left'untouched, leaving an attribute-context breakout vector if a result excerpt was ever rendered inside a single-quoted attribute. Added'→'to the chain — mirrors the v1.1.11 hardening onSchema.astro.
Performance
- Cached Pagefind init Promise + O(1) keyboard navigation in
Search.astro:loadPagefindnow memoizes the in-flight Promise rather than only the resolved module, so concurrent callers (keyboard shortcut + rapid keystrokes) share one init cycle.setActiveupdates only the previously-active and newly-active result nodes instead of iterating the full NodeList per keypress (O(N) → O(1)).
UX
- Tactile click feedback + focus ring on
/download/installer cards:.cardelements now scale to0.96on:active(wrapped inprefers-reduced-motion: no-preference) and render an explicit:focus-visibleoutline. Disabled cards are excluded. Extends the v1.1.12 landing-page CTA affordance to the download surface. - Focus-visible + active states on
DocsSidebar.astrolinks: docs sidebar links now show a 2px inset focus-visible outline and scale to0.96on:active, honouring reduced-motion. Closes the last navigation surface that lacked keyboard / tactile parity.
CI
pnpm/action-setupbumped 6.0.5 → 6.0.6 (upstream fix:bin_destoutput now points to the self-updated pnpm rather than the bootstrap binary).
- Single-quote escape in Pagefind result rendering (
- v1.1.12Open on GitHub
Security
- Content-Security-Policy header on all responses:
public/_headersnow sets a baseline CSP under/*so every HTML response carries it. Policy is restrictive-by-default (default-src 'self',frame-ancestors 'none',object-src 'none',base-uri 'self',form-action 'self',block-all-mixed-content,upgrade-insecure-requests) with the minimum allowances Astro and the analytics bundle need:'unsafe-inline'onscript-src/style-src(Astro emits inline hydration shims and scoped style blocks),https:onscript-src/connect-src/img-srcfor the third-party analytics endpoint, anddata:onimg-src/font-src. Adds a defense-in-depth layer behind the existing input-sanitization fixes from v1.1.9 / v1.1.11 — if any future XSS vector slipped through, the CSP would block eval, mixed content, framing, and external object loads.
Performance
- Cached focus-trap NodeList in
Header.astro: the mobile-nav focus trap was callingpanel.querySelectorAll(focusableSelector)inside thekeydownhandler on everyTabpress, repeating the same DOM query and risking layout thrashing during rapid keyboard navigation. The query now runs once when the nav opens and is stored in a closure-scopedcachedFocusable; thekeydownbranch reads the cachedNodeListinstead. Same focus-trap semantics, near-zero per-keystroke DOM cost.
UX
- Tactile click feedback on landing-page CTAs and comparison cards:
src/pages/index.astroandsrc/components/CompareCTA.astronow applytransform: scale(0.96)on:activefor.cta-primary,.cta-secondary, and.compare-card, withtransformadded to each element'stransitionlist so the scale eases in / out atvar(--duration-fast)rather than snapping. The:activerule is wrapped in@media (prefers-reduced-motion: no-preference)to honour the reduced-motion contract. Extends the same tactile affordance shipped on the 404 page in v1.1.11 to the primary conversion surfaces on the landing page.
Full changelog: https://github.com/voyvodka/LumaSync-Site/blob/v1.1.12/CHANGELOG.md
- Content-Security-Policy header on all responses:
- v1.1.11Open on GitHub
Security
- Single-quote escape in JSON-LD serialization:
src/components/Schema.astrowas injectingJSON.stringifyoutput into<script type="application/ld+json">viaset:htmlwith<,>, and&escaped to their\u00xxforms, but single quotes left through unchanged. While JSON does not require escaping', leaving it raw inside an HTML script-context payload is a latent script-breakout vector if the script-tag is ever wrapped in a single-quoted attribute or the surrounding template shifts. Added.replace(/'/g, '\\u0027')to the existing escape chain so all four script-context-sensitive characters are uniformly neutralized regardless of where the schema string ends up.
UX
- Tactile click feedback on 404 page CTAs: the
Search,Home, and recovery-link controls onsrc/pages/404.astronow scale to0.96on:active, wrapped in@media (prefers-reduced-motion: no-preference)so reduced-motion users are unaffected. Matches the same affordance applied to header / search / nav controls in v1.1.9, restoring perceived responsiveness on the one user-facing page that had been missed.
Full changelog: https://github.com/voyvodka/LumaSync-Site/blob/v1.1.11/CHANGELOG.md
- Single-quote escape in JSON-LD serialization:
- v1.1.10Open on GitHub
SEO
- Internal-link canonicalization across the URL surface. Astro config sets
trailingSlash: 'always'so canonical URLs and sitemap entries end with/, but the rest of the site had drifted to slashless internal references — every header / footer nav item, every body link, everybreadcrumbSchema/itemList/softwareApp.downloadUrlfield, bothcompareHref/docHrefhelpers insrc/lib/content.ts, the legacy alias targets inastro.config.mjs, every URL inpublic/llms.txt, and ~100 markdown links acrosssrc/content/**/*.mdx. Each slashless link triggered a 308 redirect, and the legacy aliases formed a 2-hop meta-refresh + 308 chain. The April fix repaired the sitemap; this commit completes the consistency pass across the remaining surfaces. Expected impact: clears the redirect-related "Page indexing" entries in Search Console and frees crawl budget previously wasted on internal redirects. Header.astroisActivesimplified. With trailing-slash hrefs, the previouspathname === href || pathname.startsWith(\${href}/`)form no longer matches sub-pages. Replaced withpathname.startsWith(href)` — same semantics, fewer special cases.
- Internal-link canonicalization across the URL surface. Astro config sets
- v1.1.9Open on GitHub
Security
- DOM-based XSS in Pagefind search results:
src/components/Search.astrowas injecting Pagefind result excerpts into the DOM viainnerHTMLwith${r.excerpt || ''}unescaped. Indexed content reaching the excerpt could break out and execute arbitrary HTML. Excerpts now flow through asanitizeExcerpthelper that HTML-escapes the whole string and then selectively restores the<mark>/</mark>tags Pagefind needs for search-term highlighting, preserving the highlight UI without trusting raw excerpt content.
Accessibility
- Native tooltips on icon-only buttons: the header search trigger, the mobile nav open/close buttons, and the search modal
Escclose button now carry matchingtitleattributes alongside theiraria-labels. Visual users hovering an icon now get the same affordance keyboard / screen-reader users already had, removing ambiguity for buttons that have no text label. - Tactile click feedback: the same controls gain a subtle
transform: scale(0.96)on:active, wrapped in@media (prefers-reduced-motion: no-preference)so users who opt out of motion are unaffected. Adds perceived responsiveness on click without compromising the reduced-motion contract.
- DOM-based XSS in Pagefind search results:
- v1.1.8 — Surface LumaSync v1.5.xOpen on GitHub
Site catches up with three upstream releases (v1.5.0 + v1.5.1 + v1.5.2). Submodule pin advanced from the v1.4.0 merge to post-v1.5.2, so
version.tsnow resolvesLATEST_VERSION/LATEST_VERSION_DATEto v1.5.2 — 2026-05-05 without code changes.Content
- Landing Shipped column rewrote from v1.4 highlights to v1.5.2 — WLED bridge, Hue Zones, Linux X11 capture, SK6812 RGBW, OS keychain credentials, per-bulb gamut clipping, beta update channel, macOS lifecycle hardening, visibility-aware polling, frontend log bridge.
- Next column rewrote from "v1.5 — in flight" to "v1.6 — queued" (Flathub, glib / gtk-rs migration, OpenRGB sink, companion firmware repo).
- Three-sink narrative — feature grid expanded to 4 cards (USB · WLED · Hue · all three synchronized). Hero copy, ItemList JSON-LD, and softwareAppSchema description rewrote.
- Linux platform support promoted from "Experimental" to "Supported" across landing, FAQ, hero platform-note, and download page asset hint.
Docs
- New
docs/usb-leds/wled.mdx— full WLED bridge reference: DDP-over-UDP, mDNS auto-discovery, IP guard,WLED_INVALID_LED_COUNTrejection, network reachability checklist. - getting-started/ — three Linux installers documented, Windows MSI v1.5.1+ stable, first-run onboarding banner, WLED pairing step, chip-type selector, expanded six-chipset USB list, corrected
WS2812B (aka SK6812)mistake (SK6812 RGBW is a distinct 4-channel chip). - usb-leds/ — six chipsets, USB-class endpoint filtering (
PORT_UNSUPPORTED), strip-chip selector documented, RGBW wire format, keyboard input on edge counts (v1.5.2), amber Rev 07 + 32 px tap targets, Bluetooth virtual port reject troubleshooting. - hue/ — mDNS-as-primary discovery, OS keychain credential storage with idempotent v1.4 → v1.5 migration, Hue Zones (zone-relative coords, AR-locked sizing, schema 1→2 migration), per-bulb gamut clipping,
HUE_STREAM_NOT_READY_ACTIVE_STREAMER403 retired in v1.5.2 via DTLSclose_notify+ idempotent deactivate token. - advanced/ — beta update channel (
updateChannel: 'stable' | 'beta'), Linux hot-plug via xcap RandR. - ambilight/screen-capture.mdx — Linux X11 via xcap as default, Windows hardware-accelerated downscale scaffold.
- reference/ — runtime network call table extended (mDNS rows, WLED
/json/info+ DDP), visibility-aware polling, OS keychain credential storage callout,wled.connectedevent, one-shotwindow.closed-to-traynotification, macOS template tray icon, frontendconsole.*→ file-sink log bridge, compact-mode deep-link auto-expand,kick_off_shutdown_and_dielifecycle path with single-instance socket-leak fix and detachedstop_hue_streamworker thread,schemaVersion/updateChannel/wled.targetsconfig fields. - compare/wled.mdx — reframed: WLED is a sink LumaSync drives natively over DDP. Recommended setup is "WLED + LumaSync together" for ESP-based hardware.
Discoverability
public/llms.txtopening summary mentions WS2812B / SK6812 RGBW USB, ESP32 / ESP8266 over DDP, OS keychain credentials. WLED moved out of "alternatives" into a complementarity note.download.astroLinux note:AppImage · experimental→AppImage · deb · rpm.
Pre-deploy patches now live
The four PRs merged into
mainafter v1.1.7 but never tagged are now part of v1.1.8 (deploy.ymlis release-driven, not push-driven):fix(security)Sentinel —community.astrowrapstriage[i].ainDOMPurify.sanitize()before<dd set:html>. Closes a latent XSS surface.feat(a11y)Palette — Pagefind.search-resultlinks get:global(.search-result:focus-visible)outline; Astro's scoped CSS otherwise ignored the rule.chore(deps)— Astro 6.1.9 → 6.2.1, marked 18.0.2 → 18.0.3, @astrojs/check 0.9.8 → 0.9.9, prettier-plugin-tailwindcss 0.7.3 → 0.8.0.chore(deps)— pnpm/action-setup SHA pin refreshed (v6.0.3 → v6.0.5).
- v1.1.7Open on GitHub
Security
- Markdown→HTML XSS hardening: the
/changelog/page readsvendor/lumasync/CHANGELOG.mdfrom the pinned submodule, parses it withmarked, and injects the result via Astro'sset:html. Becausemarkedpreserves inline raw HTML, an upstream payload (<script>/onclick=etc.) would have rendered verbatim into the deployed page. The parsed HTML now passes throughDOMPurify.sanitize()(viaisomorphic-dompurifyfor SSR) before being assigned, so unsafe tags and attributes are stripped at build time. Defends against a supply-chain compromise of the vendor changelog content path.
- Markdown→HTML XSS hardening: the
- v1.1.6Open on GitHub
Fixed
/securityURL alias: addedpublic/_redirectswith single-hop 301 rules for/securityand/security/pointing at the canonical/.well-known/security.txt. Closes the UX gap where bare/securityprobes (the convention exposed by GitHub, Stripe, Cloudflare) returned 404 even though the RFC 9116 endpoint was always live. Cloudflare Pages evaluates_redirectsahead of the trailing-slash 308 layer, so each rule catches the request directly without the double-hop a meta-refresh redirect would introduce.