Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
editor_bridge.hpp
Go to the documentation of this file.
1#pragma once
2
3// Renderer-agnostic editor↔processor message bridge for plugin editors.
4//
5// Plugin editors (WebView panels today, native-JS-runtime imports
6// from Claude Design tomorrow) send JSON envelopes to the C++
7// processor and consume JSON responses. EditorBridge owns the
8// envelope parse, the type→handler dispatch table, the response
9// builders, and the standard error vocabulary so each plugin only
10// writes its own domain handlers.
11//
12// ── Envelope ───────────────────────────────────────────────────────────
13//
14// Inbound envelopes are JSON objects of this shape:
15//
16// { "type": "<kind>", "payload": { ... } } // payload optional
17//
18// Responses are always one of:
19//
20// { "ok": true } // success, no extras
21// { "ok": true, ... } // success, handler-supplied extras
22// { "ok": false, "error": "..." } // failure
23//
24// dispatch_json(...) and dispatch(...) are noexcept and always return
25// a well-formed response envelope. Handlers may throw — the catch path
26// emits an `internal_error` response.
27//
28// ── Standard error vocabulary ──────────────────────────────────────────
29//
30// Envelope-level failures emit errors whose message contains one of
31// these substrings (lowercase tokens are the categories; the human
32// strings are designed to be substring-compatible with existing
33// consumers, notably Spectr's editor_bridge tests):
34//
35// malformed_json → "malformed JSON" (parse failed)
36// "envelope must be an object"
37// unknown_type → "unknown message type" (no handler registered)
38// missing_field → "envelope missing 'type'" (or handler-emitted)
39// wrong_type → handler-emitted via err_response()
40// internal_error → "internal error" (handler threw)
41//
42// Plugin-level handlers may use err_response("...") with any message.
43// The framework reserves the substrings above only for envelope-level
44// failures it emits itself.
45
46#include <choc/containers/choc_Value.h>
47
48#include <cstddef>
49#include <functional>
50#include <memory>
51#include <string>
52#include <string_view>
53#include <vector>
54
55namespace pulp::view {
56
57class WebViewPanel;
58class ScriptEngine;
60
61// Forward-declared so EditorBridge can expose attach_native_runtime()
62// before the JS engine surface is fully available in this header. The
63// concrete JsRuntime type lives in pulp/view/js_engine.hpp.
64class JsRuntime;
65
84public:
89 using Handler = std::function<std::string(const choc::value::ValueView& payload)>;
90
93
94 // Non-copyable AND non-movable.
95 //
96 // attach_webview() and attach_native_runtime() install callbacks
97 // that reference this bridge's internal state. Allowing moves
98 // would let an attached bridge be relocated out from under those
99 // callbacks (vector reallocation, std::optional emplace, move-
100 // construction of an editor that contains one), and the next
101 // inbound message would dereference a moved-from instance with
102 // a null impl. Requiring construction-in-place surfaces that
103 // mistake at compile time.
104 EditorBridge(const EditorBridge&) = delete;
108
111 void add_handler(std::string_view type, Handler fn);
112
114 void remove_handler(std::string_view type);
115
117 bool has_handler(std::string_view type) const noexcept;
118
120 std::size_t handler_count() const noexcept;
121
127 std::vector<std::string> handlers() const;
128
132 std::string dispatch(std::string_view type,
133 const choc::value::ValueView& payload) const noexcept;
134
139 std::string dispatch_json(std::string_view json) const noexcept;
140
145 std::string dispatch_webview_message(std::string_view type,
146 std::string_view payload_json) const noexcept;
147
154
161
171 void attach_native_runtime(JsRuntime& runtime, std::string_view handler_name);
172
176 void attach_native_runtime(ScriptEngine& engine, std::string_view handler_name);
177
182 std::string_view handler_name);
184 std::string_view handler_name);
185
186 // ── Static value-coercion helpers for handler authors ────────────
187 //
188 // ValueView's typed getters throw on type mismatch. These helpers
189 // never throw; they return the default (or empty string) when the
190 // key is absent or carries the wrong JSON type. Numeric helpers
191 // accept any of int32 / int64 / float64 and coerce.
192
193 static float get_float (const choc::value::ValueView& v,
194 const char* key,
195 float dflt) noexcept;
196 static std::size_t get_uint (const choc::value::ValueView& v,
197 const char* key,
198 std::size_t dflt) noexcept;
199 static std::string get_string(const choc::value::ValueView& v,
200 const char* key) noexcept;
201
202 // ── Static response builders ─────────────────────────────────────
203
205 static std::string ok_response() noexcept;
206
209 static std::string ok_response(const choc::value::ValueView& extras) noexcept;
210
212 static std::string err_response(std::string_view msg) noexcept;
213
214private:
215 struct Impl;
216 std::unique_ptr<Impl> impl_;
217};
218
225std::string render_claude_bridge_scaffold(const std::string& generated_js_path);
226
227} // namespace pulp::view
EditorBridge & operator=(const EditorBridge &)=delete
void add_handler(std::string_view type, Handler fn)
Register a handler for the given message type.
static std::size_t get_uint(const choc::value::ValueView &v, const char *key, std::size_t dflt) noexcept
void attach_webview(WebViewPanel &panel)
Hook this bridge up to a WebViewPanel's structured message channel.
void attach_native_runtime(JsRuntime &runtime, std::string_view handler_name)
Hook this bridge up to a native JS runtime's postMessage source.
EditorBridge & operator=(EditorBridge &&)=delete
EditorBridge(EditorBridge &&)=delete
EditorBridge(const EditorBridge &)=delete
std::function< std::string(const choc::value::ValueView &payload)> Handler
Handler signature.
Definition editor_bridge.hpp:89
static std::string get_string(const choc::value::ValueView &v, const char *key) noexcept
std::string dispatch_webview_message(std::string_view type, std::string_view payload_json) const noexcept
Convenience: dispatch a WebViewPanel message that has already been split into type + payload_json by ...
void detach_native_runtime(ScriptedUiSession &session, std::string_view handler_name)
void remove_handler(std::string_view type)
Remove a handler. No-op if no handler was registered.
static std::string err_response(std::string_view msg) noexcept
{"ok":false,"error":"<msg>"}.
std::string dispatch_json(std::string_view json) const noexcept
Parse a JSON envelope and dispatch.
bool has_handler(std::string_view type) const noexcept
True if a handler is registered for type.
std::string dispatch(std::string_view type, const choc::value::ValueView &payload) const noexcept
Dispatch an already-parsed envelope.
std::size_t handler_count() const noexcept
Number of registered handlers.
static float get_float(const choc::value::ValueView &v, const char *key, float dflt) noexcept
static std::string ok_response() noexcept
{"ok":true}.
std::vector< std::string > handlers() const
Return the registered message types in canonical lexicographic order.
void detach_webview(WebViewPanel &panel)
Clear the message handler installed by attach_webview().
Definition script_engine.hpp:22
Definition scripted_ui.hpp:125
Definition web_view.hpp:167
Definition generated_event_source.hpp:33
Hover-cursor tracking for platform window hosts.
Definition fwd.hpp:27
@ type
Built-in / common type name (e.g. int, Number).
Definition code_editor_tokenizer.hpp:39
std::string render_claude_bridge_scaffold(const std::string &generated_js_path)
Build the starter bridge_handlers.cpp text the CLI emits next to the generated JS view when --from cl...
@ value
the widget's primary value (Knob, Fader, RangeSlider, Toggle, ProgressBar)
Definition binding_diagnostics.hpp:19
@ v
Definition input_events.hpp:41
STL namespace.