{"id":51631915,"url":"https://github.com/ajsb85/rive-peek","last_synced_at":"2026-07-13T08:31:04.374Z","repository":{"id":365837125,"uuid":"1273993244","full_name":"ajsb85/rive-peek","owner":"ajsb85","description":"RivePeek — native Windows Explorer preview handler that renders animated Rive (.riv) files in the preview pane via a Direct2D backend over the Rive C++ runtime.","archived":false,"fork":false,"pushed_at":"2026-06-19T06:54:31.000Z","size":2534,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-19T07:25:51.863Z","etag":null,"topics":["animation","com","cpp","direct2d","file-preview","ipreviewhandler","prevhost","preview-handler","riv","rive","shell-extension","thumbnail-provider","wic","windows","windows-explorer"],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ajsb85.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-19T04:45:51.000Z","updated_at":"2026-06-19T06:54:35.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ajsb85/rive-peek","commit_stats":null,"previous_names":["ajsb85/rive-win-preview"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/ajsb85/rive-peek","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ajsb85%2Frive-peek","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ajsb85%2Frive-peek/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ajsb85%2Frive-peek/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ajsb85%2Frive-peek/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ajsb85","download_url":"https://codeload.github.com/ajsb85/rive-peek/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ajsb85%2Frive-peek/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35416386,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-13T02:00:06.543Z","response_time":119,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["animation","com","cpp","direct2d","file-preview","ipreviewhandler","prevhost","preview-handler","riv","rive","shell-extension","thumbnail-provider","wic","windows","windows-explorer"],"created_at":"2026-07-13T08:31:00.052Z","updated_at":"2026-07-13T08:31:04.368Z","avatar_url":"https://github.com/ajsb85.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RivePeek — Rive (`.riv`) preview handler for Windows Explorer\n\n![platform](https://img.shields.io/badge/platform-Windows%2010%20%2F%2011%20x64-0078D6?logo=windows)\n![language](https://img.shields.io/badge/C%2B%2B-17-00599C?logo=cplusplus)\n![graphics](https://img.shields.io/badge/render-Direct2D%20%2B%20WIC-5C2D91)\n[![license](https://img.shields.io/github/license/ajsb85/rive-peek)](LICENSE)\n[![release](https://img.shields.io/github/v/release/ajsb85/rive-peek?include_prereleases\u0026sort=semver)](https://github.com/ajsb85/rive-peek/releases)\n\nRivePeek is a native Windows **Shell preview handler** that renders animated\n[Rive](https://rive.app) `.riv` files live in the File Explorer preview pane —\nthe same mechanism Windows uses to preview PDFs, Office documents and images.\n\nSelect a `.riv` file with the preview pane open (**Alt + P**) and the default\nartboard plays, fit and centered, isolated inside Windows' `prevhost.exe`\nsurrogate process so a malformed file can never destabilize Explorer.\n\nIt also registers an **`IThumbnailProvider`**, so `.riv` files show a static\nfirst-frame thumbnail as their icon in the Explorer file grid.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/demo.gif\" width=\"820\"\n       alt=\"File Explorer showing .riv files with first-frame thumbnails in the grid and an animated Rive character playing in the preview pane.\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\u003csub\u003eRivePeek in File Explorer — static \u003ccode\u003e.riv\u003c/code\u003e thumbnails in the grid and a live, animated preview pane.\u003c/sub\u003e\u003c/p\u003e\n\n| Headless render (`rivshot`) | Live preview pane (captured from the real COM handler) |\n|---|---|\n| ![coin](docs/render-coin.png) | ![preview pane](docs/preview-pane.png) |\n\n\u003e Validated against **887 marketplace `.riv` files: 886 render successfully\n\u003e (99.9%)**. The single outlier is rejected as `malformed` by the Rive runtime\n\u003e itself during import.\n\n---\n\n## Features\n\n- 🎬 **Animated preview pane** — the default artboard's state machine (or first\n  animation) plays live while the file is selected.\n- 🖼️ **Static thumbnails** — first-frame `.riv` thumbnails as grid icons via\n  `IThumbnailProvider`.\n- 🔤 **Text rendering** — `WITH_RIVE_TEXT` enabled, shaping via HarfBuzz +\n  SheenBidi (statically linked); text runs draw with the Direct2D backend.\n- 📐 **Flexbox layout** — `WITH_RIVE_LAYOUT` enabled, backed by Yoga, so\n  layout-driven artboards and responsive components position correctly.\n- 🛡️ **Process-isolated + fault-guarded** — runs in Windows' `prevhost.exe`\n  surrogate, and load/advance/draw/teardown are wrapped in SEH so even a\n  pathological file degrades to a blank preview instead of crashing the host.\n- 💤 **Idles when backgrounded** — animation pauses when the host/preview isn't\n  in the foreground, and resizes never steal focus from Explorer's file list.\n- 🪶 **Self-contained** — the core Rive runtime plus HarfBuzz/SheenBidi are\n  compiled in; rendering is pure Direct2D + WIC. `RivePeek.dll` links the static\n  CRT and needs no redistributables.\n- 🔒 **Per-user install** — registers under `HKCU`; no administrator rights.\n- 🧪 **Headless tooling** — `rivshot` renders any `.riv` to PNG and validates a\n  whole corpus.\n\n---\n\n## How it works\n\nWindows previews files through COM components hosted out-of-process. When you\nselect a `.riv` file:\n\n1. **Explorer** reads `HKCR\\.riv\\ShellEx\\{8895b1c6-…}` to find the preview\n   handler's CLSID.\n2. The handler's `AppID` points at the **`prevhost.exe`** surrogate, so Windows\n   loads `RivePeek.dll` into that isolated host — not into `explorer.exe`.\n3. Explorer hands the file to the handler as a read-only `IStream`\n   (`IInitializeWithStream`), then gives it an `HWND` + bounding rectangle\n   (`IPreviewHandler::SetWindow`) and calls `DoPreview()`.\n4. RivePeek parses the file with the **Rive C++ runtime**, then draws every\n   frame with a **Direct2D** renderer into a child window, advancing the default\n   state machine (or first animation) on a ~30 fps timer.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/architecture-dark.svg\" width=\"820\"\n       alt=\"Pipeline: explorer.exe hands foo.riv to RivePeek inside the isolated prevhost.exe surrogate via CoCreateInstance, IInitializeWithStream::Initialize and IPreviewHandler::SetWindow/DoPreview; RivePeek imports the file with the Rive C++ runtime and renders it with a Direct2D renderer into an ID2D1HwndRenderTarget.\"\u003e\n\u003c/p\u003e\n\n### The Direct2D backend\n\nRather than compiling Rive's heavyweight GPU \"Rive Renderer\" (and its shader\ndependency chain), RivePeek implements Rive's two abstract embedding interfaces\ndirectly on top of Direct2D + WIC:\n\n- **`rive::Factory`** → `D2DFactory`: builds paths (`ID2D1PathGeometry`),\n  solid/linear/radial brushes, and decodes images via **WIC**.\n- **`rive::Renderer`** → `D2DRenderer`: maps `save`/`restore`, `transform`,\n  `clipPath` (geometry-mask layers), `drawPath` (fill/stroke), `drawImage`, and\n  `drawImageMesh` (per-triangle affine-textured fills) onto an\n  `ID2D1RenderTarget`. Non-`srcOver` blend modes are composited with the\n  Direct2D `Blend` effect (via `ID2D1DeviceContext`).\n\nThe same backend powers a **WIC bitmap render target** for headless thumbnail\ngeneration (no GPU or window required), which doubles as the test harness.\n\nThe **core** Rive runtime is compiled with text (`WITH_RIVE_TEXT`, linking\nHarfBuzz + SheenBidi) and layout (`WITH_RIVE_LAYOUT`, linking Yoga) enabled;\naudio and scripting stay `#ifdef`-guarded off (see [Limitations](#limitations)).\n\n---\n\n## Repository layout\n\n```\nsrc/\n  guids.hpp           CLSID + registration constants\n  rive_d2d.hpp/.cpp   Direct2D implementation of rive::Factory + rive::Renderer\n  rive_scene.hpp/.cpp loads a .riv, instantiates artboard + state machine, draws\n  preview_handler.*   the COM handler (IPreviewHandler, IInitializeWithStream,\n                      IOleWindow, IObjectWithSite, IPreviewHandlerVisuals,\n                      IThumbnailProvider)\n  dllmain.cpp         COM class factory, DLL exports, (un)registration\n  rivepeek.def        exported entry points\ntools/\n  rivshot.cpp         headless .riv → .png renderer / validation harness\n  preview_test.cpp    in-process integration test of the full COM pipeline\n  surrogate_test.cpp  out-of-process (prevhost.exe) activation test\n  thumb_test.cpp      IThumbnailProvider test (.riv → thumbnail .png)\nbuild/                MSVC build scripts (no CMake required)\ninstall/              register.ps1 / unregister.ps1 / RivePeek.reg\nrive-runtime/         Rive C++ runtime (git submodule)\nthird_party/          HarfBuzz + SheenBidi (text) and Yoga (layout), gitignored\n                      (see get_text_deps.bat / get_yoga.bat)\n```\n\n---\n\n## Quick start (prebuilt)\n\n1. Download `RivePeek.dll` (and the `install` scripts) from the\n   [latest release](https://github.com/ajsb85/rive-peek/releases).\n2. Put `RivePeek.dll` somewhere stable, e.g. `%LOCALAPPDATA%\\RivePeek\\`.\n3. Register it (per-user, no admin):\n   ```powershell\n   .\\install\\register.ps1 -Dll \"$env:LOCALAPPDATA\\RivePeek\\RivePeek.dll\"\n   ```\n4. In Explorer, press **Alt + P** for the preview pane and select a `.riv` file.\n\nPrefer to build from source? See below.\n\n---\n\n## Building\n\nRequirements: **Visual Studio 2022 Build Tools** (MSVC v143) and the **Windows\n10/11 SDK**. No CMake needed — the build scripts configure the toolchain\nthemselves.\n\n```bat\ngit clone --recurse-submodules https://github.com/ajsb85/rive-peek.git\ncd rive-peek\nbuild\\build_all.bat\n```\n\n`build_all.bat` first runs `get_text_deps.bat` and `get_yoga.bat`, which clone\nthe dependencies into `third_party/` (~160 MB) — needs `git` and network access\non the first build. They are pinned to the revisions Rive uses\n(`rive-app/harfbuzz @ rive_13.1.1`, `Tehreer/SheenBidi @ v2.6`,\n`rive-app/yoga @ rive_changes_v2_0_1_2`) and are compiled into `text_deps.lib`\nand `yoga.lib`. **Yoga must be Rive's fork**, not upstream — the runtime stores\n`YGNode` by value, which upstream made opaque.\n\nThis produces, in `build\\bin\\`:\n\n| Artifact | Purpose |\n|---|---|\n| `RivePeek.dll` | the preview + thumbnail handler (register to install) |\n| `rivshot.exe` | `rivshot in.riv out.png [size] [seconds]` — headless render |\n| `preview_test.exe` | drives the COM pipeline in-process and screenshots it |\n| `surrogate_test.exe` | activates the handler in `prevhost.exe` (like Explorer) |\n| `thumb_test.exe` | renders the `IThumbnailProvider` thumbnail to a PNG |\n\nIndividual steps: `get_text_deps.bat` / `get_yoga.bat` (clone deps),\n`build_text_deps.bat` (`text_deps.lib`), `build_yoga.bat` (`yoga.lib`),\n`build_rive_core.bat` (the `rive_core.lib` static lib), `build_dll.bat`,\n`build_rivshot.bat`, `build_test.bat`, `build_surrogate_test.bat`,\n`build_thumb_test.bat`.\n\n\u003e The build environment is configured by `build\\env.bat`, which auto-detects the\n\u003e newest MSVC toolset and Windows SDK via `vswhere`. It deliberately avoids\n\u003e `vcvars64.bat` because that script aborts when the optional `cmake` developer\n\u003e extension is absent.\n\n---\n\n## Installing\n\nRegistration writes the COM class, the `.riv` association, and the\napproved-preview-handlers entry under `HKEY_CURRENT_USER\\Software\\Classes` —\n**per-user, so no administrator rights are required** (the same scheme\nMicrosoft's own preview-handler sample uses).\n\n```powershell\n# install\ninstall\\register.ps1\n\n# uninstall\ninstall\\unregister.ps1\n```\n\nEquivalent manual routes: `regsvr32 build\\bin\\RivePeek.dll`, or edit + merge\n`install\\RivePeek.reg`.\n\nAfter installing, open Explorer, enable the preview pane (**Alt + P**) and click\na `.riv` file. The handler is loaded by the native 64-bit\n`prevhost.exe` surrogate (AppID `{6d2b5079-2f0b-48dd-ab7f-97cec514d30b}`).\n\n---\n\n## Testing\n\n```powershell\n# Render one file headlessly\nbuild\\bin\\rivshot.exe sample.riv sample.png 512\n\n# Exercise the entire COM handler in-process and capture the preview window\nbuild\\bin\\preview_test.exe sample.riv capture.png\n\n# Render every .riv in a folder and tally pass/fail\nbuild\\validate.ps1 -Dir \"path\\to\\riv\\files\"\n\n# Replicate Explorer's out-of-process activation (loads the handler in prevhost.exe)\nbuild\\bin\\surrogate_test.exe sample.riv\n\n# Produce the static thumbnail the file grid would show\nbuild\\bin\\thumb_test.exe sample.riv thumb.png 256\n```\n\n**Diagnostics:** set the environment variable `RIVEPEEK_LOG=1` to make the\nhandler append a trace of each COM call (`Initialize`, `DoPreview`,\n`GetThumbnail`, errors) to `%TEMP%\\RivePeek.log`. It is silent otherwise.\n\n`preview_test.exe` performs the exact sequence the Shell does\n(`DllGetClassObject` → `IClassFactory::CreateInstance` → `QueryInterface` for\nevery required interface → `IInitializeWithStream::Initialize` → `SetWindow` →\n`DoPreview`) and verifies it without needing the DLL registered.\n\n---\n\n## Limitations\n\n- **Text** (`WITH_RIVE_TEXT`, HarfBuzz + SheenBidi) and **flexbox layout**\n  (`WITH_RIVE_LAYOUT`, Yoga) are both supported. Vector art, shapes, gradients,\n  clipping, raster images, image meshes, bones, constraints, state machines,\n  text and layout all render. **Audio** and **scripting** remain unsupported.\n- **Scripting** (Rive's experimental scripted objects) is not supported; such\n  objects are skipped on import. One corpus file exercises a destruction-order\n  bug in those objects — the SEH teardown guard contains it (blank preview)\n  rather than crashing.\n- **Blend modes** are fully supported (all 15 Rive modes via the Direct2D\n  `Blend` effect). One caveat: a blended shape *inside a clip* composites against\n  the pre-clip backdrop, since the clip is realized with a D2D layer whose\n  contents aren't yet flattened when the backdrop is sampled.\n- Files using a different Rive **major version** than the bundled runtime\n  (currently 7) are reported as unsupported.\n- Adding text/layout is a forward path: build the relevant runtime\n  dependencies, define `WITH_RIVE_TEXT` / `WITH_RIVE_LAYOUT`, and implement\n  `Factory::decodeFont`.\n\n---\n\n## Troubleshooting\n\n| Symptom | Fix |\n|---|---|\n| Preview pane is blank / shows nothing | Make sure it's the **Preview** pane (Alt + P), not the **Details** pane. Re-run `install\\register.ps1` (it restarts Explorer). |\n| `.riv` shows a generic picture/file icon | Old thumbnail cache. Restart Explorer, or clear it: `taskkill /f /im explorer.exe \u0026 del /q \"%LOCALAPPDATA%\\Microsoft\\Windows\\Explorer\\thumbcache_*.db\" \u0026 start explorer`. |\n| Nothing happens at all | Confirm **Folder Options ▸ View ▸ \"Show preview handlers in preview pane\"** is enabled. |\n| Want to see what the handler is doing | Set `RIVEPEEK_LOG=1` and read `%TEMP%\\RivePeek.log` — it traces every COM call. |\n| Build can't overwrite `RivePeek.dll` | The DLL is loaded by a running `prevhost.exe`/Explorer. Close previews (or `taskkill /f /im prevhost.exe`) and rebuild. |\n\n---\n\n## Roadmap\n\n- [x] **WiX MSI installer** (`installer/` — dual-scope per-user / per-machine,\n  `WixUI_Advanced`); attached to each [release](https://github.com/ajsb85/rive-peek/releases)\n- [ ] **Code-sign** the DLL and MSI (Authenticode) — the current installer is unsigned\n- [x] **Full blend-mode support** — all 15 Rive blend modes via the Direct2D\n  `Blend` effect (`ID2D1DeviceContext`), with a clean srcOver fallback\n- [x] **Text rendering** (`WITH_RIVE_TEXT` — HarfBuzz + SheenBidi, statically\n  linked); shaping + variable/embedded fonts\n- [x] **Yoga-based layout** (`WITH_RIVE_LAYOUT` — rive-app/yoga, statically\n  linked); flexbox positioning for responsive artboards\n- [x] **Idle pause** — the animation timer stops advancing when the host/preview\n  isn't in the foreground, and resize/reparent use `SWP_NOACTIVATE` so the pane\n  never steals keyboard focus from Explorer's file list\n\n---\n\n## Contributing\n\nContributions are welcome — see [`CONTRIBUTING.md`](CONTRIBUTING.md) for how to\nbuild, the project layout, coding conventions, and good first issues (the\nroadmap items above are all self-contained starting points).\n\n---\n\n## Credits \u0026 license\n\n- Built on the [Rive C++ runtime](https://github.com/rive-app/rive-runtime)\n  (MIT), included as a submodule.\n- COM preview-handler structure (interface set, `QISearch` dispatch, per-user\n  `HKCU` registration, prevhost surrogate AppID) follows Microsoft's canonical\n  [`RecipePreviewHandler`](https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/winui/shell/appshellintegration/RecipePreviewHandler)\n  sample.\n- RivePeek itself is MIT licensed — see [`LICENSE`](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fajsb85%2Frive-peek","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fajsb85%2Frive-peek","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fajsb85%2Frive-peek/lists"}