Feat/new hooks and ccomponents - #32
Merged
Merged
Conversation
…ebugging - Added DevTools API to allow third-party plugins to contribute HTTP headers, observe network requests, and publish debug events. - Implemented header contributions via `addRequestHeader` method, allowing multiple plugins to contribute to the same header. - Introduced request observation with `onRequest` method to subscribe to completed network calls, including optional request/response headers. - Established a generic debug bus for session-based event publishing and subscription, facilitating real-time debugging data streaming. - Enhanced existing window configuration to include `ownerHandle` for better plugin attribution. - Updated public API and types to reflect new DevTools functionalities. - Implemented server-side support for debug event publishing and REST endpoint for fetching debug events.
…debug session handling
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
DevTools / cross-plugin instrumentation surface (+ supporting components)
These new hooks and components would allow amazing things as:
https://github.com/user-attachments/assets/89ba2828-9aa5-46bb-b6f4-0cf20c2aa5ac
## Test plugin
query-monitor.zip
Why
Desktop Mode is a plugin platform — every window can come from a different plugin, and the most useful devtools (a SQL inspector, a network logger, a perf profiler) are themselves plugins that need to attach behavior to other plugins' windows. Today there's no documented surface for that. The escape hatches plugin authors reach for —
iframe.contentWindow.fetch = …, walking another plugin's DOM, custom REST endpoints with hand-rolled poll loops — fight each other and break across permalink schemes.This PR adds the supported surface, plus the UI primitives those devtools end up needing.
The work was driven by a real plugin (an external "SQL Inspector") whose author hit four compounding gaps in succession. Each round of debugging surfaced one more gap; each gap is fixed and covered by a regression test below.
What's new
1.
wp.desktop.devtools— the cross-plugin instrumentation surfaceA new public API on
wp.desktopthat lets one plugin attach behavior to a window registered by another plugin without reaching into iframe globals.addRequestHeader(windowId, name, value)onRequest(windowId, cb, { observe })observe: trueadds request + response headers.reloadWithDebugSession(windowId, sessionId, opts?)debug.startSession()/publish()/subscribe()Architecture: the shell owns one instrumentation channel per window and brokers contributions from many devtools so they don't fight each other. Reference-counted contributions, deterministic header merging (RFC 7230 comma-join for duplicates), per-window
loadlistener that re-pushes instrumentation on every iframe document load.2.
desktop_mode_debug_publish()— server-side debug busPHP companion to
wp.desktop.devtools.debug. Plugins running inside an admin / REST / AJAX request hook a capture (e.g.SAVEQUERIESfor SQL,pre_http_requestfor outbound HTTP), then publish to a per-(session, channel) ring buffer. The shell polls and replays events to client-side subscribers.GET /wp-desktop/v1/debug?sessionId=…&since=…&channels[]=…. Permission: logged-in admin (manage_options); override viadesktop_mode_debug_rest_permission.desktop_mode_debug_publishaction fires on every publish for observability widgets that don't want to round-trip through the poll loop.3.
Window.config.ownerHandle— attributionEvery window registered via
desktop_mode_register_window( $args )(with'script' => 'my-plugin-handle') carries that handle through to JS-sideWindow.config.ownerHandle. Lets devtools identify which plugin owns a window without parsing URLs.4. UI components —
<wpd-log>,<wpd-badge>,<wpd-code copy>Drawn from real needs in the SQL Inspector plugin, but generic enough to belong in the kit:
<wpd-log>— virtualized streaming list. Append-only API (push,pushMany), LRU cap (max-rows), tail-stickiness (classictail -f), per-row renderer. Two modes:total = entries × rowHeight, no measurement passes. Required when feed rates are high (a chatty admin page can fire 200+ queries per request).auto-row-height— measured per-row heights, cumulative offsets, binary-search viewport finding. One layout pass per visible row but unblocks variable-content rows (header + body, expandable details).<wpd-badge>— colored-dot status pill. Five tones (success,warning,danger,info,neutral) plusno-dotfor count chips. Tiny, recurring need that every plugin was reinventing badly.<wpd-code copy>— added acopyboolean attribute to the existing<wpd-code>that surfaces a copy-to-clipboard affordance. Hover-revealed inline, always-visible onblock. Fireswpd-copyafter a successful clipboard write. Falls back todocument.execCommand('copy')under permission-locked iframes.Bug fixes surfaced during plugin author's six rounds of integration
Every one of these is a regression test in this PR.
Bug 1 — poll loop didn't pass channels (silent empty drains)
The JS poll URL omitted the active subscription channels entirely. The server-side drain has no channel set to walk without an explicit param OR a
desktop_mode_debug_channelsfilter contributor, so it returned{ events: [] }on every poll. Plugins following the docs verbatim saw zero events with no error. Fix: stamp every active subscription channel aschannels[]=…on every poll. The server already supported it.Bug 2 — URL composition broke under ugly permalinks
${restUrl}wp-desktop/v1/debug?sessionId=…works on pretty-permalink installs (/wp-json/) but produces<site>/?rest_route=/wp-desktop/v1/debug?sessionId=…— two?separators, malformed — under ugly permalinks. WordPress routed the request to the homepage, returned HTML,JSON.parseblew up. Fix: compose the URL via WHATWGURL+searchParams.set/append; the parser folds the second batch of params into the existing query.Bug 3 —
<wpd-log>clipped variable-content rows silentlyDefault fixed-row-height mode is by design (it's what makes the virtualizer tractable), but it wasn't documented and didn't have an escape hatch. Plugin authors discovered it by shipping: dev with one-line entries looked fine, production with multi-line entries got the bottom half eaten. Fix: prominent JSDoc + in-product Help-tab warning, plus an opt-in
auto-row-heightmode for variable-content rows.Bug 4 —
addRequestHeaderinstrumentation didn't survive manual iframe reloadsThe shell relies on a
wp-desktop-readypostMessage to fireIFRAME_READYand re-push instrumentation. That signal isn't actually emitted by either iframe-side bridge today, so the re-push path was dead code. Plugins that rewroteiframe.srcdirectly (to add a debug-session token visible to the document load) saw their headers silently dropped on the new document. Fix: install a per-windowloadlistener that re-pushes instrumentation deterministically, plus thereloadWithDebugSession()primitive that bundles the four-step boilerplate so the next plugin author doesn't have to wire any of it up.Test coverage
<wpd-log>, 3 to<wpd-badge>, plus regression tests for each of the four bugs above).tsc --noEmit).src/**/*.ts).desktop[.min].jsandiframe-bridge[.min].js.Files
Added
src/devtools/index.ts— public API moduleincludes/devtools.php— server-side debug bus + REST routesrc/ui/components/wpd-badge/— badge component + styles + testssrc/ui/components/wpd-log/— virtualized log component + styles + teststests/vitest/devtools.test.ts— 16 tests covering the API surface and every bug regressiondocs/examples/devtools-instrumentation.md— full SQL Inspector walkthroughModified
src/types.ts—ownerHandleonWindowConfig+NativeWindowServerEntrysrc/desktop.ts+src/public-api.ts— mountwp.desktop.devtoolson the public API; export new typessrc/native-windows.ts— propagateownerHandlethrough both server-sync open path andcreateRegisterWindowsrc/window/iframe-bridge.ts— extendIFRAME_NETWORK_COMPLETEDpayload with optionalrequestHeaders/responseHeaderssrc/ui/components/index.ts— register<wpd-log>,<wpd-badge>src/ui/components/wpd-code/—copyattribute + clipboard handler + stylesincludes/render.php— chromeless inline bridge: instrumentation message listener, header merging into fetch / XHR / sendBeacon,observe-mode header captureincludes/helpers.php— emitownerHandleon the native-window payloaddesktop-mode.php— require the new devtools includedocs/hooks-reference.md+docs/javascript-reference.md+docs/examples/README.md— full doc coverageDeployment notes
@since 0.6.0). Backwards-compatibility guarantees apply once promoted to Stable in a future release.IFRAME_NETWORK_COMPLETEDaction gained two optional payload fields — pre-existing subscribers see no behavioral change.try/catchso a malfunction in the instrumentation doesn't tank the rest of the bridge.assets/js/desktop[.min].js,assets/js/iframe-bridge[.min].js) are gitignored — runnpm run buildbefore tagging a release.