From 6841e177f9aee096441994b98cd812aa526a93d6 Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Fri, 23 Oct 2026 12:12:47 -0400 Subject: [PATCH 1/7] docs: correct PHP comments that drifted from the code --- .../presence-api/includes/avatar-stack.php | 5 ++- plugins/presence-api/includes/heartbeat.php | 5 +-- .../includes/network-functions.php | 4 +-- .../includes/post-lock-bridge.php | 3 +- plugins/presence-api/includes/presence.php | 33 ++++++++++--------- .../class-wp-rest-presence-controller.php | 4 +-- .../includes/screen-revisions.php | 13 ++++---- ...wp-presence-network-widget-whos-online.php | 3 +- .../class-wp-presence-widget-active-posts.php | 6 ++-- 9 files changed, 41 insertions(+), 35 deletions(-) diff --git a/plugins/presence-api/includes/avatar-stack.php b/plugins/presence-api/includes/avatar-stack.php index 06a270f1..ae138be4 100644 --- a/plugins/presence-api/includes/avatar-stack.php +++ b/plugins/presence-api/includes/avatar-stack.php @@ -42,9 +42,8 @@ function wp_presence_enqueue_avatar_stack_style() { /** * Renders a small avatar stack for a list of users. * - * Shared across every surface that shows an overlapping avatar stack (the - * dashboard widget's overflow indicator, the network Sites list column, the - * network dashboard widget) so they all render the stack identically. + * Shared by the network Sites list column and the network dashboard widget, + * so both render the stack identically. * * assets/css/avatar-stack.css sizes the avatars; the attributes below only * reserve the space until it loads. diff --git a/plugins/presence-api/includes/heartbeat.php b/plugins/presence-api/includes/heartbeat.php index dd6183d0..8ed769bc 100644 --- a/plugins/presence-api/includes/heartbeat.php +++ b/plugins/presence-api/includes/heartbeat.php @@ -13,7 +13,7 @@ /** * Builds the state stored on a user's entry in a post room. * - * Shared so the two writers of that entry cannot drift apart on its shape. + * Shared so the writers of that entry cannot drift apart on its shape. * * @access private * @@ -337,12 +337,13 @@ function wp_presence_register_packages_scripts( $scripts ) { * This is the API's primary write path. It runs regardless of which dashboard * widgets are registered. * + * Nonce verification is handled by WordPress in wp_ajax_heartbeat(). + * * @since 0.1.10 * * @param array $response The Heartbeat response. * @param array $data The $_POST data sent. * @param string $screen_id The screen ID. - * Nonce verification is handled by WordPress in wp_ajax_heartbeat(). * * @return array The Heartbeat response. */ diff --git a/plugins/presence-api/includes/network-functions.php b/plugins/presence-api/includes/network-functions.php index 16dfd242..7fa0168f 100644 --- a/plugins/presence-api/includes/network-functions.php +++ b/plugins/presence-api/includes/network-functions.php @@ -483,8 +483,8 @@ function wp_presence_network_capability() { /** * Returns the distinct user IDs online anywhere on the network. * - * Reads the snapshot rather than the summary: the Users list column asks only - * whether a given row's user is in this set, so resolving a display name and + * Reads the snapshot rather than the summary: the Online view, its count and + * the admin bar only ask who is in this set, so resolving a display name and * an avatar URL for everyone online to answer that would be the whole cost of * the read for none of its output. * diff --git a/plugins/presence-api/includes/post-lock-bridge.php b/plugins/presence-api/includes/post-lock-bridge.php index 6c3baba7..89ee5f6f 100644 --- a/plugins/presence-api/includes/post-lock-bridge.php +++ b/plugins/presence-api/includes/post-lock-bridge.php @@ -25,12 +25,13 @@ * Fallback path, for a client that refreshes the core lock without sending * presence-editor-ping. * + * Nonce verification is handled by WordPress in wp_ajax_heartbeat(). + * * @since 0.1.1 * * @param array $response The Heartbeat response. * @param array $data The $_POST data sent. * @param string $screen_id The screen ID. - * Nonce verification is handled by WordPress in wp_ajax_heartbeat(). * * @return array The Heartbeat response. */ diff --git a/plugins/presence-api/includes/presence.php b/plugins/presence-api/includes/presence.php index 99dc25c9..8ac143db 100644 --- a/plugins/presence-api/includes/presence.php +++ b/plugins/presence-api/includes/presence.php @@ -15,6 +15,10 @@ * wp_presence_recording_enabled() * wp_presence_is_available() * wp_presence_is_agent_user() + * wp_presence_parse_room() + * wp_get_presence_by_room_prefix() + * wp_get_presence_room_versions() + * wp_get_presence_room_next_expiry() * * @package Presence_API */ @@ -378,9 +382,9 @@ function wp_presence_with_current_user( $entries ) { * Whether presence is recorded on this site. * * The controller-level switch, checked at the single write path. Nothing new is - * stored while this is false and every surface empties within one - * WP_PRESENCE_DEFAULT_TTL as the rows already there expire, so there is no - * separate teardown to run. + * stored while this is false and every surface empties as the rows already + * there expire, within one TTL for Heartbeat rows and wp_presence_max_expires_in() + * at most, so there is no separate teardown to run. * * Recording is on by default. Presence is a negative signal, and the post lock * bridge is where its absence stops two people overwriting each other, so the @@ -1654,8 +1658,8 @@ function wp_presence_is_agent_user( $user_id ) { /** * Filters whether a user is an AI agent, for labelling its presence rows. * - * Every column a presence row's `user_id` might have come from — the - * database, JSON, this function's own callers — reads back as a string, + * Every column a presence row's `user_id` might have come from (the + * database, JSON, this function's own callers) reads back as a string, * so it is cast to an int before it ever reaches a filter, and a filter * can compare against a plain int without tripping over the difference. * @@ -1739,9 +1743,9 @@ function wp_presence_on_agent_post_saved( $post_id, $post ) { * * An agent runs no Heartbeat and joins no `admin/online` room of its own; it * writes only the post room it is editing (see the "Agents" section of the - * README). Rather than have it write a second room, Who's Online and the - * admin bar both call this instead of `wp_get_presence( wp_presence_admin_room() )` - * directly, so an agent's row is picked up from wherever it already is. + * README). Rather than have it write a second room, the admin bar calls this + * instead of `wp_get_presence( wp_presence_admin_room() )` directly, so an + * agent's row is picked up from wherever it already is. * * An agent user with a row of its own in `admin/online` is left as that row * reads; only agent users absent from it are backfilled from a post room, @@ -1796,8 +1800,8 @@ function wp_presence_admin_room_entries( $timeout = null ) { /* * * The following functions are used by the plugin's widgets, CLI, REST - * controller, and cron jobs. They are not part of the public API contract - * and may change or be removed without notice. Do not depend on them. + * controller, and cron jobs. Those marked `@access private` are not part of + * the public API contract and may change or be removed without notice. */ if ( ! function_exists( 'wp_presence_get_timeout' ) ) { @@ -1955,11 +1959,11 @@ function wp_get_presence_summary( $timeout = null ) { if ( ! function_exists( 'wp_delete_expired_presence_data' ) ) { /** - * Deletes stale presence entries older than the default TTL. + * Deletes presence rows past their own expiry. * * Runs on the every-minute cron event. Rather than looping without a ceiling * over the MySQL-only `DELETE ... LIMIT` construct, this selects a bounded - * page of primary keys older than the cutoff and deletes them by key, for a + * page of expired primary keys and deletes them by key, for a * fixed number of passes per invocation. Any remaining backlog is left for the * next cron run, so a single request cannot run until `max_execution_time` * when a site returns from a cron outage with a large backlog. Deleting by @@ -2180,9 +2184,8 @@ function wp_presence_hydrate_room_users( $rooms, $timeout = null ) { /** * Renders the small "Agent" badge shown next to an agent's presence row. * - * Shared across every surface that lists a presence row by user — Who's - * Online, the admin bar, the Active Posts widget and the post list's - * Editors column — so an agent reads the same way wherever it shows up. + * The admin bar shows it beside each agent's name, while the Active Posts + * widget and the Editors column label agents in plain text. * * @access private * diff --git a/plugins/presence-api/includes/rest-api/endpoints/class-wp-rest-presence-controller.php b/plugins/presence-api/includes/rest-api/endpoints/class-wp-rest-presence-controller.php index 47e511a9..1250fffd 100644 --- a/plugins/presence-api/includes/rest-api/endpoints/class-wp-rest-presence-controller.php +++ b/plugins/presence-api/includes/rest-api/endpoints/class-wp-rest-presence-controller.php @@ -513,7 +513,7 @@ public function create_item( $request ) { // 1. The UNIQUE KEY on (room, client_id) prevents duplicate rows. // 2. Heartbeat re-establishes correct ownership within seconds. // 3. Worst case: two users briefly share an entry until the next ping. - // 4. Data is ephemeral (150s default TTL) — stale entries self-correct. + // 4. Data is ephemeral (150s default TTL), so stale entries self-correct. // Table-level locking is not warranted for ephemeral data. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $existing_user_id = $wpdb->get_var( @@ -622,7 +622,7 @@ public function delete_item_permissions_check( $request ) { ) ); - // Entry doesn't exist — allow the delete (it's a no-op). + // Entry doesn't exist, so allow the delete, which removes nothing. if ( null === $entry_user_id ) { return true; } diff --git a/plugins/presence-api/includes/screen-revisions.php b/plugins/presence-api/includes/screen-revisions.php index 802ffd12..359762cc 100644 --- a/plugins/presence-api/includes/screen-revisions.php +++ b/plugins/presence-api/includes/screen-revisions.php @@ -13,8 +13,8 @@ * (post.php), user edits (user-edit.php, profile.php), term edits * (edit-tags.php), comment edits (comment.php), and on multisite Network * Settings and the Edit Site tabs. JS-driven and REST-driven - * screens can opt in via the `wp_presence_current_screen_key` filter plus a - * future client-side `markScreenStale()` API. + * screens can opt in via the `wp_presence_current_screen_key` filter plus the + * client-side `wp.presence.markScreenStale()`. * * @package Presence_API */ @@ -37,10 +37,11 @@ * * Shape: array( '' => array( 'rev' => int, 'actor_id' => int, 'time' => int ) ). * The actor's display name and avatar are looked up fresh on each heartbeat - * tick — not stored — so renames and avatar changes show immediately. + * tick rather than stored, so renames and avatar changes show immediately. * - * Only screen keys that don't map to a post, user, term, comment, or a known - * Settings page land here — see wp_presence_parse_screen_key_target(). Those + * Only screen keys that don't map to a post, user, term, comment, a known + * Settings page or a network screen land here; see + * wp_presence_parse_screen_key_target(). Those * write straight to the object they describe instead of this shared option, * so a save on one screen doesn't invalidate the cache entry for every other * screen in the map. @@ -187,7 +188,7 @@ function wp_presence_get_screen_revision( $screen_key ) { return null; } // mysql2date( 'U', ... ) adds the site's timezone offset even when - // $translate is false — core's own get_post_time() skips that add + // $translate is false; core's own get_post_time() skips that add // only for its $gmt branch. post_modified_gmt is already a naive // UTC string, and WordPress forces PHP's default timezone to UTC // at bootstrap, so a plain strtotime() is the correct conversion. diff --git a/plugins/presence-api/includes/widgets/class-wp-presence-network-widget-whos-online.php b/plugins/presence-api/includes/widgets/class-wp-presence-network-widget-whos-online.php index 45d05c49..caf3536d 100644 --- a/plugins/presence-api/includes/widgets/class-wp-presence-network-widget-whos-online.php +++ b/plugins/presence-api/includes/widgets/class-wp-presence-network-widget-whos-online.php @@ -178,12 +178,13 @@ private static function summary_markup( $summary ) { * tick costs one lookup here, never the capability check or the * summary query. * + * Nonce verification is handled by WordPress in wp_ajax_heartbeat(). + * * @since 0.2.0 * * @param array $response The Heartbeat response. * @param array $data The $_POST data sent. * @param string $screen_id The screen ID. - * Nonce verification is handled by WordPress in wp_ajax_heartbeat(). * * @return array The Heartbeat response. */ diff --git a/plugins/presence-api/includes/widgets/class-wp-presence-widget-active-posts.php b/plugins/presence-api/includes/widgets/class-wp-presence-widget-active-posts.php index 4b695d18..e3f53137 100644 --- a/plugins/presence-api/includes/widgets/class-wp-presence-widget-active-posts.php +++ b/plugins/presence-api/includes/widgets/class-wp-presence-widget-active-posts.php @@ -96,7 +96,7 @@ private static function list_markup( $posts ) { break; } } - // Only show status when it differs — "Idle" is the signal. + // Only show status when it differs, since "Idle" is the signal. // Active is the default state; labeling it adds noise. $status_label = $any_active ? '' : __( 'Idle', 'presence-api' ); @@ -134,8 +134,8 @@ private static function list_markup( $posts ) { /** * Suffixes a display name to mark it as an agent's, everywhere this widget - * names one in plain text rather than through wp_presence_render_agent_badge() - * — every editor_label string is passed through esc_html(), which would + * names one in plain text rather than through wp_presence_render_agent_badge(), + * since every editor_label string is passed through esc_html(), which would * strip that badge's markup. * * @since 0.9.0 From db6b81a376c9f8182474e51081bd92fd26d69e1d Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Fri, 23 Oct 2026 12:12:47 -0400 Subject: [PATCH 2/7] docs: correct JavaScript comments that drifted from the code --- plugins/presence-api/assets/js/presence-ping.js | 3 ++- plugins/presence-api/assets/js/stale-screen.js | 4 ++-- plugins/presence-api/assets/js/tab-coordinator.js | 4 ++++ plugins/presence-api/src/utils/heartbeat-events.js | 7 +++---- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/plugins/presence-api/assets/js/presence-ping.js b/plugins/presence-api/assets/js/presence-ping.js index ac6affc8..86b7541c 100644 --- a/plugins/presence-api/assets/js/presence-ping.js +++ b/plugins/presence-api/assets/js/presence-ping.js @@ -240,7 +240,8 @@ // window, app switched away) so the existing entries expire via // the default TTL. One early-return suppresses both presence-ping // and presence-editor-ping, since the consolidated handler emits - // both. + // both. Core's lock refresh goes too, or the post-lock bridge + // would keep the hidden tab's lock and editor row alive. if ( document.visibilityState === 'hidden' ) { delete data[ 'wp-refresh-post-lock' ]; return; diff --git a/plugins/presence-api/assets/js/stale-screen.js b/plugins/presence-api/assets/js/stale-screen.js index dd66ebbd..4d6da0df 100644 --- a/plugins/presence-api/assets/js/stale-screen.js +++ b/plugins/presence-api/assets/js/stale-screen.js @@ -26,7 +26,7 @@ return; } - // Tabs on the same screen send an identical ping — key by screenKey. + // Tabs on the same screen send an identical ping, so key by screenKey. const pingContextKey = 'wp-presence-screen-ping:' + screenKey; const tabCoordinator = window.wpPresenceCreateTabCoordinator( @@ -132,7 +132,7 @@ avatar.src = info.actor_avatar_url; avatar.width = 24; avatar.height = 24; - // Decorative — the actor name is already in the adjacent text. + // Decorative, since the actor name is already in the adjacent text. avatar.alt = ''; avatar.className = 'wp-presence-stale-avatar'; p.appendChild( avatar ); diff --git a/plugins/presence-api/assets/js/tab-coordinator.js b/plugins/presence-api/assets/js/tab-coordinator.js index 72837ba1..9e4dde74 100644 --- a/plugins/presence-api/assets/js/tab-coordinator.js +++ b/plugins/presence-api/assets/js/tab-coordinator.js @@ -9,6 +9,10 @@ 'use strict'; /** + * Creates a coordinator for one key, shared by the classic scripts and src/. + * + * @private + * * @param {string} key Unique lock/channel name. * @param {Object} [options] * @param {string[]} [options.relayedKeys] heartbeat-tick response keys to relay from the leader to followers. diff --git a/plugins/presence-api/src/utils/heartbeat-events.js b/plugins/presence-api/src/utils/heartbeat-events.js index 6bcd6fb7..1e97225f 100644 --- a/plugins/presence-api/src/utils/heartbeat-events.js +++ b/plugins/presence-api/src/utils/heartbeat-events.js @@ -1,9 +1,8 @@ /** * Heartbeat event utilities. * - * WordPress core's heartbeat.js currently triggers events via jQuery. - * These utilities provide a forward-compatible abstraction that works - * with or without jQuery. + * WordPress core's heartbeat.js triggers events via jQuery. These utilities + * also listen for a native CustomEvent, which only the unit tests dispatch. * * @package Presence_API */ @@ -35,7 +34,7 @@ export function onHeartbeatTick( callback ) { } // Fallback: if jQuery isn't available, listen for native CustomEvent. - // This path is used in test environments and future jQuery-free WordPress. + // Only the unit tests reach this path, since core's Heartbeat needs jQuery. const nativeCallback = ( event ) => { callback( event.detail ); }; From 33393e2cf947fce7c9a058eb407c94ce5f76f0b8 Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Fri, 23 Oct 2026 12:12:47 -0400 Subject: [PATCH 3/7] docs: correct the hook README and the @private example --- .github/CONTRIBUTING.md | 6 +++--- plugins/presence-api/src/README.md | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 8b6430be..5d03082a 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -145,11 +145,11 @@ JavaScript has no `@since` line to sit under, so `@private` on its own is enough ```js /** - * Builds an avatar stack. + * Creates a tab coordinator shared by the classic scripts and src/. * * @private */ -window.wpPresenceBuildAvatarStack = function ( users, max ) {}; +window.wpPresenceCreateTabCoordinator = function ( key, options ) {}; ``` @@ -167,7 +167,7 @@ Linking late still counts. Release props are assembled when the release goes out ## Releases -Releases are automated by [release-please](https://github.com/googleapis/release-please). Use [Conventional Commits](https://www.conventionalcommits.org/) in the pull request title — release-please reads it to decide the next version and to generate the changelog: +Releases are automated by [release-please](https://github.com/googleapis/release-please). Use [Conventional Commits](https://www.conventionalcommits.org/) in the pull request title, which release-please reads to decide the next version and to generate the changelog: - `feat: ...` → minor bump - `fix: ...` → patch bump diff --git a/plugins/presence-api/src/README.md b/plugins/presence-api/src/README.md index f64cf86b..75879eaf 100644 --- a/plugins/presence-api/src/README.md +++ b/plugins/presence-api/src/README.md @@ -80,7 +80,7 @@ Presence room id (e.g. `postType/post:123`). When `null` or `undefined`, no requ Returns an object with: - `isPresent` `boolean` - Whether any users are present in the room. -- `isLoading` `boolean` - True until the first fetch completes. Use this to avoid flickering empty states. +- `isLoading` `boolean` - True until the first fetch completes, or false at once with no `room` or no Heartbeat. Use this to avoid flickering empty states. - `users` `Array` - Array of user objects, each containing: - `id` `number` - The user ID. - `displayName` `string` - The user's display name. @@ -112,8 +112,7 @@ Example live region pattern: ### Notes -- The hook automatically subscribes to WordPress Heartbeat's `heartbeat-tick` event to avoid duplicating network traffic. +- The hook polls on WordPress Heartbeat's `heartbeat-tick`, so it follows Heartbeat's schedule. - Deduplicates users by ID. - Prevents race conditions: if a heartbeat tick fires while a fetch is in progress, the second request is skipped. - Coalesces polling across tabs and instances sharing a room and `fields`: one visible tab per site polls (via Web Locks, where available) and shares results with the rest over `BroadcastChannel`. -- In development mode, logs warnings and errors to the console for debugging. From 52b3ab05657427e74207aef119882308c6af5873 Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Fri, 23 Oct 2026 12:13:29 -0400 Subject: [PATCH 4/7] docs: document the watchingRoom and collaboration JavaScript actions --- plugins/presence-api/assets/js/presence-ping.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/plugins/presence-api/assets/js/presence-ping.js b/plugins/presence-api/assets/js/presence-ping.js index 86b7541c..3983c245 100644 --- a/plugins/presence-api/assets/js/presence-ping.js +++ b/plugins/presence-api/assets/js/presence-ping.js @@ -29,9 +29,14 @@ ? window.location.search : ''; - // Fired synchronously, ahead of Heartbeat's first tick, so a listener can - // tell "presence-api isn't here" apart from "here, no tick yet." if ( editorRoom ) { + /** + * Fires once, ahead of Heartbeat's first tick, so a listener can tell "presence-api isn't here" apart from "here, no tick yet". + * + * @since 0.6.0 + * + * @param {string} room The editor's room. + */ wp.hooks.doAction( 'presence-api.watchingRoom', editorRoom ); } @@ -376,6 +381,14 @@ } hasCollaborators = nowHasCollaborators; + /** + * Fires `presence-api.collaborationStarted` when a second person joins the editor's room, and `presence-api.collaborationEnded` when only one is left. + * + * @since 0.6.0 + * + * @param {string} room The editor's room. + * @param {number} count How many people are in the room. + */ wp.hooks.doAction( nowHasCollaborators ? 'presence-api.collaborationStarted' From eeb9330055af2de2f62b83278a73c3ca8c06b1be Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Fri, 23 Oct 2026 12:13:47 -0400 Subject: [PATCH 5/7] docs: tighten the recording note and the new JavaScript action docblocks --- plugins/presence-api/assets/js/presence-ping.js | 4 ++-- plugins/presence-api/includes/presence.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/presence-api/assets/js/presence-ping.js b/plugins/presence-api/assets/js/presence-ping.js index 3983c245..f57dbf62 100644 --- a/plugins/presence-api/assets/js/presence-ping.js +++ b/plugins/presence-api/assets/js/presence-ping.js @@ -31,7 +31,7 @@ if ( editorRoom ) { /** - * Fires once, ahead of Heartbeat's first tick, so a listener can tell "presence-api isn't here" apart from "here, no tick yet". + * Fires synchronously, ahead of Heartbeat's first tick, so a listener can tell "presence-api isn't here" apart from "here, no tick yet". * * @since 0.6.0 * @@ -387,7 +387,7 @@ * @since 0.6.0 * * @param {string} room The editor's room. - * @param {number} count How many people are in the room. + * @param {number} count The room's collaborator count. */ wp.hooks.doAction( nowHasCollaborators diff --git a/plugins/presence-api/includes/presence.php b/plugins/presence-api/includes/presence.php index 8ac143db..ea3d1405 100644 --- a/plugins/presence-api/includes/presence.php +++ b/plugins/presence-api/includes/presence.php @@ -383,8 +383,8 @@ function wp_presence_with_current_user( $entries ) { * * The controller-level switch, checked at the single write path. Nothing new is * stored while this is false and every surface empties as the rows already - * there expire, within one TTL for Heartbeat rows and wp_presence_max_expires_in() - * at most, so there is no separate teardown to run. + * there expire, within one TTL for Heartbeat rows and at most + * wp_presence_max_expires_in() for the rest, so there is no teardown to run. * * Recording is on by default. Presence is a negative signal, and the post lock * bridge is where its absence stops two people overwriting each other, so the From eafabe6d1d66b2c83f70c90bcdca4cacb8f521e5 Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Fri, 23 Oct 2026 12:14:04 -0400 Subject: [PATCH 6/7] docs: trim the new JavaScript action docblocks --- plugins/presence-api/assets/js/presence-ping.js | 4 ++-- .../rest-api/endpoints/class-wp-rest-presence-controller.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/presence-api/assets/js/presence-ping.js b/plugins/presence-api/assets/js/presence-ping.js index f57dbf62..6650be9f 100644 --- a/plugins/presence-api/assets/js/presence-ping.js +++ b/plugins/presence-api/assets/js/presence-ping.js @@ -31,7 +31,7 @@ if ( editorRoom ) { /** - * Fires synchronously, ahead of Heartbeat's first tick, so a listener can tell "presence-api isn't here" apart from "here, no tick yet". + * Fires synchronously, before Heartbeat's first tick, so a listener can tell "presence-api isn't here" from "here, no tick yet". * * @since 0.6.0 * @@ -382,7 +382,7 @@ hasCollaborators = nowHasCollaborators; /** - * Fires `presence-api.collaborationStarted` when a second person joins the editor's room, and `presence-api.collaborationEnded` when only one is left. + * Fires `presence-api.collaborationStarted` when a second person joins the editor's room, and `presence-api.collaborationEnded` when one is left. * * @since 0.6.0 * diff --git a/plugins/presence-api/includes/rest-api/endpoints/class-wp-rest-presence-controller.php b/plugins/presence-api/includes/rest-api/endpoints/class-wp-rest-presence-controller.php index 1250fffd..831d81b3 100644 --- a/plugins/presence-api/includes/rest-api/endpoints/class-wp-rest-presence-controller.php +++ b/plugins/presence-api/includes/rest-api/endpoints/class-wp-rest-presence-controller.php @@ -622,7 +622,7 @@ public function delete_item_permissions_check( $request ) { ) ); - // Entry doesn't exist, so allow the delete, which removes nothing. + // Entry doesn't exist; allow the delete, which removes nothing. if ( null === $entry_user_id ) { return true; } From a9f5a1aecb9bf5bae70d01278845e876c8293d15 Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Fri, 23 Oct 2026 12:35:42 -0400 Subject: [PATCH 7/7] docs: say what keeps a hidden tab's lock alive, and where network screen revisions go --- plugins/presence-api/assets/js/presence-ping.js | 6 +++--- plugins/presence-api/includes/screen-revisions.php | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/presence-api/assets/js/presence-ping.js b/plugins/presence-api/assets/js/presence-ping.js index 6650be9f..5fddf1f6 100644 --- a/plugins/presence-api/assets/js/presence-ping.js +++ b/plugins/presence-api/assets/js/presence-ping.js @@ -245,8 +245,8 @@ // window, app switched away) so the existing entries expire via // the default TTL. One early-return suppresses both presence-ping // and presence-editor-ping, since the consolidated handler emits - // both. Core's lock refresh goes too, or the post-lock bridge - // would keep the hidden tab's lock and editor row alive. + // both. Core's lock refresh goes too, or core would keep the hidden + // tab's lock alive and the post-lock bridge its editor row. if ( document.visibilityState === 'hidden' ) { delete data[ 'wp-refresh-post-lock' ]; return; @@ -387,7 +387,7 @@ * @since 0.6.0 * * @param {string} room The editor's room. - * @param {number} count The room's collaborator count. + * @param {number} count How many editors are in the room, you included. */ wp.hooks.doAction( nowHasCollaborators diff --git a/plugins/presence-api/includes/screen-revisions.php b/plugins/presence-api/includes/screen-revisions.php index 359762cc..5a02a148 100644 --- a/plugins/presence-api/includes/screen-revisions.php +++ b/plugins/presence-api/includes/screen-revisions.php @@ -41,10 +41,10 @@ * * Only screen keys that don't map to a post, user, term, comment, a known * Settings page or a network screen land here; see - * wp_presence_parse_screen_key_target(). Those - * write straight to the object they describe instead of this shared option, - * so a save on one screen doesn't invalidate the cache entry for every other - * screen in the map. + * wp_presence_parse_screen_key_target(). The rest write to the object they + * describe, or network screens to a site option, instead of this shared + * option, so a save on one screen doesn't invalidate the cache entry for every + * other screen in the map. * * @since 0.1.3 *