Skip to content

Uploading demo video #1

Closed
epeicher wants to merge 2 commits into
trunkfrom
test-branch
Closed

Uploading demo video #1
epeicher wants to merge 2 commits into
trunkfrom
test-branch

Conversation

@epeicher

Copy link
Copy Markdown
Collaborator
Demo.mov

Embed a 10-minute screen recording of the plugin in action right under
the intro. File is H.264 + AAC in an MP4 container with faststart, so
it streams in every major browser (including Firefox, which is picky
about .mov QuickTime).

- docs/demo.mp4: the demo, 66 MB.
- .gitattributes: export-ignore the video so `npm run package` keeps
  the plugin zip lean.
- README.md: new "Demo" section between the intro and the TOC, with
  a matching Contents entry.
@epeicher epeicher closed this Apr 22, 2026
@epeicher epeicher deleted the test-branch branch April 23, 2026 07:58
AllTerrainDeveloper added a commit that referenced this pull request May 21, 2026
Top-priority bugs found by a plugin author working against the
public API:

1. HOOKS.IFRAME_READY never fired. The parent listens for
   `desktop-mode-ready` (`src/window/iframe-bridge.ts:187-191`)
   but no iframe-side code emitted it. Two consumers
   (connection-rearm in desktop.ts, devtools replay) silently
   never ran. Now both `includes/render/chromeless-bridge.php`
   and `src/iframe-bridge-standalone.ts` post the message after
   listener wiring completes.

2. `wp.desktop.iframe.publish` silently dropped when no
   connection was open. Now console.warns the dropped topic +
   why, instead of vanishing.

3. iframeContent handshake race — fixed automatically by #1
   AND backstopped by new `Window.whenContentReady(): Promise<void>`
   on the Window facade so plugin code can `await win.whenContentReady()`
   without wiring iframe.load themselves.

4. Component classes / npm package — fixed without bloating the
   shipped bundle. The components are already in desktop.min.js
   (side-effect imports via src/ui/components/index.ts), and
   already re-exported from src/public-api.ts. Added `"import"`
   to the package.json `exports` map so plugin bundlers can
   resolve `import { WpdLog } from 'desktop-mode'` via a local
   `file:../desktop-mode` dep — no npm publish needed. Documented
   the workflow in `docs/use-from-a-plugin.md`.

5. No public lookup of a Connection by id. Added
   `wp.desktop.getConnection( connectionId )` returning the live
   `WindowConnection` (or null). Also ships `connection: conn` in
   the HOOKS.CONNECTION_OPENED payload so iframe-initiated
   connections can be subscribed to directly from the hook.

6. Iframe didn't know its own window id. The parent's handshake
   message now carries `targetWindowId`; the iframe stashes it
   and exposes `wp.desktop.iframe.windowId` (sync) +
   `whenWindowId(): Promise<string>` (resolves on first
   handshake). Replaces the brittle `iframe.contentWindow ===`
   walk plugin code had to do parent-side.

Doc gaps found along the way:

8. `docs/components-reference.md` was referenced by the runtime
   missing-import warner and 3 other doc files but didn't exist.
   Created the canonical mapping: every wpd-* tag → exported
   class → source file → one-line purpose, grouped by surface
   (layout, form, buttons, menus, display, lists, tabs, color).

9. No example for the iframe-initiated topology (PluginSidebar
   wants to open a Preview window). Existing
   docs/examples/connect-to-window.md only covered the inverse
   case. Added `docs/examples/iframe-initiated-window.md` with
   a full Live Preview recipe wiring
   `wp.desktop.iframe.publish` ↔ `wp.desktop.connect`.

10. Cross-origin bridge support was undefined. Three bridges
    hard-filter on `window.location.origin` and silently drop
    everything cross-origin. Documented as an explicit non-goal
    in `docs/bridge-protocol.md` with the security rationale,
    and added `wp.desktop.iframe.isParentReachable()` so plugins
    can fail fast / degrade gracefully instead of debugging
    vanishing messages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AllTerrainDeveloper added a commit that referenced this pull request May 21, 2026
* feat(desktop-files): smooth repaint on adds/removes

Eliminates the visible "desktop reloaded" flash on file creation,
shortcut drop, and tile deletion. The renderer used to fall through
to a wholesale `container.replaceChildren()` rebuild for any
structural change, repainting every tile from scratch even when
only one had appeared or disappeared.

Adds an incremental path (`tryPatchIncremental`) between the
existing position-only fast path and the wholesale rebuild:

  1. tryPatchPositions  — same set, only X/Y changed (drag-within).
  2. tryPatchIncremental — adds and/or removes, no structural
     changes on shared tiles. Preserves DOM identity for the
     unchanged tiles.
  3. Wholesale rebuild — last resort for pin-flag flips, parent-
     folder moves, file-type changes.

Per-tile setup is extracted into `wireTile()` so all three paths
share one source of truth — wholesale + incremental call the same
helper to build + position + wire new tiles. Layout (pinned slots
+ displacement) factored into `computeLayout()` so it can run on
the live DOM without a rebuild.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(files): qualify ambiguous `id` in folder cascade-trash query

`desktop_mode_files_trash_folder()` runs a JOIN between
`wp_desktop_mode_folders f` and `wp_desktop_mode_file_placements p`
to find nested folders that need to cascade-trash. The SELECT list
just said `id` — and both tables have an `id` column — so MySQL
rejected it with "Column 'id' in field list is ambiguous".

Symptom: trashing any folder that contained other folders dumped
`<div id="error"><p class="wpdberror">…</p></div>` into the REST
response (visible to the user when the wpdberror block got
prepended before headers, breaking the JSON parse on the client),
and the cascade silently skipped the nested folders so they stayed
visible on the desktop after the parent was trashed.

Fix: select `f.id` explicitly. One-character change.

Found via Docker debug.log; reported by user after dropping a
folder.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(user-edit): center main column inside its grid cell

The Edit User window splits into a 280px sidebar + a `minmax(0, 1fr)`
main column. The main column's content (`<wpd-form>`) caps at 720px
for readability, but with no `margin-inline` it sat flush-left
inside its cell — on wide windows the right edge of the form sat
hundreds of pixels short of the right edge of the window, with the
empty band reading as "this form is broken / forgot to fill the
width".

`margin-inline: auto` + `width: 100%` keeps the 720px line-length
cap intact and pushes the unused horizontal space equally to both
sides instead of pooling it on the right. The Edit User window is
now visually balanced regardless of viewport width.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(window-chrome): center dashicon glyph inside title-bar meta buttons

The title-bar `.desktop-mode-window__meta-btn` buttons (Help, etc.)
already use flex centering on the parent, but the dashicon span
inside inherits `display: inline-block` from core's Dashicons rules
and renders the glyph via text layout. The font's baseline isn't
quite the geometric centre of its em-box, so the visible character
drifts a px or two off-centre — visible on the `?` (editor-help)
glyph as a slight up-and-left lean inside the 28×28 button.

Making the span itself a flex centerer (`display: inline-flex` +
`align-items: center` + `justify-content: center` + `line-height: 1`)
re-anchors the glyph to the geometric centre regardless of the
font's intrinsic metrics. Same span dimensions, same icon size —
just precise centring.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(drag): bridge legacy Media Library drags through to Gutenberg

The Media Library enhancement script (`assets/js/media-library-
enhanced.js`, since 0.14.0) makes attachment tiles draggable with
a rich DataTransfer payload: `text/html` (an `<img>` tag),
`text/uri-list`, `text/plain`, AND a custom
`application/x-wp-media-attachment` MIME carrying the full WP
attachment record. It also `postMessage`s the same record to the
parent shell via `desktop-mode-drag-start` so the bridge can keep
the rich payload available across iframe hops.

Symptom: dragging a media tile out of the Media Library window
and dropping on an open Gutenberg post window does nothing — the
tile lifts, ghost tracks, but Gutenberg never inserts a block.
Root cause: Chromium strips non-standard DataTransfer MIMEs at
the iframe boundary, so by the time `drop` fires inside the
Gutenberg iframe, `application/x-wp-media-attachment` is gone and
Gutenberg's own drop handler doesn't recognize what's left as a
media insert.

Fix wires the existing DragBridge end-to-end:

  1. drag-bridge.ts now normalizes the legacy
     `{id, url, title, alt, mime, sizes, thumbnailUrl}` shape into
     the tagged `{kind: 'attachment', ...}` union as soon as the
     payload lands at the parent shell. Downstream receivers only
     handle one shape.

  2. drag/iframe-drop-targets.ts forwards
     `DRAG_BRIDGE_EVENTS.START` / `.END` to every iframe as
     `desktop-mode-drag-over` / `-drag-leave` messages so the
     receiver inside knows a cross-frame drag is in flight and
     stashes the payload up-front.

  3. gutenberg-drop-receiver.ts stashes the bridge payload on
     incoming `desktop-mode-drag-over` and adds a native HTML5
     `drop` handler (capture phase, `stopImmediatePropagation`) that
     uses the stashed payload to insert the matching block via
     `wp.data.dispatch('core/block-editor').insertBlocks(…)`.
     Pairs with a `dragover` listener that calls `preventDefault`
     so the browser treats the iframe as a valid drop target while
     the bridge session is live.

End-to-end: Media Library tile drag → parent bridge stores +
normalizes → fans `drag-over` to every iframe → user drops on
Gutenberg → receiver wins the native drop event, inserts the
image. No change to media-library-enhanced.js; no risk to
in-Gutenberg native drag (no stashed payload → no interception).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(drag): implement iframe-to-iframe drag intercept for desktop mode

* fix(dashicon): center glyph in title-bar meta buttons to prevent drift

* feat(plugin-dx): fix the 9 documented-but-broken plugin surfaces

Top-priority bugs found by a plugin author working against the
public API:

1. HOOKS.IFRAME_READY never fired. The parent listens for
   `desktop-mode-ready` (`src/window/iframe-bridge.ts:187-191`)
   but no iframe-side code emitted it. Two consumers
   (connection-rearm in desktop.ts, devtools replay) silently
   never ran. Now both `includes/render/chromeless-bridge.php`
   and `src/iframe-bridge-standalone.ts` post the message after
   listener wiring completes.

2. `wp.desktop.iframe.publish` silently dropped when no
   connection was open. Now console.warns the dropped topic +
   why, instead of vanishing.

3. iframeContent handshake race — fixed automatically by #1
   AND backstopped by new `Window.whenContentReady(): Promise<void>`
   on the Window facade so plugin code can `await win.whenContentReady()`
   without wiring iframe.load themselves.

4. Component classes / npm package — fixed without bloating the
   shipped bundle. The components are already in desktop.min.js
   (side-effect imports via src/ui/components/index.ts), and
   already re-exported from src/public-api.ts. Added `"import"`
   to the package.json `exports` map so plugin bundlers can
   resolve `import { WpdLog } from 'desktop-mode'` via a local
   `file:../desktop-mode` dep — no npm publish needed. Documented
   the workflow in `docs/use-from-a-plugin.md`.

5. No public lookup of a Connection by id. Added
   `wp.desktop.getConnection( connectionId )` returning the live
   `WindowConnection` (or null). Also ships `connection: conn` in
   the HOOKS.CONNECTION_OPENED payload so iframe-initiated
   connections can be subscribed to directly from the hook.

6. Iframe didn't know its own window id. The parent's handshake
   message now carries `targetWindowId`; the iframe stashes it
   and exposes `wp.desktop.iframe.windowId` (sync) +
   `whenWindowId(): Promise<string>` (resolves on first
   handshake). Replaces the brittle `iframe.contentWindow ===`
   walk plugin code had to do parent-side.

Doc gaps found along the way:

8. `docs/components-reference.md` was referenced by the runtime
   missing-import warner and 3 other doc files but didn't exist.
   Created the canonical mapping: every wpd-* tag → exported
   class → source file → one-line purpose, grouped by surface
   (layout, form, buttons, menus, display, lists, tabs, color).

9. No example for the iframe-initiated topology (PluginSidebar
   wants to open a Preview window). Existing
   docs/examples/connect-to-window.md only covered the inverse
   case. Added `docs/examples/iframe-initiated-window.md` with
   a full Live Preview recipe wiring
   `wp.desktop.iframe.publish` ↔ `wp.desktop.connect`.

10. Cross-origin bridge support was undefined. Three bridges
    hard-filter on `window.location.origin` and silently drop
    everything cross-origin. Documented as an explicit non-goal
    in `docs/bridge-protocol.md` with the security rationale,
    and added `wp.desktop.iframe.isParentReachable()` so plugins
    can fail fast / degrade gracefully instead of debugging
    vanishing messages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(review): address all code-review findings from PR #262

- Remove duplicate 'Wholesale rebuild' comment block in layer.ts
- Harden normalizeLegacyPayload pass-through: check obj.kind !== undefined/null
  instead of an explicit known-kinds allowlist that needs updating per new kind
- Add whenWindowId() non-reject warning + isParentReachable() guard pattern
  to docs/use-from-a-plugin.md and docs/examples/iframe-initiated-window.md

Co-authored-by: Daniel López Sánchez (prismiwi2015) <AllTerrainDeveloper@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Daniel López Sánchez (prismiwi2015) <AllTerrainDeveloper@users.noreply.github.com>
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