The Omnix website, served at https://www.omnix-linux.com.
The homepage's Omarchy Plugins section (#plugins) introduces upstream plugin capabilities, links to the community catalog and Autarchy's support issue, and labels Omnix integration as under development. KDE Plasma support is an investigation requiring an adapted host; it is not native Plasma widget compatibility. Keep these claims and llms.txt aligned with verified runtime results.
A static page (index.html) with an OffscreenCanvas GPU worker. The oversized O uses Coronal by @Xor, derived from 3D Fire. coronal.js handles DOM placement and worker lifecycle; coronal-webgpu.js contains the preferred WebGPU/WGSL renderer; coronal-shader.js supplies the WebGL fallback. shader-worker.js owns the transferred canvas and all GPU resources, with no three.js dependency or per-frame main-thread rendering.
The worker keeps two RGBA GPU textures (framebuffers on WebGL) for the previous and current shader samples. A cheap texture pass crossfades those samples at up to 60fps; the full shader runs at 20fps on desktop and 15fps at mobile viewport sizes. Scrolling below the hero dims the shader for readability without reducing its frame rate or pixel budget. Crossfading adds about one shader-sample interval of latency. No pixels, ImageBitmaps, or per-frame messages are copied back to the main thread.
Cache resolution is capped at 320k desktop / 160k mobile pixels and decreases under GPU pressure using asynchronous GPU timestamps (worker-frame timing is the fallback). Both texture caches together use at most about 2.44 MiB on desktop / 1.22 MiB on mobile, excluding the output/compositor buffers. They are replaced on buffer-size changes, reused otherwise, and rebuilt after device/context recovery. Independent CSS-to-buffer scaling on both axes preserves the circular O. The smaller mnix lettering is persistent white DOM text anchored through the JSON geometry.
Reduced motion and hidden pages pause worker rendering. Unsupported OffscreenCanvas, worker startup failures, and unavailable worker GPU rendering leave the white wordmark text visible. GPU device/context loss hides the canvas while the worker rebuilds its rendering state. Resize/font changes are coalesced on the main thread; visibility, motion preferences and page-cache transitions are forwarded as lifecycle messages. The worker uses its own RAF, with a timer fallback where worker RAF is unavailable. Frame deadlines retain their remainder so a 30fps target does not drift toward 20fps on a 60Hz display.
Fonts are served from local Latin WOFF2 assets in fonts/ (about 75 KiB total), with font-display: swap and same-origin preloads. The assets are the Google Fonts versions of JetBrains Mono, Instrument Sans, and Lexend; each font's upstream SIL Open Font License is included beside it. There is no runtime Google Fonts stylesheet or cross-origin font dependency.
Logo placement batches DOM measurements before style writes, fits the suffix using its measured width-to-font-size ratio, and sends worker resize messages only when shader geometry changes. Section entrances are prepared after first paint, one nearby section per task. Headings animate as a single element rather than being rewritten into independently animated word layers. The installer caches its last markup and labels; its playback timer runs only while the demo is visible, playing, and the page is foregrounded.
Content sections use paint/layout containment and play their fade/slide entrances once. After a section has been rendered, content-visibility: auto lets the browser skip its offscreen contents while remembering its natural height; it is not applied to unvisited sections, avoiding estimated-height jumps on the first scroll. The section rail observes a narrow viewport-center band, changes only the old/new active links, and animates only its scale rather than repainting a color transition every frame. Panels do not blur the continuously animated background, and text does not animate paint masks or blur filters; those effects caused compositor and paint stalls during mobile scrolling. The suffix is fixed beside the shader, above the darkening scrim and beneath page content so its white text retains full contrast. The hero tagline sits directly beneath the suffix on desktop, using its computed anchor and font size. On mobile it uses the full width beneath the ring to keep the copy readable. It stays fixed in the same text layer. The suffix and tagline are full-opacity white while the hero is in view and fade completely out below it. The shader dims to 24% opacity; scroll dimming remains independent of shader speed.
The white mnix text is present in the initial HTML, with its logo and display fonts eagerly preloaded at high priority. It stays hidden until Lexend loads, the geometry-based size and anchor are applied, and the first shader frame is ready, avoiding fallback-font flashes and intermediate sizing. No shader placeholder image is requested. The worker initializes asynchronously after first paint; its first rendered frame starts a shared 450ms opacity fade for the shader and text. Reduced motion and unavailable GPU rendering keep the canvas hidden. coronal-poster.png remains an offline reference asset, not a page placeholder.
A transparent fixed <header> contains the main navigation: Models on the left, and Report a bug, GitHub and Install on the right. Install is a normal #install anchor; root scroll padding keeps the destination below the header. The highlighted Try Omnix section offers the platform installer download and the Docker app source with setup instructions. Its two option cards stack on mobile. Header structure takes inspiration from Omarchy’s navigation and install anchor, with Omnix styling and no blur or opaque background.
Link previews use Open Graph and Twitter Card metadata in the static HTML head. social-preview.png is the public 1200 × 630 preview image; social-preview.svg is its editable source. After editing the SVG, render it to an opaque PNG at the same dimensions and update both files.
shader-profile.json is a versioned shader registry. activeShader selects an enum in shaders (currently CORONAL); each entry links its source, renderer, poster, reference screenshot, and measured geometry. Coordinates are normalized square artwork with a top-left origin. Coronal's isolated 560 × 560 render at time 6 seconds is saved in coronal-reference.png: the circular rim is approximately centered at (280, 280), radius 162 pixels. Thus geometry.ring.center is (0.5, 0.5), and radius is 162 / 560. Ring bounds exclude animated plumes; effectBounds covers the rendered glow.
shader-layout.js computes the shared text anchor in CSS pixels: x = artworkLeft + (ring.center.x + ring.radius + clearance) × artworkSize, y = artworkTop + ring.center.y × artworkSize. The suffix stays vertically centered at that point; its width is 46% of the viewport. The O rim diameter is 1.61 times the suffix font size so it reads as a letter; on mobile the profile's ringDiameterToFontSize raises it to 2.35, since a phone has vertical slack the desktop letter ratio would otherwise leave empty above the wordmark. The combined wordmark is centered, and the subtitle is left-aligned with the suffix. Placement keeps the complete O rim inside the viewport. The page, profile, worker, and shader imports share a logo asset version to prevent stale combinations after an update. Desktop/mobile clearance is configurable; it is currently zero so the suffix sits directly beside the measured rim. The shader and white text use the same placement geometry. GL samples use independent CSS-to-buffer conversion on both axes, preserving the circle at any render resolution.
To add another O shader, add an enum entry with its source links, renderer identifier, screenshot measurement, center, radius, derived ring bounds, shader origin, and effect bounds. Implement its worker renderer, then select its enum through activeShader or the canvas's data-shader attribute. An unsupported renderer leaves the white text visible. Layout and render budgets are shared across entries. CSS provides the initial/no-JavaScript fallback until the registry loads.
Run the geometry, pacing, cache reuse and lifecycle checks with node --test tests/*.test.cjs. The worker GL command tests use a controlled clock/GL recorder; real pixel output, driver errors, thread isolation, mobile resizing and context recovery also require browser verification.
column-scroll.js applies differential scrolling to every section with a two-column .split. On desktop, a shorter left column starts aligned with the right column's top and moves more slowly as the section passes the header. If the left height is L, the right height is R, and scroll distance past that starting point is d, its downward translation within the section is (R - L) × clamp(d / R, 0, 1). Its resulting screen speed is L / R of normal scrolling, and the translation stays between zero and the available height difference. The right column uses normal document scrolling throughout. Equal or taller left columns, the stacked layout at 860px and below, and reduced-motion mode retain the original layout and scrolling.
The left column's pane clips overflow without creating a nested scroll container. A 28px gradient fades text crossing the visible top boundary beneath the header or the bottom of the viewport; fully visible text stays opaque. Set data-scroll-fade="false" on a .split to retain differential scrolling without edge fading. Focus within the copy also removes its mask so focused content is visible. Natural column sizes and document positions are cached after font loads, resizing, and section reveals. Passive scroll events only schedule one animation frame, which computes translations and masks from cached geometry; unchanged styles are not written again.
.split always keeps the copy first in the DOM, so reading order and column-scroll.js's copy/panel assumption hold in every section. The desktop showcase in #flavors leads with the interactive demo instead, so it swaps the columns visually with grid order and a wider demo track; both properties are reset at 860px so the stacked layout reads copy first again.
The Plasma - Atrium preview mirrors the real Atrium desktop: its wallpaper is a CSS gradient approximating the pink/blue ribbon theme, it shows the two virtual desktops Atrium configures, and its dock packs many small icons. The tiled Hyprland demo keeps four workspaces. Apps with no icon asset get a CSS glyph (dock-glyph) rather than a text stub; an icon value with no / in it is a glyph name.
In the Plasma - Atrium preview, each terminal group carries a slot-N class fixing its seat on the desktop, and front/back marks only which group is focused. The two are separate so that focusing a group raises it with a new z-index and dims the other with filter, without moving either window; binding the transform to the focus state instead makes the two windows trade places on every click.
Choosing a desktop in #flavors opens a terminal overlay inside the preview pane showing the command that performs the switch, replayed line by line from switchSteps in desktop-preview.js; the timers are cleared and restarted on each change so a repeated switch still reads from the top. omnix apply desktop=… theme=… stands in for a nixos-rebuild switch, and the closing line repeats that the previous desktop stays in the boot menu. The command types itself in at 11ms per character (~600ms for the ~54-character command) with a blinking caret, and the four response lines are scheduled relative to the moment the last character lands, so the response provably cannot start early. The command line is kept fully opaque throughout — the staggered fade-in the response lines use would otherwise show the opening characters at partial opacity, and .switch-log .switch-cmd needs that extra specificity to beat .switch-log span.
The console terminates itself once the apply reports success: SETTLE_MS (220ms) after the final line it closes, then REBOOT_PAUSE (250ms) later the desktop changes over. autoClosing stops closeSwitch from committing the desktop itself in that path, which would otherwise flip the theme twice.
The console is a fixed size (height: clamp(132px, 44%, 190px), overflow: hidden) so it never enlarges as lines arrive and the text never re-wraps. The swap waits out a REBOOT_PAUSE of 250ms after the final response line, so the command finishes, the system appears to reboot, and only then does the desktop change over.
The desktop swap itself is deferred: clicking a tab updates the tab and caption immediately, but demo.dataset.desktop is not touched until the final response line prints, at which point commitDesktop swaps the whole preview (workspaces, wallpaper, dock, window chrome) and adds .themed. Setting the attribute on click made the OS change instantly while the command was still typing, so the apply read as decoration over an already-finished swap. Closing mid-sequence commits the pending desktop so the preview can never disagree with the pressed tab.
When the final line prints, the preview surface behind the terminal tints to that desktop's palette: Hyprland resolves its tint from the live --o-accent over 240ms, Plasma uses its blues over 420ms, matching each desktop's spawn animation. closeSwitch forces .themed for whichever desktop is selected, so closing mid-sequence cannot leave a half-transitioned surface.
The overlay only opens on a click, never on load, and closes via its Close button, a click on the backdrop, or Escape, which returns focus to the tab that opened it. It is absolutely positioned inside .desktop-demo, so it covers the preview pane and nothing else, and the demo is its containing block. Each desktop spawns windows its own way, so the terminal does too: Hyprland - Omarchy arrives as a tile sliding in from the right edge over 190ms with a crisp ease-out and no overshoot, right-aligned like a tile in a tiled layout, themed from the active --o-accent; KDE Plasma - Atrium floats up from below over 340ms with a softer settle, themed in the Plasma blues used elsewhere in the preview. Reduced motion drops both and shows every line at once, so the two per-desktop rules are listed explicitly in the prefers-reduced-motion block rather than relying on the generic override. The overlay is decorative — the switch itself is performed by the tab buttons behind it.
Use Hyprland - Omarchy and KDE Plasma - Atrium in desktop labels. Use Omarchy in prose. Follow the Omnix naming policy; existing flake URLs and installer IDs are compatibility identifiers.
The favicon is rendered from coronal-shader.js at time 6 seconds, with the same white O rim as the hero. It uses an artwork diameter of 1.35 times the icon size so the O fills the tab icon. The SVG embeds a 64px shader render; PNG and ICO variants include renders at native tab sizes.
The #who statement (eyebrow, headline, copy and buttons) magnetically falls into view as one unit, holds, then releases, all in CSS: scroll-driven animations on #who's view timeline (view-timeline: --who) plus position: sticky, with no scroll listeners. .statement-inner is sticky at the header height + 24px; it pins D = 100svh - pin + section top padding into the section's entry. The rush (translate, entry 0% to entry D) starts 45svh low and eases out on cubic-bezier(0.08, 0.82, 0.17, 1), rising up to ~4x faster than the scroll and reaching 1:1 exactly when the pin engages. The lock (transform, entry D to entry D + --who-lock) starts --who-lock / 2 low on a quadratic ease-out whose start speed is exactly 1:1, so the pinned block decelerates continuously to a dead stop with no kink or jump. #who::after adds --who-lock + --who-hold of scroll; the block then stays put for --who-hold (40svh above 860px, 22svh on phones, lock 24svh / 16svh) and the section's end carries it away 1:1. #who is display: block because a grid item's sticky box is confined to its grid row; section paint containment does not create a scroll container, so sticky resolves against #page. #who's scroll margin makes the rail link land at the start of the hold. There is no scroll snap: with the hold the section is taller than the screen, and Chrome's proximity snap pulled wheel scrolls back to its edges. Reduced motion and browsers without animation-timeline: view() keep the static layout, no hold and the normal reveal. tests/who-motion.test.cjs pins this contract.
Section reveals observe the first eyebrow or heading inside the viewport band from 10% to 65% of its height. The hero observes its wordmark with 12% top/bottom insets and fades when less than 65% remains visible. Its suffix and subtitle fade out in 180ms; the corona drops to 8% opacity. Pixel observer margins are rebuilt on resize because percentage root margins follow viewport width. This gives the hero time to dim before section copy appears.
Page copy follows the language guidelines in issue #2: benefit-first headings, acronyms explained on first use, no hype, and planned work labelled as planned. The FAQ's visible text and the FAQPage JSON-LD in <head> must match; edit both together. robots.txt, sitemap.xml and llms.txt describe the site to search engines and AI assistants; update lastmod and llms.txt when product facts change.
viewport-chrome.js decides whether a resize is only mobile browser chrome: iOS Safari's URL bar and toolbars sliding in and out while scrolling. Every resize-driven layout path (hero placement and the shader canvas, the scroll-linked hero fade, section reveals, the section rail, column scrolling and the desktop preview's reserved height) asks it first. A width change always re-runs layout. A height-only change is treated as chrome at any size on touch screens, and an event whose size has not changed is ignored, because iOS fires visualViewport resizes like that during the toolbar animation. Re-running the hero placement on those events rewrote the canvas and restarted the shader's render buffers mid-scroll, so the shader jerked when the toolbars returned. The hero placement also reads the wordmark's position from layout offsets rather than getBoundingClientRect() plus scrollY, so a legitimate re-placement mid-scroll lands in the same place. The fixed backdrops (body::before, .scrim) take the large viewport height (100lvh), which stays constant while the toolbars move. tests/viewport-chrome.test.cjs and tests/fixed-backdrop.test.cjs cover both.
The document itself never scrolls: html and body are overflow: hidden, and main and footer scroll inside #page, a fixed full-screen scroll container. The shader canvas, wordmark, tagline, backdrops, header and rail are siblings of #page, so a scroll never moves or re-composites them. As fixed layers of a scrolling document on iOS, the canvas was repositioned a frame late whenever its worker drew mid-scroll, so the shader jittered vertically during touch scrolling. The hero fade is a CSS scroll-driven animation on #page's named timeline (scroll-timeline: --page, shared with the fixed layers through timeline-scope), so opacity is computed in the same frame as the scroll. Browsers without scroll timelines fall back to coronal.js setting --hero-fade from #page's scroll position. column-scroll.js reads #page's scrollTop. #page takes keyboard focus on load, and again after in-page links in the header or rail, so arrow keys, Page Down and space scroll it. On iOS, Safari's toolbars no longer collapse while scrolling, and tapping the status bar no longer scrolls to the top.
benchmarks/index.html, linked from the Models button on the left of the top nav, is a generated static page. It ranks AI models by BridgeBench's published overall rating and joins each one to the OpenRouter catalog: price per million tokens, context length, and a per-row Route dialog with copy-ready config (base URL, model id, OPENROUTER_API_KEY placeholder, curl, OpenAI SDK and OPENAI_BASE_URL forms). No API key is ever embedded. OpenRouter-only models are behind a toggle; BridgeBench models that aren't on OpenRouter stay in the ranking with a disabled Route button.
Regenerate it locally and commit the output; there is no workflow for it:
python3 scripts/build_leaderboard.py
git add benchmarks && git commit -m "Refresh benchmarks snapshot"Edit scripts/leaderboard_template.html, not the generated page. The script (stdlib only) fetches two public sources:
- BridgeBench ratings from BenchLM's BridgeBench page, which republishes BridgeBench's overall ratings with their update date and links to BridgeBench's leaderboard and methodology. The script reads the page's embedded data; BenchLM's
robots.txtallows/benchmarks/. bridgebench.ai itself is never contacted: it sits behind a Cloudflare challenge and itsrobots.txtdisallows/api/. Only the overall rating is published there, so the per-axis scores aren't shown. Tied ratings share a rank. - The OpenRouter catalog from
https://openrouter.ai/api/v1/models(no key needed).:free/:batchvariants collapse into their base model and~…-latestaliases are dropped.
If BenchLM can't be fetched, the script falls back to benchmarks/bridgebench.json, a hand-maintained copy (schema in the file), and then to the previous snapshot marked stale. The OpenRouter source works the same way. The script exits non-zero only when both sources fail. Names are matched after normalization (GPT-6.1 Sol and openai/gpt-6.1-sol both become gpt-6-1-sol). The script prints BridgeBench models it couldn't match; add an alias to benchmarks/aliases.json only when the two are certainly the same model, since a wrong id routes requests to a different model.
Tests: python3 -m unittest discover -s tests -p 'test_*.py' covers normalization, variant collapsing, joining, the BenchLM parser and tie ranks, source precedence and the stale fallback; tests/leaderboard-data.test.cjs checks the committed snapshot's shape.
apps/index.html, linked from the Apps button next to Models in the top nav, lists apps with the workflow an agent verifies on Omnix, a link to the NixOS test that runs it, the last verification (date, commit, result) and a copy-ready install snippet. It is generated from apps/registry.json, the source of truth (its _schema documents every field), by scripts/build_apps.py (stdlib) and scripts/apps_template.html. Edit the registry or the template, never the generated page.
Presentation policy: apps/AGENTS.md requires every space to serve a clear purpose. Use content-sized layouts, give descriptions the available width, and show the compact Details menu directly on hover/focus.
Sections: the page groups apps into the sections listed, in order, in the registry's sections array ({id, title, blurb}): today Video, Image, Office, Documents and publishing, Browsers, Communication, Terminals, Developer tools and Desktops. Every app and candidate needs a section naming one of those ids; an unknown or missing id fails the build. Each section stays expanded as a regular block with its title, a one-line blurb and a summary such as "7 apps · 7 passing", followed by its cards; a row of anchor chips with counts under the lineup heading jumps to each section. A section with no apps or candidates is not rendered, and candidates appear in their own section marked Unknown. To add a group, append it to sections and point apps at it.
Status rules:
-
Every card shows one status colour: green Passing or Patched (works because Omnix carries a fix of its own, cited in the card), yellow Known gap, red Failing, gray Unknown (
verificationisnullbecause the test hasn't been run yet, or the app is a candidate with no test). Collapsed app cards show icon, name, category, and one expandable status pill with a chevron. The status pill opens Details; recorded Test run thumbnails live inside the dropdown and open a separate preview on hover/focus or tap. The preview uses natural dimensions when they fit and otherwise scales to the free space beside Details or above/below it without upscaling, keeping the focused controls visible. A 1:1 control shows original pixels with scrolling; Original opens the image separately. Moving into the preview keeps Details open, and Escape dismisses the image before its parent dropdown. The thumbnail remains an image link without JavaScript. Wide desktops use three columns, tablets two, and phones one. Details opens the full compact dropdown on hover or keyboard focus, at 80% of the app card's width and aligned to its right edge. Package source is first and open by default; Install, Verified workflow and Validation details are optional disclosures. Moving into the dropdown keeps it open. Leaving both closes it after a short delay; small page scrolls keep an active dropdown anchored without pinning it; major page scrolls (80px) dismiss both Details and screenshot previews, even when pinned; its evidence scrolls normally, and wheel gestures outside it scroll the page. One click/tap pins the dropdown. The pinned view follows its card when the page scrolls, fits the viewport, and flips above the card when space below is limited. Escape, the close button, the same Details control or a click outside dismisses it; keyboard dismissal restores focus. Expanded Details uses a compact icon/name header, consistent 13px description text, grouped date metadata, and flat 44px disclosure rows with trailing chevrons. Its position uses actual content height to choose the available space. Additional content lives in Package source, Install, Verified workflow and Validation details disclosures, with logs disclosed separately. Native app/evidence disclosures work without JavaScript, with the recorded screenshot linked under Validation details. -
verification.statusmust bepassing,patched,known-gap,failingoruntested; anything else fails the build. -
passing,patched,known-gapandfailingneedtest.pathandverification.verified_at, or the build fails and writes nothing. -
candidatesare apps with no test yet. They always render as Untested and cannot carry a status. -
A status is never set by hand without a test run behind it.
How an agent adds an app:
- Write a NixOS test (
pkgs.testers.runNixOSTest) in the repo that owns the app, e.g.Omnix/tests/fhs.nix, wired into the flake'schecks. - Add a registry entry: id, section, name, icon, category, kind, description, platforms, the workflow steps the test performs,
test(repo, path, check, url) andinstall. - Run the test, e.g.
nix build github:Omnix-Linux/Omnix#checks.x86_64-linux.fhs -L. - Record the result in
verification: status,verified_at, the tested commit, notes and a short log excerpt. - Rebuild and commit:
python3 scripts/build_apps.py
git add apps && git commit -m "Apps: verify <app>"Re-run the tests and update verification whenever Omnix, nixpkgs or an app changes. Tests: tests/test_build_apps.py covers validation (including section ids), rendering (sections in order, empty ones skipped, summary counts, every app exactly once) and that the committed page is current; tests/apps-data.test.cjs checks the registry and page shape and the nav on all three pages.
Browser acceptance checks for issue #8: serve the repository with python3 -m http.server 4187 --bind 127.0.0.1, then run node tests/apps-browser-check.cjs with Playwright available (PLAYWRIGHT_MODULE may point to its installed module path). APPS_BASE_URL overrides the local base URL. Checks cover desktop/tablet/mobile density, anchored details dropdown without page reflow, always-expanded sections, accordions, copy, in-dropdown test screenshots, keyboard/Escape, touch, no-JavaScript access and the fallback without the Popover API. The reduced card controls support three desktop columns with compact rows. Details appears directly on hover/focus with no intermediate preview or second expansion step. Playwright checks full hover access, pinning, hover retention during page/panel scrolling, and unchanged page layout.
Candidates and apps can carry a distribution block: where the app is published (GitHub releases, nixpkgs, ...), the release, artifact, license, checksum file and attestation status, plus the date it was checked. A card shows Attested only when GitHub's attestations API (or gh attestation verify) confirms the artifact's digest; otherwise it says Not attested. As of 2026-10-06, none of the listed GitHub artifacts have attestations.
App icons in assets/app-icons/ come from each app's source repository: the ArtCraft apps (PhotoCraft, VectorCraft, FilmCraft, LightCraft, PrintCraft, EffectCraft, DesignCraft) from github.com/storytold under Apache-2.0. They're downscaled to 128 px WebP.
The hero shader dims on a trigger rather than with the scroll position, with hysteresis. Two 1px markers sit in the hero: scrolling down, html.hero-dimmed is added once .hero-dim-trigger (20svh) passes above the top of #page; scrolling back up, it's removed as soon as .hero-wake-trigger (45svh) comes back, while more than half the hero is on screen. Between the two the state holds, so there's no thin band where the hero is mostly visible but the shader is still dark. The class drives a fixed 0.5s CSS fade to 8% opacity and back, from an IntersectionObserver in coronal.js. A fast flick past the hero therefore can't skip the fade. The wordmark and tagline keep their scroll-driven fade. tests/shader-dim.test.cjs pins this.
The Apps catalog separates Terminals, Browsers and Communication. Terminal coverage includes Kitty, tmux, WezTerm, Alacritty, Ghostty and xterm; tmux is identified as a multiplexer. Browsers include Brave, Chrome, Chromium, Helium and Firefox. Communication includes Telegram, Signal, Slack, Discord, Element and Thunderbird.
The coordinated Omnix tests/apps-catalog.nix checks test terminal PTYs, terminfo, shell input and tmux session/pane behavior; browser checks load an offline HTTP fixture and verify JavaScript execution; communication checks cover unprivileged desktop startup. Account sign-in, messages, calls and mail delivery are outside these checks. Existing Kitty, Brave, Telegram, Signal and Slack test records remain linked to their original workflows.
Office includes suites (LibreOffice, ONLYOFFICE, Calligra, WPS Office and SoftMaker FreeOffice), standalone tools (Gnumeric and AbiWord), email and calendars (Thunderbird, Evolution, Kontact and Geary), notes (Joplin), and references (Zotero). LibreOffice's six components are described under its single suite entry. Thunderbird moves from Communication with its recorded startup evidence preserved. The twelve new entries have no recorded compatibility result, so their status is Unknown; the page's tested count includes only actual recorded runs. Empty workflow disclosures are omitted.