Skip to content

Add WebView templates, fix Windows CI and the REAPER extension's release UI - #26

Open
olilarkin wants to merge 8 commits into
masterfrom
templates
Open

Add WebView templates, fix Windows CI and the REAPER extension's release UI#26
olilarkin wants to merge 8 commits into
masterfrom
templates

Conversation

@olilarkin

Copy link
Copy Markdown
Member

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

TemplateProjectWebView and TemplateProjectReaperExtensionWebView, both CMake-only, plus their CLAUDE.md docs. duplicate.py no longer rewrites the repo root by default: a fourth argument places the project outside this repo and rewrites its iPlug2 paths, and --rewrite-root is now needed to repoint bump_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-latest is now the windows-2025-vs2026 image, 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-arm64ec presets with no generator field, so CMake picks whichever Visual Studio is installed. The windows-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-templates matrix job now builds the two new templates, which nothing covered before. They are configured directly rather than via a preset, because the root CMakeLists.txt only references PROJECT_NAME and adding the other templates to it would be mangled by duplicate.py's root rewrite.

REAPER extension release UI

The template was derived from IPlugReaperExtensionWebUI but dropped both 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.

Restored using the WEB_RESOURCES_DIR argument added upstream in iPlug2#1388, rather than the example's hand-rolled add_custom_command; the submodule is bumped to pick it up. CLAUDE.md had 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, and SHARED_RESOURCES_SUBPATH in config.h has to agree with it.

Skill doc

Every example in .claude/skills/build-cmake used cmake --preset X -S [ProjectName], which cannot work: presets are read from the source directory and only the repo root has a CMakePresets.json. Removed -S, documented the direct-configure alternative, and corrected the web section, which named a wam preset that does not exist.

Testing

Windows, VS2022. All three templates configure and build clean; TemplateProject and TemplateProjectWebView produce .exe/.vst3/.clap, the extension produces reaper_*.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.html and script.js under UserPlugins/TemplateProjectReaperExtensionWebView/.

Two known gaps, neither introduced here:

  • The release WebView UI for plugins is still broken on WindowsLoadIndexHtml's release arm carries a TODO: make this work for windows and passes a bare filename, and _iplug_add_web_resources only sets MACOSX_PACKAGE_LOCATION, so nothing is staged into the bundle. TemplateProjectWebView is no worse than iPlug2's own IPlugWebUI example here; both work in debug only. Worth fixing upstream.
  • macOS is untested for the REAPER extension deploy. The path comes from iplug_get_default_deploy_path(REAPEREXT), which resolves correctly there, but that arm was not exercised.

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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +131 to +133
<button-control onclick="SAMFUI(0)">Small</button-control>
<button-control onclick="SAMFUI(1)">Medium</button-control>
<button-control onclick="SAMFUI(2)">Large</button-control>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment on lines +140 to +142
if (typeof window['SPVFUI'] === 'function') {
window['SPVFUI'](this.paramId, normValue);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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)" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@claude

claude Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review

Reviewed the diff (I focused on duplicate.py, the two new templates' CMakeLists.txt/C++/CMake presets, CI workflow, and skill docs; skipped the boilerplate resource/asset files carried over from the existing template).

Overall this is a well-scoped, well-documented change — the commit messages and per-file CLAUDE.md docs make the "why" unusually easy to follow, and the PR body honestly flags the two known gaps (Windows plugin WebView release UI, untested macOS REAPER-ext deploy) rather than glossing over them.

Code quality / correctness

  • duplicate.py's new --rewrite-root opt-in is a real bug fix (the old unconditional root rewrite would mangle TemplateProjectWebView<NewName>WebView via naive substring match when duplicating TemplateProject). The ROOT_FILTERED_FILE_NAMES exclusion for CLAUDE.md/README.md is a sensible complement.
  • dirwalk()'s recursion is gated on SUBFOLDERS_TO_SEARCH/xcodeproj-name matches, so calling it on scriptpath for the root-rewrite pass won't accidentally descend into TemplateProject*/ — worth double-checking this wasn't obvious from the diff alone, but tracing the logic confirms it's safe.
  • config["PLUG_UNIQUE_ID"] is now only randomised if config["PLUG_UNIQUE_ID"]: for templates without a unique ID (the REAPER extension). This assumes parse_config() (in the iPlug2 submodule, bumped in this PR) returns a falsy value rather than raising KeyError for a config.h with no PLUG_UNIQUE_ID define. Since that function lives outside this repo I couldn't verify it directly, but the commit message states duplication of the REAPER extension template was verified end-to-end, so this is presumably covered — just flagging it as the one place where correctness depends on submodule behavior not visible in this diff.
  • Nice touch in TemplateProjectReaperExtensionWebView.cpp: gain is clamped for stereo-only WaveWriter, output paths are uniquified instead of overwritten, and section-source filenames are resolved via GetMediaSourceParent(). The comments explain non-obvious REAPER API behavior (e.g. IMPAPI silently null on typos, InsertMedia moving the edit cursor) rather than restating the code.
  • SendArbitraryMsgFromUI's kMsgTagProcessItem branch smuggles the gain percentage through ctrlTag (an int) rather than the data payload — documented in CLAUDE.md, a little unusual but reasonable given there's no parameter system to piggyback on.

CI

  • The windows / windows-arm64ec presets dropping the pinned generator to fix the VS2026 image swap is a reasonable, minimal fix, and keeping windows-vs2022* around for anyone who wants to pin is a good compromise.
  • The new windows-templates matrix job configuring TemplateProjectWebView/TemplateProjectReaperExtensionWebView directly (bypassing the root CMakeLists.txt) makes sense given duplicate.py's root-rewrite constraint, and closes a real coverage gap (nothing built these on Windows before).

Minor nits (non-blocking)

  • TemplateProjectWebView/CMakeLists.txt and TemplateProjectReaperExtensionWebView/CMakeLists.txt (and the .code-workspace files) are missing trailing newlines — cosmetic only.
  • The REAPER-extension release UI fix (using WEB_RESOURCES_DIR + bumping the submodule) is well explained, but its correctness ultimately rests on the iPlug2 submodule pin at 5c2df9d, which is outside this repo's diff — worth a quick sanity check that submodule change is itself merged/stable upstream before merging here, if that isn't already the case.

Test coverage

  • Testing described in the PR body (Windows/VS2022 for all three templates, CI green on macOS + 3 Windows jobs, manual deploy-directory verification for the REAPER extension release build) is solid manual coverage for what is inherently hard-to-unit-test scaffolding/build tooling. No automated test exercises duplicate.py's new prompting/--rewrite-root logic, but given this is a dev-tooling script rather than shipped product code, manual verification (as documented) seems like a reasonable bar here.

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.
@claude

claude Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review

Went through duplicate.py, both new templates (source, CMakeLists.txt, config.h), the CI workflow changes, CMakePresets.json, and the skill docs. This is a well-scoped, well-tested PR — the commit messages are unusually precise about why each fix is needed, and the accompanying CLAUDE.md docs for the new templates are thorough (e.g. the REAPER extension doc calling out the IMPAPI/null-pointer footgun and the SHARED_RESOURCES_SUBPATHWEB_RESOURCES_DIR coupling). A few observations, nothing blocking:

Minor: --rewrite-root silently no-ops for the two new templates

Root CMakeLists.txt is add_subdirectory(TemplateProject) and is not touched by this PR. duplicate.py's root-rewrite pass does a substring replace of inputprojectnameoutputprojectname over the repo root — so duplicating TemplateProject with --rewrite-root correctly repoints it, but duplicating TemplateProjectWebView or TemplateProjectReaperExtensionWebView with --rewrite-root won't touch that line at all (the string "TemplateProjectWebView" doesn't appear in root CMakeLists.txt, only "TemplateProject" does). The user would end up with bump_version.py/.vscode/.github repointed but the root CMake build still building the old TemplateProject, silently. The PR's own "known gaps" section in the description calls out two other gaps but not this one — might be worth a one-line callout in CLAUDE.md next to the --rewrite-root docs, since it's newly reachable now that two more templates exist to duplicate.

Nice fixes worth calling out

  • find_iplug2_root() falling back to the submodule path for config-less CMake templates, and fix_cmake_iplug2_dir() rewriting IPLUG2_DIR for out-of-source duplicates — this was a real gap (previously only the xcconfig path was rewritten, leaving CMake out-of-source builds broken, including for TemplateProject itself).
  • ROOT_FILTERED_FILE_NAMES skipping CLAUDE.md/README.md during root rewrite is a good catch — without it, a substring rewrite of TemplateProjectMyPlugin would have mangled TemplateProjectWebView mentions in the docs.
  • The Windows CI fix (unpinning the VS generator via windows/windows-arm64ec presets with no generator field, while keeping windows-vs2022* for pinning) is a clean way to survive the runner image swap without losing the ability to pin.
  • Restoring the REAPER extension's release-mode UI loading via WEB_RESOURCES_DIR (upstream) rather than a hand-rolled add_custom_command is the right call, and the .cpp comments correctly cross-reference SHARED_RESOURCES_SUBPATH in config.h so a future rename doesn't break it silently without a clue.

Nit

  • dirwalk()'s new filterednames=FILTERED_FILE_NAMES parameter uses a mutable default argument. It's never mutated in place here so it's safe, but it's a common Python footgun if someone extends this function later.

Not evaluated

  • Actual Windows/macOS build behavior (no CI access from this review) — the PR description documents thorough manual verification, which covers this well.
  • The upstream iPlug2#1388 WEB_RESOURCES_DIR submodule bump itself, since the submodule isn't checked out in this environment.

Overall: solid, well-documented, and the fixes are narrowly scoped to the problems described. The --rewrite-root gap above is the only thing I'd consider addressing (or at least documenting) before merge.

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.
@claude

claude Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review

This 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, $(DefaultPlatformToolset) in the vcxproj files) are correct, minimal responses to the underlying VS2026 image change. duplicate.py's opt-in root-rewrite is a sensible fix for the substring-collision bug it describes. Nice work overall.

One concrete issue I found worth fixing before merge:

--rewrite-root silently fails to repoint bump_version.py / root CMakeLists.txt for the two new templates

duplicate.py's root-rewrite pass (main(), the rewriteroot branch) does a plain substring replace of inputprojectnameoutputprojectname over the repo's root files. That works for TemplateProject, but not for TemplateProjectWebView or TemplateProjectReaperExtensionWebView:

  • Root CMakeLists.txt (line 13): add_subdirectory(TemplateProject) — the literal text is TemplateProject, which is shorter than TemplateProjectWebView/TemplateProjectReaperExtensionWebView, so it never appears as a substring and str.replace() is a no-op.
  • bump_version.py (line 10): PROJECT_ROOT = "TemplateProject" — same issue.

So running ./duplicate.py TemplateProjectWebView MyPlugin MyBrand --rewrite-root leaves the root CMakeLists.txt still building TemplateProject and bump_version.py still bumping TemplateProject/config.h, even though both the PR description and the new-plugin/CLAUDE.md docs added in this PR explicitly promise --rewrite-root repoints "bump_version.py, CMakeLists.txt, .vscode/ and .github/ at the new project." For a user duplicating one of the two new templates and turning the repo into their own (the documented use case for the flag), this means the build silently keeps building the original template instead of their plugin, and bump_version.py silently edits the wrong project's config.h.

(For comparison: dirwalk() does handle a related case correctly — it replaces both searchproject and searchproject.upper(), and it now skips CLAUDE.md/README.md at the root specifically because "duplicating TemplateProject also renamed TemplateProjectWebView ... wherever the docs mentioned it." Root CMakeLists.txt/bump_version.py need an analogous fix, e.g. an explicit rewrite of the add_subdirectory(...)/PROJECT_ROOT = ... lines rather than relying on the generic substring pass.)

Minor / worth a second look (not verified as bugs, just flagging for the author to double-check)

  • main()'s if config["PLUG_UNIQUE_ID"]: guard (for skipping unique-ID randomization on the REAPER extension template, which has no PLUG_UNIQUE_ID) depends on parse_config() returning a falsy value rather than raising KeyError for a missing define. The PR description says this was tested end-to-end, so likely fine — just noting the implicit dependency on parse_config's missing-key behavior, since that file isn't part of this diff.
  • Root CMakeLists.txt's single add_subdirectory(TemplateProject) plus the new windows-templates CI matrix job (which configures the two new templates directly, bypassing the root CMakeLists.txt/presets) means the root project can only ever build one of the three templates at a time. That's called out in the PR description as intentional, so just confirming I read the tradeoff the same way — not a defect.

Nits

  • bump_version.py and parse_config.py weren't touched by this PR, so the gap above isn't something introduced here exactly — but the PR is what makes it reachable, since it's the PR that adds the two new templates and documents --rewrite-root as the mechanism for pointing root tooling at them.

No concerns on the CI YAML, CMakePresets.json, or the new C++ (TemplateProjectWebView.cpp/h, TemplateProjectReaperExtensionWebView.cpp/h) — the offline media-processing code in the REAPER extension template in particular is careful about channel clamping, section-source resolution, and output-path collisions, and matches what the CLAUDE.md docs claim.

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.
@olilarkin

Copy link
Copy Markdown
Member Author

All three confirmed against the code and fixed in dc77046.

Editor-size buttons — correct, and Small was the only one reaching C++ at all: kMsgTagTest = 0 matched SAMFUI(0) by coincidence and printed to stdout, while 1 and 2 hit default: break. Added kMsgTagButtonSmall/Medium/Large with Resize() calls inside the PLUG_MIN_/PLUG_MAX_ bounds from config.h, guarded by WEBVIEW_EDITOR_DELEGATE rather than IPLUG_EDITOR since Resize() is the webview delegate's — matching Examples/IPlugWebUI. index.html now uses named constants instead of bare ints.

Knob echo — correct. Worth adding that the echo also happens outside any BPCFUI/EPCFUI pair, so the host sees bare parameter writes with no gesture around them. updateValue now takes a notifyHost flag: drag and wheel keep the default true, updateValueFromHost passes false. Those are the only call sites.

REAPER slider — correct, and slightly worse than described: mGain is also what SaveProjectState() writes into the .RPP, so saving a project after moving the slider persisted the stale value too, not just the context-menu action. Now sends kMsgTagSetGain on every move.

That last one nearly reintroduced the second: OnSetGain() (C++ → UI) called OnGainInput(), so putting the send in OnGainInput would have echoed every C++-originated update straight back. Split into SetGainLabel (shared redraw), OnGainInput (user → notify) and OnSetGain (C++ → redraw only).

One you didn't flag but which belongs with the first: both CLAUDE.md files already documented kMsgTagButtonSmall/Medium/Large mapping to Resize(...) calls, describing code that didn't exist. Those docs are what an agent reads before editing the template, so they're now accurate, and the host-echo rule is written down in both — it's invisible until something automates the parameter.

@claude

claude Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review

Overall 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 duplicate.py, and genuinely useful CLAUDE.md/skill doc updates. The REAPER extension's ProcessSelectedItem implementation and the debug/release WebView-loading split are both carefully commented and easy to follow. One correctness issue stood out that's worth fixing before merge.

Bug: --rewrite-root can corrupt build-cmake.yml's new template matrix

duplicate.py's dirwalk() still does a naive substring replace (line.replace(s, r), duplicate.py:89) with no word-boundary check. ROOT_FILTERED_FILE_NAMES (duplicate.py:46) was added to keep this from mangling CLAUDE.md/README.md when the root docs mention TemplateProjectWebView/TemplateProjectReaperExtensionWebView by name, but the same exposure exists in .github/workflows/build-cmake.yml, which isn't in that filtered list — it's rewritten with the plain FILTERED_FILE_NAMES set (duplicate.py:367).

build-cmake.yml now contains, as of this PR:
```yaml
PROJECT_NAME: TemplateProject
...
include:
- template: TemplateProjectWebView
artifact: TemplateProjectWebView.exe
- template: TemplateProjectReaperExtensionWebView
artifact: reaper_TemplateProjectReaperExtensionWebView.dll
```

Running the documented flow —
```bash
./duplicate.py TemplateProject MySynth AcmeInc --rewrite-root
```
(this exact invocation is the example in .claude/skills/new-plugin/SKILL.md and CLAUDE.md, and --rewrite-root is the explicitly-recommended flag "when this repo is becoming your plugin's repo") — replaces every occurrence of the substring TemplateProject in build-cmake.yml, including inside TemplateProjectWebView and TemplateProjectReaperExtensionWebView. The matrix becomes:
```yaml
- template: MySynthWebView
artifact: MySynthWebView.exe
- template: MySynthReaperExtensionWebView
artifact: reaper_MySynthReaperExtensionWebView.dll
```
but the actual directories on disk are still named TemplateProjectWebView and TemplateProjectReaperExtensionWebView (only TemplateProject itself was duplicated/renamed). The windows-templates job's cmake -S ${{ matrix.template }} -B build/${{ matrix.template }} step then fails because MySynthWebView doesn't exist — breaking CI in a repo that just followed the documented steps.

This only bites when duplicating the base TemplateProject specifically (its name is a prefix of the other two template names); duplicating TemplateProjectWebView or TemplateProjectReaperExtensionWebView directly happens to self-correct, since the full matched string is what gets replaced.

Since build-cmake.yml also needs its real edit (PROJECT_NAME: TemplateProject → the new project), simply adding it to ROOT_FILTERED_FILE_NAMES isn't quite right — that would leave PROJECT_NAME stale. A targeted fix (word-boundary-aware replacement, or special-casing the windows-templates matrix block the way the other root docs are handled) would close this without losing the intended rewrite.

Minor / non-blocking

  • duplicate.py:216 find_iplug2_root() only reads the -mac.xcconfig even on a project that also ships a CMake path; that is fine today since CMAKE_IPLUG2_DIR is a separate hardcoded constant rewritten by fix_cmake_iplug2_dir(), but worth a comment if the two ever need to disagree.
  • TemplateProjectReaperExtensionWebView.cpp's ProcessSelectedItem doesn't guard against totalFrames <= 0 (e.g. a zero-length take) — it will still write and insert an empty/near-empty wav rather than erroring. Minor, unlikely to be hit in practice.

Nice touches worth calling out

  • The opt-in --rewrite-root default (and the non-interactive parser.error when required args are missing) is a good, safe API change.
  • .gitignore's /build/ addition matches the new preset-driven build/<preset> layout.
  • The windows / windows-arm64ec presets dropping the pinned generator is a clean fix for the VS2026 image swap, and keeping windows-vs2022* around for pinning is a nice touch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant