Conversation
New sibling template alongside TemplateProject for plugins whose UI is
authored in HTML/CSS/JS via iPlug2's WebViewEditorDelegate instead of
IGraphics. Modeled on iPlug2's Examples/IPlugWebUI (current CMake
UI WEBVIEW + WEB_RESOURCES + WASM webview dist):
- TemplateProjectWebView.{cpp,h}: WebViewEditorDelegate plugin, single
Gain param, LoadIndexHtml() editor init, OnMessage() demo resize.
- CMakeLists.txt: UI WEBVIEW + WEB_RESOURCES (resources/web/*).
- resources/web/: index.html + script.js bridge + knob/button web
components (one Gain knob).
The WebView build is wired only through CMake (iPlug2::WebView defines
WEBVIEW_EDITOR_DELEGATE/NO_IGRAPHICS, links WebKit, bundles resources/web).
This template is CMake-only, so the legacy build machinery is omitted vs
the IGraphics template.
duplicate.py: add "web" to SUBFOLDERS_TO_SEARCH so resources/web assets
get project-name substituted when scaffolding from this template.
New template alongside TemplateProject and TemplateProjectWebView, for a REAPER extension (not a plugin) whose UI is authored in HTML/CSS/JS. Ported from iPlug2's Examples/IPlugReaperExtensionWebUI on the examples/reaper-ext branch. Carries the example's C++ across whole: IMPAPI imports, registered actions, dock toggle, per-project state via SaveProjectState/LoadProjectStateLine, the OnActionRun + OnIdle refresh pair, and the offline media-item processing that writes a gain-scaled .wav through an audio accessor and inserts it. CMake only, following TemplateProjectWebView, so the example's .sln, .xcworkspace, projects/, config/ and scripts/ are omitted. CMakeLists.txt points IPLUG2_DIR at ../iPlug2 and raises the minimum to 3.25 to match the sibling templates. config.h gains PLUG_NAME/PLUG_MFR/version/copyright so duplicate.py's AcmeInc rewrite has something to act on; the rest stays minimal, since an extension declares no formats, channel I/O or parameters. Verified: builds and links to reaper_*.dylib exporting only ReaperPluginEntry and SWELL_dllMain; duplicate.py produces a project with no leftover template strings (including the uppercase .RPP state key) that builds clean. Note the release-build limitation documented in the template's CLAUDE.md: an extension is not a bundle, so LoadIndexHtml() can only find the web UI in debug builds. Tracked upstream in iPlug2/iPlug2#1149. Requires no iPlug2 change: the framework is identical between the submodule's current master pin and the reaper-ext branch, so the pointer is left as is. CLAUDE.md: list all three templates, and note that the CMake-only ones have no config/ directory and so only work with duplicate.py's three-argument form.
Three bugs, all reproduced in a sandbox copy of the repo root before fixing.
The repo's own root files were rewritten unconditionally. That is right when the repo
is becoming your plugin's repo, and wrong when you are just adding a project alongside
the templates - and because the replace is a naive substring match, duplicating
TemplateProject also renamed TemplateProjectWebView to <NewName>WebView wherever the
docs mentioned it. Root rewriting is now off by default, prompted for when running on a
terminal, and forced with --rewrite-root / --no-rewrite-root. The root pass additionally
skips CLAUDE.md and README.md, which describe the templates rather than referring to a
project, so rewriting them is never correct.
The four-argument out-of-source form crashed on the CMake-only templates, which have no
config/ directory to read IPLUG2_ROOT from. find_iplug2_root() falls back to the iPlug2
submodule for those.
The same form never fixed CMakeLists.txt. It rewrote IPLUG2_ROOT in the xcconfig but
left IPLUG2_DIR at ${CMAKE_CURRENT_SOURCE_DIR}/../iPlug2, which is only true for a
project inside this repo, so the CMake build of an out-of-source duplicate was broken -
including TemplateProject's. fix_cmake_iplug2_dir() rewrites it to the right relative
path. Verified that the xcconfig and CMake paths both resolve to a real iPlug2 for an
out-of-source duplicate of each template, and that in-source duplicates keep ../iPlug2.
Also: prompt for the template, project name and manufacturer when arguments are omitted;
argparse for the usage text; skip PLUG_UNIQUE_ID randomisation when config.h has no such
id, as with a REAPER extension; drop the Python 2 claim, since input() differs there and
the shebang has always been python3; fix the PLUG_MFR_UID typo in the closing message.
CLAUDE.md and the new-plugin skill: document the templates, the prompt, --rewrite-root,
and that the CMake-only templates have no xcworkspace to open.
windows-latest is now the windows-2025-vs2026 image, which ships Visual Studio 2026. The windows-vs2022 preset pinned "Visual Studio 17 2022" and configure failed with "could not find any instance of Visual Studio". Add windows / windows-arm64ec presets with no generator field so CMake picks whichever Visual Studio is installed, and point CI at them. The vs2022 presets stay for anyone who wants to pin. Add a windows-templates matrix job for TemplateProjectWebView and TemplateProjectReaperExtensionWebView, which nothing built before. They are configured directly rather than via a preset: the root CMakeLists.txt only references PROJECT_NAME, and adding the other templates to it would be rewritten by duplicate.py. Also fix the build-cmake skill, whose every example passed --preset with -S [ProjectName]. That cannot work: presets are read from the source directory and only the repo root has a CMakePresets.json. The web section also named a "wam" preset that does not exist.
The template was derived from IPlugReaperExtensionWebUI but dropped the two halves of that example's release handling: the #else branch that loads the UI from REAPER's resource path, and the build-time deploy that puts it there. It built and ran in debug, where the UI is loaded from the source tree via __FILE__, and showed "file not found" in release. Restore both, using the WEB_RESOURCES_DIR argument added in iPlug2#1388 rather than the example's hand-rolled add_custom_command, and bump the submodule to pick it up. CLAUDE.md described the release case as an inherent limitation and told the reader to solve it themselves, which was never true - the example next door already solved it. It now documents both branches and the coupling that will break silently if only one side is renamed: the deploy directory is the project name, and SHARED_RESOURCES_SUBPATH in config.h has to agree with it.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 952e9c4395
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| <button-control onclick="SAMFUI(0)">Small</button-control> | ||
| <button-control onclick="SAMFUI(1)">Medium</button-control> | ||
| <button-control onclick="SAMFUI(2)">Large</button-control> |
There was a problem hiding this comment.
Wire the editor-size buttons to resize handlers
All three controls are presented as editor-size buttons, but the only corresponding C++ message tag is kMsgTagTest = 0, whose handler merely prints to stdout. Consequently, Small does not resize the editor, while Medium and Large send unhandled tags and do nothing; add distinct message tags and invoke the editor resize API for each size.
Useful? React with 👍 / 👎.
| if (typeof window['SPVFUI'] === 'function') { | ||
| window['SPVFUI'](this.paramId, normValue); | ||
| } |
There was a problem hiding this comment.
Do not echo host knob updates back as UI edits
When host automation, preset recall, or initialization calls OnParamChange, updateValueFromHost() reaches this unconditional SPVFUI call, sending the host-originated value back through the UI-to-host path. This can report automation changes as UI edits and create redundant feedback through the delegate; the host-update path should redraw without notifying the host, while drag and wheel paths should opt into notification.
Useful? React with 👍 / 👎.
| <h2>Offline processing</h2> | ||
| <div class="row"> | ||
| <label for="gain">Gain</label> | ||
| <input type="range" id="gain" min="0" max="100" value="100" oninput="OnGainInput(this.value)" /> |
There was a problem hiding this comment.
Synchronize slider changes before context-menu processing
Moving this slider only updates the displayed percentage; it does not send the new value to C++, so mGain remains unchanged until the page's Process button is clicked. If the user adjusts the slider and then invokes the registered “Process selected item” REAPER context-menu action, that action processes audio using the previous persisted gain rather than the value currently shown in the UI.
Useful? React with 👍 / 👎.
ReviewReviewed the diff (I focused on Overall this is a well-scoped, well-documented change — the commit messages and per-file Code quality / correctness
CI
Minor nits (non-blocking)
Test coverage
Nothing here blocks merging — solid work. |
The projects/*.vcxproj files pin PlatformToolset v143. The windows-2025-vs2026 runner image ships v143 for x86/x64, as VC.14.44.17.14.x86.x64, but only the VS2026 toolset for ARM64EC - so the x64 pass built fine and the ARM64EC pass failed with MSB8020, which read like a total toolchain failure but was not. Add the matching v143 ARM64EC component before building. This is a patch with a shelf life: the component ID carries the toolset version, so it tracks the image. Retiring the checked-in project files is the real fix, tracked in #27. Also correct the WAM target name in the build-cmake skill, which said -web-dist. No such target exists; WAMDist.cmake creates <name>-wam-dist.
ReviewWent through Minor:
|
The projects pinned PlatformToolset v143 in all 30 configuration blocks. When the GitHub runner image moved to VS2026 this broke: the image carries v143 for x86/x64 as a compatibility component, but only the VS2026 toolset for ARM64EC, so the x64 pass built and the ARM64EC pass failed with MSB8020. Use $(DefaultPlatformToolset) instead, so the projects build with whichever Visual Studio is installed - VS2022 gives v143, VS2026 gives its own. This is the same fix as the windows CMake preset earlier on this branch, which dropped its pinned generator for the same reason. Microsoft.Cpp.Default.props, which defines the property, is imported before the Label="Configuration" property groups that consume it, so the ordering is fine. Reverts the previous attempt at this, which tried to add the v143 ARM64EC component with vs_installer. That cannot work: VS2026 has no such component to add, and modify --quiet exits 0 without installing anything when asked for one that is not in the catalogue.
ReviewThis is a large but well-organized PR — the two new templates are thorough, the commit messages explain root causes clearly, and the CI/toolset fixes (unpinning the VS generator, One concrete issue I found worth fixing before merge:
|
All three are things a template teaches by example, so they propagate. Editor-size buttons did nothing. index.html sent SAMFUI(0/1/2) but EMsgTags only had kMsgTagTest = 0, whose handler printed to stdout - so Small printed a line and Medium and Large fell through to default. Add kMsgTagButtonSmall / Medium / Large and resize to sizes within the PLUG_MIN_/PLUG_MAX_ bounds in config.h. Guarded by WEBVIEW_EDITOR_DELEGATE rather than IPLUG_EDITOR, since Resize() comes from the webview delegate, matching Examples/IPlugWebUI. The knob echoed host values back to the host. OnParamChange -> updateValueFromHost -> updateValue called SPVFUI unconditionally, so host automation, preset recall and initialisation were all reported back as UI edits, outside any BPCFUI/EPCFUI gesture. updateValue takes a notifyHost flag; drag and wheel keep the default of true, updateValueFromHost passes false. The REAPER extension's gain slider only updated a label. mGain changed solely when Process was clicked, but it is also read by the "Media item context" action and written by SaveProjectState() - so right-clicking an item, or just saving the project, used a stale value while the UI showed the new one. Send kMsgTagSetGain on every move. Note the direction split: OnGainInput() is the user moving the slider and notifies C++, OnSetGain() is C++ setting it and only redraws. Routing the latter through the former would reintroduce the echo bug above. Both CLAUDE.md files described kMsgTagButtonSmall/Medium/Large mapping to Resize() calls that did not exist. That is now true rather than aspirational, and the host-echo rule is written down in both, since it is invisible until something automates the parameter.
|
All three confirmed against the code and fixed in dc77046. Editor-size buttons — correct, and Small was the only one reaching C++ at all: Knob echo — correct. Worth adding that the echo also happens outside any REAPER slider — correct, and slightly worse than described: That last one nearly reintroduced the second: One you didn't flag but which belongs with the first: both |
ReviewOverall this is a well-executed PR — two new CMake-only templates, a much-needed CI fix for the VS2026 image swap, opt-in root rewriting in Bug:
|
Adds the two WebView templates (plugin and REAPER extension), makes
duplicate.py's root rewriting opt-in, and — from getting all three templates verified on Windows — fixes CI and the REAPER extension's release build.The templates
TemplateProjectWebViewandTemplateProjectReaperExtensionWebView, both CMake-only, plus theirCLAUDE.mddocs.duplicate.pyno longer rewrites the repo root by default: a fourth argument places the project outside this repo and rewrites its iPlug2 paths, and--rewrite-rootis now needed to repointbump_version.py,CMakeLists.txt,.vscode/and.github/— which is what you want when the repo is becoming your plugin's repo rather than gaining another project alongside the templates.CI was broken independently of this branch
windows-latestis now thewindows-2025-vs2026image, which ships Visual Studio 2026. The preset pinned"Visual Studio 17 2022", so configure failed with "could not find any instance of Visual Studio". The last green Windows CMake run was 2026-05-17, before the image swap.Fixed by adding
windows/windows-arm64ecpresets with nogeneratorfield, so CMake picks whichever Visual Studio is installed. Thewindows-vs2022*presets stay for pinning. Verified both ways: the runner compiled with MSVC 19.51 (VS2026's toolset) while the same preset resolves to VS2022 locally.A
windows-templatesmatrix job now builds the two new templates, which nothing covered before. They are configured directly rather than via a preset, because the rootCMakeLists.txtonly referencesPROJECT_NAMEand adding the other templates to it would be mangled byduplicate.py's root rewrite.REAPER extension release UI
The template was derived from
IPlugReaperExtensionWebUIbut dropped both halves of that example's release handling: the#elsebranch that loads the UI from REAPER's resource path, and the build-time deploy that puts it there. It built and ran in debug — where the UI is loaded from the source tree via__FILE__— and showed "file not found" in release.Restored using the
WEB_RESOURCES_DIRargument added upstream in iPlug2#1388, rather than the example's hand-rolledadd_custom_command; the submodule is bumped to pick it up.CLAUDE.mdhad described this as an inherent limitation and told the reader to solve it themselves, which was never true; it now documents both branches and the coupling that breaks silently if only one side is renamed — the deploy directory is the project name, andSHARED_RESOURCES_SUBPATHinconfig.hhas to agree with it.Skill doc
Every example in
.claude/skills/build-cmakeusedcmake --preset X -S [ProjectName], which cannot work: presets are read from the source directory and only the repo root has aCMakePresets.json. Removed-S, documented the direct-configure alternative, and corrected the web section, which named awampreset that does not exist.Testing
Windows, VS2022. All three templates configure and build clean;
TemplateProjectandTemplateProjectWebViewproduce.exe/.vst3/.clap, the extension producesreaper_*.dll. CI is green on macOS and all three Windows jobs.The REAPER extension's release deploy was checked end to end against the merged submodule by deleting the deployed directory and confirming the rebuild recreated
index.htmlandscript.jsunderUserPlugins/TemplateProjectReaperExtensionWebView/.Two known gaps, neither introduced here:
LoadIndexHtml's release arm carries aTODO: make this work for windowsand passes a bare filename, and_iplug_add_web_resourcesonly setsMACOSX_PACKAGE_LOCATION, so nothing is staged into the bundle.TemplateProjectWebViewis no worse than iPlug2's ownIPlugWebUIexample here; both work in debug only. Worth fixing upstream.iplug_get_default_deploy_path(REAPEREXT), which resolves correctly there, but that arm was not exercised.